summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2017-09-06 14:15:11 -0700
committerGitHub <noreply@github.com>2017-09-06 14:15:11 -0700
commitca16ede67d3fc34ec1cc81b8f835199c5ef1ab9a (patch)
treefd83b7e861dbc6a0d442d5a913a1bcd9df547408 /source/slang
parente59a1b39317c10815baaed3b70c4a6580dbe1e63 (diff)
parent5900f32fff9970b4221ce7fb7e94133e387ff9de (diff)
Merge pull request #176 from tfoleyNV/ir-work
Continue work on IR-based codegen
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/emit.cpp614
-rw-r--r--source/slang/intrinsic-defs.h4
-rw-r--r--source/slang/ir-inst-defs.h16
-rw-r--r--source/slang/ir.cpp143
-rw-r--r--source/slang/ir.h92
-rw-r--r--source/slang/lower-to-ir.cpp255
-rw-r--r--source/slang/modifier-defs.h14
-rw-r--r--source/slang/options.cpp4
-rw-r--r--source/slang/parser.cpp15
-rw-r--r--source/slang/slang-stdlib.cpp31
10 files changed, 1011 insertions, 177 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 2deb0cea7..450d9eb86 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -1845,106 +1845,28 @@ struct EmitVisitor
{
auto funcDeclRef = funcDeclRefExpr->declRef;
auto funcDecl = funcDeclRef.getDecl();
- if(!funcDecl)
+ if (!funcDecl)
{
emitUncheckedCallExpr(callExpr, funcDeclRefExpr->name, arg);
return;
}
- else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
- {
- switch (intrinsicOpModifier->op)
- {
- #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
- CASE(Mul, *);
- CASE(Div, / );
- CASE(Mod, %);
- CASE(Add, +);
- CASE(Sub, -);
- CASE(Lsh, << );
- CASE(Rsh, >> );
- CASE(Eql, == );
- CASE(Neq, != );
- CASE(Greater, >);
- CASE(Less, <);
- CASE(Geq, >= );
- CASE(Leq, <= );
- CASE(BitAnd, &);
- CASE(BitXor, ^);
- CASE(BitOr, | );
- CASE(And, &&);
- CASE(Or, || );
- #undef CASE
-
- #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
- CASE(Assign, =);
- CASE(AddAssign, +=);
- CASE(SubAssign, -=);
- CASE(MulAssign, *=);
- CASE(DivAssign, /=);
- CASE(ModAssign, %=);
- CASE(LshAssign, <<=);
- CASE(RshAssign, >>=);
- CASE(OrAssign, |=);
- CASE(AndAssign, &=);
- CASE(XorAssign, ^=);
- #undef CASE
-
- case IntrinsicOp::Sequence: EmitBinExpr(outerPrec, kEOp_Comma, ",", callExpr); return;
-
- #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
- CASE(Pos, +);
- CASE(Neg, -);
- CASE(Not, !);
- CASE(BitNot, ~);
- #undef CASE
-
- #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
- CASE(PreInc, ++);
- CASE(PreDec, --);
- #undef CASE
-
- #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Postfix, "", #OP, callExpr); return
- CASE(PostInc, ++);
- CASE(PostDec, --);
- #undef CASE
-
- case IntrinsicOp::InnerProduct_Vector_Vector:
- // HLSL allows `mul()` to be used as a synonym for `dot()`,
- // so we need to translate to `dot` for GLSL
- if (context->shared->target == CodeGenTarget::GLSL)
- {
- Emit("dot(");
- EmitExpr(callExpr->Arguments[0]);
- Emit(", ");
- EmitExpr(callExpr->Arguments[1]);
- Emit(")");
- return;
- }
- break;
-
- case IntrinsicOp::InnerProduct_Matrix_Matrix:
- case IntrinsicOp::InnerProduct_Matrix_Vector:
- case IntrinsicOp::InnerProduct_Vector_Matrix:
- // HLSL exposes these with the `mul()` function, while GLSL uses ordinary
- // `operator*`.
- //
- // The other critical detail here is that the way we handle matrix
- // conventions requires that the operands to the product be swapped.
- if (context->shared->target == CodeGenTarget::GLSL)
- {
- Emit("((");
- EmitExpr(callExpr->Arguments[1]);
- Emit(") * (");
- EmitExpr(callExpr->Arguments[0]);
- Emit("))");
- return;
- }
- break;
-
- default:
- break;
- }
- }
+ // Note: We check for a "target intrinsic" modifier that flags the
+ // operation as having a custom elaboration for a specific target
+ // *before* we check for an "intrinsic op." The basic problem is
+ // that a single operation could have both finds of modifiers on it.
+ // The "target" intrinsic modifier tags something expansion during
+ // our current source-to-source translation approach, while an
+ // intrinsic op is needed for helping things lower to our IR.
+ //
+ // We need to check for this case first to make sure that when a
+ // function gets an intrinsic op added it doesn't break existing
+ // cross-compilation logic.
+ //
+ // The long term fix will be to not use the AST-based cross-compilation
+ // logic (which has all kinds of problems) and instead use the IR
+ // exclusively, at which point the notion of a "target intrinsic" modifier
+ // goes away (although we may have something similar to express how
+ // a particular op should lower/expand for a given target).
else if(auto targetIntrinsicModifier = findTargetIntrinsicModifier(funcDecl))
{
if (context->shared->target == CodeGenTarget::GLSL)
@@ -2220,6 +2142,101 @@ struct EmitVisitor
}
}
}
+ else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
+ {
+ switch (intrinsicOpModifier->op)
+ {
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
+ CASE(Mul, *);
+ CASE(Div, / );
+ CASE(Mod, %);
+ CASE(Add, +);
+ CASE(Sub, -);
+ CASE(Lsh, << );
+ CASE(Rsh, >> );
+ CASE(Eql, == );
+ CASE(Neq, != );
+ CASE(Greater, > );
+ CASE(Less, < );
+ CASE(Geq, >= );
+ CASE(Leq, <= );
+ CASE(BitAnd, &);
+ CASE(BitXor, ^);
+ CASE(BitOr, | );
+ CASE(And, &&);
+ CASE(Or, || );
+#undef CASE
+
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
+ CASE(Assign, =);
+ CASE(AddAssign, +=);
+ CASE(SubAssign, -=);
+ CASE(MulAssign, *=);
+ CASE(DivAssign, /=);
+ CASE(ModAssign, %=);
+ CASE(LshAssign, <<=);
+ CASE(RshAssign, >>=);
+ CASE(OrAssign, |=);
+ CASE(AndAssign, &=);
+ CASE(XorAssign, ^=);
+#undef CASE
+
+ case IntrinsicOp::Sequence: EmitBinExpr(outerPrec, kEOp_Comma, ",", callExpr); return;
+
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
+ CASE(Pos, +);
+ CASE(Neg, -);
+ CASE(Not, !);
+ CASE(BitNot, ~);
+#undef CASE
+
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
+ CASE(PreInc, ++);
+ CASE(PreDec, --);
+#undef CASE
+
+#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Postfix, "", #OP, callExpr); return
+ CASE(PostInc, ++);
+ CASE(PostDec, --);
+#undef CASE
+
+ case IntrinsicOp::InnerProduct_Vector_Vector:
+ // HLSL allows `mul()` to be used as a synonym for `dot()`,
+ // so we need to translate to `dot` for GLSL
+ if (context->shared->target == CodeGenTarget::GLSL)
+ {
+ Emit("dot(");
+ EmitExpr(callExpr->Arguments[0]);
+ Emit(", ");
+ EmitExpr(callExpr->Arguments[1]);
+ Emit(")");
+ return;
+ }
+ break;
+
+ case IntrinsicOp::InnerProduct_Matrix_Matrix:
+ case IntrinsicOp::InnerProduct_Matrix_Vector:
+ case IntrinsicOp::InnerProduct_Vector_Matrix:
+ // HLSL exposes these with the `mul()` function, while GLSL uses ordinary
+ // `operator*`.
+ //
+ // The other critical detail here is that the way we handle matrix
+ // conventions requires that the operands to the product be swapped.
+ if (context->shared->target == CodeGenTarget::GLSL)
+ {
+ Emit("((");
+ EmitExpr(callExpr->Arguments[1]);
+ Emit(") * (");
+ EmitExpr(callExpr->Arguments[0]);
+ Emit("))");
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
}
else if (auto overloadedExpr = funcExpr.As<OverloadedExpr>())
{
@@ -3877,7 +3894,13 @@ emitDeclImpl(decl, nullptr);
{
if(auto decoration = inst->findDecoration<IRHighLevelDeclDecoration>())
{
- return getText(decoration->decl->getName());
+ auto decl = decoration->decl;
+ if (auto reflectionNameMod = decl->FindModifier<ParameterBlockReflectionName>())
+ {
+ return getText(reflectionNameMod->nameAndLoc.name);
+ }
+
+ return getText(decl->getName());
}
StringBuilder sb;
@@ -3891,9 +3914,11 @@ emitDeclImpl(decl, nullptr);
enum class Flavor
{
Simple,
+ Ptr,
};
Flavor flavor;
+ IRDeclaratorInfo* next;
String const* name;
};
@@ -3910,9 +3935,36 @@ emitDeclImpl(decl, nullptr);
emit(" ");
emit(*declarator->name);
break;
+
+ case IRDeclaratorInfo::Flavor::Ptr:
+ emit("*");
+ emitDeclarator(context, declarator->next);
+ break;
+ }
+ }
+
+ void emitIRSimpleValue(
+ EmitContext* context,
+ IRInst* inst)
+ {
+ switch(inst->op)
+ {
+ case kIROp_IntLit:
+ emit(((IRConstant*) inst)->u.intVal);
+ break;
+
+ case kIROp_FloatLit:
+ emit(((IRConstant*) inst)->u.floatVal);
+ break;
+
+ default:
+ SLANG_UNIMPLEMENTED_X("val case for emit");
+ break;
}
+
}
+
void emitIRSimpleType(
EmitContext* context,
IRType* type)
@@ -3928,29 +3980,56 @@ emitDeclImpl(decl, nullptr);
#undef CASE
- default:
- SLANG_UNIMPLEMENTED_X("type case for emit");
+
+ case kIROp_VectorType:
+ emitIRVectorType(context, (IRVectorType*) type);
break;
- }
- }
+ case kIROp_StructType:
+ emit(getName(type));
+ break;
- void emitIRSimpleValue(
- EmitContext* context,
- IRInst* inst)
- {
- switch(inst->op)
- {
- case kIROp_IntLit:
- emit(((IRConstant*) inst)->u.intVal);
+ case kIROp_TextureType:
+ {
+ auto textureType = (IRTextureType*) type;
+
+ // TODO: actually look at the flavor and emit the right name
+ emit("Texture2D");
+
+ emit("<");
+ emitIRType(context, textureType->getElementType(), nullptr);
+ emit(">");
+ }
break;
- case kIROp_FloatLit:
- emit(((IRConstant*) inst)->u.floatVal);
+ case kIROp_ConstantBufferType:
+ {
+ auto tt = (IRConstantBufferType*) type;
+ emit("ConstantBuffer<");
+ emitIRType(context, tt->getElementType(), nullptr);
+ emit(">");
+ }
+ break;
+
+ case kIROp_TextureBufferType:
+ {
+ auto tt = (IRTextureBufferType*) type;
+ emit("ConstantBuffer<");
+ emitIRType(context, tt->getElementType(), nullptr);
+ emit(">");
+ }
break;
+ case kIROp_SamplerType:
+ {
+ // TODO: actually look at the flavor and emit the right name
+ emit("SamplerState");
+ }
+ break;
+
+
default:
- SLANG_UNIMPLEMENTED_X("val case for emit");
+ SLANG_UNIMPLEMENTED_X("type case for emit");
break;
}
@@ -3974,14 +4053,15 @@ emitDeclImpl(decl, nullptr);
{
switch( type->op )
{
- case kIROp_VectorType:
- emitIRVectorType(context, (IRVectorType*) type);
- emitDeclarator(context, declarator);
- break;
+ case kIROp_PtrType:
+ {
+ auto ptrType = (IRPtrType*) type;
- case kIROp_StructType:
- emit(getName(type));
- emitDeclarator(context, declarator);
+ IRDeclaratorInfo ptrDeclarator;
+ ptrDeclarator.flavor = IRDeclaratorInfo::Flavor::Ptr;
+ ptrDeclarator.next = declarator;
+ emitIRType(context, ptrType->getValueType(), &ptrDeclarator);
+ }
break;
default:
@@ -4010,11 +4090,82 @@ emitDeclImpl(decl, nullptr);
emitIRType(context, type, (IRDeclaratorInfo*) nullptr);
}
+ bool shouldFoldIRInstIntoUseSites(
+ EmitContext* context,
+ IRInst* inst)
+ {
+ // Certain opcodes should always be folded in
+ switch( inst->op )
+ {
+ default:
+ break;
+
+ case kIROp_IntLit:
+ case kIROp_FloatLit:
+ case kIROp_FieldAddress:
+ return true;
+ }
+
+ // Certain *types* will usually want to be folded in
+ auto type = inst->getType();
+ switch (type->op)
+ {
+ case kIROp_ConstantBufferType:
+ case kIROp_TextureBufferType:
+ // TODO: we need to be careful here, because
+ // HLSL shader model 6 allows these as explicit
+ // types.
+ return true;
+
+ default:
+ break;
+ }
+
+ // By default we will *not* fold things into their use sites.
+ return false;
+ }
+
+ bool isDerefBaseImplicit(
+ EmitContext* context,
+ IRInst* inst)
+ {
+ auto type = inst->getType();
+ switch (type->op)
+ {
+ case kIROp_ConstantBufferType:
+ case kIROp_TextureBufferType:
+ // TODO: we need to be careful here, because
+ // HLSL shader model 6 allows these as explicit
+ // types.
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+
+
void emitIROperand(
EmitContext* context,
IRInst* inst)
{
- emit(getName(inst));
+ if( shouldFoldIRInstIntoUseSites(context, inst) )
+ {
+ emit("(");
+ emitIRInstExpr(context, inst);
+ emit(")");
+ return;
+ }
+
+ switch(inst->op)
+ {
+ default:
+ emit(getName(inst));
+ break;
+ }
}
void emitIRArgs(
@@ -4037,28 +4188,49 @@ emitDeclImpl(decl, nullptr);
EmitContext* context,
IRInst* inst)
{
- emitIRType(context, inst->getType(), getName(inst));
+ auto type = inst->getType();
+ if(!type || type->op == kIROp_VoidType)
+ return;
+
+ emitIRType(context, type, getName(inst));
emit(" = ");
}
- void emitIRInst(
+ void emitIRInstExpr(
EmitContext* context,
IRInst* inst)
{
- // TODO: need to be able to `switch` on the IR opcode here,
- // so there is some work to be done.
switch(inst->op)
{
- case kIROp_Param:
- // Don't emit parameters, since they are declared as part of the function.
+ case kIROp_IntLit:
+ {
+ auto irConst = (IRConstant*) inst;
+ emit(irConst->u.intVal);
+ }
+ break;
+
+ case kIROp_FloatLit:
+ {
+ auto irConst = (IRConstant*) inst;
+ emit(irConst->u.floatVal);
+ }
break;
case kIROp_Construct:
// Simple constructor call
- emitIRInstResultDecl(context, inst);
- emitIRType(context, inst->getType());
- emitIRArgs(context, inst);
- emit(";\n");
+ if( inst->getArgCount() == 2 )
+ {
+ // Need to emit as cast for HLSL
+ emit("(");
+ emitIRType(context, inst->getType());
+ emit(") ");
+ emitIROperand(context, inst->getArg(1));
+ }
+ else
+ {
+ emitIRType(context, inst->getType());
+ emitIRArgs(context, inst);
+ }
break;
case kIROp_FieldExtract:
@@ -4067,14 +4239,95 @@ emitDeclImpl(decl, nullptr);
IRFieldExtract* fieldExtract = (IRFieldExtract*) inst;
- emitIRInstResultDecl(context, inst);
emitIROperand(context, fieldExtract->getBase());
emit(".");
emit(getName(fieldExtract->getField()));
- emit(";\n");
}
break;
+ case kIROp_FieldAddress:
+ {
+ // Extract field "address" from aggregate
+
+ IRFieldAddress* ii = (IRFieldAddress*) inst;
+
+ if (!isDerefBaseImplicit(context, ii->getBase()))
+ {
+ emitIROperand(context, ii->getBase());
+ emit(".");
+ }
+
+ emit(getName(ii->getField()));
+ }
+ break;
+
+ case kIROp_Intrinsic_Add:
+ emitIROperand(context, inst->getArg(1));
+ emit(" + ");
+ emitIROperand(context, inst->getArg(2));
+ break;
+
+
+ case kIROp_Intrinsic_sample_t_s_u:
+ emitIROperand(context, inst->getArg(1));
+ emit(".Sample(");
+ emitIROperand(context, inst->getArg(2));
+ emit(", ");
+ emitIROperand(context, inst->getArg(3));
+ emit(")");
+ break;
+
+ case kIROp_Load:
+ // TODO: this logic will really only work for a simple variable reference...
+ emitIROperand(context, inst->getArg(1));
+ break;
+
+ case kIROp_Call:
+ {
+ emitIROperand(context, inst->getArg(1));
+ emit("(");
+ UInt argCount = inst->getArgCount() - 2;
+ for( UInt aa = 0; aa < argCount; ++aa )
+ {
+ if(aa != 0) emit(", ");
+ emitIROperand(context, inst->getArg(aa + 2));
+ }
+ emit(")");
+ }
+ break;
+
+ default:
+ emit("/* uhandled */");
+ break;
+ }
+ }
+
+ void emitIRInst(
+ EmitContext* context,
+ IRInst* inst)
+ {
+ if (shouldFoldIRInstIntoUseSites(context, inst))
+ {
+ return;
+ }
+
+ switch(inst->op)
+ {
+ default:
+ emitIRInstResultDecl(context, inst);
+ emitIRInstExpr(context, inst);
+ emit(";\n");
+ break;
+
+ case kIROp_Param:
+ // Don't emit parameters, since they are declared as part of the function.
+ break;
+
+ case kIROp_FieldAddress:
+ // skip during code emit, since it should be
+ // folded into use site(s)
+ break;
+
case kIROp_ReturnVoid:
emit("return;\n");
break;
@@ -4084,10 +4337,6 @@ emitDeclImpl(decl, nullptr);
emitIROperand(context, ((IRReturnVal*) inst)->getVal());
emit(";\n");
break;
-
- default:
- emit("// uhandled\n");
- break;
}
}
@@ -4102,6 +4351,17 @@ emitDeclImpl(decl, nullptr);
}
}
+ void emitIRLayoutSemantics(
+ EmitContext* context,
+ IRInst* inst)
+ {
+ auto decoration = inst->findDecoration<IRLayoutDecoration>();
+ if (decoration)
+ {
+ emitHLSLRegisterSemantics((VarLayout*) decoration->layout);
+ }
+ }
+
void emitIRFunc(
EmitContext* context,
IRFunc* func)
@@ -4172,6 +4432,70 @@ emitDeclImpl(decl, nullptr);
emit("};\n");
}
+ void emitIRParameterBlock(
+ EmitContext* context,
+ IRVar* varDecl,
+ IRUniformBufferType* type)
+ {
+ emit("cbuffer ");
+ emit(getName(varDecl));
+ emitIRLayoutSemantics(context, varDecl);
+ emit("\n{\n");
+
+ auto elementType = type->getElementType();
+ switch( elementType->op )
+ {
+ case kIROp_StructType:
+ {
+ auto structType = (IRStructDecl*) elementType;
+ for(auto ff = structType->getFirstField(); ff; ff = ff->getNextField())
+ {
+ auto fieldType = ff->getFieldType();
+ emitIRType(context, fieldType, getName(ff));
+
+ emitIRSemantics(context, ff);
+
+ emit(";\n");
+ }
+ }
+ break;
+
+ default:
+ emit("/* unexpected */");
+ break;
+ }
+
+ emit("}\n");
+ }
+
+ void emitIRVar(
+ EmitContext* context,
+ IRVar* varDecl)
+ {
+ auto allocatedType = varDecl->getType();
+ auto varType = ((IRPtrType*) allocatedType)->getValueType();
+
+ switch( varType->op )
+ {
+ case kIROp_ConstantBufferType:
+ case kIROp_TextureBufferType:
+ emitIRParameterBlock(context, varDecl, (IRUniformBufferType*) varType);
+ return;
+
+ default:
+ break;
+ }
+
+
+ emitIRType(context, varType, getName(varDecl));
+
+ emitIRSemantics(context, varDecl);
+
+ emitIRLayoutSemantics(context, varDecl);
+
+ emit(";\n");
+ }
+
void emitIRGlobalInst(
EmitContext* context,
IRInst* inst)
@@ -4188,6 +4512,10 @@ emitDeclImpl(decl, nullptr);
emitIRStruct(context, (IRStructDecl*) inst);
break;
+ case kIROp_Var:
+ emitIRVar(context, (IRVar*) inst);
+ break;
+
default:
break;
}
@@ -4320,8 +4648,8 @@ String emitEntryPoint(
//
// We'll try to detect the cases here:
//
-#if 0
- if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ))
+ if((translationUnit->compileRequest->compileFlags & SLANG_COMPILE_FLAG_USE_IR)
+ && !(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ))
{
// This seems to be case (3), because the user is asking for full
// checking, and so we can assume we understand the code fully.
@@ -4332,20 +4660,14 @@ String emitEntryPoint(
auto lowered = lowerEntryPointToIR(entryPoint, programLayout, target);
- dumpIR(lowered);
+// dumpIR(lowered);
// TODO: do we want to emit directly from IR, or translate the
// IR back into AST for emission?
visitor.emitIRModule(&context, lowered);
-
- throw 99;
-
}
- else if(
-#else
- if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ) ||
-#endif
+ else if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ) ||
translationUnit->compileRequest->loadedModulesList.Count() != 0)
{
// The user has `import`ed some Slang modules, and so we are in case (2)
diff --git a/source/slang/intrinsic-defs.h b/source/slang/intrinsic-defs.h
index a272bd51c..336f1eff1 100644
--- a/source/slang/intrinsic-defs.h
+++ b/source/slang/intrinsic-defs.h
@@ -68,6 +68,10 @@ INTRINSIC(InnerProduct_Vector_Matrix)
INTRINSIC(InnerProduct_Matrix_Vector)
INTRINSIC(InnerProduct_Matrix_Matrix)
+// Texture sampling operation of the form `t.Sample(s,u)`
+INTRINSIC(sample_t_s_u)
+
+
diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h
index d594d2755..2548dc22a 100644
--- a/source/slang/ir-inst-defs.h
+++ b/source/slang/ir-inst-defs.h
@@ -16,11 +16,17 @@ INST(Int32Type, type.i32, 0, 0)
INST(UInt32Type, type.u32, 0, 0)
INST(StructType, type.struct, 0, PARENT)
INST(FuncType, func_type, 0, 0)
+INST(PtrType, ptr_type, 1, 0)
+INST(TextureType, texture_type, 2, 0)
+INST(SamplerType, sampler_type, 1, 0)
+INST(ConstantBufferType, constant_buffer_type, 1, 0)
+INST(TextureBufferType, texture_buffer_type, 1, 0)
INST(IntLit, integer_constant, 0, 0)
INST(FloatLit, float_constant, 0, 0)
-INST(Construct, construct, 0, 0)
+INST(Construct, construct, 0, 0)
+INST(Call, call, 1, 0)
INST(Module, module, 0, PARENT)
INST(Func, func, 0, PARENT)
@@ -28,8 +34,14 @@ INST(Block, block, 0, PARENT)
INST(Param, param, 0, 0)
INST(StructField, field, 0, 0)
+INST(Var, var, 0, 0)
+
+INST(Load, load, 1, 0)
+INST(Store, store, 2, 0)
+
+INST(FieldExtract, get_field, 2, 0)
+INST(FieldAddress, get_field_addr, 2, 0)
-INST(FieldExtract, get_field, 2, 0)
INST(ReturnVal, return_val, 1, 0)
INST(ReturnVoid, return_void, 1, 0)
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index 8e4f789ef..3f07a685f 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -365,8 +365,10 @@ namespace Slang
UInt size,
IROp op,
IRType* type,
- UInt argCount,
- IRValue* const* args)
+ UInt fixedArgCount,
+ IRValue* const* fixedArgs,
+ UInt varArgCount = 0,
+ IRValue* const* varArgs = nullptr)
{
// First, we need to pick a good insertion point
// for the instruction, which we do by looking
@@ -378,9 +380,14 @@ namespace Slang
{
parent = joinParentInstsForInsertion(parent, type->parent);
}
- for( UInt aa = 0; aa < argCount; ++aa )
+ for( UInt aa = 0; aa < fixedArgCount; ++aa )
{
- auto arg = args[aa];
+ auto arg = fixedArgs[aa];
+ parent = joinParentInstsForInsertion(parent, arg->parent);
+ }
+ for( UInt aa = 0; aa < varArgCount; ++aa )
+ {
+ auto arg = varArgs[aa];
parent = joinParentInstsForInsertion(parent, arg->parent);
}
@@ -405,7 +412,7 @@ namespace Slang
// way: we will construct a temporary instruction and
// then use it to look up in a cache of instructions.
- IRInst* keyInst = createInstImpl(builder, size, op, type, argCount ,args);
+ IRInst* keyInst = createInstImpl(builder, size, op, type, fixedArgCount, fixedArgs, varArgCount, varArgs);
keyInst->parent = parent;
IRInstKey key;
@@ -452,6 +459,27 @@ namespace Slang
static T* findOrEmitInst(
IRBuilder* builder,
IROp op,
+ IRType* type,
+ UInt fixedArgCount,
+ IRValue* const* fixedArgs,
+ UInt varArgCount,
+ IRValue* const* varArgs)
+ {
+ return (T*) findOrEmitInstImpl(
+ builder,
+ sizeof(T) + varArgCount * sizeof(IRUse),
+ op,
+ type,
+ fixedArgCount,
+ fixedArgs,
+ varArgCount,
+ varArgs);
+ }
+
+ template<typename T>
+ static T* findOrEmitInst(
+ IRBuilder* builder,
+ IROp op,
IRType* type)
{
return (T*) findOrEmitInstImpl(
@@ -630,6 +658,22 @@ namespace Slang
getTypeType());
}
+ IRType* IRBuilder::getIntrinsicType(
+ IROp op,
+ UInt argCount,
+ IRValue* const* args)
+ {
+ return findOrEmitInst<IRType>(
+ this,
+ op,
+ getTypeType(),
+ 0,
+ nullptr,
+ argCount,
+ args);
+ }
+
+
IRStructDecl* IRBuilder::createStructType()
{
return createInst<IRStructDecl>(
@@ -652,6 +696,7 @@ namespace Slang
IRType* const* paramTypes,
IRType* resultType)
{
+ // TODO: need to unique things here!
auto inst = createInstWithTrailingArgs<IRFuncType>(
this,
kIROp_FuncType,
@@ -664,6 +709,19 @@ namespace Slang
return inst;
}
+ IRType* IRBuilder::getPtrType(
+ IRType* valueType)
+ {
+ auto inst = findOrEmitInst<IRPtrType>(
+ this,
+ kIROp_PtrType,
+ getTypeType(),
+ 1,
+ (IRValue* const*) &valueType);
+ return inst;
+ }
+
+
IRValue* IRBuilder::getBoolValue(bool value)
{
SLANG_UNIMPLEMENTED_X("IR");
@@ -689,6 +747,24 @@ namespace Slang
&value);
}
+ IRInst* IRBuilder::emitCallInst(
+ IRType* type,
+ IRValue* func,
+ UInt argCount,
+ IRValue* const* args)
+ {
+ auto inst = createInstWithTrailingArgs<IRCall>(
+ this,
+ kIROp_Call,
+ type,
+ 1,
+ &func,
+ argCount,
+ args);
+ addInst(inst);
+ return inst;
+ }
+
IRInst* IRBuilder::emitIntrinsicInst(
IRType* type,
IntrinsicOp intrinsicOp,
@@ -763,6 +839,40 @@ namespace Slang
return inst;
}
+ IRVar* IRBuilder::emitVar(
+ IRType* type)
+ {
+ auto allocatedType = getPtrType(type);
+ auto inst = createInst<IRVar>(
+ this,
+ kIROp_Var,
+ allocatedType);
+ addInst(inst);
+ return inst;
+ }
+
+ IRInst* IRBuilder::emitLoad(
+ IRValue* ptr)
+ {
+ auto ptrType = ptr->getType();
+ if( ptrType->op != kIROp_PtrType )
+ {
+ // Bad!
+ return nullptr;
+ }
+
+ auto valueType = ((IRPtrType*) ptrType)->getValueType();
+
+ auto inst = createInst<IRLoad>(
+ this,
+ kIROp_Load,
+ valueType,
+ ptr);
+
+ addInst(inst);
+ return inst;
+ }
+
IRInst* IRBuilder::emitFieldExtract(
IRType* type,
IRValue* base,
@@ -779,6 +889,22 @@ namespace Slang
return inst;
}
+ IRInst* IRBuilder::emitFieldAddress(
+ IRType* type,
+ IRValue* base,
+ IRStructField* field)
+ {
+ auto inst = createInst<IRFieldAddress>(
+ this,
+ kIROp_FieldAddress,
+ type,
+ base,
+ field);
+
+ addInst(inst);
+ return inst;
+ }
+
IRInst* IRBuilder::emitReturn(
IRValue* val)
{
@@ -824,6 +950,13 @@ namespace Slang
return decoration;
}
+ IRLayoutDecoration* IRBuilder::addLayoutDecoration(IRInst* inst, Layout* layout)
+ {
+ auto decoration = addDecoration<IRLayoutDecoration>(inst);
+ decoration->layout = layout;
+ return decoration;
+ }
+
//
diff --git a/source/slang/ir.h b/source/slang/ir.h
index fcebf5d15..aa7b6a045 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -7,6 +7,8 @@
// similar in spirit to LLVM (but much simpler).
//
+#include "type-layout.h"
+
// We need the definition of `BaseType` which currently belongs to the AST
#include "syntax.h"
@@ -72,6 +74,7 @@ struct IRUse
enum IRDecorationOp : uint16_t
{
kIRDecorationOp_HighLevelDecl,
+ kIRDecorationOp_Layout,
};
// A "decoration" that gets applied to an instruction.
@@ -161,6 +164,15 @@ struct IRHighLevelDeclDecoration : IRDecoration
Decl* decl;
};
+// Associates an IR-level decoration with a source layout
+struct IRLayoutDecoration : IRDecoration
+{
+ enum { kDecorationOp = kIRDecorationOp_Layout };
+
+ Layout* layout;
+};
+
+
typedef long long IRIntegerValue;
typedef double IRFloatingPointValue;
@@ -211,6 +223,41 @@ struct IRFuncType : IRType
}
};
+struct IRPtrType : IRType
+{
+ IRUse valueType;
+
+ IRType* getValueType() { return (IRType*) valueType.usedValue; }
+};
+
+struct IRTextureType : IRType
+{
+ IRUse flavor;
+ IRUse elementType;
+
+ IRIntegerValue getFlavor() { return ((IRConstant*) flavor.usedValue)->u.intVal; }
+ IRType* getElementType() { return (IRType*) elementType.usedValue; }
+};
+
+struct IRUniformBufferType : IRType
+{
+ IRUse elementType;
+ IRType* getElementType() { return (IRType*) elementType.usedValue; }
+};
+
+struct IRConstantBufferType : IRUniformBufferType {};
+struct IRTextureBufferType : IRUniformBufferType {};
+
+struct IRCall : IRInst
+{
+ IRUse func;
+};
+
+struct IRLoad : IRInst
+{
+ IRUse ptr;
+};
+
struct IRStructField;
struct IRFieldExtract : IRInst
{
@@ -221,6 +268,16 @@ struct IRFieldExtract : IRInst
IRStructField* getField() { return (IRStructField*) field.usedValue; }
};
+struct IRFieldAddress : IRInst
+{
+ IRUse base;
+ IRUse field;
+
+ IRInst* getBase() { return base.usedValue; }
+ IRStructField* getField() { return (IRStructField*) field.usedValue; }
+};
+
+
// A instruction that ends a basic block (usually because of control flow)
struct IRTerminatorInst : IRInst
{};
@@ -285,6 +342,9 @@ struct IRParam : IRInst
IRParam* getNextParam();
};
+struct IRVar : IRInst
+{};
+
// A function is a parent to zero or more blocks of instructions.
//
// A function is itself a value, so that it can be a direct operand of
@@ -360,6 +420,11 @@ struct IRBuilder
IRType* getVoidType();
IRType* getBlockType();
+ IRType* getIntrinsicType(
+ IROp op,
+ UInt argCount,
+ IRValue* const* args);
+
IRStructDecl* createStructType();
IRStructField* createStructField(IRType* fieldType);
@@ -368,10 +433,19 @@ struct IRBuilder
IRType* const* paramTypes,
IRType* resultType);
+ IRType* getPtrType(
+ IRType* valueType);
+
IRValue* getBoolValue(bool value);
IRValue* getIntValue(IRType* type, IRIntegerValue value);
IRValue* getFloatValue(IRType* type, IRFloatingPointValue value);
+ IRInst* emitCallInst(
+ IRType* type,
+ IRValue* func,
+ UInt argCount,
+ IRValue* const* args);
+
IRInst* emitIntrinsicInst(
IRType* type,
IntrinsicOp intrinsicOp,
@@ -393,11 +467,22 @@ struct IRBuilder
IRParam* emitParam(
IRType* type);
+ IRVar* emitVar(
+ IRType* type);
+
+ IRInst* emitLoad(
+ IRValue* ptr);
+
IRInst* emitFieldExtract(
IRType* type,
IRValue* base,
IRStructField* field);
+ IRInst* emitFieldAddress(
+ IRType* type,
+ IRValue* basePtr,
+ IRStructField* field);
+
IRInst* emitReturn(
IRValue* val);
@@ -414,7 +499,14 @@ struct IRBuilder
return (T*) addDecorationImpl(inst, sizeof(T), op);
}
+ template<typename T>
+ T* addDecoration(IRInst* inst)
+ {
+ return (T*) addDecorationImpl(inst, sizeof(T), IRDecorationOp(T::kDecorationOp));
+ }
+
IRHighLevelDeclDecoration* addHighLevelDeclDecoration(IRInst* inst, Decl* decl);
+ IRLayoutDecoration* addLayoutDecoration(IRInst* inst, Layout* layout);
};
void dumpIR(IRModule* module);
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 781209dce..d4cac0337 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -14,6 +14,7 @@ struct LoweredValInfo
{
None,
Simple,
+ Ptr,
};
union
@@ -35,7 +36,14 @@ struct LoweredValInfo
info.val = v;
return info;
}
-};
+
+ static LoweredValInfo ptr(IRValue* v)
+ {
+ LoweredValInfo info;
+ info.flavor = Flavor::Ptr;
+ info.val = v;
+ return info;
+ }};
struct SharedIRGenContext
{
@@ -70,6 +78,25 @@ IRValue* getSimpleVal(LoweredValInfo lowered)
}
}
+IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered)
+{
+ switch(lowered.flavor)
+ {
+ case LoweredValInfo::Flavor::None:
+ return nullptr;
+
+ case LoweredValInfo::Flavor::Simple:
+ return lowered.val;
+
+ case LoweredValInfo::Flavor::Ptr:
+ return context->irBuilder->emitLoad(lowered.val);
+
+ default:
+ SLANG_UNEXPECTED("unhandled value flavor");
+ return nullptr;
+ }
+}
+
struct LoweredTypeInfo
{
enum class Flavor
@@ -192,8 +219,46 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
SLANG_UNIMPLEMENTED_X("type lowering");
}
+ LoweredTypeInfo visitFuncType(FuncType* type)
+ {
+ LoweredValInfo loweredFunc = ensureDecl(context, type->declRef);
+ return getSimpleVal(loweredFunc)->getType();
+ }
+
+ void addGenericArgs(List<IRValue*>* ioArgs, DeclRefBase declRef)
+ {
+ auto subs = declRef.substitutions;
+ while(subs)
+ {
+ for(auto aa : subs->args)
+ {
+ (*ioArgs).Add(getSimpleVal(lowerVal(context, aa)));
+ }
+ subs = subs->outer;
+ }
+ }
+
LoweredTypeInfo visitDeclRefType(DeclRefType* type)
{
+ // We need to detect builtin/intrinsic types here, since they should map to custom modifiers
+ // We need to catch builtin/intrinsic types here
+ if( auto intrinsicTypeMod = type->declRef.getDecl()->FindModifier<IntrinsicTypeModifier>() )
+ {
+ auto builder = getBuilder();
+ auto intType = builder->getBaseType(BaseType::Int);
+ //
+ List<IRValue*> irArgs;
+ for( auto val : intrinsicTypeMod->irOperands )
+ {
+ irArgs.Add(builder->getIntValue(intType, val));
+ }
+
+ addGenericArgs(&irArgs, type->declRef);
+
+ auto irType = getBuilder()->getIntrinsicType(IROp(intrinsicTypeMod->irOp), irArgs.Count(), irArgs.Buffer());
+ return LoweredTypeInfo(irType);
+ }
+
// Catch-all for user-defined type references
LoweredValInfo loweredDeclRef = ensureDecl(context, type->declRef);
@@ -222,7 +287,6 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
return getBuilder()->getVectorType(irElementType, irElementCount);
}
-
};
LoweredValInfo lowerVal(
@@ -306,7 +370,8 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo>
switch( argInfo.flavor )
{
case LoweredValInfo::Flavor::Simple:
- args.Add(getSimpleVal(argInfo));
+ case LoweredValInfo::Flavor::Ptr:
+ args.Add(getSimpleVal(context, argInfo));
break;
default:
@@ -315,19 +380,39 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo>
}
}
- LoweredValInfo lowerIntrinsicCall(
- InvokeExpr* expr,
- IntrinsicOp intrinsicOp)
+ // Collect the lowered arguments for a call expression
+ void addCallArgs(
+ InvokeExpr* expr,
+ List<IRValue*>* ioArgs)
{
- auto type = lowerSimpleType(context, expr->type);
+ auto& irArgs = *ioArgs;
+
+ // TODO: should unwrap any layers of identity expressions around this...
+ if( auto baseMemberExpr = expr->FunctionExpr.As<MemberExpr>() )
+ {
+ // This call took the form of a member function call, so
+ // we need to correctly add the `this` argument as
+ // an explicit argument.
+ //
+ auto loweredBase = lowerExpr(context, baseMemberExpr->BaseExpression);
+ addArgs(&irArgs, loweredBase);
+ }
- List<IRValue*> irArgs;
for( auto arg : expr->Arguments )
{
auto loweredArg = lowerExpr(context, arg);
addArgs(&irArgs, loweredArg);
}
+ }
+
+ LoweredValInfo lowerIntrinsicCall(
+ InvokeExpr* expr,
+ IntrinsicOp intrinsicOp)
+ {
+ auto type = lowerSimpleType(context, expr->type);
+ List<IRValue*> irArgs;
+ addCallArgs(expr, &irArgs);
UInt argCount = irArgs.Count();
return LoweredValInfo::simple(getBuilder()->emitIntrinsicInst(type, intrinsicOp, argCount, &irArgs[0]));
@@ -335,14 +420,15 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo>
LoweredValInfo lowerSimpleCall(InvokeExpr* expr)
{
+ auto type = lowerSimpleType(context, expr->type);
+
auto loweredFunc = lowerExpr(context, expr->FunctionExpr);
- for( auto arg : expr->Arguments )
- {
- auto loweredArg = lowerExpr(context, arg);
- }
+ List<IRValue*> irArgs;
+ addCallArgs(expr, &irArgs);
+ UInt argCount = irArgs.Count();
- SLANG_UNIMPLEMENTED_X("codegen for invoke expression");
+ return LoweredValInfo::simple(getBuilder()->emitCallInst(type, getSimpleVal(loweredFunc), argCount, irArgs.Buffer()));
}
LoweredValInfo visitInvokeExpr(InvokeExpr* expr)
@@ -409,6 +495,19 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo>
}
break;
+ case LoweredValInfo::Flavor::Ptr:
+ {
+ // We are "extracting" a field from an lvalue address,
+ // which means we should just compute an lvalue
+ // representing the field address.
+ IRValue* irBasePtr = base.val;
+ return LoweredValInfo::ptr(
+ getBuilder()->emitFieldAddress(
+ getBuilder()->getPtrType(getSimpleType(fieldType)),
+ irBasePtr,
+ (IRStructField*) getSimpleVal(field)));
+ }
+ break;
default:
SLANG_UNIMPLEMENTED_X("codegen for field extract");
}
@@ -438,7 +537,39 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo>
LoweredValInfo visitDerefExpr(DerefExpr* expr)
{
- SLANG_UNIMPLEMENTED_X("codegen for deref expression");
+ auto loweredType = lowerType(context, expr->type);
+ auto loweredBase = lowerExpr(context, expr->base);
+
+ // TODO: handle tupel-type for `base`
+
+ // The type of the lowered base must by some kind of pointer,
+ // in order for a dereference to make senese, so we just
+ // need to extract the value type from that pointer here.
+ //
+ auto loweredBaseVal = getSimpleVal(context, loweredBase);
+ auto loweredBaseType = loweredBaseVal->getType();
+ switch( loweredBaseType->op )
+ {
+ case kIROp_PtrType:
+ // TODO: should we enumerate these explicitly?
+ case kIROp_ConstantBufferType:
+ case kIROp_TextureBufferType:
+ // Note that we do *not* perform an actual `load` operation
+ // here, but rather just use the pointer value to construct
+ // an appropriate `LoweredValInfo` representing the underlying
+ // dereference.
+ //
+ // This is important so that an expression like `&((*foo).bar)`
+ // (which is desugared from `&foo->bar`) can be handled; such
+ // an expression does *not* perform a dereference at runtime,
+ // and is just a bit of pointer math.
+ //
+ return LoweredValInfo::ptr(loweredBaseVal);
+
+ default:
+ SLANG_UNIMPLEMENTED_X("codegen for deref expression");
+ return LoweredValInfo();
+ }
}
LoweredValInfo visitTypeCastExpr(TypeCastExpr* expr)
@@ -521,15 +652,29 @@ void lowerStmt(
return visitor.dispatch(stmt);
}
+void assign(
+ IRGenContext* context,
+ LoweredValInfo const& left,
+ LoweredValInfo const& right)
+{
+ SLANG_UNIMPLEMENTED_X("assignment");
+}
+
struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
{
- IRGenContext* context;
+ IRGenContext* context;
+ Layout* layout;
IRBuilder* getBuilder()
{
return context->irBuilder;
}
+ Layout* getLayout()
+ {
+ return layout;
+ }
+
LoweredValInfo visitDeclBase(DeclBase* decl)
{
SLANG_UNIMPLEMENTED_X("decl catch-all");
@@ -540,6 +685,59 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
SLANG_UNIMPLEMENTED_X("decl catch-all");
}
+ LoweredValInfo visitVarDeclBase(VarDeclBase* decl)
+ {
+ // A user-defined variable declaration will usually turn into
+ // an `alloca` operation for the variable's storage,
+ // plus some code to initialize it and then store to the variable.
+ //
+ // TODO: we may want to special-case things when the variable's
+ // type, qualifiers, or context mark it as something that can't
+ // be mutable (or even do some limited dataflow pass to check
+ // which variables ever get assigned) so that we can directly
+ // emit an SSA value in this common case.
+ //
+
+ auto varType = lowerType(context, decl->getType());
+
+ LoweredValInfo varVal;
+
+ switch( varType.flavor )
+ {
+ case LoweredTypeInfo::Flavor::Simple:
+ {
+ auto irAlloc = getBuilder()->emitVar(getSimpleType(varType));
+
+ getBuilder()->addHighLevelDeclDecoration(irAlloc, decl);
+
+ if (getLayout())
+ {
+ getBuilder()->addLayoutDecoration(irAlloc, getLayout());
+ }
+
+
+ varVal = LoweredValInfo::ptr(irAlloc);
+ }
+ break;
+
+ default:
+ SLANG_UNIMPLEMENTED_X("struct field type");
+ }
+
+ if( auto initExpr = decl->initExpr )
+ {
+ auto initVal = lowerExpr(context, initExpr);
+
+ assign(context, varVal, initVal);
+ }
+
+ context->shared->declValues.Add(
+ DeclRef<VarDeclBase>(decl, nullptr),
+ varVal);
+
+ return varVal;
+ }
+
LoweredValInfo visitAggTypeDecl(AggTypeDecl* decl)
{
// User-defined aggregate type: need to translate into
@@ -621,7 +819,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
auto irFuncType = getBuilder()->getFuncType(
paramTypes.Count(),
- &paramTypes[0],
+ paramTypes.Buffer(),
irResultType);
irFunc->type.init(irFunc, irFuncType);
@@ -637,9 +835,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
LoweredValInfo lowerDecl(
IRGenContext* context,
- Decl* decl)
+ Decl* decl,
+ Layout* layout)
{
DeclLoweringVisitor visitor;
+ visitor.layout = layout;
visitor.context = context;
return visitor.dispatch(decl);
}
@@ -658,9 +858,26 @@ LoweredValInfo ensureDecl(
// from the declaration reference, so that they can be
// applied correctly to the declaration itself...
+ IRBuilder subIRBuilder;
+ subIRBuilder.shared = context->irBuilder->shared;
+ subIRBuilder.parentInst = subIRBuilder.shared->module;
+
IRGenContext subContext = *context;
- result = lowerDecl(context, declRef.getDecl());
+ subContext.irBuilder = &subIRBuilder;
+
+ RefPtr<VarLayout> layout;
+ auto globalScopeLayout = shared->programLayout->globalScopeLayout;
+ if (auto globalParameterBlockLayout = globalScopeLayout.As<ParameterBlockTypeLayout>())
+ {
+ globalScopeLayout = globalParameterBlockLayout->elementTypeLayout;
+ }
+ if (auto globalStructTypeLayout = globalScopeLayout.As<StructTypeLayout>())
+ {
+ globalStructTypeLayout->mapVarToLayout.TryGetValue(declRef.getDecl(), layout);
+ }
+
+ result = lowerDecl(&subContext, declRef.getDecl(), layout);
shared->declValues[declRef] = result;
@@ -699,7 +916,7 @@ static void lowerEntryPointToIR(
// TODO: entry point lowering is probably *not* just like lowering a function...
- lowerDecl(context, entryPointFunc);
+ lowerDecl(context, entryPointFunc, entryPointLayout);
}
IRModule* lowerEntryPointToIR(
diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h
index 597d59d9a..d7d0391e4 100644
--- a/source/slang/modifier-defs.h
+++ b/source/slang/modifier-defs.h
@@ -209,6 +209,20 @@ SYNTAX_CLASS(MagicTypeModifier, Modifier)
FIELD(uint32_t, tag)
END_SYNTAX_CLASS()
+// A modifier applied to declarations of builtin types to indicate how they
+// should be lowered to the IR.
+//
+// TODO: This should really subsume `BuiltinTypeModifier` and
+// `MagicTypeModifier` so that we don't have to apply all of them.
+SYNTAX_CLASS(IntrinsicTypeModifier, Modifier)
+ // The IR opcode to use when constructing a type
+ FIELD(uint32_t, irOp)
+
+ // Additional literal opreands to provide when creating instances.
+ // (e.g., for a texture type this passes in shape/mutability info)
+ FIELD(List<uint32_t>, irOperands)
+END_SYNTAX_CLASS()
+
// Modifiers that affect the storage layout for matrices
SIMPLE_SYNTAX_CLASS(MatrixLayoutModifier, Modifier)
diff --git a/source/slang/options.cpp b/source/slang/options.cpp
index 0e329ccd2..669ab1827 100644
--- a/source/slang/options.cpp
+++ b/source/slang/options.cpp
@@ -258,6 +258,10 @@ struct OptionsParser
{
flags |= SLANG_COMPILE_FLAG_SPLIT_MIXED_TYPES;
}
+ else if(argStr == "-use-ir" )
+ {
+ flags |= SLANG_COMPILE_FLAG_USE_IR;
+ }
else if (argStr == "-backend" || argStr == "-target")
{
String name = tryReadCommandLineArgument(arg, &argCursor, argEnd);
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index 7da309d29..9c0d692e6 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -3916,6 +3916,20 @@ namespace Slang
return modifier;
}
+ static RefPtr<RefObject> parseIntrinsicTypeModifier(Parser* parser, void* /*userData*/)
+ {
+ RefPtr<IntrinsicTypeModifier> modifier = new IntrinsicTypeModifier();
+ parser->ReadToken(TokenType::LParent);
+ modifier->irOp = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content));
+ while( AdvanceIf(parser, TokenType::Comma) )
+ {
+ auto operand = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content));
+ modifier->irOperands.Add(operand);
+ }
+ parser->ReadToken(TokenType::RParent);
+
+ return modifier;
+ }
static RefPtr<RefObject> parseImplicitConversionModifier(Parser* parser, void* /*userData*/)
{
RefPtr<ImplicitConversionModifier> modifier = new ImplicitConversionModifier();
@@ -4020,6 +4034,7 @@ namespace Slang
MODIFIER(__builtin_type, parseBuiltinTypeModifier);
MODIFIER(__magic_type, parseMagicTypeModifier);
+ MODIFIER(__intrinsic_type, parseIntrinsicTypeModifier);
MODIFIER(__implicit_conversion, parseImplicitConversionModifier);
#undef MODIFIER
diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp
index 89fcd0800..afcca4d7f 100644
--- a/source/slang/slang-stdlib.cpp
+++ b/source/slang/slang-stdlib.cpp
@@ -1,6 +1,7 @@
// slang-stdlib.cpp
#include "compiler.h"
+#include "ir.h"
#include "syntax.h"
#define STRINGIZE(x) STRINGIZE2(x)
@@ -1393,8 +1394,15 @@ namespace Slang
// Declare additional built-in generic types
// EMIT_LINE_DIRECTIVE();
- sb << "__generic<T> __magic_type(ConstantBuffer) struct ConstantBuffer {};\n";
- sb << "__generic<T> __magic_type(TextureBuffer) struct TextureBuffer {};\n";
+
+
+ sb << "__generic<T>\n";
+ sb << "__intrinsic_type(" << kIROp_ConstantBufferType << ")\n";
+ sb << "__magic_type(ConstantBuffer) struct ConstantBuffer {};\n";
+
+ sb << "__generic<T>\n";
+ sb << "__intrinsic_type(" << kIROp_TextureBufferType << ")\n";
+ sb << "__magic_type(TextureBuffer) struct TextureBuffer {};\n";
static const char* kComponentNames[]{ "x", "y", "z", "w" };
@@ -1527,8 +1535,15 @@ namespace Slang
// Declare built-in texture and sampler types
- sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ") struct SamplerState {};";
- sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerComparisonState) << ") struct SamplerComparisonState {};";
+
+
+ sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ")\n";
+ sb << "__intrinsic_type(" << kIROp_SamplerType << ", " << int(SamplerStateType::Flavor::SamplerState) << ")\n";
+ sb << "struct SamplerState {};";
+
+ sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerComparisonState) << ")\n";
+ sb << "__intrinsic_type(" << kIROp_SamplerType << ", " << int(SamplerStateType::Flavor::SamplerComparisonState) << ")\n";
+ sb << "struct SamplerComparisonState {};";
// TODO(tfoley): Need to handle `RW*` variants of texture types as well...
static const struct {
@@ -1582,7 +1597,9 @@ namespace Slang
// TODO: allow for multisample count to come in as well...
sb << "__generic<T = float4> ";
- sb << "__magic_type(Texture," << int(flavor) << ") struct ";
+ sb << "__magic_type(Texture," << int(flavor) << ")\n";
+ sb << "__intrinsic_type(" << kIROp_TextureType << ", " << flavor << ")\n";
+ sb << "struct ";
sb << kBaseTextureAccessLevels[accessLevel].name;
sb << name;
if (isMultisample) sb << "MS";
@@ -1787,6 +1804,10 @@ namespace Slang
// `Sample()`
sb << "__intrinsic(glsl, \"texture($p, $1)\")\n";
+
+ // TODO: only enable if IR is being used?
+ sb << "__intrinsic_op(sample_t_s_u)\n";
+
sb << "__intrinsic\n";
sb << "T Sample(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location);\n";