diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/check.cpp | 281 | ||||
| -rw-r--r-- | source/slang/compiler.cpp | 21 | ||||
| -rw-r--r-- | source/slang/compiler.h | 41 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 4 | ||||
| -rw-r--r-- | source/slang/core.meta.slang.h | 4 | ||||
| -rw-r--r-- | source/slang/diagnostic-defs.h | 8 | ||||
| -rw-r--r-- | source/slang/dxc-support.cpp | 3 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 100 | ||||
| -rw-r--r-- | source/slang/expr-defs.h | 41 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 29 | ||||
| -rw-r--r-- | source/slang/parameter-binding.cpp | 10 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 24 | ||||
| -rw-r--r-- | source/slang/profile.h | 9 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 53 | ||||
| -rw-r--r-- | source/slang/syntax.cpp | 6 | ||||
| -rw-r--r-- | source/slang/type-defs.h | 3 |
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) |
