summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/check.cpp281
-rw-r--r--source/slang/compiler.cpp21
-rw-r--r--source/slang/compiler.h41
-rw-r--r--source/slang/core.meta.slang4
-rw-r--r--source/slang/core.meta.slang.h4
-rw-r--r--source/slang/diagnostic-defs.h8
-rw-r--r--source/slang/dxc-support.cpp3
-rw-r--r--source/slang/emit.cpp100
-rw-r--r--source/slang/expr-defs.h41
-rw-r--r--source/slang/lower-to-ir.cpp29
-rw-r--r--source/slang/parameter-binding.cpp10
-rw-r--r--source/slang/parser.cpp24
-rw-r--r--source/slang/profile.h9
-rw-r--r--source/slang/slang.cpp53
-rw-r--r--source/slang/syntax.cpp6
-rw-r--r--source/slang/type-defs.h3
16 files changed, 535 insertions, 102 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 1af91e9fd..070d4c606 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -1407,6 +1407,30 @@ namespace Slang
return constIntVal;
}
+ // Check an expression, coerce it to the `String` type, and then
+ // ensure that it has a literal (not just compile-time constant) value.
+ bool checkLiteralStringVal(
+ RefPtr<Expr> expr,
+ String* outVal)
+ {
+ // TODO: This should actually perform semantic checking, etc.,
+ // but for now we are just going to look for a direct string
+ // literal AST node.
+
+ if(auto stringLitExpr = expr.As<StringLiteralExpr>())
+ {
+ if(outVal)
+ {
+ *outVal = stringLitExpr->value;
+ }
+ return true;
+ }
+
+ getSink()->diagnose(expr, Diagnostics::expectedAStringLiteral);
+
+ return false;
+ }
+
void visitSyntaxDecl(SyntaxDecl*)
{
// These are only used in the stdlib, so no checking is needed
@@ -1456,6 +1480,36 @@ namespace Slang
}
}
+ Stage findStageByName(String const& name)
+ {
+ static const struct
+ {
+ char const* name;
+ Stage stage;
+ } kStages[] =
+ {
+ { "vertex", Stage::Vertex },
+ { "hull", Stage::Hull },
+ { "domain", Stage::Domain },
+ { "geometry", Stage::Geometry },
+ { "fragment", Stage::Fragment },
+ { "compute", Stage::Compute },
+
+ // Allow `pixel` as an alias of `fragment`
+ { "pixel", Stage::Fragment },
+ };
+
+ for(auto entry : kStages)
+ {
+ if(name == entry.name)
+ {
+ return entry.stage;
+ }
+ }
+
+ return Stage::Unknown;
+ }
+
bool validateAttribute(RefPtr<Attribute> attr)
{
if(auto numThreadsAttr = attr.As<NumThreadsAttribute>())
@@ -1487,6 +1541,24 @@ namespace Slang
instanceAttr->value = (int32_t)val->value;
}
+ else if(auto entryPointAttr = attr.As<EntryPointAttribute>())
+ {
+ SLANG_ASSERT(attr->args.Count() == 1);
+
+ String stageName;
+ if(!checkLiteralStringVal(attr->args[0], &stageName))
+ {
+ return false;
+ }
+
+ auto stage = findStageByName(stageName);
+ if(stage == Stage::Unknown)
+ {
+ getSink()->diagnose(attr->args[0], Diagnostics::unknownStageName, stageName);
+ }
+
+ entryPointAttr->stage = stage;
+ }
else
{
if(attr->args.Count() == 0)
@@ -3172,35 +3244,51 @@ namespace Slang
stmt->Expression = CheckExpr(stmt->Expression);
}
- RefPtr<Expr> visitConstantExpr(ConstantExpr *expr)
+ RefPtr<Expr> visitBoolLiteralExpr(BoolLiteralExpr* expr)
{
- // The expression might already have a type, determined by its suffix
- if(expr->type.type)
- return expr;
+ expr->type = getSession()->getBoolType();
+ return expr;
+ }
- switch (expr->ConstType)
+ RefPtr<Expr> visitIntegerLiteralExpr(IntegerLiteralExpr* expr)
+ {
+ // The expression might already have a type, determined by its suffix.
+ // It it doesn't, we will give it a default type.
+ //
+ // TODO: We should be careful to pick a "big enough" type
+ // based on the size of the value (e.g., don't try to stuff
+ // a constant in an `int` if it requires 64 or more bits).
+ //
+ // The long-term solution here is to give a type to a literal
+ // based on the context where it is used, but that requires
+ // a more sophisticated type system than we have today.
+ //
+ if(!expr->type.type)
{
- case ConstantExpr::ConstantType::Int:
expr->type = getSession()->getIntType();
- break;
- case ConstantExpr::ConstantType::Bool:
- expr->type = getSession()->getBoolType();
- break;
- case ConstantExpr::ConstantType::Float:
+ }
+ return expr;
+ }
+
+ RefPtr<Expr> visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr)
+ {
+ if(!expr->type.type)
+ {
expr->type = getSession()->getFloatType();
- break;
- default:
- expr->type = QualType(getSession()->getErrorType());
- throw "Invalid constant type.";
- break;
}
return expr;
}
- IntVal* GetIntVal(ConstantExpr* expr)
+ RefPtr<Expr> visitStringLiteralExpr(StringLiteralExpr* expr)
+ {
+ expr->type = getSession()->getStringType();
+ return expr;
+ }
+
+ IntVal* GetIntVal(IntegerLiteralExpr* expr)
{
// TODO(tfoley): don't keep allocating here!
- return new ConstantIntVal(expr->integerValue);
+ return new ConstantIntVal(expr->value);
}
Name* getName(String const& text)
@@ -3339,9 +3427,9 @@ namespace Slang
}
// TODO(tfoley): more serious constant folding here
- if (auto constExp = dynamic_cast<ConstantExpr*>(expr))
+ if (auto intLitExpr = dynamic_cast<IntegerLiteralExpr*>(expr))
{
- return GetIntVal(constExp);
+ return GetIntVal(intLitExpr);
}
// it is possible that we are referring to a generic value param
@@ -6898,7 +6986,46 @@ namespace Slang
return type;
}
+ // Validate that an entry point function conforms to any additional
+ // constraints based on the stage (and profile?) it specifies.
void validateEntryPoint(
+ EntryPointRequest* /*entryPoint*/)
+ {
+ // TODO: We currently don't do any checking here, but this is the
+ // right place to perform the following validation checks:
+ //
+ // * Does the entry point specify all of the attributes required
+ // by the chosen stage (e.g., a `[domain(...)]` attribute for]
+ // a hull shader.
+ //
+ // * Are the function input/output parameters and result type
+ // all valid for the chosen stage? (e.g., there shouldn't be
+ // an `OutputStream<X>` type in a vertex shader signature)
+ //
+ // * For any varying input/output, are there semantics specified
+ // (Note: this potentially overlaps with layout logic...), and
+ // are the system-value semantics valid for the given stage?
+ //
+ // There's actually a lot of detail to semantic checking, in
+ // that the AST-level code should probably be validating the
+ // use of system-value semantics by linking them to explicit
+ // declarations in the standard library. We should also be
+ // using profile information on those declarations to infer
+ // appropriate profile restrictions on the entry point.
+ //
+ // * Is the entry point actually usable on the given stage/profile?
+ // E.g., if we have a vertex shader that (transitively) calls
+ // `Texture2D.Sample`, then that should produce an error because
+ // that function is specific to the fragment profile/stage.
+ //
+ }
+
+ // Given an `EntryPointRequest` specified via API or command line options,
+ // attempt to find a matching AST declaration that implements the specified
+ // entry point. If such a function is found, then validate that it actually
+ // meets the requirements for the selected stage/profile.
+ //
+ void findAndValidateEntryPoint(
EntryPointRequest* entryPoint)
{
// The first step in validating the entry point is to find
@@ -7070,10 +7197,116 @@ namespace Slang
}
if (sink->errorCount != 0)
return;
- // TODO: after all that work, we are now in a position to start
- // validating the declaration itself. E.g., we should check if
- // the declared input/output parameters have suitable semantics,
- // if they are of types that are appropriate to the stage, etc.
+
+ // Now that we've *found* the entry point, it is time to validate
+ // that it actually meets the constraints for the chosen stage/profile.
+ validateEntryPoint(entryPoint);
+ }
+
+ void validateEntryPoints(
+ CompileRequest* compileRequest)
+ {
+ // The validation of entry points here will be modal, and controlled
+ // by whether the user specified any entry points directly via
+ // API or command-line options.
+ //
+ // TODO: We may want to make this choice explicit rather than implicit.
+ //
+ // First, check if the user request any entry points explicitly via
+ // the API or command line.
+ //
+ bool anyExplicitEntryPointRequests = false;
+ for (auto& translationUnit : compileRequest->translationUnits)
+ {
+ if( translationUnit->entryPoints.Count() != 0)
+ {
+ anyExplicitEntryPointRequests = true;
+ break;
+ }
+ }
+
+ if( anyExplicitEntryPointRequests )
+ {
+ // If there were any explicit requests for entry points to be
+ // checked, then we will *only* check those.
+
+ for (auto& translationUnit : compileRequest->translationUnits)
+ {
+ for (auto entryPoint : translationUnit->entryPoints)
+ {
+ findAndValidateEntryPoint(entryPoint);
+ }
+ }
+ }
+ else
+ {
+ // Otherwise, scan for any `[shader(...)]` attributes in
+ // the user's code, and construct `EntryPointRequest`s to
+ // represent them.
+ //
+ // This ensures that downstream code only has to consider
+ // the central list of entry point requests, and doesn't
+ // have to know where they came from.
+
+ // TODO: A comprehensive approach here would need to search
+ // recursively for entry points, because they might appear
+ // as, e.g., member function of a `struct` type.
+ //
+ // For now we'll start with an extremely basic approach that
+ // should work for typical HLSL code.
+ //
+ UInt translationUnitCount = compileRequest->translationUnits.Count();
+ for(UInt tt = 0; tt < translationUnitCount; ++tt)
+ {
+ auto translationUnit = compileRequest->translationUnits[tt];
+ for( auto globalDecl : translationUnit->SyntaxNode->Members )
+ {
+ auto maybeFuncDecl = globalDecl;
+ if( auto genericDecl = maybeFuncDecl->As<GenericDecl>() )
+ {
+ maybeFuncDecl = genericDecl->inner;
+ }
+
+ auto funcDecl = maybeFuncDecl->As<FuncDecl>();
+ if(!funcDecl)
+ continue;
+
+ auto entryPointAttr = funcDecl->FindModifier<EntryPointAttribute>();
+ if(!entryPointAttr)
+ continue;
+
+ // We've discovered a valid entry point. It is a function (possibly
+ // generic) that has a `[shader(...)]` attribute to mark it as an
+ // entry point.
+ //
+ // We will now register that entry point as an `EntryPointRequest`
+ // with an appropriately chosen profile.
+ //
+ // The profile will only include a stage, so that the profile "family"
+ // and "version" are left unspecified. Downstream code will need
+ // to be able to handle this case.
+ //
+ Profile profile;
+ profile.setStage(entryPointAttr->stage);
+
+ // We manually fill in the entry point request object.
+ RefPtr<EntryPointRequest> entryPointReq = new EntryPointRequest();
+ entryPointReq->compileRequest = compileRequest;
+ entryPointReq->translationUnitIndex = int(tt);
+ entryPointReq->decl = funcDecl;
+ entryPointReq->name = funcDecl->getName();
+ entryPointReq->profile = profile;
+
+ // Apply the common validation logic to this entry point.
+ validateEntryPoint(entryPointReq);
+
+ // Add the entry point to the list in the translation unit,
+ // and also the global list in the compile request.
+ translationUnit->entryPoints.Add(entryPointReq);
+ compileRequest->entryPoints.Add(entryPointReq);
+ }
+ }
+ }
}
void checkTranslationUnit(
diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp
index acbf51e2e..b7cbc2327 100644
--- a/source/slang/compiler.cpp
+++ b/source/slang/compiler.cpp
@@ -213,6 +213,21 @@ namespace Slang
char const* GetHLSLProfileName(Profile profile)
{
+ switch( profile.getFamily() )
+ {
+ case ProfileFamily::DX:
+ // Profile version is a DX one, so stick with it.
+ break;
+
+ default:
+ // Profile is a non-DX profile family, so we need to try
+ // to clobber it with something to get a default.
+ //
+ // TODO: This is a huge hack...
+ profile.setVersion(ProfileVersion::DX_5_0);
+ break;
+ }
+
switch(profile.raw)
{
#define PROFILE(TAG, NAME, STAGE, VERSION) case Profile::TAG: return #NAME;
@@ -262,6 +277,8 @@ namespace Slang
auto hlslCode = emitHLSLForEntryPoint(entryPoint, targetReq);
maybeDumpIntermediate(entryPoint->compileRequest, hlslCode.Buffer(), CodeGenTarget::HLSL);
+ auto profile = getEffectiveProfile(entryPoint, targetReq);
+
ID3DBlob* codeBlob;
ID3DBlob* diagnosticsBlob;
HRESULT hr = D3DCompile_(
@@ -271,7 +288,7 @@ namespace Slang
nullptr,
nullptr,
getText(entryPoint->name).begin(),
- GetHLSLProfileName(entryPoint->profile),
+ GetHLSLProfileName(profile),
0,
0,
&codeBlob,
@@ -485,7 +502,7 @@ String dissassembleDXILUsingDXC(
glslang_CompileRequest request;
request.action = GLSLANG_ACTION_COMPILE_GLSL_TO_SPIRV;
request.sourcePath = "slang";
- request.slangStage = (SlangStage)entryPoint->profile.GetStage();
+ request.slangStage = (SlangStage)entryPoint->getStage();
request.inputBegin = rawGLSL.begin();
request.inputEnd = rawGLSL.end();
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index 6d0475a05..7ab47e6b3 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -107,10 +107,20 @@ namespace Slang
List<String> genericParameterTypeNames;
// The profile that the entry point will be compiled for
- // (this is a combination of the target state, and also
+ // (this is a combination of the target stage, and also
// a feature level that sets capabilities)
+ //
+ // Note: the profile-version part of this should probably
+ // be moving towards deprecation, in favor of the version
+ // information (e.g., "Shader Model 5.1") always coming
+ // from the target, while the stage part is all that is
+ // intrinsic to the entry point.
+ //
Profile profile;
+ // Get the stage that the entry point is being compiled for.
+ Stage getStage() { return profile.GetStage(); }
+
// The index of the translation unit (within the parent
// compile request) that the entry point function is
// supposed to be defined in.
@@ -118,6 +128,19 @@ namespace Slang
// The output path requested for this entry point.
// (only used when compiling from the command line)
+ //
+ // TODO: This should get dropped. When compiling from the
+ // command line, the user should be either:
+ //
+ // * Compiling a single entry point for a single target, so
+ // that only a single output path is needed for the whole request.
+ //
+ // * Compiling for a target that supports multiple entry points directly
+ // (e.g., a recent DXIL version), so that only one output file is needed.
+ //
+ // * Compiling to a slang module container, so that as many entry
+ // points and targets as needed can be specified.
+ //
String outputPath;
// The translation unit that this entry point came from
@@ -203,6 +226,21 @@ namespace Slang
Dictionary<Type*, RefPtr<TypeLayout>> typeLayouts;
};
+ // Compute the "effective" profile to use when outputting the given entry point
+ // for the chosen code-generation target.
+ //
+ // The stage of the effective profile will always come from the entry point, while
+ // the profile version (aka "shader model") will be computed as follows:
+ //
+ // - If the entry point and target belong to the same profile family, then take
+ // the latest version between the two (e.g., if the entry point specified `ps_5_1`
+ // and the target specifies `sm_5_0` then use `sm_5_1` as the version).
+ //
+ // - If the entry point and target disagree on the profile family, always use the
+ // profile family and version from the target.
+ //
+ Profile getEffectiveProfile(EntryPointRequest* entryPoint, TargetRequest* target);
+
// A directory to be searched when looking for files (e.g., `#include`)
struct SearchDirectory
{
@@ -468,6 +506,7 @@ namespace Slang
Type* getInitializerListType();
Type* getOverloadedType();
Type* getErrorType();
+ Type* getStringType();
Type* getConstExprRate();
RefPtr<RateQualifiedType> getRateQualifiedType(
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index f105b98aa..785ef4406 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -114,6 +114,10 @@ __magic_type(InOutType)
struct InOut
{};
+__magic_type(StringType)
+struct String
+{};
+
${{{{
// Declare vector and matrix types
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index 5e1f2ec89..bc0fbb53d 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -114,6 +114,10 @@ SLANG_RAW("__magic_type(InOutType)\n")
SLANG_RAW("struct InOut\n")
SLANG_RAW("{};\n")
SLANG_RAW("\n")
+SLANG_RAW("__magic_type(StringType)\n")
+SLANG_RAW("struct String\n")
+SLANG_RAW("{};\n")
+SLANG_RAW("\n")
// Declare vector and matrix types
diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h
index ae815cd40..e74126f1b 100644
--- a/source/slang/diagnostic-defs.h
+++ b/source/slang/diagnostic-defs.h
@@ -197,11 +197,16 @@ DIAGNOSTIC(30051, Error, invalidValueForArgument, "invalid value for argument '$
DIAGNOSTIC(30052, Error, invalidSwizzleExpr, "invalid swizzle pattern '$0' on type '$1'")
DIAGNOSTIC(33070, Error, expectedFunction, "expression preceding parenthesis of apparent call must have function type.")
+DIAGNOSTIC(33071, Error, expectedAStringLiteral, "expected a string literal")
+
// Attributes
DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'")
DIAGNOSTIC(31001, Error, attributeArgumentCountMismatch, "attribute '$0' expects $1 arguments ($2 provided)")
DIAGNOSTIC(31001, Error, attributeNotApplicable, "attribute '$0' is not valid here")
+DIAGNOSTIC(31100, Error, unknownStageName, "unknown stage name '$0'")
+
+
// 303xx: interfaces and associated types
DIAGNOSTIC(30300, Error, assocTypeInInterfaceOnly, "'associatedtype' can only be defined in an 'interface'.")
DIAGNOSTIC(30301, Error, globalGenParamInGlobalScopeOnly, "'__generic_param' can only be defined global scope.")
@@ -299,7 +304,6 @@ DIAGNOSTIC(40007, Internal, irValidationFailed, "IR validation failed: $0")
//
// 5xxxx - Target code generation.
//
-DIAGNOSTIC(50020, Error, unknownStageType, "Unknown stage type '$0'.")
DIAGNOSTIC(50020, Error, invalidTessCoordType, "TessCoord must have vec2 or vec3 type.")
DIAGNOSTIC(50020, Error, invalidFragCoordType, "FragCoord must be a vec4.")
DIAGNOSTIC(50020, Error, invalidInvocationIdType, "InvocationId must have int type.")
@@ -337,7 +341,7 @@ DIAGNOSTIC(51092, Error, stageDoesntHaveInputWorld, "'$0' doesn't appear to have
// 99999 - Internal compiler errors, and not-yet-classified diagnostics.
DIAGNOSTIC(99999, Internal, unimplemented, "unimplemented: $0")
-DIAGNOSTIC(99999, Internal, unexpected, "uuexpected: $0")
+DIAGNOSTIC(99999, Internal, unexpected, "unexpected: $0")
DIAGNOSTIC(99999, Internal, internalCompilerError, "internal compiler error")
DIAGNOSTIC(99999, Error, compilationAborted, "compilation aborted due to internal error");
diff --git a/source/slang/dxc-support.cpp b/source/slang/dxc-support.cpp
index b18a25e43..056fefe2d 100644
--- a/source/slang/dxc-support.cpp
+++ b/source/slang/dxc-support.cpp
@@ -125,7 +125,8 @@ namespace Slang
String entryPointName = getText(entryPoint->name);
OSString wideEntryPointName = entryPointName.ToWString();
- String profileName = GetHLSLProfileName(entryPoint->profile);
+ auto profile = getEffectiveProfile(entryPoint, targetReq);
+ String profileName = GetHLSLProfileName(profile);
OSString wideProfileName = profileName.ToWString();
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)
{
diff --git a/source/slang/expr-defs.h b/source/slang/expr-defs.h
index 3909c6b63..278196421 100644
--- a/source/slang/expr-defs.h
+++ b/source/slang/expr-defs.h
@@ -42,25 +42,32 @@ SYNTAX_CLASS(OverloadedExpr2, Expr)
FIELD(List<RefPtr<Expr>>, candidiateExprs)
END_SYNTAX_CLASS()
-SYNTAX_CLASS(ConstantExpr, Expr)
+ABSTRACT_SYNTAX_CLASS(LiteralExpr, Expr)
+ // The token that was used to express the literal. This can be
+ // used to get the raw text of the literal, including any suffix.
FIELD(Token, token)
+END_SYNTAX_CLASS()
+
+SYNTAX_CLASS(IntegerLiteralExpr, LiteralExpr)
+ FIELD(IntegerLiteralValue, value)
+END_SYNTAX_CLASS()
+
+SYNTAX_CLASS(FloatingPointLiteralExpr, LiteralExpr)
+ FIELD(FloatingPointLiteralValue, value)
+END_SYNTAX_CLASS()
+
+SYNTAX_CLASS(BoolLiteralExpr, LiteralExpr)
+ FIELD(bool, value)
+END_SYNTAX_CLASS()
- RAW(
- enum class ConstantType
- {
- Int,
- Bool,
- Float,
- String,
- };
- ConstantType ConstType;
- union
- {
- IntegerLiteralValue integerValue;
- FloatingPointLiteralValue floatingPointValue;
- };
- String stringValue;
- )
+SYNTAX_CLASS(StringLiteralExpr, LiteralExpr)
+ // TODO: consider storing the "segments" of the string
+ // literal, in the case where multiple literals were
+ //lined up at the lexer level, e.g.:
+ //
+ // "first" "second" "third"
+ //
+ FIELD(String, value)
END_SYNTAX_CLASS()
// An initializer list, e.g. `{ 1, 2, 3 }`
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 0970eef98..24181c75a 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -1390,23 +1390,26 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
UNREACHABLE_RETURN(LoweredValInfo());
}
- LoweredValInfo visitConstantExpr(ConstantExpr* expr)
+ LoweredValInfo visitBoolLiteralExpr(BoolLiteralExpr* expr)
+ {
+ return LoweredValInfo::simple(context->irBuilder->getBoolValue(expr->value));
+ }
+
+ LoweredValInfo visitIntegerLiteralExpr(IntegerLiteralExpr* expr)
{
auto type = lowerSimpleType(context, expr->type);
+ return LoweredValInfo::simple(context->irBuilder->getIntValue(type, expr->value));
+ }
- switch( expr->ConstType )
- {
- case ConstantExpr::ConstantType::Bool:
- return LoweredValInfo::simple(context->irBuilder->getBoolValue(expr->integerValue != 0));
- case ConstantExpr::ConstantType::Int:
- return LoweredValInfo::simple(context->irBuilder->getIntValue(type, expr->integerValue));
- case ConstantExpr::ConstantType::Float:
- return LoweredValInfo::simple(context->irBuilder->getFloatValue(type, expr->floatingPointValue));
- case ConstantExpr::ConstantType::String:
- break;
- }
+ LoweredValInfo visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr)
+ {
+ auto type = lowerSimpleType(context, expr->type);
+ return LoweredValInfo::simple(context->irBuilder->getFloatValue(type, expr->value));
+ }
- SLANG_UNEXPECTED("unexpected constant type");
+ LoweredValInfo visitStringLiteralExpr(StringLiteralExpr*)
+ {
+ SLANG_UNEXPECTED("string literal encountered during code emit");
}
LoweredValInfo visitAggTypeCtorExpr(AggTypeCtorExpr* /*expr*/)
diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp
index 3ff2be61f..572235280 100644
--- a/source/slang/parameter-binding.cpp
+++ b/source/slang/parameter-binding.cpp
@@ -1922,7 +1922,7 @@ static void collectEntryPointParameters(
state.ioSemanticIndex = &defaultSemanticIndex;
state.optSemanticName = nullptr;
state.semanticSlotCount = 0;
- state.stage = entryPoint->profile.GetStage();
+ state.stage = entryPoint->getStage();
for( auto m : entryPointFuncDecl->Members )
{
@@ -2020,12 +2020,14 @@ inferStageForTranslationUnit(
// and have only a single entry point, use the stage
// of the entry point.
//
- // TODO: can we generalize this at all?
+ // TODO: now that we've dropped official GLSL support,
+ // we probably should drop this as well.
+ //
if( translationUnit->sourceLanguage == SourceLanguage::GLSL )
{
if( translationUnit->entryPoints.Count() == 1 )
{
- return translationUnit->entryPoints[0]->profile.GetStage();
+ return translationUnit->entryPoints[0]->getStage();
}
}
@@ -2080,7 +2082,7 @@ static void collectParameters(
// Next consider parameters for entry points
for( auto& entryPoint : translationUnit->entryPoints )
{
- context->stage = entryPoint->profile.GetStage();
+ context->stage = entryPoint->getStage();
collectEntryPointParameters(context, entryPoint.Ptr(), SubstitutionSet());
}
context->entryPointLayout = nullptr;
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index 5fb7445e9..a3afbe87a 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -3603,10 +3603,9 @@ namespace Slang
static RefPtr<Expr> parseBoolLitExpr(Parser* /*parser*/, bool value)
{
- RefPtr<ConstantExpr> constExpr = new ConstantExpr();
- constExpr->ConstType = ConstantExpr::ConstantType::Bool;
- constExpr->integerValue = value ? 1 : 0;
- return constExpr;
+ RefPtr<BoolLiteralExpr> expr = new BoolLiteralExpr();
+ expr->value = value;
+ return expr;
}
static RefPtr<RefObject> parseTrueExpr(Parser* parser, void* /*userData*/)
@@ -3696,7 +3695,7 @@ namespace Slang
case TokenType::IntegerLiteral:
{
- RefPtr<ConstantExpr> constExpr = new ConstantExpr();
+ RefPtr<IntegerLiteralExpr> constExpr = new IntegerLiteralExpr();
parser->FillPosition(constExpr.Ptr());
auto token = parser->tokenReader.AdvanceToken();
@@ -3756,8 +3755,7 @@ namespace Slang
}
}
- constExpr->ConstType = ConstantExpr::ConstantType::Int;
- constExpr->integerValue = value;
+ constExpr->value = value;
constExpr->type = QualType(suffixType);
return constExpr;
@@ -3766,7 +3764,7 @@ namespace Slang
case TokenType::FloatingPointLiteral:
{
- RefPtr<ConstantExpr> constExpr = new ConstantExpr();
+ RefPtr<FloatingPointLiteralExpr> constExpr = new FloatingPointLiteralExpr();
parser->FillPosition(constExpr.Ptr());
auto token = parser->tokenReader.AdvanceToken();
@@ -3825,8 +3823,7 @@ namespace Slang
}
}
- constExpr->ConstType = ConstantExpr::ConstantType::Float;
- constExpr->floatingPointValue = value;
+ constExpr->value = value;
constExpr->type = QualType(suffixType);
return constExpr;
@@ -3834,16 +3831,15 @@ namespace Slang
case TokenType::StringLiteral:
{
- RefPtr<ConstantExpr> constExpr = new ConstantExpr();
+ RefPtr<StringLiteralExpr> constExpr = new StringLiteralExpr();
auto token = parser->tokenReader.AdvanceToken();
constExpr->token = token;
parser->FillPosition(constExpr.Ptr());
- constExpr->ConstType = ConstantExpr::ConstantType::String;
if (!parser->LookAheadToken(TokenType::StringLiteral))
{
// Easy/common case: a single string
- constExpr->stringValue = getStringLiteralTokenValue(token);
+ constExpr->value = getStringLiteralTokenValue(token);
}
else
{
@@ -3854,7 +3850,7 @@ namespace Slang
token = parser->tokenReader.AdvanceToken();
sb << getStringLiteralTokenValue(token);
}
- constExpr->stringValue = sb.ProduceString();
+ constExpr->value = sb.ProduceString();
}
return constExpr;
diff --git a/source/slang/profile.h b/source/slang/profile.h
index 3b5242ada..d6a2a39af 100644
--- a/source/slang/profile.h
+++ b/source/slang/profile.h
@@ -67,14 +67,19 @@ namespace Slang
bool operator!=(Profile const& other) const { return raw != other.raw; }
Stage GetStage() const { return Stage((uint32_t(raw) >> 16) & 0xFFFF); }
- ProfileVersion GetVersion() const { return ProfileVersion(uint32_t(raw) & 0xFFFF); }
- ProfileFamily getFamily() const { return getProfileFamily(GetVersion()); }
+ void setStage(Stage stage)
+ {
+ raw = (raw & 0x0000FFFF) | (uint32_t(stage) << 16);
+ }
+ ProfileVersion GetVersion() const { return ProfileVersion(uint32_t(raw) & 0xFFFF); }
void setVersion(ProfileVersion version)
{
raw = (raw & ~0xFFFF) | uint32_t(version);
}
+ ProfileFamily getFamily() const { return getProfileFamily(GetVersion()); }
+
static Profile LookUp(char const* name);
RawVal raw = Unknown;
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 1d888dcb5..4fa6f5130 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -112,6 +112,38 @@ struct IncludeHandlerImpl : IncludeHandler
}
};
+//
+
+
+Profile getEffectiveProfile(EntryPointRequest* entryPoint, TargetRequest* target)
+{
+ auto entryPointProfile = entryPoint->profile;
+ auto targetProfile = target->targetProfile;
+
+ auto entryPointProfileVersion = entryPointProfile.GetVersion();
+ auto targetProfileVersion = targetProfile.GetVersion();
+
+ // Default to the entry point profile, since we know that has the right stage.
+ Profile effectiveProfile = entryPointProfile;
+
+ // Ignore the input from the target profile if it is missing.
+ if( targetProfile.getFamily() != ProfileFamily::Unknown )
+ {
+ // If the target comes from a different profile family, *or* it is from
+ // the same family but has a greater version number, then use the target's version.
+ if( targetProfile.getFamily() != entryPointProfile.getFamily()
+ || (targetProfileVersion > entryPointProfileVersion) )
+ {
+ effectiveProfile.setVersion(targetProfileVersion);
+ }
+ }
+
+ return effectiveProfile;
+}
+
+
+//
+
CompileRequest::CompileRequest(Session* session)
: mSession(session)
{
@@ -224,7 +256,7 @@ void CompileRequest::parseTranslationUnit(
}
}
-void validateEntryPoint(EntryPointRequest*);
+void validateEntryPoints(CompileRequest*);
void CompileRequest::checkAllTranslationUnits()
{
@@ -235,23 +267,8 @@ void CompileRequest::checkAllTranslationUnits()
checkTranslationUnit(translationUnit.Ptr());
}
- for (auto& translationUnit : translationUnits)
- {
- // Next, do follow-up validation on any entry
- // points that the user declared via API or
- // command line, to ensure that they meet
- // requirements.
- //
- // Note: We may eventually have syntax to
- // identify entry points via a modifier on
- // declarations, and in this case they should
- // probably get validated as part of orindary
- // checking above.
- for (auto entryPoint : translationUnit->entryPoints)
- {
- validateEntryPoint(entryPoint);
- }
- }
+ // Next, do follow-up validation on any entry points.
+ validateEntryPoints(this);
}
void CompileRequest::generateIR()
diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp
index 88c23f314..4881f301e 100644
--- a/source/slang/syntax.cpp
+++ b/source/slang/syntax.cpp
@@ -296,6 +296,12 @@ void Type::accept(IValVisitor* visitor, void* extra)
return constExprRate;
}
+ Type* Session::getStringType()
+ {
+ auto stringTypeDecl = findMagicDecl(this, "StringType");
+ return DeclRefType::Create(this, makeDeclRef<Decl>(stringTypeDecl));
+ }
+
RefPtr<RateQualifiedType> Session::getRateQualifiedType(
Type* rate,
Type* valueType)
diff --git a/source/slang/type-defs.h b/source/slang/type-defs.h
index 8b61953bc..433c5e15c 100644
--- a/source/slang/type-defs.h
+++ b/source/slang/type-defs.h
@@ -388,6 +388,9 @@ protected:
)
END_SYNTAX_CLASS()
+// The built-in `String` type
+SIMPLE_SYNTAX_CLASS(StringType, DeclRefType)
+
// Base class for types that map down to
// simple pointers as part of code generation.
SYNTAX_CLASS(PtrTypeBase, DeclRefType)