summaryrefslogtreecommitdiffstats
path: root/source/slang/emit.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoley@nvidia.com>2017-09-06 14:56:28 -0700
committerTim Foley <tfoley@nvidia.com>2017-09-07 09:16:41 -0700
commitad3539574f52634c51523cfec1747e7565ad8876 (patch)
treef32462f6c191a30aa1333e6695c9be0aa2fd4df1 /source/slang/emit.cpp
parentca16ede67d3fc34ec1cc81b8f835199c5ef1ab9a (diff)
Replace old notion of "intrinsic" operations
The code previously had an enumerated type for "intrinsic" operations, and allowed functions to be marked `__intrinsic_op(...)` to indicate the operation they map to. The nature of the IR meant that each of these intrinsic ops had to have a corresponding IR opcode, but the `enum` types weren't the same. This change cleans things up a bit by deciding that the `__intrinsic_op(...)` modifier names an actual IR opcode, and so the `IntrinsicOp` enum is gone. The biggest source of complexity here is that there are certain operations that need to be "intrinsic"-ish for the purposes of the current AST-based translation path, because we need them to round-trip from source to AST and back. Right now this is being handled by defining a bunch of "pseudo-ops" which can be used in the `__intrinsic_op` modifier, but which are *not* meant to be represented in the IR. Currently I don't actually handle this during IR generation. In the long run, once we are using IR for everything that needs cross-compilation, we should be able to eliminate the pseudo-ops in favor of just having these be ordinary (inline) functions defined in the stdlib (e.g., the `+=` operator can just have a direct definition). There was a second category of modifier that gets a little caught up in this, which is the `__intrinsic` modifier, which got used in two ways: 1. A function marked `__intrinsic(glsl, ...)` had what I call a "target intrinsic" modifier, which specified how to lower it for a specific target (e.g., GLSL). 2. A function just marked `__intrinsic` was supposed to be a marker for "this function shouldn't be emitted in the output, because the implementation is expected to be provided" The latter category of function should really be an `__intrinsic_op`, so I translated all those uses. I added a tiny bit of sugar so that `__intrinsic_op` without an explicit opcode will look up an opcode based on the name of the function being called, so that an operation like `sin` can automatically be plumbed through to an equivalent IR op. (The first category is a stopgap for the AST-based cross-compilation, and will hopefully be replaced by something better as we get the IR-based path working). Getting the switch from `__intrinsic` to `__intrinsic_op` working required shuffling around some code in `emit.cpp` that handles looking up those modifiers and emitting builtin operations appropriately during cross-compilation. Depending on where we go with things, a possible extension of this approach is to allow multiple operands to `__intrinsic_op` so that the first specifies the opcode, and then the rest are literal arguments to specify "sub-ops." This could help us handle stuff like texture-fetch operations without an explosion in the number of opcodes. I still need to think about whether this is a good idea or not.
Diffstat (limited to 'source/slang/emit.cpp')
-rw-r--r--source/slang/emit.cpp211
1 files changed, 141 insertions, 70 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 450d9eb86..a0cf8eb19 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -2117,36 +2117,13 @@ struct EmitVisitor
return;
}
- // TODO: emit as approperiate for this target
-
- // We might be calling an intrinsic subscript operation,
- // and should desugar it accordingly
- if(auto subscriptDeclRef = funcDeclRef.As<SubscriptDecl>())
- {
- // We expect any subscript operation to be invoked as a member,
- // so the function expression had better be in the correct form.
- if(auto memberExpr = funcExpr.As<MemberExpr>())
- {
-
- Emit("(");
- EmitExpr(memberExpr->BaseExpression);
- Emit(")[");
- UInt argCount = callExpr->Arguments.Count();
- for (UInt aa = 0; aa < argCount; ++aa)
- {
- if (aa != 0) Emit(", ");
- EmitExpr(callExpr->Arguments[aa]);
- }
- Emit("]");
- return;
- }
- }
+ // If we fall through here, we will treat the call like any other.
}
else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
{
switch (intrinsicOpModifier->op)
{
-#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
+#define CASE(NAME, OP) case kIROp_##NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
CASE(Mul, *);
CASE(Div, / );
CASE(Mod, %);
@@ -2167,7 +2144,7 @@ struct EmitVisitor
CASE(Or, || );
#undef CASE
-#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
+#define CASE(NAME, OP) case kIRPseudoOp_##NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return
CASE(Assign, =);
CASE(AddAssign, +=);
CASE(SubAssign, -=);
@@ -2181,26 +2158,26 @@ struct EmitVisitor
CASE(XorAssign, ^=);
#undef CASE
- case IntrinsicOp::Sequence: EmitBinExpr(outerPrec, kEOp_Comma, ",", callExpr); return;
+ case kIRPseudoOp_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, ~);
+#define CASE(NAME, OP) case NAME: EmitUnaryExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
+ CASE(kIRPseudoOp_Pos, +);
+ CASE(kIROp_Neg, -);
+ CASE(kIROp_Not, !);
+ CASE(kIRPseudoOp_BitNot, ~);
#undef CASE
-#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return
+#define CASE(NAME, OP) case kIRPseudoOp_##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
+#define CASE(NAME, OP) case kIRPseudoOp_##NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Postfix, "", #OP, callExpr); return
CASE(PostInc, ++);
CASE(PostDec, --);
#undef CASE
- case IntrinsicOp::InnerProduct_Vector_Vector:
+ case kIROp_Dot:
// 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)
@@ -2214,9 +2191,9 @@ struct EmitVisitor
}
break;
- case IntrinsicOp::InnerProduct_Matrix_Matrix:
- case IntrinsicOp::InnerProduct_Matrix_Vector:
- case IntrinsicOp::InnerProduct_Vector_Matrix:
+ case kIROp_Mul_Matrix_Matrix:
+ case kIROp_Mul_Vector_Matrix:
+ case kIROp_Mul_Matrix_Vector:
// HLSL exposes these with the `mul()` function, while GLSL uses ordinary
// `operator*`.
//
@@ -2236,6 +2213,35 @@ struct EmitVisitor
default:
break;
}
+
+ // If none of the above matched, then we don't have a specific
+ // case implemented to handle the opcode on the callee.
+ //
+ // We do one more special-case check here, in case the operation
+ // is a "subscript" (array indexing) operation, because we'd
+ // generally like to reproduce those as array indexing operations
+ // in the output if that is how they were written in the input.
+ if(auto subscriptDeclRef = funcDeclRef.As<SubscriptDecl>())
+ {
+ // We expect any subscript operation to be invoked as a member,
+ // so the function expression had better be in the correct form.
+ if(auto memberExpr = funcExpr.As<MemberExpr>())
+ {
+
+ Emit("(");
+ EmitExpr(memberExpr->BaseExpression);
+ Emit(")[");
+ UInt argCount = callExpr->Arguments.Count();
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ if (aa != 0) Emit(", ");
+ EmitExpr(callExpr->Arguments[aa]);
+ }
+ Emit("]");
+ return;
+ }
+ }
+
}
}
else if (auto overloadedExpr = funcExpr.As<OverloadedExpr>())
@@ -3377,6 +3383,36 @@ struct EmitVisitor
return varLayout;
}
+ void emitHLSLParameterBlockFieldLayoutSemantics(
+ RefPtr<VarLayout> layout,
+ RefPtr<VarLayout> fieldLayout)
+ {
+ for( auto rr : fieldLayout->resourceInfos )
+ {
+ auto kind = rr.kind;
+
+ auto offsetResource = rr;
+
+ if(kind != LayoutResourceKind::Uniform)
+ {
+ // Add the base index from the cbuffer into the index of the field
+ //
+ // TODO(tfoley): consider maybe not doing this, since it actually
+ // complicates logic around constant buffers...
+
+ // If the member of the cbuffer uses a resource, it had better
+ // appear as part of the cubffer layout as well.
+ auto cbufferResource = layout->FindResourceInfo(kind);
+ SLANG_RELEASE_ASSERT(cbufferResource);
+
+ offsetResource.index += cbufferResource->index;
+ offsetResource.space += cbufferResource->space;
+ }
+
+ emitHLSLRegisterSemantic(offsetResource, "packoffset");
+ }
+ }
+
void emitHLSLParameterBlockDecl(
RefPtr<VarDeclBase> varDecl,
RefPtr<ParameterBlockType> parameterBlockType,
@@ -3436,30 +3472,7 @@ struct EmitVisitor
SLANG_RELEASE_ASSERT(fieldLayout->varDecl.GetName() == field.GetName());
// Emit explicit layout annotations for every field
- for( auto rr : fieldLayout->resourceInfos )
- {
- auto kind = rr.kind;
-
- auto offsetResource = rr;
-
- if(kind != LayoutResourceKind::Uniform)
- {
- // Add the base index from the cbuffer into the index of the field
- //
- // TODO(tfoley): consider maybe not doing this, since it actually
- // complicates logic around constant buffers...
-
- // If the member of the cbuffer uses a resource, it had better
- // appear as part of the cubffer layout as well.
- auto cbufferResource = layout->FindResourceInfo(kind);
- SLANG_RELEASE_ASSERT(cbufferResource);
-
- offsetResource.index += cbufferResource->index;
- offsetResource.space += cbufferResource->space;
- }
-
- emitHLSLRegisterSemantic(offsetResource, "packoffset");
- }
+ emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout);
Emit(";\n");
}
@@ -4261,14 +4274,14 @@ emitDeclImpl(decl, nullptr);
}
break;
- case kIROp_Intrinsic_Add:
+ case kIROp_Add:
emitIROperand(context, inst->getArg(1));
emit(" + ");
emitIROperand(context, inst->getArg(2));
break;
- case kIROp_Intrinsic_sample_t_s_u:
+ case kIROp_Sample:
emitIROperand(context, inst->getArg(1));
emit(".Sample(");
emitIROperand(context, inst->getArg(2));
@@ -4277,6 +4290,19 @@ emitDeclImpl(decl, nullptr);
emit(")");
break;
+ case kIROp_SampleGrad:
+ emitIROperand(context, inst->getArg(1));
+ emit(".SampleGrad(");
+ emitIROperand(context, inst->getArg(2));
+ emit(", ");
+ emitIROperand(context, inst->getArg(3));
+ emit(", ");
+ emitIROperand(context, inst->getArg(4));
+ emit(", ");
+ emitIROperand(context, inst->getArg(5));
+ emit(")");
+ break;
+
case kIROp_Load:
// TODO: this logic will really only work for a simple variable reference...
emitIROperand(context, inst->getArg(1));
@@ -4351,14 +4377,26 @@ emitDeclImpl(decl, nullptr);
}
}
+ VarLayout* getVarLayout(
+ EmitContext* context,
+ IRInst* var)
+ {
+ auto decoration = var->findDecoration<IRLayoutDecoration>();
+ if (!decoration)
+ return nullptr;
+
+ return (VarLayout*) decoration->layout;
+ }
+
void emitIRLayoutSemantics(
EmitContext* context,
- IRInst* inst)
+ IRInst* inst,
+ char const* uniformSemanticSpelling = "register")
{
- auto decoration = inst->findDecoration<IRLayoutDecoration>();
- if (decoration)
+ auto layout = getVarLayout(context, inst);
+ if (layout)
{
- emitHLSLRegisterSemantics((VarLayout*) decoration->layout);
+ emitHLSLRegisterSemantics(layout, uniformSemanticSpelling);
}
}
@@ -4439,21 +4477,54 @@ emitDeclImpl(decl, nullptr);
{
emit("cbuffer ");
emit(getName(varDecl));
- emitIRLayoutSemantics(context, varDecl);
+
+ auto layout = getVarLayout(context, varDecl);
+ assert(layout);
+
+ auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer);
+ SLANG_RELEASE_ASSERT(info);
+ emitHLSLRegisterSemantic(*info);
+
emit("\n{\n");
auto elementType = type->getElementType();
+
+ auto typeLayout = layout->typeLayout;
+ if( auto parameterBlockTypeLayout = typeLayout.As<ParameterBlockTypeLayout>() )
+ {
+ typeLayout = parameterBlockTypeLayout->elementTypeLayout;
+ }
+
switch( elementType->op )
{
case kIROp_StructType:
{
auto structType = (IRStructDecl*) elementType;
+
+ auto structTypeLayout = typeLayout.As<StructTypeLayout>();
+ assert(structTypeLayout);
+
+ UInt fieldIndex = 0;
for(auto ff = structType->getFirstField(); ff; ff = ff->getNextField())
{
+ // TODO: need a plan to deal with the case where the IR-level
+ // `struct` type might not match the high-level type, so that
+ // the numbering of fields is different.
+ //
+ // The right plan is probably to require that the lowering pass
+ // create a fresh layout for any type/variable that it splits
+ // in this fashion, so that the layout information it attaches
+ // can always be assumed to apply to the actual instruciton.
+ //
+
+ auto fieldLayout = structTypeLayout->fields[fieldIndex++];
+
+
+
auto fieldType = ff->getFieldType();
emitIRType(context, fieldType, getName(ff));
- emitIRSemantics(context, ff);
+ emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout);
emit(";\n");
}