diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/check.cpp | 515 | ||||
| -rw-r--r-- | source/slang/compiler.cpp | 2 | ||||
| -rw-r--r-- | source/slang/compiler.h | 30 | ||||
| -rw-r--r-- | source/slang/parameter-binding.cpp | 644 | ||||
| -rw-r--r-- | source/slang/syntax.h | 14 | ||||
| -rw-r--r-- | source/slang/type-layout.h | 1 |
6 files changed, 608 insertions, 598 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index a9f84c5c3..998324612 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -9306,7 +9306,7 @@ namespace Slang } /// Recursively walk `paramDeclRef` and add any required existential slots to `ioSlots`. - static void _collectExistentialParamsRec( + static void _collectExistentialSlotsRec( ExistentialSlots& ioSlots, DeclRef<VarDeclBase> paramDeclRef) { @@ -9339,7 +9339,7 @@ namespace Slang if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>()) continue; - _collectExistentialParamsRec(ioSlots, fieldDeclRef); + _collectExistentialSlotsRec(ioSlots, fieldDeclRef); } } } @@ -9349,11 +9349,26 @@ namespace Slang // element types. } + /// Add information about a shader parameter to `ioParams` and `ioSlots` + static void _collectExistentialSlotsForShaderParam( + ShaderParamInfo& ioParamInfo, + ExistentialSlots& ioSlots, + DeclRef<VarDeclBase> paramDeclRef) + { + UInt startSlot = ioSlots.types.Count(); + _collectExistentialSlotsRec(ioSlots, paramDeclRef); + UInt endSlot = ioSlots.types.Count(); + UInt slotCount = endSlot - startSlot; + + ioParamInfo.firstExistentialTypeSlot = startSlot; + ioParamInfo.existentialTypeSlotCount = slotCount; + } + /// Enumerate the existential-type parameters of an `EntryPoint`. /// /// Any parameters found will be added to the list of existential slots on `this`. /// - void EntryPoint::_collectExistentialParams() + void EntryPoint::_collectShaderParams() { // Note: we defensively test whether there is a function decl-ref // because this routine gets called from the constructor, and @@ -9363,7 +9378,15 @@ namespace Slang { for( auto paramDeclRef : GetParameters(funcDeclRef) ) { - _collectExistentialParamsRec(m_existentialSlots, paramDeclRef); + ShaderParamInfo shaderParamInfo; + shaderParamInfo.paramDeclRef = paramDeclRef; + + _collectExistentialSlotsForShaderParam( + shaderParamInfo, + m_existentialSlots, + paramDeclRef); + + m_shaderParams.Add(shaderParamInfo); } } } @@ -9644,16 +9667,433 @@ namespace Slang return entryPoint; } + /// Get the name a variable will use for reflection purposes +Name* getReflectionName(VarDeclBase* varDecl) +{ + if (auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>()) + return reflectionNameModifier->nameAndLoc.name; + + return varDecl->getName(); +} + +// Information tracked when doing a structural +// match of types. +struct StructuralTypeMatchStack +{ + DeclRef<VarDeclBase> leftDecl; + DeclRef<VarDeclBase> rightDecl; + StructuralTypeMatchStack* parent; +}; + +static void diagnoseParameterTypeMismatch( + DiagnosticSink* sink, + StructuralTypeMatchStack* inStack) +{ + SLANG_ASSERT(inStack); + + // The bottom-most entry in the stack should represent + // the shader parameters that kicked things off + auto stack = inStack; + while(stack->parent) + stack = stack->parent; + + sink->diagnose(stack->leftDecl, Diagnostics::shaderParameterDeclarationsDontMatch, getReflectionName(stack->leftDecl)); + sink->diagnose(stack->rightDecl, Diagnostics::seeOtherDeclarationOf, getReflectionName(stack->rightDecl)); +} + +// Two types that were expected to match did not. +// Inform the user with a suitable message. +static void diagnoseTypeMismatch( + DiagnosticSink* sink, + StructuralTypeMatchStack* inStack) +{ + auto stack = inStack; + SLANG_ASSERT(stack); + diagnoseParameterTypeMismatch(sink, stack); + + auto leftType = GetType(stack->leftDecl); + auto rightType = GetType(stack->rightDecl); + + if( stack->parent ) + { + sink->diagnose(stack->leftDecl, Diagnostics::fieldTypeMisMatch, getReflectionName(stack->leftDecl), leftType, rightType); + sink->diagnose(stack->rightDecl, Diagnostics::seeOtherDeclarationOf, getReflectionName(stack->rightDecl)); + + stack = stack->parent; + if( stack ) + { + while( stack->parent ) + { + sink->diagnose(stack->leftDecl, Diagnostics::usedInDeclarationOf, getReflectionName(stack->leftDecl)); + stack = stack->parent; + } + } + } + else + { + sink->diagnose(stack->leftDecl, Diagnostics::shaderParameterTypeMismatch, leftType, rightType); + } +} + +// Two types that were expected to match did not. +// Inform the user with a suitable message. +static void diagnoseTypeFieldsMismatch( + DiagnosticSink* sink, + DeclRef<Decl> const& left, + DeclRef<Decl> const& right, + StructuralTypeMatchStack* stack) +{ + diagnoseParameterTypeMismatch(sink, stack); + + sink->diagnose(left, Diagnostics::fieldDeclarationsDontMatch, left.GetName()); + sink->diagnose(right, Diagnostics::seeOtherDeclarationOf, right.GetName()); + + if( stack ) + { + while( stack->parent ) + { + sink->diagnose(stack->leftDecl, Diagnostics::usedInDeclarationOf, getReflectionName(stack->leftDecl)); + stack = stack->parent; + } + } +} + +static void collectFields( + DeclRef<AggTypeDecl> declRef, + List<DeclRef<VarDecl>>& outFields) +{ + for( auto fieldDeclRef : getMembersOfType<VarDecl>(declRef) ) + { + if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>()) + continue; + + outFields.Add(fieldDeclRef); + } +} + +static bool validateTypesMatch( + DiagnosticSink* sink, + Type* left, + Type* right, + StructuralTypeMatchStack* stack); + +static bool validateIntValuesMatch( + DiagnosticSink* sink, + IntVal* left, + IntVal* right, + StructuralTypeMatchStack* stack) +{ + if(left->EqualsVal(right)) + return true; + + // TODO: are there other cases we need to handle here? + + diagnoseTypeMismatch(sink, stack); + return false; +} + + +static bool validateValuesMatch( + DiagnosticSink* sink, + Val* left, + Val* right, + StructuralTypeMatchStack* stack) +{ + if( auto leftType = dynamicCast<Type>(left) ) + { + if( auto rightType = dynamicCast<Type>(right) ) + { + return validateTypesMatch(sink, leftType, rightType, stack); + } + } + + if( auto leftInt = dynamicCast<IntVal>(left) ) + { + if( auto rightInt = dynamicCast<IntVal>(right) ) + { + return validateIntValuesMatch(sink, leftInt, rightInt, stack); + } + } + + if( auto leftWitness = dynamicCast<SubtypeWitness>(left) ) + { + if( auto rightWitness = dynamicCast<SubtypeWitness>(right) ) + { + return true; + } + } + + diagnoseTypeMismatch(sink, stack); + return false; +} + +static bool validateGenericSubstitutionsMatch( + DiagnosticSink* sink, + GenericSubstitution* left, + GenericSubstitution* right, + StructuralTypeMatchStack* stack) +{ + if( !left ) + { + if( !right ) + { + return true; + } + + diagnoseTypeMismatch(sink, stack); + return false; + } + + + + UInt argCount = left->args.Count(); + if( argCount != right->args.Count() ) + { + diagnoseTypeMismatch(sink, stack); + return false; + } + + for( UInt aa = 0; aa < argCount; ++aa ) + { + auto leftArg = left->args[aa]; + auto rightArg = right->args[aa]; + + if(!validateValuesMatch(sink, leftArg, rightArg, stack)) + return false; + } + + return true; +} + +static bool validateThisTypeSubstitutionsMatch( + DiagnosticSink* /*sink*/, + ThisTypeSubstitution* /*left*/, + ThisTypeSubstitution* /*right*/, + StructuralTypeMatchStack* /*stack*/) +{ + // TODO: actual checking. + return true; +} + +static bool validateSpecializationsMatch( + DiagnosticSink* sink, + SubstitutionSet left, + SubstitutionSet right, + StructuralTypeMatchStack* stack) +{ + auto ll = left.substitutions; + auto rr = right.substitutions; + for(;;) + { + // Skip any global generic substitutions. + if(auto leftGlobalGeneric = as<GlobalGenericParamSubstitution>(ll)) + { + ll = leftGlobalGeneric->outer; + continue; + } + if(auto rightGlobalGeneric = as<GlobalGenericParamSubstitution>(rr)) + { + rr = rightGlobalGeneric->outer; + continue; + } + + // If either ran out, then we expect both to have run out. + if(!ll || !rr) + return !ll && !rr; + + auto leftSubst = ll; + auto rightSubst = rr; + + ll = ll->outer; + rr = rr->outer; + + if(auto leftGeneric = as<GenericSubstitution>(leftSubst)) + { + if(auto rightGeneric = as<GenericSubstitution>(rightSubst)) + { + if(validateGenericSubstitutionsMatch(sink, leftGeneric, rightGeneric, stack)) + { + continue; + } + } + } + else if(auto leftThisType = as<ThisTypeSubstitution>(leftSubst)) + { + if(auto rightThisType = as<ThisTypeSubstitution>(rightSubst)) + { + if(validateThisTypeSubstitutionsMatch(sink, leftThisType, rightThisType, stack)) + { + continue; + } + } + } + + return false; + } + + return true; +} + +// Determine if two types "match" for the purposes of `cbuffer` layout rules. +// +static bool validateTypesMatch( + DiagnosticSink* sink, + Type* left, + Type* right, + StructuralTypeMatchStack* stack) +{ + if(left->Equals(right)) + return true; + + // It is possible that the types don't match exactly, but + // they *do* match structurally. + + // Note: the following code will lead to infinite recursion if there + // are ever recursive types. We'd need a more refined system to + // cache the matches we've already found. + + if( auto leftDeclRefType = as<DeclRefType>(left) ) + { + if( auto rightDeclRefType = as<DeclRefType>(right) ) + { + // Are they references to matching decl refs? + auto leftDeclRef = leftDeclRefType->declRef; + auto rightDeclRef = rightDeclRefType->declRef; + + // Do the reference the same declaration? Or declarations + // with the same name? + // + // TODO: we should only consider the same-name case if the + // declarations come from translation units being compiled + // (and not an imported module). + if( leftDeclRef.getDecl() == rightDeclRef.getDecl() + || leftDeclRef.GetName() == rightDeclRef.GetName() ) + { + // Check that any generic arguments match + if( !validateSpecializationsMatch( + sink, + leftDeclRef.substitutions, + rightDeclRef.substitutions, + stack) ) + { + return false; + } + + // Check that any declared fields match too. + if( auto leftStructDeclRef = leftDeclRef.as<AggTypeDecl>() ) + { + if( auto rightStructDeclRef = rightDeclRef.as<AggTypeDecl>() ) + { + List<DeclRef<VarDecl>> leftFields; + List<DeclRef<VarDecl>> rightFields; + + collectFields(leftStructDeclRef, leftFields); + collectFields(rightStructDeclRef, rightFields); + + UInt leftFieldCount = leftFields.Count(); + UInt rightFieldCount = rightFields.Count(); + + if( leftFieldCount != rightFieldCount ) + { + diagnoseTypeFieldsMismatch(sink, leftDeclRef, rightDeclRef, stack); + return false; + } + + for( UInt ii = 0; ii < leftFieldCount; ++ii ) + { + auto leftField = leftFields[ii]; + auto rightField = rightFields[ii]; + + if( leftField.GetName() != rightField.GetName() ) + { + diagnoseTypeFieldsMismatch(sink, leftDeclRef, rightDeclRef, stack); + return false; + } + + auto leftFieldType = GetType(leftField); + auto rightFieldType = GetType(rightField); + + StructuralTypeMatchStack subStack; + subStack.parent = stack; + subStack.leftDecl = leftField; + subStack.rightDecl = rightField; + + if(!validateTypesMatch(sink, leftFieldType,rightFieldType, &subStack)) + return false; + } + } + } + + // Everything seemed to match recursively. + return true; + } + } + } + + // If we are looking at `T[N]` and `U[M]` we want to check that + // `T` is structurally equivalent to `U` and `N` is the same as `M`. + else if( auto leftArrayType = as<ArrayExpressionType>(left) ) + { + if( auto rightArrayType = as<ArrayExpressionType>(right) ) + { + if(!validateTypesMatch(sink, leftArrayType->baseType, rightArrayType->baseType, stack) ) + return false; + + if(!validateValuesMatch(sink, leftArrayType->ArrayLength, rightArrayType->ArrayLength, stack)) + return false; + + return true; + } + } + + diagnoseTypeMismatch(sink, stack); + return false; +} + +// This function is supposed to determine if two global shader +// parameter declarations represent the same logical parameter +// (so that they should get the exact same binding(s) allocated). +// +static bool doesParameterMatch( + DiagnosticSink* sink, + DeclRef<VarDeclBase> varDeclRef, + DeclRef<VarDeclBase> existingVarDeclRef) +{ + StructuralTypeMatchStack stack; + stack.parent = nullptr; + stack.leftDecl = varDeclRef; + stack.rightDecl = existingVarDeclRef; + + validateTypesMatch(sink, GetType(varDeclRef), GetType(existingVarDeclRef), &stack); + + return true; +} + + + + /// Enumerate the existential-type parameters of a `Program`. /// /// Any parameters found will be added to the list of existential slots on `this`. /// - void Program::_collectExistentialParams() + void Program::_collectShaderParams(DiagnosticSink* sink) { - // We need to inspect all of the global shader parameters + // We need to collect all of the global shader parameters // referenced by the compile request, and for each we - // need to determine what existential types parameters it implies. + // need to do a few things: + // + // * We need to determine if the parameter is a duplicate/redeclaration + // of the "same" parameter in another translation unit, and collapse + // those into one logical shader parameter if so. + // + // * We need to determine what existential type slots are introduced + // by the parameter, and associate that information with the parameter. + // + // To deal with the first issue, we will maintain a map from a parameter + // name to the index of an existing parameter with that name. // + Dictionary<Name*, Int> mapNameToParamIndex; + for( auto module : getModuleDependencies() ) { auto moduleDecl = module->getModuleDecl(); @@ -9662,9 +10102,55 @@ namespace Slang if(!isGlobalShaderParameter(globalVar)) continue; - _collectExistentialParamsRec( + // This declaration may represent the same logical parameter + // as a declaration that came from a different translation unit. + // If that is the case, we want to re-use the same `ShaderParamInfo` + // across both parameters. + // + // TODO: This logic currently detects *any* global-scope parameters + // with matching names, but it should eventually be narrowly + // scoped so that it only applies to parameters from unnamed modules + // (that is, modules that represent directly-compiled shader files + // and not `import`ed code). + // + // First we look for an existing entry matching the name + // of this parameter: + // + auto paramName = getReflectionName(globalVar); + Int existingParamIndex = -1; + if( mapNameToParamIndex.TryGetValue(paramName, existingParamIndex) ) + { + // If the parameters have the same name, but don't "match" according to some reasonable rules, + // then we will treat them as distinct global parameters. + // + // Note: all of the mismatch cases currently report errors, so that + // compilation will fail on a mismatch. + // + auto& existingParam = m_shaderParams[existingParamIndex]; + if( doesParameterMatch(sink, makeDeclRef(globalVar.Ptr()), existingParam.paramDeclRef) ) + { + // If we hit this case, then we had a match, and we should + // consider the new variable to be a redclaration of + // the existing one. + + existingParam.additionalParamDeclRefs.Add( + makeDeclRef(globalVar.Ptr())); + continue; + } + } + + Int newParamIndex = Int(m_shaderParams.Count()); + mapNameToParamIndex.Add(paramName, newParamIndex); + + GlobalShaderParamInfo shaderParamInfo; + shaderParamInfo.paramDeclRef = makeDeclRef(globalVar.Ptr()); + + _collectExistentialSlotsForShaderParam( + shaderParamInfo, m_globalExistentialSlots, makeDeclRef(globalVar.Ptr())); + + m_shaderParams.Add(shaderParamInfo); } } } @@ -9795,7 +10281,7 @@ namespace Slang } } - program->_collectExistentialParams(); + program->_collectShaderParams(sink); return program; } @@ -10181,8 +10667,15 @@ namespace Slang specializedProgram->setGlobalGenericSubsitution(globalGenericSubsts); - // Now deal with the existential arguments - specializedProgram->_collectExistentialParams(); + // Now deal with the shader parameters and existential arguments + // + // Note: We should in theory be able to just copy over the shader + // parameters and existential slot information from the unspecialized + // program. This could save some time, but it would also mean that + // the only way to create a specialized program is by creating an + // unspecialized on first, which is maybe not always desirable. + // + specializedProgram->_collectShaderParams(sink); specializedProgram->_specializeExistentialSlots(globalExistentialArgs, sink); return specializedProgram; diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp index 9037c0944..40bc83052 100644 --- a/source/slang/compiler.cpp +++ b/source/slang/compiler.cpp @@ -229,7 +229,7 @@ namespace Slang // Collect any existential-type parameters used by the entry point // - _collectExistentialParams(); + _collectShaderParams(); } Module* EntryPoint::getModule() diff --git a/source/slang/compiler.h b/source/slang/compiler.h index d3b872054..843cae1b3 100644 --- a/source/slang/compiler.h +++ b/source/slang/compiler.h @@ -137,6 +137,22 @@ namespace Slang List<Arg> args; }; + /// Information collected about global or entry-point shader parameters + struct ShaderParamInfo + { + DeclRef<VarDeclBase> paramDeclRef; + UInt firstExistentialTypeSlot = 0; + UInt existentialTypeSlotCount = 0; + }; + + /// Extended information specific to global shader parameters + struct GlobalShaderParamInfo : ShaderParamInfo + { + // Additional global-scope declarations that are conceptually + // declaring the "same" parameter as the `paramDeclRef`. + List<DeclRef<VarDeclBase>> additionalParamDeclRefs; + }; + /// A request for the front-end to find and validate an entry-point function struct FrontEndEntryPointRequest : RefObject { @@ -287,6 +303,8 @@ namespace Slang Type* getExistentialSlotType(UInt index) { return m_existentialSlots.types[index]; } ExistentialSlots::Arg getExistentialSlotArg(UInt index) { return m_existentialSlots.args[index]; } + List<ShaderParamInfo> const& getShaderParams() { return m_shaderParams; } + void _specializeExistentialSlots( List<RefPtr<Expr>> const& args, DiagnosticSink* sink); @@ -297,7 +315,7 @@ namespace Slang Profile profile, DeclRef<FuncDecl> funcDeclRef); - void _collectExistentialParams(); + void _collectShaderParams(); // The name of the entry point function (e.g., `main`) // @@ -310,6 +328,9 @@ namespace Slang /// The existential/interface slots associated with the entry point parameter scope. ExistentialSlots m_existentialSlots; + /// Information about entry-point parameters + List<ShaderParamInfo> m_shaderParams; + // The profile that the entry point will be compiled for // (this is a combination of the target stage, and also // a feature level that sets capabilities) @@ -920,7 +941,9 @@ namespace Slang Type* getExistentialSlotType(UInt index) { return m_globalExistentialSlots.types[index]; } ExistentialSlots::Arg getExistentialSlotArg(UInt index) { return m_globalExistentialSlots.args[index]; } - void _collectExistentialParams(); + List<GlobalShaderParamInfo> const& getShaderParams() { return m_shaderParams; } + + void _collectShaderParams(DiagnosticSink* sink); void _specializeExistentialSlots( List<RefPtr<Expr>> const& args, DiagnosticSink* sink); @@ -949,6 +972,9 @@ namespace Slang // The existential/interface slots associated with the global scope. ExistentialSlots m_globalExistentialSlots; + /// Information about global shader parameters + List<GlobalShaderParamInfo> m_shaderParams; + // Generated IR for this program. RefPtr<IRModule> m_irModule; diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp index dda3a8621..a472a9280 100644 --- a/source/slang/parameter-binding.cpp +++ b/source/slang/parameter-binding.cpp @@ -395,9 +395,6 @@ struct ParameterBindingContext // the resource usage of shader parameters. TypeLayoutContext layoutContext; - // A dictionary to accelerate looking up parameters by name - Dictionary<Name*, ParameterInfo*> mapNameToParameterInfo; - // What stage (if any) are we compiling for? Stage stage; @@ -573,424 +570,6 @@ LayoutSemanticInfo ExtractLayoutSemanticInfo( return info; } -static Name* getReflectionName(VarDeclBase* varDecl) -{ - if (auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>()) - return reflectionNameModifier->nameAndLoc.name; - - return varDecl->getName(); -} - -// Information tracked when doing a structural -// match of types. -struct StructuralTypeMatchStack -{ - DeclRef<VarDeclBase> leftDecl; - DeclRef<VarDeclBase> rightDecl; - StructuralTypeMatchStack* parent; -}; - -static void diagnoseParameterTypeMismatch( - ParameterBindingContext* context, - StructuralTypeMatchStack* inStack) -{ - SLANG_ASSERT(inStack); - - // The bottom-most entry in the stack should represent - // the shader parameters that kicked things off - auto stack = inStack; - while(stack->parent) - stack = stack->parent; - - getSink(context)->diagnose(stack->leftDecl, Diagnostics::shaderParameterDeclarationsDontMatch, getReflectionName(stack->leftDecl)); - getSink(context)->diagnose(stack->rightDecl, Diagnostics::seeOtherDeclarationOf, getReflectionName(stack->rightDecl)); -} - -// Two types that were expected to match did not. -// Inform the user with a suitable message. -static void diagnoseTypeMismatch( - ParameterBindingContext* context, - StructuralTypeMatchStack* inStack) -{ - auto stack = inStack; - SLANG_ASSERT(stack); - diagnoseParameterTypeMismatch(context, stack); - - auto leftType = GetType(stack->leftDecl); - auto rightType = GetType(stack->rightDecl); - - if( stack->parent ) - { - getSink(context)->diagnose(stack->leftDecl, Diagnostics::fieldTypeMisMatch, getReflectionName(stack->leftDecl), leftType, rightType); - getSink(context)->diagnose(stack->rightDecl, Diagnostics::seeOtherDeclarationOf, getReflectionName(stack->rightDecl)); - - stack = stack->parent; - if( stack ) - { - while( stack->parent ) - { - getSink(context)->diagnose(stack->leftDecl, Diagnostics::usedInDeclarationOf, getReflectionName(stack->leftDecl)); - stack = stack->parent; - } - } - } - else - { - getSink(context)->diagnose(stack->leftDecl, Diagnostics::shaderParameterTypeMismatch, leftType, rightType); - } -} - -// Two types that were expected to match did not. -// Inform the user with a suitable message. -static void diagnoseTypeFieldsMismatch( - ParameterBindingContext* context, - DeclRef<Decl> const& left, - DeclRef<Decl> const& right, - StructuralTypeMatchStack* stack) -{ - diagnoseParameterTypeMismatch(context, stack); - - getSink(context)->diagnose(left, Diagnostics::fieldDeclarationsDontMatch, left.GetName()); - getSink(context)->diagnose(right, Diagnostics::seeOtherDeclarationOf, right.GetName()); - - if( stack ) - { - while( stack->parent ) - { - getSink(context)->diagnose(stack->leftDecl, Diagnostics::usedInDeclarationOf, getReflectionName(stack->leftDecl)); - stack = stack->parent; - } - } -} - -static void collectFields( - DeclRef<AggTypeDecl> declRef, - List<DeclRef<VarDecl>>& outFields) -{ - for( auto fieldDeclRef : getMembersOfType<VarDecl>(declRef) ) - { - if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>()) - continue; - - outFields.Add(fieldDeclRef); - } -} - -static bool validateTypesMatch( - ParameterBindingContext* context, - Type* left, - Type* right, - StructuralTypeMatchStack* stack); - -static bool validateIntValuesMatch( - ParameterBindingContext* context, - IntVal* left, - IntVal* right, - StructuralTypeMatchStack* stack) -{ - if(left->EqualsVal(right)) - return true; - - // TODO: are there other cases we need to handle here? - - diagnoseTypeMismatch(context, stack); - return false; -} - - -static bool validateValuesMatch( - ParameterBindingContext* context, - Val* left, - Val* right, - StructuralTypeMatchStack* stack) -{ - if( auto leftType = dynamicCast<Type>(left) ) - { - if( auto rightType = dynamicCast<Type>(right) ) - { - return validateTypesMatch(context, leftType, rightType, stack); - } - } - - if( auto leftInt = dynamicCast<IntVal>(left) ) - { - if( auto rightInt = dynamicCast<IntVal>(right) ) - { - return validateIntValuesMatch(context, leftInt, rightInt, stack); - } - } - - if( auto leftWitness = dynamicCast<SubtypeWitness>(left) ) - { - if( auto rightWitness = dynamicCast<SubtypeWitness>(right) ) - { - return true; - } - } - - diagnoseTypeMismatch(context, stack); - return false; -} - -static bool validateGenericSubstitutionsMatch( - ParameterBindingContext* context, - GenericSubstitution* left, - GenericSubstitution* right, - StructuralTypeMatchStack* stack) -{ - if( !left ) - { - if( !right ) - { - return true; - } - - diagnoseTypeMismatch(context, stack); - return false; - } - - - - UInt argCount = left->args.Count(); - if( argCount != right->args.Count() ) - { - diagnoseTypeMismatch(context, stack); - return false; - } - - for( UInt aa = 0; aa < argCount; ++aa ) - { - auto leftArg = left->args[aa]; - auto rightArg = right->args[aa]; - - if(!validateValuesMatch(context, leftArg, rightArg, stack)) - return false; - } - - return true; -} - -static bool validateThisTypeSubstitutionsMatch( - ParameterBindingContext* /*context*/, - ThisTypeSubstitution* /*left*/, - ThisTypeSubstitution* /*right*/, - StructuralTypeMatchStack* /*stack*/) -{ - // TODO: actual checking. - return true; -} - -static bool validateSpecializationsMatch( - ParameterBindingContext* context, - SubstitutionSet left, - SubstitutionSet right, - StructuralTypeMatchStack* stack) -{ - auto ll = left.substitutions; - auto rr = right.substitutions; - for(;;) - { - // Skip any global generic substitutions. - if(auto leftGlobalGeneric = as<GlobalGenericParamSubstitution>(ll)) - { - ll = leftGlobalGeneric->outer; - continue; - } - if(auto rightGlobalGeneric = as<GlobalGenericParamSubstitution>(rr)) - { - rr = rightGlobalGeneric->outer; - continue; - } - - // If either ran out, then we expect both to have run out. - if(!ll || !rr) - return !ll && !rr; - - auto leftSubst = ll; - auto rightSubst = rr; - - ll = ll->outer; - rr = rr->outer; - - if(auto leftGeneric = as<GenericSubstitution>(leftSubst)) - { - if(auto rightGeneric = as<GenericSubstitution>(rightSubst)) - { - if(validateGenericSubstitutionsMatch(context, leftGeneric, rightGeneric, stack)) - { - continue; - } - } - } - else if(auto leftThisType = as<ThisTypeSubstitution>(leftSubst)) - { - if(auto rightThisType = as<ThisTypeSubstitution>(rightSubst)) - { - if(validateThisTypeSubstitutionsMatch(context, leftThisType, rightThisType, stack)) - { - continue; - } - } - } - - return false; - } - - return true; -} - -// Determine if two types "match" for the purposes of `cbuffer` layout rules. -// -static bool validateTypesMatch( - ParameterBindingContext* context, - Type* left, - Type* right, - StructuralTypeMatchStack* stack) -{ - if(left->Equals(right)) - return true; - - // It is possible that the types don't match exactly, but - // they *do* match structurally. - - // Note: the following code will lead to infinite recursion if there - // are ever recursive types. We'd need a more refined system to - // cache the matches we've already found. - - if( auto leftDeclRefType = as<DeclRefType>(left) ) - { - if( auto rightDeclRefType = as<DeclRefType>(right) ) - { - // Are they references to matching decl refs? - auto leftDeclRef = leftDeclRefType->declRef; - auto rightDeclRef = rightDeclRefType->declRef; - - // Do the reference the same declaration? Or declarations - // with the same name? - // - // TODO: we should only consider the same-name case if the - // declarations come from translation units being compiled - // (and not an imported module). - if( leftDeclRef.getDecl() == rightDeclRef.getDecl() - || leftDeclRef.GetName() == rightDeclRef.GetName() ) - { - // Check that any generic arguments match - if( !validateSpecializationsMatch( - context, - leftDeclRef.substitutions, - rightDeclRef.substitutions, - stack) ) - { - return false; - } - - // Check that any declared fields match too. - if( auto leftStructDeclRef = leftDeclRef.as<AggTypeDecl>() ) - { - if( auto rightStructDeclRef = rightDeclRef.as<AggTypeDecl>() ) - { - List<DeclRef<VarDecl>> leftFields; - List<DeclRef<VarDecl>> rightFields; - - collectFields(leftStructDeclRef, leftFields); - collectFields(rightStructDeclRef, rightFields); - - UInt leftFieldCount = leftFields.Count(); - UInt rightFieldCount = rightFields.Count(); - - if( leftFieldCount != rightFieldCount ) - { - diagnoseTypeFieldsMismatch(context, leftDeclRef, rightDeclRef, stack); - return false; - } - - for( UInt ii = 0; ii < leftFieldCount; ++ii ) - { - auto leftField = leftFields[ii]; - auto rightField = rightFields[ii]; - - if( leftField.GetName() != rightField.GetName() ) - { - diagnoseTypeFieldsMismatch(context, leftDeclRef, rightDeclRef, stack); - return false; - } - - auto leftFieldType = GetType(leftField); - auto rightFieldType = GetType(rightField); - - StructuralTypeMatchStack subStack; - subStack.parent = stack; - subStack.leftDecl = leftField; - subStack.rightDecl = rightField; - - if(!validateTypesMatch(context, leftFieldType,rightFieldType, &subStack)) - return false; - } - } - } - - // Everything seemed to match recursively. - return true; - } - } - } - - // If we are looking at `T[N]` and `U[M]` we want to check that - // `T` is structurally equivalent to `U` and `N` is the same as `M`. - else if( auto leftArrayType = as<ArrayExpressionType>(left) ) - { - if( auto rightArrayType = as<ArrayExpressionType>(right) ) - { - if(!validateTypesMatch(context, leftArrayType->baseType, rightArrayType->baseType, stack) ) - return false; - - if(!validateValuesMatch(context, leftArrayType->ArrayLength, rightArrayType->ArrayLength, stack)) - return false; - - return true; - } - } - - diagnoseTypeMismatch(context, stack); - return false; -} - -// This function is supposed to determine if two global shader -// parameter declarations represent the same logical parameter -// (so that they should get the exact same binding(s) allocated). -// -static bool doesParameterMatch( - ParameterBindingContext* context, - RefPtr<VarLayout> varLayout, - ParameterInfo* parameterInfo) -{ - // Any "varying" parameter should automatically be excluded - // - // Note that we use the `typeLayout` field rather than - // looking at resource information on the variable directly, - // because this may be called when binding hasn't been performed. - for (auto rr : varLayout->typeLayout->resourceInfos) - { - switch (rr.kind) - { - case LayoutResourceKind::VertexInput: - case LayoutResourceKind::FragmentOutput: - return false; - - default: - break; - } - } - - StructuralTypeMatchStack stack; - stack.parent = nullptr; - stack.leftDecl = varLayout->varDecl; - stack.rightDecl = parameterInfo->varLayouts[0]->varDecl; - - validateTypesMatch(context, varLayout->typeLayout->type, parameterInfo->varLayouts[0]->typeLayout->type, &stack); - - return true; -} // @@ -1020,90 +599,6 @@ static bool findLayoutArg( return findLayoutArg<T>(declRef.getDecl(), outVal); } -// - -static bool isGLSLBuiltinName(VarDeclBase* varDecl) -{ - return getText(getReflectionName(varDecl)).StartsWith("gl_"); -} - -RefPtr<Type> tryGetEffectiveTypeForGLSLVaryingInput( - ParameterBindingContext* context, - VarDeclBase* varDecl) -{ - if (isGLSLBuiltinName(varDecl)) - return nullptr; - - auto type = varDecl->getType(); - if( varDecl->HasModifier<InModifier>() || as<GLSLInputParameterGroupType>(type)) - { - // Special case to handle "arrayed" shader inputs, as used - // for Geometry and Hull input - switch( context->stage ) - { - case Stage::Geometry: - case Stage::Hull: - case Stage::Domain: - // Tessellation `patch` variables should stay as written - if( !varDecl->HasModifier<GLSLPatchModifier>() ) - { - // Unwrap array type, if present - if( auto arrayType = as<ArrayExpressionType>(type) ) - { - type = arrayType->baseType.Ptr(); - } - } - break; - - default: - break; - } - - return type; - } - - return nullptr; -} - -RefPtr<Type> tryGetEffectiveTypeForGLSLVaryingOutput( - ParameterBindingContext* context, - VarDeclBase* varDecl) -{ - if (isGLSLBuiltinName(varDecl)) - return nullptr; - - auto type = varDecl->getType(); - if( varDecl->HasModifier<OutModifier>() || as<GLSLOutputParameterGroupType>(type)) - { - // Special case to handle "arrayed" shader outputs, as used - // for Hull Shader output - // - // Note(tfoley): there is unfortunate code duplication - // with the `in` case above. - switch( context->stage ) - { - case Stage::Hull: - // Tessellation `patch` variables should stay as written - if( !varDecl->HasModifier<GLSLPatchModifier>() ) - { - // Unwrap array type, if present - if( auto arrayType = as<ArrayExpressionType>(type) ) - { - type = arrayType->baseType.Ptr(); - } - } - break; - - default: - break; - } - - return type; - } - - return nullptr; -} - /// Determine how to lay out a global variable that might be a shader parameter. /// /// Returns `nullptr` if the declaration does not represent a shader parameter. @@ -1183,19 +678,21 @@ static void collectGlobalGenericParameter( // Collect a single declaration into our set of parameters static void collectGlobalScopeParameter( - ParameterBindingContext* context, - RefPtr<VarDeclBase> varDecl, - SubstitutionSet globalGenericSubst) + ParameterBindingContext* context, + GlobalShaderParamInfo const& shaderParamInfo, + SubstitutionSet globalGenericSubst) { + auto varDeclRef = shaderParamInfo.paramDeclRef; + // We apply any substitutions for global generic parameters here. - auto type = varDecl->getType()->Substitute(globalGenericSubst).as<Type>(); + auto type = GetType(varDeclRef)->Substitute(globalGenericSubst).as<Type>(); // We use a single operation to both check whether the // variable represents a shader parameter, and to compute // the layout for that parameter's type. auto typeLayout = getTypeLayoutForGlobalShaderParameter( context, - varDecl, + varDeclRef.getDecl(), type); // If we did not find appropriate layout rules, then it @@ -1207,45 +704,43 @@ static void collectGlobalScopeParameter( // Now create a variable layout that we can use RefPtr<VarLayout> varLayout = new VarLayout(); varLayout->typeLayout = typeLayout; - varLayout->varDecl = DeclRef<Decl>(varDecl.Ptr(), nullptr).as<VarDeclBase>(); + varLayout->varDecl = varDeclRef; - // This declaration may represent the same logical parameter - // as a declaration that came from a different translation unit. - // If that is the case, we want to re-use the same `VarLayout` - // across both parameters. + // The logic in `check.cpp` that created the `GlobalShaderParamInfo` + // will have identified any cases where there might be multiple + // global variables that logically represent the same shader parameter. // - // TODO: This logic currently detects *any* global-scope parameters - // with matching names, but it should eventually be narrowly - // scoped so that it only applies to parameters from unnamed modules. + // We will track the same basic information during layout using + // the `ParameterInfo` type. // - // First we look for an existing entry matching the name - // of this parameter: - auto parameterName = getReflectionName(varDecl); - ParameterInfo* parameterInfo = nullptr; - if( context->mapNameToParameterInfo.TryGetValue(parameterName, parameterInfo) ) - { - // If the parameters have the same name, but don't "match" according to some reasonable rules, - // then we need to bail out. - if( !doesParameterMatch(context, varLayout, parameterInfo) ) - { - parameterInfo = nullptr; - } - } + // TODO: `ParameterInfo` should probably become `LayoutParamInfo`. + // + ParameterInfo* parameterInfo = new ParameterInfo(); + context->shared->parameters.Add(parameterInfo); - // If we didn't find a matching parameter, then we need to create one here - if( !parameterInfo ) - { - parameterInfo = new ParameterInfo(); - context->shared->parameters.Add(parameterInfo); - context->mapNameToParameterInfo.AddIfNotExists(parameterName, parameterInfo); - } - else + // Add the first variable declaration to the list of declarations for the parameter + parameterInfo->varLayouts.Add(varLayout); + + // Add any additional variables to the list of declarations + for( auto additionalVarDeclRef : shaderParamInfo.additionalParamDeclRefs ) { - varLayout->flags |= VarLayoutFlag::IsRedeclaration; - } + // TODO: We should either eliminate the design choice where different + // declarations of the "same" shade parameter get merged across + // translation units (it is effectively just a compatiblity feature), + // or we should clean things up earlier in the chain so that we can + // re-use a single `VarLayout` across all of the different declarations. + // + // TODO: It would also make sense in these cases to ensure that + // such global shader parameters get the same mangled name across + // all translation units, so that they can automatically be collapsed + // during linking. - // Add this variable declaration to the list of declarations for the parameter - parameterInfo->varLayouts.Add(varLayout); + RefPtr<VarLayout> additionalVarLayout = new VarLayout(); + additionalVarLayout->typeLayout = typeLayout; + additionalVarLayout->varDecl = additionalVarDeclRef; + + parameterInfo->varLayouts.Add(additionalVarLayout); + } } static RefPtr<UsedRangeSet> findUsedRangeSetForSpace( @@ -1783,41 +1278,6 @@ static void completeBindingsForParameter( applyBindingInfoToParameter(varLayout, bindingInfos); } - - -static void collectGlobalScopeParameters( - ParameterBindingContext* context, - ModuleDecl* program, - SubstitutionSet globalGenericSubst) -{ - // First enumerate parameters at global scope - // We collect two things here: - // 1. A shader parameter, which is always a variable - // 2. A global entry-point generic parameter type (`type_param`), - // which is a GlobalGenericParamDecl - // We collect global generic type parameters in the first pass, - // So we can fill in the correct index into ordinary type layouts - // for generic types in the second pass. - for (auto decl : program->Members) - { - if (auto genParamDecl = as<GlobalGenericParamDecl>(decl)) - collectGlobalGenericParameter(context, genParamDecl); - } - for (auto decl : program->Members) - { - if (auto varDecl = as<VarDeclBase>(decl)) - collectGlobalScopeParameter(context, varDecl, globalGenericSubst); - } - - // Next, we need to enumerate the parameters of - // each entry point (which requires knowing what the - // entry points *are*) - - // TODO(tfoley): Entry point functions should be identified - // by looking for a generated modifier that is attached - // to global-scope function declarations. -} - struct SimpleSemanticInfo { String name; @@ -2274,7 +1734,7 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter( static RefPtr<TypeLayout> computeEntryPointParameterTypeLayout( ParameterBindingContext* context, SubstitutionSet typeSubst, - DeclRef<ParamDecl> paramDeclRef, + DeclRef<VarDeclBase> paramDeclRef, RefPtr<VarLayout> paramVarLayout, EntryPointParameterState& state) { @@ -2537,8 +1997,10 @@ static void collectEntryPointParameters( scopeBuilder.beginLayout(context); auto paramsStructLayout = scopeBuilder.m_structLayout; - for( auto paramDeclRef : getMembersOfType<ParamDecl>(entryPointFuncDeclRef) ) + for( auto& shaderParamInfo : entryPoint->getShaderParams() ) { + auto paramDeclRef = shaderParamInfo.paramDeclRef; + // Any error messages we emit during the process should // refer to the location of this parameter. // @@ -2656,17 +2118,33 @@ static void collectParameters( // logical namespace/"linkage" so that two parameters // with the same name should represent the same // parameter, and get the same binding(s) + ParameterBindingContext contextData = *inContext; auto context = &contextData; + context->stage = Stage::Unknown; auto globalGenericSubst = program->getGlobalGenericSubstitution(); + // We will start by looking for any global generic type parameters. + for(RefPtr<Module> module : program->getModuleDependencies()) { - context->stage = Stage::Unknown; + for( auto genParamDecl : module->getModuleDecl()->getMembersOfType<GlobalGenericParamDecl>() ) + { + collectGlobalGenericParameter(context, genParamDecl); + } + } + + // Once we have enumerated global generic type parameters, we can + // begin enumerating shader parameters, starting at the global scope. + // + // Because we have already enumerated the global generic type parameters, + // we will be able to look up the index of a global generic type parameter + // when we see it referenced in the type of one of the shader parameters. - // First look at global-scope parameters - collectGlobalScopeParameters(context, module->getModuleDecl(), globalGenericSubst); + for(auto& globalParamInfo : program->getShaderParams() ) + { + collectGlobalScopeParameter(context, globalParamInfo, globalGenericSubst); } // Next consider parameters for entry points diff --git a/source/slang/syntax.h b/source/slang/syntax.h index 5198a44b2..cb45c0363 100644 --- a/source/slang/syntax.h +++ b/source/slang/syntax.h @@ -1221,6 +1221,20 @@ namespace Slang return rs; } + /// The the user-level name for a variable that might be a shader parameter. + /// + /// In most cases this is just the name of the variable declaration itself, + /// but in the specific case of a `cbuffer`, the name that the user thinks + /// of is really metadata. For example: + /// + /// cbuffer C { int x; } + /// + /// In this example, error messages relating to the constant buffer should + /// really use the name `C`, but that isn't the name of the declaration + /// (it is in practice anonymous, and `C` can be used for a different + /// declaration in the same file). + /// + Name* getReflectionName(VarDeclBase* varDecl); inline RefPtr<Type> GetType(DeclRef<VarDeclBase> const& declRef) { diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h index 18a51c155..bb32d769e 100644 --- a/source/slang/type-layout.h +++ b/source/slang/type-layout.h @@ -376,7 +376,6 @@ public: typedef unsigned int VarLayoutFlags; enum VarLayoutFlag : VarLayoutFlags { - IsRedeclaration = 1 << 0, ///< This is a redeclaration of some shader parameter HasSemantic = 1 << 1 }; |
