summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/bytecode.cpp16
-rw-r--r--source/slang/compiler.h12
-rw-r--r--source/slang/core.meta.slang28
-rw-r--r--source/slang/core.meta.slang.h28
-rw-r--r--source/slang/diagnostic-defs.h2
-rw-r--r--source/slang/emit.cpp72
-rw-r--r--source/slang/ir-constexpr.cpp531
-rw-r--r--source/slang/ir-constexpr.h12
-rw-r--r--source/slang/ir-insts.h15
-rw-r--r--source/slang/ir-legalize-types.cpp8
-rw-r--r--source/slang/ir-ssa.cpp10
-rw-r--r--source/slang/ir.cpp85
-rw-r--r--source/slang/ir.h5
-rw-r--r--source/slang/lower-to-ir.cpp136
-rw-r--r--source/slang/modifier-defs.h1
-rw-r--r--source/slang/slang.cpp1
-rw-r--r--source/slang/slang.vcxproj2
-rw-r--r--source/slang/slang.vcxproj.filters2
-rw-r--r--source/slang/syntax.cpp101
-rw-r--r--source/slang/type-defs.h39
20 files changed, 1002 insertions, 104 deletions
diff --git a/source/slang/bytecode.cpp b/source/slang/bytecode.cpp
index eb6b755fc..6b6e4170e 100644
--- a/source/slang/bytecode.cpp
+++ b/source/slang/bytecode.cpp
@@ -319,7 +319,7 @@ void encodeOperand(
bool opHasResult(IRValue* inst)
{
- auto type = inst->getType();
+ auto type = inst->getDataType();
if (!type) return false;
// As a bit of a hack right now, we need to check whether
@@ -352,7 +352,7 @@ void generateBytecodeForInst(
auto argCount = inst->getArgCount();
encodeUInt(context, inst->op);
- encodeOperand(context, inst->getType());
+ encodeOperand(context, inst->getDataType());
encodeUInt(context, argCount);
for( UInt aa = 0; aa < argCount; ++aa )
{
@@ -381,7 +381,7 @@ void generateBytecodeForInst(
{
auto ii = (IRConstant*) inst;
encodeUInt(context, ii->op);
- encodeOperand(context, ii->getType());
+ encodeOperand(context, ii->getDataType());
// TODO: probably want distinct encodings
// for signed vs. unsigned here.
@@ -396,7 +396,7 @@ void generateBytecodeForInst(
{
auto cInst = (IRConstant*) inst;
encodeUInt(context, cInst->op);
- encodeOperand(context, cInst->getType());
+ encodeOperand(context, cInst->getDataType());
static const UInt size = sizeof(IRFloatingPointValue);
unsigned char buffer[size];
@@ -446,7 +446,7 @@ void generateBytecodeForInst(
// We need to encode the type being stored, to make
// our lives easier.
- encodeOperand(context, inst->getArg(1)->getType());
+ encodeOperand(context, inst->getArg(1)->getDataType());
encodeOperand(context, inst->getArg(0));
encodeOperand(context, inst->getArg(1));
}
@@ -455,7 +455,7 @@ void generateBytecodeForInst(
case kIROp_Load:
{
encodeUInt(context, inst->op);
- encodeOperand(context, inst->getType());
+ encodeOperand(context, inst->getDataType());
encodeOperand(context, inst->getArg(0));
encodeOperand(context, inst);
}
@@ -611,7 +611,7 @@ uint32_t getTypeIDForGlobalSymbol(
BytecodeGenerationContext* context,
IRValue* inst)
{
- auto type = inst->getType();
+ auto type = inst->getDataType();
if(!type)
return 0;
@@ -836,7 +836,7 @@ BytecodeGenerationPtr<BCSymbol> generateBytecodeSymbolForInst(
bcRegs[localID+1].op = ii->op;
bcRegs[localID+1].previousVarIndexPlusOne = (uint32_t)localID+1;
bcRegs[localID+1].typeID = getTypeID(context,
- (ii->getType()->As<PtrType>())->getValueType());
+ (ii->getDataType()->As<PtrType>())->getValueType());
}
break;
}
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index 845de5c81..fe3ee9533 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -448,6 +448,7 @@ namespace Slang
RefPtr<Type> errorType;
RefPtr<Type> initializerListType;
RefPtr<Type> overloadedType;
+ RefPtr<Type> constExprRate;
RefPtr<Type> irBasicBlockType;
Dictionary<int, RefPtr<Type>> builtinTypes;
@@ -467,6 +468,17 @@ namespace Slang
Type* getOverloadedType();
Type* getErrorType();
+ Type* getConstExprRate();
+ RefPtr<RateQualifiedType> getRateQualifiedType(
+ Type* rate,
+ Type* valueType);
+
+ RefPtr<RateQualifiedType> getConstExprType(
+ Type* valueType)
+ {
+ return getRateQualifiedType(getConstExprRate(), valueType);
+ }
+
// Should not be used in front-end code
Type* getIRBasicBlockType();
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index b395bd08b..d54eea94d 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -1,5 +1,9 @@
// Slang `core` library
+// Modifier for variables that must resolve to compile-time constants
+// as part of translation.
+syntax constexpr : ConstExprModifier;
+
// A type that can be used as an operand for builtins
interface __BuiltinType {}
@@ -623,7 +627,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
{
sb << ", int sampleIndex";
}
- sb << ", int" << loadCoordCount << " offset";
+ sb << ", constexpr int" << loadCoordCount << " offset";
sb << ");\n";
@@ -633,7 +637,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
{
sb << ", int sampleIndex";
}
- sb << ", int" << kBaseTextureTypes[tt].coordCount << " offset";
+ sb << ", constexpr int" << kBaseTextureTypes[tt].coordCount << " offset";
sb << ", out uint status";
sb << ");\n";
}
@@ -699,14 +703,14 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"textureOffset($$p, $2, $3)\")\n";
sb << "T Sample(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
sb << "T Sample(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
if( baseShape != TextureType::ShapeCube )
{
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, ";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset, ";
}
sb << "float clamp);\n";
@@ -714,7 +718,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
if( baseShape != TextureType::ShapeCube )
{
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, ";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset, ";
}
sb << "float clamp, out uint status);\n";
@@ -729,7 +733,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"textureOffset($$p, $2, $3, $4)\")\n";
sb << "T SampleBias(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
// `SampleCmp()` and `SampleCmpLevelZero`
@@ -796,12 +800,12 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "T SampleCmp(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
sb << "T SampleCmpLevelZero(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
@@ -821,7 +825,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradY, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
// `SampleLevel`
@@ -837,7 +841,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "T SampleLevel(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float level, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
}
@@ -908,12 +912,12 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"textureGatherOffset($$p, $2, $3, " << componentIndex << ")\")\n";
sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " location, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
EMIT_LINE_DIRECTIVE();
sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " location, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, ";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset, ";
sb << "out uint status);\n";
EMIT_LINE_DIRECTIVE();
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index 054174d3f..4d63bac5f 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -1,5 +1,9 @@
sb << "// Slang `core` library\n";
sb << "\n";
+sb << "// Modifier for variables that must resolve to compile-time constants\n";
+sb << "// as part of translation.\n";
+sb << "syntax constexpr : ConstExprModifier;\n";
+sb << "\n";
sb << "// A type that can be used as an operand for builtins\n";
sb << "interface __BuiltinType {}\n";
sb << "\n";
@@ -626,7 +630,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
{
sb << ", int sampleIndex";
}
- sb << ", int" << loadCoordCount << " offset";
+ sb << ", constexpr int" << loadCoordCount << " offset";
sb << ");\n";
@@ -636,7 +640,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
{
sb << ", int sampleIndex";
}
- sb << ", int" << kBaseTextureTypes[tt].coordCount << " offset";
+ sb << ", constexpr int" << kBaseTextureTypes[tt].coordCount << " offset";
sb << ", out uint status";
sb << ");\n";
}
@@ -702,14 +706,14 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"textureOffset($p, $2, $3)\")\n";
sb << "T Sample(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
sb << "T Sample(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
if( baseShape != TextureType::ShapeCube )
{
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, ";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset, ";
}
sb << "float clamp);\n";
@@ -717,7 +721,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
if( baseShape != TextureType::ShapeCube )
{
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, ";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset, ";
}
sb << "float clamp, out uint status);\n";
@@ -732,7 +736,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"textureOffset($p, $2, $3, $4)\")\n";
sb << "T SampleBias(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
// `SampleCmp()` and `SampleCmpLevelZero`
@@ -799,12 +803,12 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "T SampleCmp(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
sb << "T SampleCmpLevelZero(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
@@ -824,7 +828,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradY, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
// `SampleLevel`
@@ -840,7 +844,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "T SampleLevel(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float level, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
}
}
@@ -911,12 +915,12 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"textureGatherOffset($p, $2, $3, " << componentIndex << ")\")\n";
sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " location, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
EMIT_LINE_DIRECTIVE();
sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " location, ";
- sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, ";
+ sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset, ";
sb << "out uint status);\n";
EMIT_LINE_DIRECTIVE();
diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h
index 82aed2d69..06e553072 100644
--- a/source/slang/diagnostic-defs.h
+++ b/source/slang/diagnostic-defs.h
@@ -286,6 +286,8 @@ DIAGNOSTIC(40005, Error, topLevelModuleUsedWithoutSpecifyingBinding, "top level
DIAGNOSTIC(49999, Error, unknownSystemValueSemantic, "unknown system-value semantic '$0'")
+DIAGNOSTIC(40006, Error, needCompileTimeConstant, "expected a compile-time constant");
+
//
// 5xxxx - Target code generation.
//
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index d2ed1c7c3..3fa50b928 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -1297,6 +1297,17 @@ struct EmitVisitor
emitTypeImpl(arrayType->baseType, &arrayDeclarator);
}
+ void visitRateQualifiedType(RateQualifiedType* type, TypeEmitArg const& arg)
+ {
+ emitTypeImpl(type->valueType, arg.declarator);
+ }
+
+ void visitConstExprRate(ConstExprRate* /*rate*/, TypeEmitArg const& /*arg*/)
+ {
+ // This should never appear as a data type
+ SLANG_UNEXPECTED("Rates not expected during emit");
+ }
+
void visitGroupSharedType(GroupSharedType* type, TypeEmitArg const& arg)
{
switch(getTarget(context))
@@ -4924,7 +4935,7 @@ emitDeclImpl(decl, nullptr);
// Certain *types* will usually want to be folded in,
// because they aren't allowed as types for temporary
// variables.
- auto type = inst->getType();
+ auto type = inst->getDataType();
while (auto ptrType = type->As<PtrTypeBase>())
{
@@ -4975,7 +4986,7 @@ emitDeclImpl(decl, nullptr);
EmitContext* /*context*/,
IRValue* inst)
{
- auto type = inst->getType();
+ auto type = inst->getDataType();
if(type->As<UniformParameterGroupType>() && !type->As<ParameterBlockType>())
{
@@ -5052,17 +5063,47 @@ emitDeclImpl(decl, nullptr);
EmitType(type);
}
+ void emitIRRateQualifiers(
+ EmitContext* ctx,
+ Type* rate)
+ {
+ if(!rate) return;
+
+ if( auto constExprRate = rate->As<ConstExprRate>() )
+ {
+ switch( getTarget(ctx) )
+ {
+ case CodeGenTarget::GLSL:
+ emit("const ");
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ void emitIRRateQualifiers(
+ EmitContext* ctx,
+ IRValue* value)
+ {
+ emitIRRateQualifiers(ctx, value->getRate());
+ }
+
+
void emitIRInstResultDecl(
EmitContext* ctx,
IRInst* inst)
{
- auto type = inst->getType();
+ auto type = inst->getDataType();
if(!type)
return;
if (type->Equals(getSession()->getVoidType()))
return;
+ emitIRRateQualifiers(ctx, inst);
+
emitIRType(ctx, type, getIRName(inst));
emit(" = ");
}
@@ -5651,13 +5692,13 @@ emitDeclImpl(decl, nullptr);
{
// Need to emit as cast for HLSL
emit("(");
- emitIRType(ctx, inst->getType());
+ emitIRType(ctx, inst->getDataType());
emit(") ");
emitIROperand(ctx, inst->getArg(0), mode);
}
else
{
- emitIRType(ctx, inst->getType());
+ emitIRType(ctx, inst->getDataType());
emitIRArgs(ctx, inst, mode);
}
break;
@@ -5667,12 +5708,12 @@ emitDeclImpl(decl, nullptr);
if( getTarget(ctx) == CodeGenTarget::HLSL )
{
emit("(");
- emitIRType(ctx, inst->getType());
+ emitIRType(ctx, inst->getDataType());
emit(")");
}
else
{
- emitIRType(ctx, inst->getType());
+ emitIRType(ctx, inst->getDataType());
}
emit("(");
emitIROperand(ctx, inst->getArg(0), mode);
@@ -5769,7 +5810,7 @@ emitDeclImpl(decl, nullptr);
case kIROp_Not:
{
- if (inst->getType()->Equals(getSession()->getBoolType()))
+ if (inst->getDataType()->Equals(getSession()->getBoolType()))
{
emit("!");
}
@@ -5976,7 +6017,7 @@ emitDeclImpl(decl, nullptr);
case kIROp_undefined:
{
- auto type = inst->getType();
+ auto type = inst->getDataType();
emitIRType(ctx, type, getIRName(inst));
emit(";\n");
}
@@ -5984,7 +6025,7 @@ emitDeclImpl(decl, nullptr);
case kIROp_Var:
{
- auto ptrType = inst->getType();
+ auto ptrType = inst->getDataType();
auto valType = ((PtrType*)ptrType)->getValueType();
auto name = getIRName(inst);
@@ -6801,7 +6842,7 @@ emitDeclImpl(decl, nullptr);
{
for (auto pp = bb->getFirstParam(); pp; pp = pp->getNextParam())
{
- emitIRType(ctx, pp->getType(), getIRName(pp));
+ emitIRType(ctx, pp->getDataType(), getIRName(pp));
emit(";\n");
}
}
@@ -6833,7 +6874,7 @@ emitDeclImpl(decl, nullptr);
emit(", ");
auto paramName = getIRName(pp);
- auto paramType = pp->getType();
+ auto paramType = pp->getDataType();
if (auto decor = pp->findDecoration<IRHighLevelDeclDecoration>())
{
if (decor->decl)
@@ -7496,7 +7537,7 @@ emitDeclImpl(decl, nullptr);
EmitContext* ctx,
IRVar* varDecl)
{
- auto allocatedType = varDecl->getType();
+ auto allocatedType = varDecl->getDataType();
auto varType = allocatedType->getValueType();
// auto addressSpace = allocatedType->getAddressSpace();
@@ -7530,6 +7571,7 @@ emitDeclImpl(decl, nullptr);
break;
}
#endif
+ emitIRRateQualifiers(ctx, varDecl);
emitIRType(ctx, varType, getIRName(varDecl));
@@ -7590,7 +7632,7 @@ emitDeclImpl(decl, nullptr);
EmitContext* ctx,
IRGlobalVar* varDecl)
{
- auto allocatedType = varDecl->getType();
+ auto allocatedType = varDecl->getDataType();
auto varType = allocatedType->getValueType();
String initFuncName;
@@ -7712,7 +7754,7 @@ emitDeclImpl(decl, nullptr);
EmitContext* ctx,
IRGlobalConstant* valDecl)
{
- auto valType = valDecl->getType();
+ auto valType = valDecl->getDataType();
emit("static const ");
emitIRType(ctx, valType, getIRName(valDecl));
diff --git a/source/slang/ir-constexpr.cpp b/source/slang/ir-constexpr.cpp
new file mode 100644
index 000000000..f5738c036
--- /dev/null
+++ b/source/slang/ir-constexpr.cpp
@@ -0,0 +1,531 @@
+// ir-constexpr.cpp
+#include "ir-constexpr.h"
+
+#include "ir.h"
+#include "ir-insts.h"
+
+namespace Slang {
+
+struct PropagateConstExprContext
+{
+ DiagnosticSink* sink;
+
+ SharedIRBuilder sharedBuilder;
+ IRBuilder builder;
+
+ List<IRGlobalValue*> workList;
+ HashSet<IRGlobalValue*> onWorkList;
+
+ IRBuilder* getBuilder() { return &builder; }
+
+ Session* getSession() { return sharedBuilder.session; }
+
+ DiagnosticSink* getSink() { return sink; }
+};
+
+bool isConstExpr(Type* type)
+{
+ if( auto rateQualifiedType = type->As<RateQualifiedType>() )
+ {
+ auto rate = rateQualifiedType->rate;
+ if(auto constExprRate = rate->As<ConstExprRate>())
+ return true;
+ }
+
+ return false;
+}
+
+bool isConstExpr(IRValue* value)
+{
+ // Certain IR value ops are implicitly `constexpr`
+ //
+ // TODO: should we just go ahead and make that explicit
+ // in the type system?
+ switch(value->op)
+ {
+ case kIROp_IntLit:
+ case kIROp_FloatLit:
+ case kIROp_boolConst:
+ case kIROp_Func:
+ return true;
+
+ default:
+ break;
+ }
+
+ if(isConstExpr(value->getFullType()))
+ return true;
+
+ return false;
+}
+
+bool opCanBeConstExpr(IROp op)
+{
+ switch( op )
+ {
+ case kIROp_IntLit:
+ case kIROp_FloatLit:
+ case kIROp_boolConst:
+ case kIROp_Add:
+ case kIROp_Sub:
+ case kIROp_Mul:
+ case kIROp_Div:
+ case kIROp_Mod:
+ case kIROp_Neg:
+ case kIROp_Construct:
+ case kIROp_makeVector:
+ case kIROp_makeArray:
+ case kIROp_makeMatrix:
+ // TODO: more cases
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool opCanBeConstExpr(IRValue* value)
+{
+ // TODO: realistically need to special-case `call`
+ // operations here, so that we check whether the
+ // callee function is fixed/known, and if it is
+ // whether it has been decoared as constant-foldable
+
+ return opCanBeConstExpr(value->op);
+}
+
+void markConstExpr(
+ PropagateConstExprContext* context,
+ IRValue* value)
+{
+ Slang::markConstExpr(context->getSession(), value);
+}
+
+
+// Propagate `constexpr`-ness in a forward direction, from the
+// operands of an instruction to the instruction itself.
+bool propagateConstExprForward(
+ PropagateConstExprContext* context,
+ IRGlobalValueWithCode* code)
+{
+ bool anyChanges = false;
+ for(;;)
+ {
+ bool changedThisIteration = false;
+ for( auto bb = code->getFirstBlock(); bb; bb = bb->getNextBlock() )
+ {
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
+ {
+ // Instruction already `constexpr`? Then skip it.
+ if(isConstExpr(ii))
+ continue;
+
+ // Is the operation one that we can actually make be constexpr?
+ if(!opCanBeConstExpr(ii))
+ continue;
+
+ // Are all arguments `constexpr`?
+ bool allArgsConstExpr = true;
+ UInt argCount = ii->getArgCount();
+ for( UInt aa = 0; aa < argCount; ++aa )
+ {
+ auto arg = ii->getArg(aa);
+
+ if( !isConstExpr(arg) )
+ {
+ allArgsConstExpr = false;
+ break;
+ }
+ }
+ if(!allArgsConstExpr)
+ continue;
+
+ // Seems like this operation can/should be made constexpr
+ markConstExpr(context, ii);
+ changedThisIteration = true;
+ }
+ }
+
+ if( !changedThisIteration )
+ return anyChanges;
+
+ anyChanges = true;
+ }
+}
+
+void maybeAddToWorkList(
+ PropagateConstExprContext* context,
+ IRGlobalValue* gv)
+{
+ if( !context->onWorkList.Contains(gv) )
+ {
+ context->workList.Add(gv);
+ context->onWorkList.Add(gv);
+ }
+}
+
+bool maybeMarkConstExpr(
+ PropagateConstExprContext* context,
+ IRValue* value)
+{
+ if(isConstExpr(value))
+ return false;
+
+ if(!opCanBeConstExpr(value))
+ return false;
+
+ markConstExpr(context, value);
+
+ // TODO: we should only allow function parameters to be
+ // changed to be `constexpr` when we are compiling "application"
+ // code, and not library code.
+ // (Or eventually we'd have a rule that only non-`public` symbols
+ // can have this kind of propagation applied).
+
+ if(value->op == kIROp_Param)
+ {
+ auto param = (IRParam*) value;
+ auto block = (IRBlock*) param->parent;
+ auto code = block->getParent();
+
+ if(block == code->getFirstBlock())
+ {
+ // We've just changed a function parameter to
+ // be `constexpr`. We need to remember that
+ // fact so taht we can mark callers of this
+ // function as `constexpr` themselves.
+
+ for( auto u = code->firstUse; u; u = u->nextUse )
+ {
+ auto user = u->getUser();
+
+ switch( user->op )
+ {
+ case kIROp_Call:
+ {
+ auto inst = (IRCall*) user;
+ auto caller = inst->getParentBlock()->getParent();
+ maybeAddToWorkList(context, caller);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+// Propagate `constexpr`-ness in a backward direction, from an instruction
+// to its operands.
+bool propagateConstExprBackward(
+ PropagateConstExprContext* context,
+ IRGlobalValueWithCode* code)
+{
+ SharedIRBuilder sharedBuilder;
+ sharedBuilder.module = code->parentModule;
+ sharedBuilder.session = sharedBuilder.module->session;
+
+ IRBuilder builder;
+ builder.sharedBuilder = &sharedBuilder;
+ builder.curFunc = code;
+ builder.curBlock = nullptr;
+
+ bool anyChanges = false;
+ for(;;)
+ {
+ // Note: we are walking the list of blocks and the instructions
+ // in each block in reverse order, to maximize the chances that
+ // we propagate multiple changes in a each pass.
+ //
+ // TODO: this should probably all be done with a work list instead,
+ // but that requires being able to detect instructions vs. other
+ // values.
+
+ bool changedThisIteration = false;
+ for( auto bb = code->getLastBlock(); bb; bb = bb->getPrevBlock() )
+ {
+ for( auto ii = bb->getLastInst(); ii; ii = ii->getPrevInst() )
+ {
+ if( isConstExpr(ii) )
+ {
+ // If this instruction is `constexpr`, then its operands should be too.
+ UInt argCount = ii->getArgCount();
+ for( UInt aa = 0; aa < argCount; ++aa )
+ {
+ auto arg = ii->getArg(aa);
+ if(isConstExpr(arg))
+ continue;
+
+ if(!opCanBeConstExpr(arg))
+ continue;
+
+ if( maybeMarkConstExpr(context, arg) )
+ {
+ changedThisIteration = true;
+ }
+ }
+ }
+ else if( ii->op == kIROp_Call )
+ {
+ // A non-constexpr call might be calling a function with one or
+ // more constexpr parameters. We should check if we can resolve
+ // the callee for this call statically, and if so try to propagate
+ // constexpr from the parameters back to the arguments.
+ auto callInst = (IRCall*) ii;
+
+ UInt operandCount = callInst->getArgCount();
+
+ UInt firstCallArg = 1;
+ UInt callArgCount = operandCount - firstCallArg;
+
+ auto callee = callInst->getArg(0);
+ while( callee->op == kIROp_specialize )
+ {
+ callee = ((IRSpecialize*) callee)->getArg(0);
+ }
+ if( callee->op == kIROp_Func )
+ {
+ auto calleeFunc = (IRFunc*) callee;
+ auto calleeFuncType = calleeFunc->getType();
+
+ UInt callParamCount = calleeFuncType->getParamCount();
+ SLANG_RELEASE_ASSERT(callParamCount == callArgCount);
+
+ // If the callee has a definition, then we can read `constexpr`
+ // information off of the parameters of its first IR block.
+ if( auto calleeFirstBlock = calleeFunc->firstBlock )
+ {
+ UInt paramCounter = 0;
+ for( auto pp = calleeFirstBlock->getFirstParam(); pp; pp = pp->getNextParam() )
+ {
+ UInt paramIndex = paramCounter++;
+
+ auto param = pp;
+ auto arg = callInst->getArg(firstCallArg + paramIndex);
+
+ if( isConstExpr(param) )
+ {
+ if( maybeMarkConstExpr(context, arg) )
+ {
+ changedThisIteration = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ // If we don't have the definition/body for the callee,
+ // then we have to glean `constexpr` information from its
+ // type instead.
+ auto calleeType = calleeFunc->getType();
+ auto paramCount = calleeType->getParamCount();
+ for( UInt pp = 0; pp < paramCount; ++pp )
+ {
+ auto paramType = calleeType->getParamType(pp);
+ auto arg = callInst->getArg(firstCallArg + pp);
+ if( isConstExpr(paramType) )
+ {
+ if( maybeMarkConstExpr(context, arg) )
+ {
+ changedThisIteration = true;
+ }
+ }
+ }
+ }
+
+ // TODO: this currently only works if the callee has a definition,
+ // because that is the only case where will generate IR values for
+ // its parameter list. Otherwise we'd need to pull this information
+ // from the function *type* for builtins.
+ if(!calleeFunc->firstBlock)
+ continue;
+
+ }
+ }
+ }
+
+ if( bb != code->getFirstBlock() )
+ {
+ // A parameter in anything butr the first block is
+ // conceptually a phi node, which means its operands
+ // are the corresponding values from the terminating
+ // branch in a predecessor block.
+
+ UInt paramCounter = 0;
+ for( auto pp = bb->getFirstParam(); pp; pp = pp->getNextParam() )
+ {
+ UInt paramIndex = paramCounter++;
+
+ if(!isConstExpr(pp))
+ continue;
+
+ for(auto pred : bb->getPredecessors())
+ {
+ auto terminator = pred->getLastInst();
+ if(terminator->op != kIROp_unconditionalBranch)
+ continue;
+
+ UInt operandIndex = paramIndex + 1;
+ SLANG_RELEASE_ASSERT(operandIndex < terminator->getArgCount());
+
+ auto operand = terminator->getArg(operandIndex);
+ if( maybeMarkConstExpr(context, operand) )
+ {
+ changedThisIteration = true;
+ }
+ }
+ }
+ }
+
+ }
+
+ if( !changedThisIteration )
+ return anyChanges;
+
+ anyChanges = true;
+ }
+}
+
+// Validate use of `constexpr` within a function (in particular,
+// diagnose places where a value that must be contexpr depends
+// on a value that cannot be)
+void validateConstExpr(
+ PropagateConstExprContext* context,
+ IRGlobalValueWithCode* code)
+{
+ for( auto bb = code->getFirstBlock(); bb; bb = bb->getNextBlock() )
+ {
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
+ {
+ if(isConstExpr(ii))
+ {
+ // For an instruction that must be `constexpr`, we need
+ // to ensure that its argumenst are all `constexpr`
+
+ UInt argCount = ii->getArgCount();
+ for( UInt aa = 0; aa < argCount; ++aa )
+ {
+ auto arg = ii->getArg(aa);
+
+ if( !isConstExpr(arg) )
+ {
+ // Diagnose the failure.
+
+ context->getSink()->diagnose(ii->sourceLoc, Diagnostics::needCompileTimeConstant);
+
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void propagateConstExpr(
+ IRModule* module,
+ DiagnosticSink* sink)
+{
+ auto session = module->session;
+
+ PropagateConstExprContext context;
+ context.sink = sink;
+ context.sharedBuilder.module = module;
+ context.sharedBuilder.session = session;
+ context.builder.sharedBuilder = &context.sharedBuilder;
+ context.builder.curFunc = nullptr;
+ context.builder.curBlock = nullptr;
+
+
+
+ // We need to propagate information both forward and backward.
+ //
+ // In the forward direction we need to check if all of the operands
+ // to an instruction are `constexpr` *and* if the operation is
+ // one that can conceptually be "promoted" to the constexpr rate.
+ //
+ // In the backward direction, if an instruction has already been
+ // marked as needing to be `constexpr`, then its operands had
+ // better be too.
+ //
+ // The backward direction needs to be interprocedural, because
+ // a parameter to a function might be `constexpr`, so that callers
+ // of that function would need to be marked too. If backwards
+ // propagation in any of the callers leads to some of their
+ // parameters being marked constexpr, then we would need to
+ // revisit their callers.
+
+ // We will build an initial work list with all of the global values in it.
+
+ for( auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
+ {
+ maybeAddToWorkList(&context, gv);
+ }
+
+ // We will iterate applying propagation to one global value at a time
+ // until we run out.
+ while( context.workList.Count() )
+ {
+ auto gv = context.workList[0];
+ context.workList.FastRemoveAt(0);
+ context.onWorkList.Remove(gv);
+
+ switch( gv->op )
+ {
+ default:
+ break;
+
+ case kIROp_Func:
+ case kIROp_global_var:
+ case kIROp_global_constant:
+ {
+ IRGlobalValueWithCode* code = (IRGlobalValueWithCode*) gv;
+
+ for( ;;)
+ {
+ bool anyChange = false;
+ if( propagateConstExprForward(&context, code) )
+ {
+ anyChange = true;
+ }
+ if( propagateConstExprBackward(&context, code) )
+ {
+ anyChange = true;
+ }
+ if(!anyChange)
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ // Okay, we've processed all our functions and found a steady state.
+ // Now we need to try and issue diagnostics for any IR values where
+ // we find that they are *required* to be `constexpr`, but *cannot*
+ // be, for some reason.
+
+ for( auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue() )
+ {
+ switch( gv->op )
+ {
+ default:
+ break;
+
+ case kIROp_Func:
+ case kIROp_global_var:
+ case kIROp_global_constant:
+ {
+ IRGlobalValueWithCode* code = (IRGlobalValueWithCode*) gv;
+ validateConstExpr(&context, code);
+ }
+ break;
+ }
+ }
+
+}
+
+}
diff --git a/source/slang/ir-constexpr.h b/source/slang/ir-constexpr.h
new file mode 100644
index 000000000..04f2e59ec
--- /dev/null
+++ b/source/slang/ir-constexpr.h
@@ -0,0 +1,12 @@
+// ir-constexpr.h
+#pragma once
+
+namespace Slang
+{
+ class DiagnosticSink;
+ struct IRModule;
+
+ void propagateConstExpr(
+ IRModule* module,
+ DiagnosticSink* sink);
+}
diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h
index aaebb9973..26cce054e 100644
--- a/source/slang/ir-insts.h
+++ b/source/slang/ir-insts.h
@@ -307,9 +307,9 @@ struct IRSwizzleSet : IRReturn
// a stack allocation of some memory.
struct IRVar : IRInst
{
- PtrType* getType()
+ PtrType* getDataType()
{
- return (PtrType*)type.Ptr();
+ return (PtrType*) ((IRValue*) this)->getDataType();
}
};
@@ -321,7 +321,10 @@ struct IRVar : IRInst
/// blocks nested inside this value.
struct IRGlobalVar : IRGlobalValueWithCode
{
- PtrType* getType() { return type.As<PtrType>(); }
+ PtrType* getDataType()
+ {
+ return (PtrType*) ((IRValue*) this)->getDataType();
+ }
};
/// @brief A global constant.
@@ -733,6 +736,12 @@ void specializeGenerics(
//
+void markConstExpr(
+ Session* session,
+ IRValue* irValue);
+
+//
+
}
#endif
diff --git a/source/slang/ir-legalize-types.cpp b/source/slang/ir-legalize-types.cpp
index 2e69898e5..9cb32de8f 100644
--- a/source/slang/ir-legalize-types.cpp
+++ b/source/slang/ir-legalize-types.cpp
@@ -627,7 +627,7 @@ static LegalVal legalizeLocalVar(
// Legalize the type for the variable's value
auto legalValueType = legalizeType(
context,
- irLocalVar->getType()->getValueType());
+ irLocalVar->getDataType()->getValueType());
RefPtr<VarLayout> varLayout = findVarLayout(irLocalVar);
RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr;
@@ -809,7 +809,7 @@ static void legalizeFunc(
// involve increasing the number of parameters
for (auto pp = bb->getFirstParam(); pp; pp = pp->nextParam)
{
- auto legalParamType = legalizeType(context, pp->getType());
+ auto legalParamType = legalizeType(context, pp->getFullType());
if (legalParamType.flavor != LegalType::Flavor::simple)
{
context->insertBeforeParam = pp;
@@ -1053,7 +1053,7 @@ static void legalizeGlobalVar(
// Legalize the type for the variable's value
auto legalValueType = legalizeType(
context,
- irGlobalVar->getType()->getValueType());
+ irGlobalVar->getDataType()->getValueType());
RefPtr<VarLayout> varLayout = findVarLayout(irGlobalVar);
RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr;
@@ -1107,7 +1107,7 @@ static void legalizeGlobalConstant(
// Legalize the type for the variable's value
auto legalValueType = legalizeType(
context,
- irGlobalConstant->getType());
+ irGlobalConstant->getFullType());
switch (legalValueType.flavor)
{
diff --git a/source/slang/ir-ssa.cpp b/source/slang/ir-ssa.cpp
index 53eefd81f..b793f5320 100644
--- a/source/slang/ir-ssa.cpp
+++ b/source/slang/ir-ssa.cpp
@@ -207,7 +207,13 @@ PhiInfo* addPhi(
IRVar* var)
{
auto builder = &blockInfo->builder;
- IRParam* phi = builder->createParam(var->getType()->getValueType());
+
+ auto valueType = var->getDataType()->getValueType();
+ if( auto rate = var->getRate() )
+ {
+ valueType = context->sharedBuilder.getSession()->getRateQualifiedType(rate, valueType);
+ }
+ IRParam* phi = builder->createParam(valueType);
RefPtr<PhiInfo> phiInfo = new PhiInfo();
context->phiInfos.Add(phi, phiInfo);
@@ -451,7 +457,7 @@ IRValue* readVarRec(
// a local lookup in the block had already failed, so
// at this point we are dealing with an undefined value.
- auto type = var->getType()->getValueType();
+ auto type = var->getDataType()->getValueType();
val = blockInfo->builder.emitUndefined(type);
}
else if (!multiplePreds)
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index 65f792577..090d0452b 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -1349,12 +1349,19 @@ namespace Slang
IRInst* IRBuilder::emitLoad(
IRValue* ptr)
{
+ // Note: a `load` operation does not consider the rate
+ // (if any) attached to its operand (see the use of `getDataType`
+ // below). This means that a load from a rate-qualified
+ // variable will still conceptually execute (and return
+ // results) at the "default" rate of the parent function,
+ // unless a subsequent analysis pass constraints it.
+
RefPtr<Type> valueType;
- if(auto ptrType = ptr->getType()->As<PtrTypeBase>())
+ if(auto ptrType = ptr->getDataType()->As<PtrTypeBase>())
{
valueType = ptrType->getValueType();
}
- else if(auto ptrLikeType = ptr->getType()->As<PointerLikeType>())
+ else if(auto ptrLikeType = ptr->getDataType()->As<PointerLikeType>())
{
valueType = ptrLikeType->getElementType();
}
@@ -1368,7 +1375,9 @@ namespace Slang
// Ugly special case: the result of loading from `groupshared`
// memory should not itself be `groupshared`.
//
- // TODO: Should this generalize to any "rate-qualified" type?
+ // TODO: This special case will go away once `GroupSharedType`
+ // is replaced by a `GroupSharedRate` that gets used together
+ // with `RateQualifiedType`.
if(auto rateType = valueType->As<GroupSharedType>())
{
valueType = rateType->valueType;
@@ -2036,6 +2045,17 @@ namespace Slang
dump(context, "@ThreadGroup ");
dumpType(context, groupSharedType->valueType);
}
+ else if(auto rateQualifiedType = type->As<RateQualifiedType>())
+ {
+ dump(context, "@");
+ dumpType(context, rateQualifiedType->rate);
+ dump(context, " ");
+ dumpType(context, rateQualifiedType->valueType);
+ }
+ else if(auto constExprRate = type->As<ConstExprRate>())
+ {
+ dump(context, "ConstExpr");
+ }
else
{
// Need a default case here
@@ -2117,7 +2137,7 @@ namespace Slang
dumpIndent(context);
dump(context, "param ");
dumpID(context, pp);
- dumpInstTypeClause(context, pp->getType());
+ dumpInstTypeClause(context, pp->getFullType());
}
context->indent -= 2;
dump(context, ")");
@@ -2271,15 +2291,16 @@ namespace Slang
dumpIndent(context);
auto opInfo = &kIROpInfos[op];
- auto type = inst->getType();
+ auto type = inst->getFullType();
+ auto dataType = inst->getDataType();
- if (!type)
+ if (!dataType)
{
// No result, okay...
}
else
{
- auto basicType = type->As<BasicExpressionType>();
+ auto basicType = dataType->As<BasicExpressionType>();
if (basicType && basicType->baseType == BaseType::Void)
{
// No result, okay...
@@ -2439,7 +2460,7 @@ namespace Slang
dumpIndent(context);
dump(context, "ir_global_var ");
dumpID(context, var);
- dumpInstTypeClause(context, var->getType());
+ dumpInstTypeClause(context, var->getFullType());
// TODO: deal with the case where a global
// might have embedded initialization logic.
@@ -2455,7 +2476,7 @@ namespace Slang
dumpIndent(context);
dump(context, "ir_global_constant ");
dumpID(context, val);
- dumpInstTypeClause(context, val->getType());
+ dumpInstTypeClause(context, val->getFullType());
// TODO: deal with the case where a global
// might have embedded initialization logic.
@@ -2574,6 +2595,22 @@ namespace Slang
//
//
+ Type* IRValue::getRate()
+ {
+ if(auto rateQualifiedType = type->As<RateQualifiedType>())
+ return rateQualifiedType->rate;
+
+ return nullptr;
+ }
+
+ Type* IRValue::getDataType()
+ {
+ if(auto rateQualifiedType = type->As<RateQualifiedType>())
+ return rateQualifiedType->valueType;
+
+ return type;
+ }
+
void IRValue::replaceUsesWith(IRValue* other)
{
// We will walk through the list of uses for the current
@@ -3566,7 +3603,7 @@ namespace Slang
// uses of the variable, and the exact logic there
// will differ a bit between the pointer and non-pointer
// cases.
- auto paramType = pp->getType();
+ auto paramType = pp->getDataType();
// Any initialization code we insert nees to be at the start
// of the block:
@@ -4167,7 +4204,15 @@ namespace Slang
IRGlobalVar* originalVar,
IROriginalValuesForClone const& originalValues)
{
- auto clonedVar = context->builder->createGlobalVar(context->maybeCloneType(originalVar->getType()->getValueType()));
+ auto clonedVar = context->builder->createGlobalVar(
+ context->maybeCloneType(originalVar->getDataType()->getValueType()));
+
+ if(auto rate = originalVar->getRate() )
+ {
+ clonedVar->type = context->builder->getSession()->getRateQualifiedType(
+ rate, clonedVar->type);
+ }
+
registerClonedValue(context, clonedVar, originalValues);
auto mangledName = originalVar->mangledName;
@@ -4196,7 +4241,7 @@ namespace Slang
IRGlobalConstant* originalVal,
IROriginalValuesForClone const& originalValues)
{
- auto clonedVal = context->builder->createGlobalConstant(context->maybeCloneType(originalVal->getType()));
+ auto clonedVal = context->builder->createGlobalConstant(context->maybeCloneType(originalVal->getFullType()));
registerClonedValue(context, clonedVal, originalValues);
auto mangledName = originalVal->mangledName;
@@ -4290,7 +4335,7 @@ namespace Slang
{
IRParam* clonedParam = builder->emitParam(
context->maybeCloneType(
- originalParam->getType()));
+ originalParam->getFullType()));
cloneDecorations(context, clonedParam, originalParam);
registerClonedValue(context, clonedParam, originalParam);
}
@@ -5718,4 +5763,18 @@ namespace Slang
}
return globalParamSubst;
}
+
+
+ void markConstExpr(
+ Session* session,
+ IRValue* irValue)
+ {
+ // We will take an IR value with type `T`,
+ // and turn it into one with type `@ConstExpr T`.
+
+ // TODO: need to be careful if the value already has a rate
+ // qualifier set.
+
+ irValue->type = session->getConstExprType(irValue->getDataType());
+ }
}
diff --git a/source/slang/ir.h b/source/slang/ir.h
index fdca73a83..3d4899824 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -157,7 +157,10 @@ struct IRValue : public IRObject
// no value.
RefPtr<Type> type;
- Type* getType() { return type; }
+ Type* getFullType() { return type; }
+
+ Type* getRate();
+ Type* getDataType();
// Source location information for this value, if any
SourceLoc sourceLoc;
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index b99e2713f..8c858f4f3 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -4,6 +4,7 @@
#include "../../slang.h"
#include "ir.h"
+#include "ir-constexpr.h"
#include "ir-insts.h"
#include "ir-ssa.h"
#include "mangle.h"
@@ -1262,7 +1263,7 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
// need to extract the value type from that pointer here.
//
IRValue* loweredBaseVal = getSimpleVal(context, loweredBase);
- RefPtr<Type> loweredBaseType = loweredBaseVal->getType();
+ RefPtr<Type> loweredBaseType = loweredBaseVal->getDataType();
if (loweredBaseType->As<PointerLikeType>()
|| loweredBaseType->As<PtrTypeBase>())
@@ -2580,7 +2581,7 @@ static LoweredValInfo maybeMoveMutableTemp(
default:
{
IRValue* irVal = getSimpleVal(context, val);
- auto type = irVal->getType();
+ auto type = irVal->getDataType();
auto var = createVar(context, type);
assign(context, var, LoweredValInfo::simple(irVal));
@@ -2660,7 +2661,7 @@ top:
// Now apply the swizzle
IRInst* irSwizzled = builder->emitSwizzleSet(
- irLeftVal->getType(),
+ irLeftVal->getDataType(),
irLeftVal,
irRightVal,
swizzleInfo->elementCount,
@@ -3503,6 +3504,30 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
return false;
}
+ bool isConstExprVar(Decl* decl)
+ {
+ if( decl->HasModifier<ConstExprModifier>() )
+ {
+ return true;
+ }
+ else if(decl->HasModifier<HLSLStaticModifier>() && decl->HasModifier<ConstModifier>())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ RefPtr<Type> maybeGetConstExprType(Type* type, Decl* decl)
+ {
+ if(isConstExprVar(decl))
+ {
+ return context->getSession()->getConstExprType(type);
+ }
+
+ return type;
+ }
+
LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl)
{
// Collect the parameter lists we will use for our new function.
@@ -3563,11 +3588,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
for( auto paramInfo : parameterLists.params )
{
RefPtr<Type> irParamType = lowerSimpleType(context, paramInfo.type);
+
switch( paramInfo.direction )
{
case kParameterDirection_In:
// Simple case of a by-value input parameter.
- paramTypes.Add(irParamType);
break;
// If the parameter is declared `out` or `inout`,
@@ -3575,18 +3600,26 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// the IR, but we will use a specialized pointer
// type that encodes the parameter direction information.
case kParameterDirection_Out:
- paramTypes.Add(
- context->getSession()->getOutType(irParamType));
+ irParamType = context->getSession()->getOutType(irParamType);
break;
case kParameterDirection_InOut:
- paramTypes.Add(
- context->getSession()->getInOutType(irParamType));
+ irParamType = context->getSession()->getInOutType(irParamType);
break;
default:
SLANG_UNEXPECTED("unknown parameter direction");
break;
}
+
+ // If the parameter was explicitly marked as being a compile-time
+ // constant (`constexpr`), then attach that information to its
+ // IR-level type explicitly.
+ if( paramInfo.decl )
+ {
+ irParamType = maybeGetConstExprType(irParamType, paramInfo.decl);
+ }
+
+ paramTypes.Add(irParamType);
}
auto irResultType = lowerSimpleType(context, declForReturnType->ReturnType);
@@ -3600,11 +3633,6 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
//
IRType* irParamType = irResultType;
paramTypes.Add(irParamType);
- subBuilder->emitParam(irParamType);
-
- // TODO: we need some way to wire this up to the `newValue`
- // or whatever name we give for that parameter inside
- // the setter body.
// Instead, a setter always returns `void`
//
@@ -3660,11 +3688,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
//
// TODO: Is this the best representation we can use?
- auto irPtrType = irParamType.As<PtrTypeBase>();
-
- IRParam* irParamPtr = subBuilder->emitParam(irPtrType);
+ IRParam* irParamPtr = subBuilder->emitParam(irParamType);
if(auto paramDecl = paramInfo.decl)
+ {
subBuilder->addHighLevelDeclDecoration(irParamPtr, paramDecl);
+ }
paramVal = LoweredValInfo::ptr(irParamPtr);
@@ -3682,27 +3710,51 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
case kParameterDirection_In:
{
// Simple case of a by-value input parameter.
- // But note that HLSL allows an input parameter
- // to be used as a local variable inside of a
- // function body, so we need to introduce a temporary
- // and then copy over to it...
//
- // TODO: we could skip this step if we knew
- // the parameter was marked `const` or similar.
-
- paramTypes.Add(irParamType);
-
+ // We start by declaring an IR parameter of the same type.
+ //
+ auto paramDecl = paramInfo.decl;
IRParam* irParam = subBuilder->emitParam(irParamType);
- if(auto paramDecl = paramInfo.decl)
+ if( paramDecl )
+ {
subBuilder->addHighLevelDeclDecoration(irParam, paramDecl);
+ }
paramVal = LoweredValInfo::simple(irParam);
-
- auto irLocal = subBuilder->emitVar(irParamType);
- auto localVal = LoweredValInfo::ptr(irLocal);
-
- assign(subContext, localVal, paramVal);
-
- paramVal = localVal;
+ //
+ // HLSL allows a function parameter to be used as a local
+ // variable in the function body (just like C/C++), so
+ // we need to support that case as well.
+ //
+ // However, if we notice that the parameter was marked
+ // `const`, then we can skip this step.
+ //
+ // TODO: we should consider having all parameter be implicitly
+ // immutable except in a specific "compatibility mode."
+ //
+ if(paramDecl && paramDecl->FindModifier<ConstModifier>())
+ {
+ // This parameter was declared to be immutable,
+ // so there should be no assignment to it in the
+ // function body, and we don't need a temporary.
+ }
+ else
+ {
+ // The parameter migth get used as a temporary in
+ // the function body. We will allocate a mutable
+ // local variable for is value, and then assign
+ // from the parameter to the local at the start
+ // of the function.
+ //
+ auto irLocal = subBuilder->emitVar(irParamType);
+ auto localVal = LoweredValInfo::ptr(irLocal);
+ assign(subContext, localVal, paramVal);
+ //
+ // When code later in the body of the function refers
+ // to the parameter declaration, it will actually refer
+ // to the value stored in the local variable.
+ //
+ paramVal = localVal;
+ }
}
break;
}
@@ -3719,6 +3771,18 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
}
}
+ if (auto setterDecl = dynamic_cast<SetterDecl*>(decl))
+ {
+ // Add the IR parameter for the new value
+ IRType* irParamType = irResultType;
+ subBuilder->emitParam(irParamType);
+
+ // TODO: we need some way to wire this up to the `newValue`
+ // or whatever name we give for that parameter inside
+ // the setter body.
+ }
+
+
lowerStmt(subContext, decl->Body);
// We need to carefully add a terminator instruction to the end
@@ -4070,7 +4134,7 @@ LoweredValInfo maybeEmitSpecializeInst(IRGenContext* context,
DeclRef<Decl> newDeclRef = DeclRef<Decl>(declRef.decl, lowedNewSubst);
RefPtr<Type> type;
- if (auto declType = val->getType())
+ if (auto declType = val->getDataType())
{
type = declType->Substitute(newDeclRef.substitutions).As<Type>();
}
@@ -4225,6 +4289,10 @@ IRModule* generateIRForTranslationUnit(
// TODO: Do basic constant folding and DCE
+ // Propagate `constexpr`-ness through the dataflow graph (and the
+ // call graph) based on constraints imposed by different instructions.
+ propagateConstExpr(module, &compileRequest->mSink);
+
// TODO: give error messages if any `undefined` or
// `unreachable` instructions remain.
diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h
index 3750f3373..725629d35 100644
--- a/source/slang/modifier-defs.h
+++ b/source/slang/modifier-defs.h
@@ -22,6 +22,7 @@ SIMPLE_MODIFIER(FromStdLib);
SIMPLE_MODIFIER(Prefix);
SIMPLE_MODIFIER(Postfix);
SIMPLE_MODIFIER(Exported);
+SIMPLE_MODIFIER(ConstExpr);
#undef SIMPLE_MODIFIER
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 42e412438..f2e5587fa 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -747,6 +747,7 @@ Session::~Session()
initializerListType = nullptr;
overloadedType = nullptr;
irBasicBlockType = nullptr;
+ constExprRate = nullptr;
builtinTypes = decltype(builtinTypes)();
// destroy modules next
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index 0154dd98f..3f2e19166 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -177,6 +177,7 @@
<ClInclude Include="diagnostics.h" />
<ClInclude Include="emit.h" />
<ClInclude Include="expr-defs.h" />
+ <ClInclude Include="ir-constexpr.h" />
<ClInclude Include="ir-inst-defs.h" />
<ClInclude Include="ir-insts.h" />
<ClInclude Include="ir-ssa.h" />
@@ -218,6 +219,7 @@
<ClCompile Include="diagnostics.cpp" />
<ClCompile Include="dxc-support.cpp" />
<ClCompile Include="emit.cpp" />
+ <ClCompile Include="ir-constexpr.cpp" />
<ClCompile Include="ir-legalize-types.cpp" />
<ClCompile Include="ir-ssa.cpp" />
<ClCompile Include="ir.cpp" />
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index e64721f6d..1d7e1e942 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -46,6 +46,7 @@
<ClInclude Include="legalize-types.h" />
<ClInclude Include="ir-ssa.h" />
<ClInclude Include="memory_pool.h" />
+ <ClInclude Include="ir-constexpr.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="check.cpp" />
@@ -77,6 +78,7 @@
<ClCompile Include="legalize-types.cpp" />
<ClCompile Include="ir-ssa.cpp" />
<ClCompile Include="memory_pool.cpp" />
+ <ClCompile Include="ir-constexpr.cpp" />
</ItemGroup>
<ItemGroup>
<CustomBuild Include="core.meta.slang" />
diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp
index 62e467a44..ab472fe9f 100644
--- a/source/slang/syntax.cpp
+++ b/source/slang/syntax.cpp
@@ -231,6 +231,9 @@ void Type::accept(IValVisitor* visitor, void* extra)
irBasicBlockType = new IRBasicBlockType();
irBasicBlockType->setSession(this);
+
+ constExprRate = new ConstExprRate();
+ constExprRate->setSession(this);
}
Type* Session::getBoolType()
@@ -288,6 +291,22 @@ void Type::accept(IValVisitor* visitor, void* extra)
return irBasicBlockType;
}
+ Type* Session::getConstExprRate()
+ {
+ return constExprRate;
+ }
+
+ RefPtr<RateQualifiedType> Session::getRateQualifiedType(
+ Type* rate,
+ Type* valueType)
+ {
+ RefPtr<RateQualifiedType> rateQualifiedType = new RateQualifiedType();
+ rateQualifiedType->setSession(this);
+ rateQualifiedType->rate = rate;
+ rateQualifiedType->valueType = valueType;
+ return rateQualifiedType;
+ }
+
RefPtr<PtrType> Session::getPtrType(
RefPtr<Type> valueType)
{
@@ -407,6 +426,88 @@ void Type::accept(IValVisitor* visitor, void* extra)
return baseType->ToString() + "[]";
}
+ // RateQualifiedType
+
+ Slang::String RateQualifiedType::ToString()
+ {
+ return "@" + rate->ToString() + " " + valueType->ToString();
+ }
+
+ bool RateQualifiedType::EqualsImpl(Type * type)
+ {
+ auto rateQualifiedType = type->As<RateQualifiedType>();
+ if(!rateQualifiedType)
+ return false;
+
+ return rate->Equals(rateQualifiedType->rate)
+ && valueType->Equals(rateQualifiedType->valueType);
+ }
+
+ RefPtr<Val> RateQualifiedType::SubstituteImpl(SubstitutionSet subst, int* ioDiff)
+ {
+ int diff = 0;
+ auto substRate = rate->SubstituteImpl(subst, &diff).As<Type>();
+ auto substValueType = valueType->SubstituteImpl(subst, &diff).As<Type>();
+ if(!diff)
+ return this;
+
+ (*ioDiff)++;
+
+ return getSession()->getRateQualifiedType(substRate, substValueType);
+ }
+
+ RefPtr<Type> RateQualifiedType::CreateCanonicalType()
+ {
+ RefPtr<Type> canRate = rate->GetCanonicalType();
+ RefPtr<Type> canValueType = valueType->GetCanonicalType();
+
+ RefPtr<RateQualifiedType> canRateQualifiedType = new RateQualifiedType();
+ canRateQualifiedType->setSession(session);
+ canRateQualifiedType->rate = canRate;
+ canRateQualifiedType->valueType = valueType;
+ return canRateQualifiedType;
+ }
+
+ int RateQualifiedType::GetHashCode()
+ {
+ auto hash = (int)(typeid(this).hash_code());
+ hash = combineHash(hash, rate->GetHashCode());
+ hash = combineHash(hash, valueType->GetHashCode());
+ return hash;
+ }
+
+ // ConstExprRate
+
+ Slang::String ConstExprRate::ToString()
+ {
+ return "ConstExpr";
+ }
+
+ bool ConstExprRate::EqualsImpl(Type * type)
+ {
+ auto constExprRate = type->As<ConstExprRate>();
+ if(!constExprRate)
+ return false;
+
+ return true;
+ }
+
+ RefPtr<Val> ConstExprRate::SubstituteImpl(SubstitutionSet /*subst*/, int* /*ioDiff*/)
+ {
+ return this;
+ }
+
+ RefPtr<Type> ConstExprRate::CreateCanonicalType()
+ {
+ return this;
+ }
+
+ int ConstExprRate::GetHashCode()
+ {
+ auto hash = (int)(typeid(this).hash_code());
+ return hash;
+ }
+
// GroupSharedType
Slang::String GroupSharedType::ToString()
diff --git a/source/slang/type-defs.h b/source/slang/type-defs.h
index 14e99caf9..70f062cdc 100644
--- a/source/slang/type-defs.h
+++ b/source/slang/type-defs.h
@@ -318,7 +318,46 @@ protected:
)
END_SYNTAX_CLASS()
+// A type that has a rate qualifier applied. Conceptually `@R T` where `R`
+// represents a rate, and `T` represents a data type.
+SYNTAX_CLASS(RateQualifiedType, Type)
+
+ // The rate `R` at which the value is computed/stored
+ SYNTAX_FIELD(RefPtr<Type>, rate);
+
+ // The underlying data type `T` of the value
+ SYNTAX_FIELD(RefPtr<Type>, valueType);
+
+RAW(
+ virtual Slang::String ToString() override;
+
+protected:
+ virtual bool EqualsImpl(Type * type) override;
+ virtual RefPtr<Type> CreateCanonicalType() override;
+ virtual RefPtr<Val> SubstituteImpl(SubstitutionSet subst, int* ioDiff) override;
+ virtual int GetHashCode() override;
+ )
+END_SYNTAX_CLASS()
+
+// A representation of the `ConstExpr` rate, to be used
+// in defining `@ConstExpr T` for particular data types `T`
+SYNTAX_CLASS(ConstExprRate, Type)
+
+RAW(
+ virtual Slang::String ToString() override;
+
+protected:
+ virtual bool EqualsImpl(Type * type) override;
+ virtual RefPtr<Type> CreateCanonicalType() override;
+ virtual RefPtr<Val> SubstituteImpl(SubstitutionSet subst, int* ioDiff) override;
+ virtual int GetHashCode() override;
+ )
+END_SYNTAX_CLASS()
+
// The effective type of a variable declared with `groupshared` storage qualifier.
+//
+// TODO: this should be converted to a `GroupSharedRate`, which then gets used
+// in conjunction with `RateQualifiedType`.
SYNTAX_CLASS(GroupSharedType, Type)
SYNTAX_FIELD(RefPtr<Type>, valueType);