summaryrefslogtreecommitdiff
path: root/source/slang/emit.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-03-19 12:45:23 -0700
committerGitHub <noreply@github.com>2018-03-19 12:45:23 -0700
commit72562144254145612c68534c6b7457764c28acf5 (patch)
tree1d33abbe3ccf6321b80e2870544b852395a4633c /source/slang/emit.cpp
parent04c9476622cc22daa0994c8141f686405858c8a7 (diff)
Entry point attribute (#447)
* Typo * Add [shader(...)] and clean up some literal handling * Add supporting for validating the `[shader(...)]` attribute, by checking that its argument is a string literal that names a known shader stage. * Split the `ConstantExpr` class into distinct subclasses rooted at `LiteralExpr`, so we have `BoolLiteralExpr`, `IntegerLiteralExpr`, `FloatingPointLiteralExpr`, and `StringLiteralExpr` * Add a `String` type to the stdlib, to be used as the type of a string literal. This change allows code using `[shader(...)]` to be accepted by the front-end again, but it does nothing about emitting it in final HLSL. * Allow entry points to be specified via [shader(...)] Before this change, the compiler would track a list of `EntryPointRequest` objects, based on what the suer specified via API and/or command-line options. Each entry point request would get matched up with an AST `FuncDecl` as part of semantic checking, and then the back end steps (layout, codegen, etc.) would work from that information. This change makes the compiler modal, in that it can *either* continue to use an explicit list of entry point requests (this is the mode when the list is non-empty), or it can rely on user-supplied attributes on entry point functions to drive codegen (this is the mode when the list is empty). User-specified `[shader(...)]` attributes are processed at the same place where the association from `EntryPointRequest`s to `FuncDecl`s would otherwise be made, and basically does the same thing in the opposite direction: looks for `FuncDecl`s with the appropriate attribute and synthesizes an `EntryPointRequest` for them. Subsequent processing should ideally not know where a given `EntryPointRequest` came from, and should handle both methods of specifying the entry points equivalently. One design choice that might not make immediate sense is that we do *not* process a function as an entry point (applying further validation, etc.) just because it has a `[shader(...)]` modifier, unless we are in the appropriate mode (which in this case is the mode where the user didn't specify their own entry points via API or command line). This is to handle cases where the user wants to explicitly compile only one entry point, so that they (1) don't want us to spend time validating code they don't care about, (2) don't want do get output they don't expect, and (3) might actually be presenting us with code that violates the language rules due to a combination of `#define`s in effect (e.g., they might have a `[shader("vertex")]` function that transitively executes a `discard` because of how the preprocessor was configured, but they don't care because they are compiling a fragment entry point). This decision might be something we revisit over time. As part of this work, I had to add some logic to pick a "profile version" to use for a combination of a target and stage (because when you specify `[shader("vertex")]` the compiler can't tell if you want `vs_5_0`, `vs_5_1`, etc.). This isn't really complete right now, because something like `-target dxbc` *also* doesn't determine a profile, so there is a bit of a kludge at present. We need to figure out a good long-term plan here, which might involve keeping target format, feature level/version, and pipeline stage as truly orthogonal concepts, rather than conflating them. That would involve more work in the API and command-line layers to de-compose things when the user specifies, e.g., `vs_5_1`, but might make downstream logic easier to manage. * Emit [shader(...)] attribute on entry point for SM 6.1 and later This should help ensure that the output from Slang can be compiled with dxc `lib_*` profiles. * Fix warning
Diffstat (limited to 'source/slang/emit.cpp')
-rw-r--r--source/slang/emit.cpp100
1 files changed, 96 insertions, 4 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index aee19018a..ace8a1559 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -124,6 +124,10 @@ struct SharedEmitContext
HashSet<String> irDeclsVisited;
HashSet<String> irTupleTypes;
+
+ // The "effective" profile that is being used to emit code,
+ // combining information from the target and entry point.
+ Profile effctiveProfile;
};
struct EmitContext
@@ -134,6 +138,7 @@ struct EmitContext
//
+#ifdef DEADCODE
void requireGLSLVersion(
EntryPointRequest* entryPoint,
ProfileVersion version)
@@ -155,6 +160,7 @@ void requireGLSLVersion(
entryPoint->profile = profile;
}
}
+#endif
//
@@ -2011,8 +2017,7 @@ struct EmitVisitor
if (context->shared->target != CodeGenTarget::GLSL)
return;
- auto entryPoint = context->shared->entryPoint;
- Slang::requireGLSLVersion(entryPoint, version);
+ Slang::requireGLSLVersionImpl(&context->shared->extensionUsageTracker, version);
}
void requireGLSLVersion(int version)
@@ -2083,12 +2088,14 @@ struct EmitVisitor
requireGLSLExtension(requiredGLSLExtensionModifier->extensionNameToken.Content);
}
+#ifdef DEADCODE
// Does this intrinsic requie a particular GLSL extension that wouldn't be available by default?
if (auto requiredGLSLVersionModifier = funcDecl->FindModifier<RequiredGLSLVersionModifier>())
{
// If so, we had better request the extension.
requireGLSLVersion((int) getIntegerLiteralValue(requiredGLSLVersionModifier->versionNumberToken));
}
+#endif
}
@@ -2673,8 +2680,15 @@ struct EmitVisitor
ExprVisitorWithArg::dispatch(derefExpr->base, arg);
}
- void visitConstantExpr(ConstantExpr* litExpr, ExprEmitArg const& arg)
+ void visitLiteralExpr(LiteralExpr*, ExprEmitArg const&)
{
+ // Disabling because we no longer use the AST-based emit path.
+ //
+ // Note: I'm keeping this code around for a while just in case
+ // we want to borrow any of the logic here for how to apply
+ // suffixes to numeric literals we emit.
+ //
+#if 0
auto outerPrec = arg.outerPrec;
bool needClose = MaybeEmitParens(outerPrec, kEOp_Atomic);
@@ -2736,6 +2750,7 @@ struct EmitVisitor
break;
}
if(needClose) Emit(")");
+#endif
}
void visitTypeCastExpr(TypeCastExpr* castExpr, ExprEmitArg const& arg)
@@ -4301,8 +4316,9 @@ struct EmitVisitor
}
void emitGLSLVersionDirective(
- ModuleDecl* program)
+ ModuleDecl* /*program*/)
{
+#ifdef DEADCODE
// Did the user provide an explicit `#version` directive in their code?
if( auto versionDirective = program->FindModifier<GLSLVersionDirective>() )
{
@@ -4350,6 +4366,48 @@ struct EmitVisitor
break;
}
}
+#endif
+
+ auto effctiveProfile = context->shared->effctiveProfile;
+ if(effctiveProfile.getFamily() == ProfileFamily::GLSL)
+ {
+ requireGLSLVersion(effctiveProfile.GetVersion());
+ }
+
+ // HACK: We aren't picking GLSL versions carefully right now,
+ // and so we might end up only requiring the initial 1.10 version,
+ // even though even basic functionality needs a higher version.
+ //
+ // For now, we'll work around this by just setting the minimum required
+ // version to a high one:
+ //
+ // TODO: Either correctly compute a minimum required version, or require
+ // the user to specify a version as part of the target.
+ requireGLSLVersionImpl(&context->shared->extensionUsageTracker, ProfileVersion::GLSL_450);
+
+ auto requiredProfileVersion = context->shared->extensionUsageTracker.profileVersion;
+ switch (requiredProfileVersion)
+ {
+#define CASE(TAG, VALUE) \
+ case ProfileVersion::TAG: Emit("#version " #VALUE "\n"); return
+
+ CASE(GLSL_110, 110);
+ CASE(GLSL_120, 120);
+ CASE(GLSL_130, 130);
+ CASE(GLSL_140, 140);
+ CASE(GLSL_150, 150);
+ CASE(GLSL_330, 330);
+ CASE(GLSL_400, 400);
+ CASE(GLSL_410, 410);
+ CASE(GLSL_420, 420);
+ CASE(GLSL_430, 430);
+ CASE(GLSL_440, 440);
+ CASE(GLSL_450, 450);
+#undef CASE
+
+ default:
+ break;
+ }
// No information is available for us to guess a profile,
// so it seems like we need to pick one out of thin air.
@@ -6694,6 +6752,32 @@ emitDeclImpl(decl, nullptr);
auto profile = entryPointLayout->profile;
auto stage = profile.GetStage();
+ if(profile.getFamily() == ProfileFamily::DX)
+ {
+ if(profile.GetVersion() >= ProfileVersion::DX_6_1 )
+ {
+ char const* stageName = nullptr;
+ switch(stage)
+ {
+ case Stage::Compute: stageName = "compute";
+ case Stage::Vertex: stageName = "vertex";
+ case Stage::Hull: stageName = "hull";
+ case Stage::Domain: stageName = "domain";
+ case Stage::Geometry: stageName = "geometry";
+ case Stage::Fragment: stageName = "pixel";
+ default:
+ break;
+ }
+
+ if(stageName)
+ {
+ emit("[shader(\"");
+ emit(stageName);
+ emit("\")]");
+ }
+ }
+ }
+
switch (stage)
{
case Stage::Compute:
@@ -8061,6 +8145,13 @@ EntryPointLayout* findEntryPointLayout(
if(entryPointLayout->entryPoint->getName() != entryPointRequest->name)
continue;
+ // TODO: We need to be careful about this check, since it relies on
+ // the profile information in the layout matching that in the request.
+ //
+ // What we really seem to want here is some dictionary mapping the
+ // `EntryPointRequest` directly to the `EntryPointLayout`, and maybe
+ // that is precisely what we should build...
+ //
if(entryPointLayout->profile != entryPointRequest->profile)
continue;
@@ -8139,6 +8230,7 @@ String emitEntryPoint(
sharedContext.target = target;
sharedContext.finalTarget = targetRequest->target;
sharedContext.entryPoint = entryPoint;
+ sharedContext.effctiveProfile = getEffectiveProfile(entryPoint, targetRequest);
if (entryPoint)
{