summaryrefslogtreecommitdiff
path: root/source/slang/slang-check-shader.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-11-19 13:53:52 -0800
committerGitHub <noreply@github.com>2019-11-19 13:53:52 -0800
commitfae3d9ae91a24e59ce6ec64b8a1217fa81f5acee (patch)
tree49e780c8aa82652e4acd5eb869c3ef7a179f5500 /source/slang/slang-check-shader.cpp
parentdd435512219f435ea13498e6124930fd4cf823a9 (diff)
Initial work for "global generic value parameters" (#1127)
* Initial work for "global generic value parameters" The main new feature here is support for the `__generic_value_param` keyword, which introduces a *global generic value parameter*. For example: __generic_value_param kOffset : uint = 0; This declaration introduces a global generic value parameter `kOffset` of type `uint` that has a nominal default value of zero. The broad strokes of how this feature was added are as follows: * A new `GlobalGenericValueParamDecl` AST node type is introduces in `slang-decl-defs.h` * A new `parseGlobalGenericValueParamDecl` subroutine is added to `slang-parser.cpp`, and is added to the list of declaration cases as the callback for the `__generic_value_param` name. * Cases for `GlobalGenericValueParamDecl` are added to the declaration checking passes in `slang-check-decl.cpp`, mirroring what is done for other variable declaration cases. * A case for `GlobalGenericValueParamDecl` is aded to the `Module::_collectShaderParams` function, so that it is recognized as a kind of specialization parameter. This introduces a specialization parameter of flavor `SpecializationParam::Flavor::GenericValue` (which was already defined before this change, although it was unused). * A case for `SpecializationParam::Flavor::GenericValue` is added in `Module::_validateSpecializationArgsImpl` to check that a specialization argument represents a compile-time-constant value (not a type). * A case for `GlobalGenericValueParmDecl` is introduced in `slang-lower-to-ir.cpp` that introduces a global generic parameter in the IR * The `IRBuilder` is extended to support creating `IRGlobalGenericParam`s for the distinct cases of type, witness-table, and value parameters. The same IR instruction type/opcode is used for all cases, and only the type of the IR instruction differs. * The existing mechanisms for lowering specialization arguments to the IR, and doing specialization on the IR itself Just Work with global generic value parameters since they already support value parameters on explicit generic declarations. That's the santized version of things, but there were also a bunch of cleanups and tweaks required along the way: * The `SpecializationParam` type was extended to also track a `SourceLoc` to help in diagnostic messages, which meant some churn in the code that collects specialization parameters. * The `_extractSpecializationArgs` function is tweaked to support any kind of "term" as a specialization argument (either a type or a value). * To allow *parsing* specialization arguments that can't possibly be types (e.g., integer literals) we replace the existing `parseTypeString` routine with `parseTermString` and then in `parseTermFromSourceFile` call through to a general case of expression parsing (which can also parse types) rather than only parsing types directly. * Right before doing back-end code generation, we check if the program we are going to emit has remaining (unspecialized) parameters, in which case we emit a diagnostic message for the parameters that haven't been specialized rather than go on to emit code that will fail to compile downstream. * Within the `render-test` tool we collapse down the arrays that held both "generic" and "existential" specialization arguments, so that we just have *global* and *entry-point* specialization argument lists. This mirrors how Slang has worked internally for a while, but the difference hasn't been important to the test tool because no tests currently mix generic and existential specialization. The logic for parsing `TEST_INPUT` lines has been streamlined down to just the global and entry-point cases, but the pre-existing keywords are still allowed so that I don't have to tweak any test cases. There are several significant caveats for this feature, which mean that it isn't really ready for users to hammer on just yet: * There is no support for `Val`s of anything but integers, so there is no way to meaningfully have a generic value param with a type other than `int` or `uint`. * We allow for a default-value expression on global generic parameters, but do not actually make use of that value for anything (e.g., to allow a programmer to omit specialization arguments), nor check that it meets the constraints of being compile-time constant. * Global generic value parameters are *not* currently being treated the same as explicit generic parameters in terms of how they can be used for things like array sizes or other things that require constants. This will probably be relaxed at some point, but allowing a global generic to be used to size an array creates questions around layout. * The IR optimization passes in Slang currently won't eliminate entire blocks of code based on constant values, so using a global generic value parameter to enable/disable features will *not* currently lead to us outputting drastically different HLSL or GLSL. That said, we expect most downstream compilers to be able to handle an `if(0)` well. * Fix regression for tagged union types The change that made specialization arguments be parsed as "terms" first, and then coerced to types meant that any special-case logic that is specific to the parsing of types would be bypassed and thus not apply. Most of that special-case logic isn't wanted for specialization arguments, since it pertains to cases were we want to, e.g, declare a `struct` type while also declaring a variable of that type. The one special case that *is* useful is the `__TaggedUnion(...)` syntax, which is the only way to introduce a tagged union type right now. In order to get that case working again, all I had to do was register the existing logic for parsing `__TaggedUnion` as an expression keyword with the right callback, and the existing logic in expression parsing kicks in (that logic was already handling expression keywords like `this` and `true`). I left in the existing logic for handling `__TaggedUnion` directly where types get parsed, rather than try to unify things. A better long-term fix is to make the base case for type parsing route into `parseAtomicExpr` so that the two paths share the core logic. That change should probably come as its own refactoring/cleanup, because it creates the potential for some subtle breakage. * fixup: typo
Diffstat (limited to 'source/slang/slang-check-shader.cpp')
-rw-r--r--source/slang/slang-check-shader.cpp86
1 files changed, 64 insertions, 22 deletions
diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp
index 333690b36..c229e8f96 100644
--- a/source/slang/slang-check-shader.cpp
+++ b/source/slang/slang-check-shader.cpp
@@ -52,7 +52,8 @@ namespace Slang
/// Recursively walk `type` and add any existential/interface specialization parameters to `ioSpecializationParams`.
static void _collectExistentialSpecializationParamsRec(
SpecializationParams& ioSpecializationParams,
- Type* type)
+ Type* type,
+ SourceLoc loc)
{
// Whether or not something is an array does not affect
// the number of existential slots it introduces.
@@ -66,7 +67,8 @@ namespace Slang
{
_collectExistentialSpecializationParamsRec(
ioSpecializationParams,
- parameterGroupType->getElementType());
+ parameterGroupType->getElementType(),
+ loc);
return;
}
@@ -81,6 +83,7 @@ namespace Slang
//
SpecializationParam specializationParam;
specializationParam.flavor = SpecializationParam::Flavor::ExistentialType;
+ specializationParam.loc = loc;
specializationParam.object = type;
ioSpecializationParams.add(specializationParam);
}
@@ -112,7 +115,8 @@ namespace Slang
{
_collectExistentialSpecializationParamsRec(
ioSpecializationParams,
- GetType(paramDeclRef));
+ GetType(paramDeclRef),
+ paramDeclRef.getLoc());
}
@@ -147,6 +151,7 @@ namespace Slang
{
SpecializationParam param;
param.flavor = SpecializationParam::Flavor::GenericType;
+ param.loc = genericTypeParam->loc;
param.object = genericTypeParam;
m_genericSpecializationParams.add(param);
}
@@ -154,6 +159,7 @@ namespace Slang
{
SpecializationParam param;
param.flavor = SpecializationParam::Flavor::GenericValue;
+ param.loc = genericValParam->loc;
param.object = genericValParam;
m_genericSpecializationParams.add(param);
}
@@ -1020,9 +1026,21 @@ static bool doesParameterMatch(
//
SpecializationParam specializationParam;
specializationParam.flavor = SpecializationParam::Flavor::GenericType;
+ specializationParam.loc = globalGenericParam->loc;
specializationParam.object = globalGenericParam;
m_specializationParams.add(specializationParam);
}
+ else if( auto globalGenericValueParam = as<GlobalGenericValueParamDecl>(globalDecl) )
+ {
+ // A global generic type parameter declaration introduces
+ // a suitable specialization parameter.
+ //
+ SpecializationParam specializationParam;
+ specializationParam.flavor = SpecializationParam::Flavor::GenericValue;
+ specializationParam.loc = globalGenericValueParam->loc;
+ specializationParam.object = globalGenericValueParam;
+ m_specializationParams.add(specializationParam);
+ }
else if( auto importDecl = as<ImportDecl>(globalDecl) )
{
// An `import` declaration creates a requirement dependency
@@ -1425,9 +1443,6 @@ static bool doesParameterMatch(
auto& arg = args[ii];
auto& param = m_specializationParams[ii];
- auto argType = arg.val.as<Type>();
- SLANG_ASSERT(argType);
-
switch( param.flavor )
{
case SpecializationParam::Flavor::GenericType:
@@ -1435,6 +1450,13 @@ static bool doesParameterMatch(
auto genericTypeParamDecl = param.object.as<GlobalGenericParamDecl>();
SLANG_ASSERT(genericTypeParamDecl);
+ RefPtr<Type> argType = as<Type>(arg.val);
+ if(!argType)
+ {
+ sink->diagnose(param.loc, Diagnostics::expectedTypeForSpecializationArg, genericTypeParamDecl);
+ argType = getLinkage()->getSessionImpl()->getErrorType();
+ }
+
// TODO: There is a serious flaw to this checking logic if we ever have cases where
// the constraints on one `type_param` can depend on another `type_param`, e.g.:
//
@@ -1520,6 +1542,13 @@ static bool doesParameterMatch(
auto interfaceType = param.object.as<Type>();
SLANG_ASSERT(interfaceType);
+ RefPtr<Type> argType = as<Type>(arg.val);
+ if(!argType)
+ {
+ sink->diagnose(param.loc, Diagnostics::expectedTypeForSpecializationArg, interfaceType);
+ argType = getLinkage()->getSessionImpl()->getErrorType();
+ }
+
auto witness = visitor.tryGetSubtypeWitness(argType, interfaceType);
if (!witness)
{
@@ -1539,6 +1568,29 @@ static bool doesParameterMatch(
}
break;
+ case SpecializationParam::Flavor::GenericValue:
+ {
+ auto paramDecl = param.object.as<GlobalGenericValueParamDecl>();
+ SLANG_ASSERT(paramDecl);
+
+ // Now we need to check that the argument `Val` has the
+ // appropriate type expected by the parameter.
+
+ RefPtr<IntVal> intVal = as<IntVal>(arg.val);
+ if(!intVal)
+ {
+ sink->diagnose(param.loc, Diagnostics::expectedValueOfTypeForSpecializationArg, paramDecl->getType(), paramDecl);
+ intVal = new ConstantIntVal(0);
+ }
+
+ ModuleSpecializationInfo::GenericArgInfo expandedArg;
+ expandedArg.paramDecl = paramDecl;
+ expandedArg.argVal = intVal;
+
+ specializationInfo->genericArgs.add(expandedArg);
+ }
+ break;
+
default:
SLANG_UNEXPECTED("unhandled specialization parameter flavor");
}
@@ -1556,27 +1608,17 @@ static bool doesParameterMatch(
{
auto linkage = componentType->getLinkage();
+ SharedSemanticsContext semanticsContext(linkage, sink);
+ SemanticsVisitor semanticsVisitor(&semanticsContext);
+
auto argCount = argExprs.getCount();
for(Index ii = 0; ii < argCount; ++ii )
{
auto argExpr = argExprs[ii];
auto paramInfo = componentType->getSpecializationParam(ii);
- // TODO: We should support non-type arguments here
-
- auto argType = checkProperType(linkage, TypeExp(argExpr), sink);
- if( !argType )
- {
- // If no witness was found, then we will be unable to satisfy
- // the conformances required.
- sink->diagnose(argExpr,
- Diagnostics::expectedAType,
- argExpr->type);
- continue;
- }
-
SpecializationArg arg;
- arg.val = argType;
+ arg.val = semanticsVisitor.ExtractGenericArgVal(argExpr);
outArgs.add(arg);
}
}
@@ -1757,7 +1799,7 @@ static bool doesParameterMatch(
RefPtr<Expr> argExpr;
for (auto & s : scopesToTry)
{
- argExpr = linkage->parseTypeString(name, s);
+ argExpr = linkage->parseTermString(name, s);
argExpr = semantics.CheckTerm(argExpr);
if( argExpr )
{
@@ -1790,7 +1832,7 @@ static bool doesParameterMatch(
SemanticsVisitor visitor(&sharedSemanticsContext);
SpecializationParams specializationParams;
- _collectExistentialSpecializationParamsRec(specializationParams, unspecializedType);
+ _collectExistentialSpecializationParamsRec(specializationParams, unspecializedType, SourceLoc());
assert(specializationParams.getCount() == argCount);