summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/slang/check.cpp263
-rw-r--r--source/slang/core.meta.slang68
-rw-r--r--source/slang/core.meta.slang.h68
-rw-r--r--source/slang/decl-defs.h7
-rw-r--r--source/slang/diagnostic-defs.h5
-rw-r--r--source/slang/emit.cpp48
-rw-r--r--source/slang/lookup.cpp27
-rw-r--r--source/slang/lookup.h9
-rw-r--r--source/slang/lower-to-ir.cpp18
-rw-r--r--source/slang/modifier-defs.h72
-rw-r--r--source/slang/parser.cpp131
-rw-r--r--source/slang/reflection.cpp2
-rw-r--r--source/slang/syntax-base-defs.h2
-rw-r--r--source/slang/syntax.h5
-rw-r--r--tests/compute/loop-unroll.slang9
15 files changed, 597 insertions, 137 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index f7cb3b575..1af91e9fd 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -1412,6 +1412,11 @@ namespace Slang
// These are only used in the stdlib, so no checking is needed
}
+ void visitAttributeDecl(AttributeDecl*)
+ {
+ // These are only used in the stdlib, so no checking is needed
+ }
+
void visitGenericTypeParamDecl(GenericTypeParamDecl*)
{
// These are only used in the stdlib, so no checking is needed for now
@@ -1427,71 +1432,209 @@ namespace Slang
// Do nothing with modifiers for now
}
- RefPtr<Modifier> checkModifier(
- RefPtr<Modifier> m,
- Decl* /*decl*/)
+ AttributeDecl* lookUpAttributeDecl(Name* attributeName, Scope* scope)
{
- if(auto hlslUncheckedAttribute = m.As<HLSLUncheckedAttribute>())
+ // Look up the name and see what we find.
+ //
+ // TODO: This needs to have some special filtering or naming
+ // rules to keep us from seeing shadowing variable declarations.
+ auto lookupResult = lookUp(getSession(), this, attributeName, scope, LookupMask::Attribute);
+
+ // If we didn't find anything, or the result was overloaded,
+ // then we aren't going to be able to extract a single decl.
+ if(!lookupResult.isValid() || lookupResult.isOverloaded())
+ return nullptr;
+
+ auto decl = lookupResult.item.declRef.getDecl();
+ if( auto attributeDecl = dynamic_cast<AttributeDecl*>(decl) )
{
- // We have an HLSL `[name(arg,...)]` attribute, and we'd like
- // to check that it is provides all the expected arguments
- //
- // For now we will do this in a completely ad hoc fashion,
- // but it would be nice to have some generic routine to
- // do the needed type checking/coercion.
- auto attribText = getText(hlslUncheckedAttribute->getName());
-
- if(attribText == "numthreads")
- {
- if(hlslUncheckedAttribute->args.Count() != 3)
- return m;
+ return attributeDecl;
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
- auto xVal = checkConstantIntVal(hlslUncheckedAttribute->args[0]);
- auto yVal = checkConstantIntVal(hlslUncheckedAttribute->args[1]);
- auto zVal = checkConstantIntVal(hlslUncheckedAttribute->args[2]);
+ bool validateAttribute(RefPtr<Attribute> attr)
+ {
+ if(auto numThreadsAttr = attr.As<NumThreadsAttribute>())
+ {
+ SLANG_ASSERT(attr->args.Count() == 3);
+ auto xVal = checkConstantIntVal(attr->args[0]);
+ auto yVal = checkConstantIntVal(attr->args[1]);
+ auto zVal = checkConstantIntVal(attr->args[2]);
- if(!xVal) return m;
- if(!yVal) return m;
- if(!zVal) return m;
+ if(!xVal) return false;
+ if(!yVal) return false;
+ if(!zVal) return false;
- auto hlslNumThreadsAttribute = new HLSLNumThreadsAttribute();
+ numThreadsAttr->x = (int32_t) xVal->value;
+ numThreadsAttr->y = (int32_t) yVal->value;
+ numThreadsAttr->z = (int32_t) zVal->value;
+ }
+ else if (auto maxVertexCountAttr = attr.As<MaxVertexCountAttribute>())
+ {
+ SLANG_ASSERT(attr->args.Count() == 1);
+ auto val = checkConstantIntVal(attr->args[0]);
- hlslNumThreadsAttribute->loc = hlslUncheckedAttribute->loc;
- hlslNumThreadsAttribute->name = hlslUncheckedAttribute->getName();
- hlslNumThreadsAttribute->args = hlslUncheckedAttribute->args;
- hlslNumThreadsAttribute->x = (int32_t) xVal->value;
- hlslNumThreadsAttribute->y = (int32_t) yVal->value;
- hlslNumThreadsAttribute->z = (int32_t) zVal->value;
+ maxVertexCountAttr->value = (int32_t)val->value;
+ }
+ else if(auto instanceAttr = attr.As<InstanceAttribute>())
+ {
+ SLANG_ASSERT(attr->args.Count() == 1);
+ auto val = checkConstantIntVal(attr->args[0]);
- return hlslNumThreadsAttribute;
+ instanceAttr->value = (int32_t)val->value;
+ }
+ else
+ {
+ if(attr->args.Count() == 0)
+ {
+ // If the attribute took no arguments, then we will
+ // assume it is valid as written.
+ }
+ else
+ {
+ // We should be special-casing the checking of any attribute
+ // with a non-zero number of arguments.
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), attr, "unhandled attribute");
+ return false;
+ }
}
- else if (attribText == "maxvertexcount")
+
+ return true;
+ }
+
+ RefPtr<AttributeBase> checkAttribute(
+ UncheckedAttribute* uncheckedAttr,
+ ModifiableSyntaxNode* attrTarget)
+ {
+ auto attrName = uncheckedAttr->getName();
+ auto attrDecl = lookUpAttributeDecl(
+ attrName,
+ uncheckedAttr->scope);
+
+ if(!attrDecl)
+ {
+ getSink()->diagnose(uncheckedAttr, Diagnostics::unknownAttributeName, attrName);
+ return uncheckedAttr;
+ }
+
+ if(!attrDecl->syntaxClass.isSubClassOf<Attribute>())
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), attrDecl, "attribute declaration does not reference an attribute class");
+ return uncheckedAttr;
+ }
+
+ RefPtr<RefObject> attrObj = attrDecl->syntaxClass.createInstance();
+ auto attr = attrObj.As<Attribute>();
+ if(!attr)
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), attrDecl, "attribute class did not yield an attribute object");
+ return uncheckedAttr;
+ }
+
+ // We are going to replace the unchecked attribute with the checked one.
+
+ // First copy all of the state over from the original attribute.
+ attr->name = uncheckedAttr->name;
+ attr->args = uncheckedAttr->args;
+ attr->loc = uncheckedAttr->loc;
+
+ // We will start with checking steps that can be applied independent
+ // of the concrete attribute type that was selected. These only need
+ // us to look at the attribute declaration itself.
+ //
+ // Start by doing argument/parameter matching
+ UInt argCount = attr->args.Count();
+ UInt paramCounter = 0;
+ bool mismatch = false;
+ for(auto paramDecl : attrDecl->getMembersOfType<ParamDecl>())
+ {
+ UInt paramIndex = paramCounter++;
+ if( paramIndex < argCount )
{
- if (hlslUncheckedAttribute->args.Count() != 1)
- return m;
- auto val = checkConstantIntVal(hlslUncheckedAttribute->args[0]);
- auto hlslMaxVertexCountAttrib = new HLSLMaxVertexCountAttribute();
+ auto arg = attr->args[paramIndex];
+
+ // TODO: support checking the argument against the declared
+ // type for the parameter.
- hlslMaxVertexCountAttrib->loc = hlslUncheckedAttribute->loc;
- hlslMaxVertexCountAttrib->name = hlslUncheckedAttribute->getName();
- hlslMaxVertexCountAttrib->args = hlslUncheckedAttribute->args;
- hlslMaxVertexCountAttrib->value = (int32_t)val->value;
- return hlslMaxVertexCountAttrib;
}
- else if (attribText == "instance")
+ else
{
- if (hlslUncheckedAttribute->args.Count() != 1)
- return m;
- auto val = checkConstantIntVal(hlslUncheckedAttribute->args[0]);
- auto attrib = new HLSLInstanceAttribute();
+ // We didn't have enough arguments for the
+ // number of parameters declared.
+ if(auto defaultArg = paramDecl->initExpr)
+ {
+ // The attribute declaration provided a default,
+ // so we should use that.
+ //
+ // TODO: we need to figure out how to hook up
+ // default arguments as needed.
+ }
+ else
+ {
+ mismatch = true;
+ }
+ }
+ }
+ UInt paramCount = paramCounter;
+
+ if(mismatch)
+ {
+ getSink()->diagnose(attr, Diagnostics::attributeArgumentCountMismatch, attrName, paramCount, argCount);
+ return uncheckedAttr;
+ }
- attrib->loc = hlslUncheckedAttribute->loc;
- attrib->name = hlslUncheckedAttribute->getName();
- attrib->args = hlslUncheckedAttribute->args;
- attrib->value = (int32_t)val->value;
- return attrib;
+ // The next bit of validation that we can apply semi-generically
+ // is to validate that the target for this attribute is a valid
+ // one for the chosen attribute.
+ //
+ // The attribute declaration will have one or more `AttributeTargetModifier`s
+ // that each specify a syntax class that the attribute can be applied to.
+ // If any of these match `attrTarget`, then we are good.
+ //
+ bool validTarget = false;
+ for(auto attrTargetMod : attrDecl->GetModifiersOfType<AttributeTargetModifier>())
+ {
+ if(attrTarget->getClass().isSubClassOf(attrTargetMod->syntaxClass))
+ {
+ validTarget = true;
+ break;
}
}
+ if(!validTarget)
+ {
+ getSink()->diagnose(attr, Diagnostics::attributeNotApplicable, attrName);
+ return uncheckedAttr;
+ }
+
+ // Now apply type-specific validation to the attribute.
+ if(!validateAttribute(attr))
+ {
+ return uncheckedAttr;
+ }
+
+
+ return attr;
+ }
+
+ RefPtr<Modifier> checkModifier(
+ RefPtr<Modifier> m,
+ ModifiableSyntaxNode* syntaxNode)
+ {
+ if(auto hlslUncheckedAttribute = m.As<UncheckedAttribute>())
+ {
+ // We have an HLSL `[name(arg,...)]` attribute, and we'd like
+ // to check that it is provides all the expected arguments
+ //
+ // First, look up the attribute name in the current scope to find
+ // the right syntax class to instantiate.
+ //
+
+ return checkAttribute(hlslUncheckedAttribute, syntaxNode);
+ }
// Default behavior is to leave things as they are,
// and assume that modifiers are mostly already checked.
//
@@ -1505,7 +1648,7 @@ namespace Slang
}
- void checkModifiers(Decl* decl)
+ void checkModifiers(ModifiableSyntaxNode* syntaxNode)
{
// TODO(tfoley): need to make sure this only
// performs semantic checks on a `SharedModifier` once...
@@ -1516,7 +1659,7 @@ namespace Slang
RefPtr<Modifier> resultModifiers;
RefPtr<Modifier>* resultModifierLink = &resultModifiers;
- RefPtr<Modifier> modifier = decl->modifiers.first;
+ RefPtr<Modifier> modifier = syntaxNode->modifiers.first;
while(modifier)
{
// Because we are rewriting the list in place, we need to extract
@@ -1528,7 +1671,7 @@ namespace Slang
// be to return a single unlinked modifier.
modifier->next = nullptr;
- auto checkedModifier = checkModifier(modifier, decl);
+ auto checkedModifier = checkModifier(modifier, syntaxNode);
if(checkedModifier)
{
// If checking gave us a modifier to add, then we
@@ -1552,7 +1695,7 @@ namespace Slang
// Whether we actually re-wrote anything or note, lets
// install the new list of modifiers on the declaration
- decl->modifiers.first = resultModifiers;
+ syntaxNode->modifiers.first = resultModifiers;
}
void visitModuleDecl(ModuleDecl* programNode)
@@ -2182,6 +2325,7 @@ namespace Slang
{
if (!stmt) return;
StmtVisitor::dispatch(stmt);
+ checkModifiers(stmt);
}
void visitFuncDecl(FuncDecl *functionNode)
@@ -2610,11 +2754,14 @@ namespace Slang
{
// TODO: This needs to bottleneck through the common variable checks
- para->type = CheckUsableType(para->type);
-
- if (para->type.Equals(getSession()->getVoidType()))
+ if(para->type.exp)
{
- getSink()->diagnose(para, Diagnostics::parameterCannotBeVoid);
+ para->type = CheckUsableType(para->type);
+
+ if (para->type.Equals(getSession()->getVoidType()))
+ {
+ getSink()->diagnose(para, Diagnostics::parameterCannotBeVoid);
+ }
}
}
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index b0b233b1d..f105b98aa 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -1028,3 +1028,71 @@ for (auto op : binaryOps)
}
}
}}}}
+
+// Statement Attributes
+
+__attributeTarget(LoopStmt)
+attribute_syntax [unroll(count: int = 0)] : UnrollAttribute;
+
+__attributeTarget(LoopStmt)
+attribute_syntax [loop] : LoopAttribute;
+
+__attributeTarget(LoopStmt)
+attribute_syntax [fastopt] : FastOptAttribute;
+
+__attributeTarget(LoopStmt)
+attribute_syntax [allow_uav_condition] : AllowUAVConditionAttribute;
+
+__attributeTarget(IfStmt)
+attribute_syntax [flatten] : FlattenAttribute;
+
+__attributeTarget(IfStmt)
+__attributeTarget(SwitchStmt)
+attribute_syntax [branch] : BranchAttribute;
+
+__attributeTarget(SwitchStmt)
+attribute_syntax [forcecase] : ForceCaseAttribute;
+
+__attributeTarget(SwitchStmt)
+attribute_syntax [call] : CallAttribute;
+
+// Entry-point Attributes
+
+// All Stages
+__attributeTarget(FuncDecl)
+attribute_syntax [shader(stage)] : EntryPointAttribute;
+
+// Hull Shader
+__attributeTarget(FuncDecl)
+attribute_syntax [maxtessfactor(factor: float)] : MaxTessFactorAttribute;
+
+__attributeTarget(FuncDecl)
+attribute_syntax [outputcontrolpoints(count: int)] : OutputControlPointsAttribute;
+
+__attributeTarget(FuncDecl)
+attribute_syntax [outputtopology(topology)] : OuptutTopologyAttribute;
+
+__attributeTarget(FuncDecl)
+attribute_syntax [partitioning(mode)] : PartitioningAttribute;
+
+__attributeTarget(FuncDecl)
+attribute_syntax [patchconstantfunc(name)] : PatchConstantFuncAttribute;
+
+// Hull/Domain Shader
+__attributeTarget(FuncDecl)
+attribute_syntax [domain(domain)] : DomainAttribute;
+
+// Geometry Shader
+__attributeTarget(FuncDecl)
+attribute_syntax [maxvertexcount(count: int)] : MaxVertexCountAttribute;
+
+__attributeTarget(FuncDecl)
+attribute_syntax [instance(count: int)] : InstanceAttribute;
+
+// Fragment ("Pixel") Shader
+__attributeTarget(FuncDecl)
+attribute_syntax [earlydepthstencil] : EarlyDepthStencilAttribute;
+
+// Compute Shader
+__attributeTarget(FuncDecl)
+attribute_syntax [numthreads(x: int, y: int = 1, z: int = 1)] : NumThreadsAttribute;
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index 974607a75..5e1f2ec89 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -1028,3 +1028,71 @@ for (auto op : binaryOps)
}
}
SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW("// Statement Attributes\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(LoopStmt)\n")
+SLANG_RAW("attribute_syntax [unroll(count: int = 0)] : UnrollAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(LoopStmt)\n")
+SLANG_RAW("attribute_syntax [loop] : LoopAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(LoopStmt)\n")
+SLANG_RAW("attribute_syntax [fastopt] : FastOptAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(LoopStmt)\n")
+SLANG_RAW("attribute_syntax [allow_uav_condition] : AllowUAVConditionAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(IfStmt)\n")
+SLANG_RAW("attribute_syntax [flatten] : FlattenAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(IfStmt)\n")
+SLANG_RAW("__attributeTarget(SwitchStmt)\n")
+SLANG_RAW("attribute_syntax [branch] : BranchAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(SwitchStmt)\n")
+SLANG_RAW("attribute_syntax [forcecase] : ForceCaseAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(SwitchStmt)\n")
+SLANG_RAW("attribute_syntax [call] : CallAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("// Entry-point Attributes\n")
+SLANG_RAW("\n")
+SLANG_RAW("// All Stages\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [shader(stage)] : EntryPointAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("// Hull Shader\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [maxtessfactor(factor: float)] : MaxTessFactorAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [outputcontrolpoints(count: int)] : OutputControlPointsAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [outputtopology(topology)] : OuptutTopologyAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [partitioning(mode)] : PartitioningAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [patchconstantfunc(name)] : PatchConstantFuncAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("// Hull/Domain Shader\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [domain(domain)] : DomainAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("// Geometry Shader\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [maxvertexcount(count: int)] : MaxVertexCountAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [instance(count: int)] : InstanceAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("// Fragment (\"Pixel\") Shader\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [earlydepthstencil] : EarlyDepthStencilAttribute;\n")
+SLANG_RAW("\n")
+SLANG_RAW("// Compute Shader\n")
+SLANG_RAW("__attributeTarget(FuncDecl)\n")
+SLANG_RAW("attribute_syntax [numthreads(x: int, y: int = 1, z: int = 1)] : NumThreadsAttribute;\n")
diff --git a/source/slang/decl-defs.h b/source/slang/decl-defs.h
index 8e1985e3f..76480e64b 100644
--- a/source/slang/decl-defs.h
+++ b/source/slang/decl-defs.h
@@ -266,3 +266,10 @@ SYNTAX_CLASS(SyntaxDecl, Decl)
FIELD(SyntaxParseCallback, parseCallback)
FIELD(void*, parseUserData)
END_SYNTAX_CLASS()
+
+// A declaration of an attribute to be used with `[name(...)]` syntax.
+//
+SYNTAX_CLASS(AttributeDecl, ContainerDecl)
+ // What type of syntax node will be produced to represent this attribute.
+ FIELD(SyntaxClass<RefObject>, syntaxClass)
+END_SYNTAX_CLASS()
diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h
index 220ad44ff..ae815cd40 100644
--- a/source/slang/diagnostic-defs.h
+++ b/source/slang/diagnostic-defs.h
@@ -197,6 +197,11 @@ 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.")
+// 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")
+
// 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.")
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 58c917fe7..aee19018a 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -2809,23 +2809,7 @@ struct EmitVisitor
void EmitLoopAttributes(RefPtr<Stmt> decl)
{
- // Don't emit these attributes for GLSL, because it doesn't understand them
- if (context->shared->target == CodeGenTarget::GLSL)
- return;
-
- // TODO(tfoley): There really ought to be a semantic checking step for attributes,
- // that turns abstract syntax into a concrete hierarchy of attribute types (e.g.,
- // a specific `LoopModifier` or `UnrollModifier`).
-
- for(auto attr : decl->GetModifiersOfType<HLSLUncheckedAttribute>())
- {
- // Emit whatever attributes the user might have attached,
- // whether or not we think they make semantic sense.
- //
- Emit("[");
- emit(attr->getName());
- Emit("]");
- }
+ // NOTE: Emit-from-AST is gone, so this code is doing nothing.
}
void EmitUnparsedStmt(RefPtr<UnparsedStmt> stmt)
@@ -3134,6 +3118,7 @@ struct EmitVisitor
// Only used by stdlib
IGNORED(SyntaxDecl)
+ IGNORED(AttributeDecl)
// Don't emit generic decls directly; we will only
// ever emit particular instantiations of them.
@@ -3346,27 +3331,6 @@ struct EmitVisitor
}
}
- // TODO: eventually we should be checked these modifiers, but for
- // now we can emit them unchecked, I guess
- else if (auto uncheckedAttr = mod.As<HLSLAttribute>())
- {
- Emit("[");
- emit(uncheckedAttr->getNameAndLoc());
- auto& args = uncheckedAttr->args;
- auto argCount = args.Count();
- if (argCount != 0)
- {
- Emit("(");
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(", ");
- EmitExpr(args[aa]);
- }
- Emit(")");
- }
- Emit("]");
- }
-
else if(auto simpleModifier = mod.As<SimpleModifier>())
{
emit(simpleModifier->getNameAndLoc());
@@ -6756,13 +6720,13 @@ emitDeclImpl(decl, nullptr);
break;
case Stage::Geometry:
{
- if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLMaxVertexCountAttribute>())
+ if (auto attrib = entryPointLayout->entryPoint->FindModifier<MaxVertexCountAttribute>())
{
emit("[maxvertexcount(");
Emit(attrib->value);
emit(")]\n");
}
- if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLInstanceAttribute>())
+ if (auto attrib = entryPointLayout->entryPoint->FindModifier<InstanceAttribute>())
{
emit("[instance(");
Emit(attrib->value);
@@ -6813,13 +6777,13 @@ emitDeclImpl(decl, nullptr);
break;
case Stage::Geometry:
{
- if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLMaxVertexCountAttribute>())
+ if (auto attrib = entryPointLayout->entryPoint->FindModifier<MaxVertexCountAttribute>())
{
emit("layout(max_vertices = ");
Emit(attrib->value);
emit(") out;\n");
}
- if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLInstanceAttribute>())
+ if (auto attrib = entryPointLayout->entryPoint->FindModifier<InstanceAttribute>())
{
emit("layout(invocations = ");
Emit(attrib->value);
diff --git a/source/slang/lookup.cpp b/source/slang/lookup.cpp
index e5ffa00f7..eebef6503 100644
--- a/source/slang/lookup.cpp
+++ b/source/slang/lookup.cpp
@@ -96,6 +96,11 @@ bool DeclPassesLookupMask(Decl* decl, LookupMask mask)
{
return (int(mask) & int(LookupMask::Function)) != 0;
}
+ // attribute declaration
+ else if( auto attrDecl = dynamic_cast<AttributeDecl*>(decl) )
+ {
+ return (int(mask) & int(LookupMask::Attribute)) != 0;
+ }
// default behavior is to assume a value declaration
// (no overloading allowed)
@@ -423,11 +428,13 @@ LookupResult lookUp(
Session* session,
SemanticsVisitor* semantics,
Name* name,
- RefPtr<Scope> scope)
+ RefPtr<Scope> scope,
+ LookupMask mask)
{
LookupRequest request;
request.semantics = semantics;
request.scope = scope;
+ request.mask = mask;
return DoLookup(session, name, request);
}
@@ -437,10 +444,12 @@ LookupResult lookUpLocal(
Session* session,
SemanticsVisitor* semantics,
Name* name,
- DeclRef<ContainerDecl> containerDeclRef)
+ DeclRef<ContainerDecl> containerDeclRef,
+ LookupMask mask)
{
LookupRequest request;
request.semantics = semantics;
+ request.mask = mask;
LookupResult result;
DoLocalLookupImpl(session, name, containerDeclRef, request, result, nullptr);
@@ -448,12 +457,13 @@ LookupResult lookUpLocal(
}
void lookUpMemberImpl(
- Session* session,
+ Session* session,
SemanticsVisitor* semantics,
Name* name,
Type* type,
LookupResult& ioResult,
- BreadcrumbInfo* inBreadcrumbs)
+ BreadcrumbInfo* inBreadcrumbs,
+ LookupMask mask)
{
if (auto declRefType = type->As<DeclRefType>())
{
@@ -475,7 +485,7 @@ void lookUpMemberImpl(
breadcrumb.declRef = constraintDeclRef;
// TODO: Need to consider case where this might recurse infinitely.
- lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb);
+ lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb, mask);
}
}
else if (auto aggTypeDeclRef = declRef.As<AggTypeDecl>())
@@ -517,7 +527,7 @@ void lookUpMemberImpl(
breadcrumb.declRef = constraintDeclRef;
// TODO: Need to consider case where this might recurse infinitely.
- lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb);
+ lookUpMemberImpl(session, semantics, name, bound, ioResult, &breadcrumb, mask);
}
}
@@ -529,10 +539,11 @@ LookupResult lookUpMember(
Session* session,
SemanticsVisitor* semantics,
Name* name,
- Type* type)
+ Type* type,
+ LookupMask mask)
{
LookupResult result;
- lookUpMemberImpl(session, semantics, name, type, result, nullptr);
+ lookUpMemberImpl(session, semantics, name, type, result, nullptr, mask);
return result;
}
diff --git a/source/slang/lookup.h b/source/slang/lookup.h
index 473ecaf96..37ab5cf06 100644
--- a/source/slang/lookup.h
+++ b/source/slang/lookup.h
@@ -21,7 +21,8 @@ LookupResult lookUp(
Session* session,
SemanticsVisitor* semantics,
Name* name,
- RefPtr<Scope> scope);
+ RefPtr<Scope> scope,
+ LookupMask mask = LookupMask::Default);
// perform lookup within the context of a particular container declaration,
// and do *not* look further up the chain
@@ -29,14 +30,16 @@ LookupResult lookUpLocal(
Session* session,
SemanticsVisitor* semantics,
Name* name,
- DeclRef<ContainerDecl> containerDeclRef);
+ DeclRef<ContainerDecl> containerDeclRef,
+ LookupMask mask = LookupMask::Default);
// Perform member lookup in the context of a type
LookupResult lookUpMember(
Session* session,
SemanticsVisitor* semantics,
Name* name,
- Type* type);
+ Type* type,
+ LookupMask mask = LookupMask::Default);
// TODO: this belongs somewhere else
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index cfd1c89a4..428044830 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -2004,17 +2004,12 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
IRInst* inst,
Stmt* stmt)
{
- for(auto attr : stmt->GetModifiersOfType<HLSLUncheckedAttribute>())
+ if( stmt->FindModifier<UnrollAttribute>() )
{
- // TODO: We should actually catch these attributes during
- // semantic checking, so that they have a strongly-typed
- // representation in the AST.
- if(getText(attr->getName()) == "unroll")
- {
- auto decoration = getBuilder()->addDecoration<IRLoopControlDecoration>(inst);
- decoration->mode = kIRLoopControl_Unroll;
- }
+ auto decoration = getBuilder()->addDecoration<IRLoopControlDecoration>(inst);
+ decoration->mode = kIRLoopControl_Unroll;
}
+ // TODO: handle other cases here
}
void visitForStmt(ForStmt* stmt)
@@ -2794,6 +2789,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
return LoweredValInfo();
}
+ LoweredValInfo visitAttributeDecl(AttributeDecl* /*decl*/)
+ {
+ return LoweredValInfo();
+ }
+
LoweredValInfo visitTypeDefDecl(TypeDefDecl * decl)
{
return LoweredValInfo::simple(context->irBuilder->getTypeVal(decl->type.type));
diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h
index 725629d35..fdbb05748 100644
--- a/source/slang/modifier-defs.h
+++ b/source/slang/modifier-defs.h
@@ -288,34 +288,88 @@ SIMPLE_SYNTAX_CLASS(HLSLUniformModifier, Modifier)
// HLSL `volatile` modifier (ignored)
SIMPLE_SYNTAX_CLASS(HLSLVolatileModifier, Modifier)
-// An HLSL `[name(arg0, ...)]` style attribute.
-SYNTAX_CLASS(HLSLAttribute, Modifier)
+SYNTAX_CLASS(AttributeTargetModifier, Modifier)
+ // A class to which the declared attribute type is applicable
+ FIELD(SyntaxClass<RefObject>, syntaxClass)
+END_SYNTAX_CLASS()
+
+// Base class for checked and unchecked `[name(arg0, ...)]` style attribute.
+SYNTAX_CLASS(AttributeBase, Modifier)
SYNTAX_FIELD(List<RefPtr<Expr>>, args)
END_SYNTAX_CLASS()
-// An HLSL `[name(...)]` attribute that hasn't undergone
-// any semantic analysis.
-// After analysis, this might be transformed into a more specific case.
-SIMPLE_SYNTAX_CLASS(HLSLUncheckedAttribute, HLSLAttribute)
+// A `[name(...)]` attribute that hasn't undergone any semantic analysis.
+// After analysis, this will be transformed into a more specific case.
+SYNTAX_CLASS(UncheckedAttribute, AttributeBase)
+ FIELD(RefPtr<Scope>, scope)
+END_SYNTAX_CLASS()
+
+// A `[name(arg0, ...)]` style attribute that has been validated.
+SYNTAX_CLASS(Attribute, AttributeBase)
+END_SYNTAX_CLASS()
+
+// An `[unroll]` or `[unroll(count)]` attribute
+SYNTAX_CLASS(UnrollAttribute, Attribute)
+ RAW(IntegerLiteralValue getCount();)
+END_SYNTAX_CLASS()
+
+SIMPLE_SYNTAX_CLASS(LoopAttribute, Attribute) // `[loop]`
+SIMPLE_SYNTAX_CLASS(FastOptAttribute, Attribute) // `[fastopt]`
+SIMPLE_SYNTAX_CLASS(AllowUAVConditionAttribute, Attribute) // `[allow_uav_condition]`
+SIMPLE_SYNTAX_CLASS(BranchAttribute, Attribute) // `[branch]`
+SIMPLE_SYNTAX_CLASS(FlattenAttribute, Attribute) // `[flatten]`
+SIMPLE_SYNTAX_CLASS(ForceCaseAttribute, Attribute) // `[forcecase]`
+SIMPLE_SYNTAX_CLASS(CallAttribute, Attribute) // `[call]`
+
+// TODO: for attributes that take arguments, the syntax node
+// classes should provide accessors for the values of those arguments.
+
+SIMPLE_SYNTAX_CLASS(MaxTessFactorAttribute, Attribute)
+SIMPLE_SYNTAX_CLASS(OutputControlPointsAttribute, Attribute)
+SIMPLE_SYNTAX_CLASS(OuptutTopologyAttribute, Attribute)
+SIMPLE_SYNTAX_CLASS(PartitioningAttribute, Attribute)
+SIMPLE_SYNTAX_CLASS(PatchConstantFuncAttribute, Attribute)
+SIMPLE_SYNTAX_CLASS(DomainAttribute, Attribute)
+
+SIMPLE_SYNTAX_CLASS(EarlyDepthStencilAttribute, Attribute)
// An HLSL `[numthreads(x,y,z)]` attribute
-SYNTAX_CLASS(HLSLNumThreadsAttribute, HLSLAttribute)
+SYNTAX_CLASS(NumThreadsAttribute, Attribute)
// The number of threads to use along each axis
+ //
+ // TODO: These should be accessors that use the
+ // ordinary `args` list, rather than side data.
FIELD(int32_t, x)
FIELD(int32_t, y)
FIELD(int32_t, z)
END_SYNTAX_CLASS()
-SYNTAX_CLASS(HLSLMaxVertexCountAttribute, HLSLAttribute)
+SYNTAX_CLASS(MaxVertexCountAttribute, Attribute)
// The number of max vertex count for geometry shader
+ //
+ // TODO: This should be an accessor that uses the
+ // ordinary `args` list, rather than side data.
FIELD(int32_t, value)
END_SYNTAX_CLASS()
-SYNTAX_CLASS(HLSLInstanceAttribute, HLSLAttribute)
+SYNTAX_CLASS(InstanceAttribute, Attribute)
// The number of instances to run for geometry shader
+ //
+ // TODO: This should be an accessor that uses the
+ // ordinary `args` list, rather than side data.
FIELD(int32_t, value)
END_SYNTAX_CLASS()
+// A `[shader("stageName")]` attribute, which marks an entry point
+// to be compiled, and specifies the stage for that entry point
+SYNTAX_CLASS(EntryPointAttribute, Attribute)
+ // The resolved stage that the entry point is targetting.
+ //
+ // TODO: This should be an accessor that uses the
+ // ordinary `args` list, rather than side data.
+ FIELD(Stage, stage);
+END_SYNTAX_CLASS()
+
// HLSL modifiers for geometry shader input topology
SIMPLE_SYNTAX_CLASS(HLSLGeometryShaderInputPrimitiveTypeModifier, Modifier)
SIMPLE_SYNTAX_CLASS(HLSLPointModifier , HLSLGeometryShaderInputPrimitiveTypeModifier)
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index 82f79af17..5fb7445e9 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -602,10 +602,20 @@ namespace Slang
parser->ReadToken(TokenType::LBracket);
for(;;)
{
+ // Note: When parsing we just construct an AST node for an
+ // "unchecked" attribute, and defer all detailed semantic
+ // checking until later.
+ //
+ // An alternative would be to perform lookup of an `AttributeDecl`
+ // at this point, similar to what we do for `SyntaxDecl`, but it
+ // seems better to not complicate the parsing process any more.
+ //
+
auto nameToken = parser->ReadToken(TokenType::Identifier);
- RefPtr<HLSLUncheckedAttribute> modifier = new HLSLUncheckedAttribute();
+ RefPtr<UncheckedAttribute> modifier = new UncheckedAttribute();
modifier->name = nameToken.getName();
modifier->loc = nameToken.getLoc();
+ modifier->scope = parser->currentScope;
if (AdvanceIf(parser, TokenType::LParent))
{
@@ -2500,6 +2510,107 @@ namespace Slang
return syntaxDecl;
}
+ // A parameter declaration in an attribute declaration.
+ //
+ // We are going to use `name: type` syntax just for simplicty, and let the type
+ // be optional, because we don't actually need it in all cases.
+ //
+ static RefPtr<ParamDecl> parseAttributeParamDecl(Parser* parser)
+ {
+ auto nameAndLoc = expectIdentifier(parser);
+
+ RefPtr<ParamDecl> paramDecl = new ParamDecl();
+ paramDecl->nameAndLoc = nameAndLoc;
+
+ if(AdvanceIf(parser, TokenType::Colon))
+ {
+ paramDecl->type = parser->ParseTypeExp();
+ }
+
+ if(AdvanceIf(parser, TokenType::OpAssign))
+ {
+ paramDecl->initExpr = parser->ParseInitExpr();
+ }
+
+ return paramDecl;
+ }
+
+ // Parse declaration of a name to be used for resolving `[attribute(...)]` style modifiers.
+ //
+ // These are distinct from `syntax` declarations, because their names don't get added
+ // to the current scope using their default name.
+ //
+ // Also, attribute-specific code doesn't get invokved during parsing. We always parse
+ // using the default attribute-parsing logic and then all specialized behavior takes
+ // place during semantic checking.
+ //
+ static RefPtr<RefObject> parseAttributeSyntaxDecl(Parser* parser, void* /*userData*/)
+ {
+ // Right now the basic form is:
+ //
+ // attribute_syntax <name:id> : <syntaxClass:id>;
+ //
+ // - `name` gives the name of the attribute to define.
+ // - `syntaxClass` is the name of an AST node class that we expect
+ // this attribute to create when checked.
+ // - `existingKeyword` is the name of an existing keyword that
+ // the new syntax should be an alias for.
+
+ expect(parser, TokenType::LBracket);
+
+ // First we parse the attribute name.
+ auto nameAndLoc = expectIdentifier(parser);
+
+ RefPtr<AttributeDecl> attrDecl = new AttributeDecl();
+ if(AdvanceIf(parser, TokenType::LParent))
+ {
+ while(!AdvanceIfMatch(parser, TokenType::RParent))
+ {
+ auto param = parseAttributeParamDecl(parser);
+
+ AddMember(attrDecl, param);
+
+ if(AdvanceIfMatch(parser, TokenType::RParent))
+ break;
+
+ expect(parser, TokenType::Comma);
+ }
+ }
+
+ expect(parser, TokenType::RBracket);
+
+ // TODO: we should allow parameters to be specified here, to cut down
+ // on the amount of per-attribute-type logic that has to occur later.
+
+ // Next we look for a clause that specified the AST node class.
+ SyntaxClass<RefObject> syntaxClass;
+ if (AdvanceIf(parser, TokenType::Colon))
+ {
+ // User is specifying the class that should be construted
+ auto classNameAndLoc = expectIdentifier(parser);
+
+ syntaxClass = parser->getSession()->findSyntaxClass(classNameAndLoc.name);
+ }
+ else
+ {
+ // For now we don't support the alternative approach where
+ // an existing piece of syntax is named to provide the parsing
+ // support.
+
+ // TODO: diagnose: a syntax class must be specified.
+ }
+
+ expect(parser, TokenType::Semicolon);
+
+ // TODO: skip creating the declaration if anything failed, just to not screw things
+ // up for downstream code?
+
+ attrDecl->nameAndLoc = nameAndLoc;
+ attrDecl->loc = nameAndLoc.loc;
+ attrDecl->syntaxClass = syntaxClass;
+ return attrDecl;
+ }
+
// Finish up work on a declaration that was parsed
static void CompleteDecl(
Parser* /*parser*/,
@@ -4182,6 +4293,20 @@ namespace Slang
return modifier;
}
+ static RefPtr<RefObject> parseAttributeTargetModifier(Parser* parser, void* /*userData*/)
+ {
+ expect(parser, TokenType::LParent);
+ auto syntaxClassNameAndLoc = expectIdentifier(parser);
+ expect(parser, TokenType::RParent);
+
+ auto syntaxClass = parser->getSession()->findSyntaxClass(syntaxClassNameAndLoc.name);
+
+ RefPtr<AttributeTargetModifier> modifier = new AttributeTargetModifier();
+ modifier->syntaxClass = syntaxClass;
+
+ return modifier;
+ }
+
RefPtr<ModuleDecl> populateBaseLanguageModule(
Session* session,
RefPtr<Scope> scope)
@@ -4205,6 +4330,7 @@ namespace Slang
DECL(__subscript, ParseSubscriptDecl);
DECL(interface, parseInterfaceDecl);
DECL(syntax, parseSyntaxDecl);
+ DECL(attribute_syntax,parseAttributeSyntaxDecl);
DECL(__import, parseImportDecl);
DECL(import, parseImportDecl);
@@ -4280,6 +4406,9 @@ namespace Slang
MODIFIER(__intrinsic_type, parseIntrinsicTypeModifier);
MODIFIER(__implicit_conversion, parseImplicitConversionModifier);
+ MODIFIER(__attributeTarget, parseAttributeTargetModifier);
+
+
#undef MODIFIER
// Add syntax for expression keywords
diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp
index 708b98f2b..65901d6af 100644
--- a/source/slang/reflection.cpp
+++ b/source/slang/reflection.cpp
@@ -961,7 +961,7 @@ SLANG_API void spReflectionEntryPoint_getComputeThreadGroupSize(
SlangUInt sizeAlongAxis[3] = { 1, 1, 1 };
// First look for the HLSL case, where we have an attribute attached to the entry point function
- auto numThreadsAttribute = entryPointFunc->FindModifier<HLSLNumThreadsAttribute>();
+ auto numThreadsAttribute = entryPointFunc->FindModifier<NumThreadsAttribute>();
if (numThreadsAttribute)
{
sizeAlongAxis[0] = numThreadsAttribute->x;
diff --git a/source/slang/syntax-base-defs.h b/source/slang/syntax-base-defs.h
index 2c15f7215..4fded014e 100644
--- a/source/slang/syntax-base-defs.h
+++ b/source/slang/syntax-base-defs.h
@@ -240,7 +240,7 @@ END_SYNTAX_CLASS()
// (that is, we don't use a bitfield, even for simple/common flags).
// This ensures that we can track source locations for all modifiers.
//
-ABSTRACT_SYNTAX_CLASS(Modifier, SyntaxNodeBase)
+ABSTRACT_SYNTAX_CLASS(Modifier, SyntaxNode)
RAW(typedef IModifierVisitor Visitor;)
RAW(virtual void accept(IModifierVisitor* visitor, void* extra) = 0;)
diff --git a/source/slang/syntax.h b/source/slang/syntax.h
index 0dbdd8238..0f23492d6 100644
--- a/source/slang/syntax.h
+++ b/source/slang/syntax.h
@@ -805,8 +805,9 @@ namespace Slang
type = 0x1,
Function = 0x2,
Value = 0x4,
+ Attribute = 0x8,
- All = type | Function | Value,
+ Default = type | Function | Value,
};
// Represents one item found during lookup
@@ -1000,7 +1001,7 @@ namespace Slang
RefPtr<Scope> scope = nullptr;
RefPtr<Scope> endScope = nullptr;
- LookupMask mask = LookupMask::All;
+ LookupMask mask = LookupMask::Default;
};
// Generate class definition for all syntax classes
diff --git a/tests/compute/loop-unroll.slang b/tests/compute/loop-unroll.slang
index 88568d1dd..b8ac97b44 100644
--- a/tests/compute/loop-unroll.slang
+++ b/tests/compute/loop-unroll.slang
@@ -17,13 +17,16 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
{
uint tid = dispatchThreadID.x;
- int val = buffers[1][tid];
+ // Note: using `unroll` as a variable name to validate that
+ // the lookup process for attribute names doesn't run into
+ // problems because of local declarations with the same name.
+ int unroll = buffers[1][tid];
[unroll]
for(int ii = 0; ii < 2; ii++)
{
- val = buffers[ii][val];
+ unroll = buffers[ii][unroll];
}
- buffers[0][tid] = val;
+ buffers[0][tid] = unroll;
} \ No newline at end of file