summaryrefslogtreecommitdiff
path: root/source/slang/ir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/ir.cpp')
-rw-r--r--source/slang/ir.cpp600
1 files changed, 375 insertions, 225 deletions
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index 72eea2e7c..0b4427f35 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -2849,6 +2849,30 @@ namespace Slang
}
break;
+ case kIRDecorationOp_TargetIntrinsic:
+ {
+ auto decoration = (IRTargetIntrinsicDecoration*) dd;
+
+ dump(context, "\n");
+ dumpIndent(context);
+ dump(context, "[targetIntrinsic(");
+ dump(context, StringRepresentation::getData(decoration->targetName));
+ dump(context, ", ");
+ dump(context, StringRepresentation::getData(decoration->definition));
+ dump(context, ")]");
+ }
+ break;
+
+ case kIRDecorationOp_VulkanRayPayload:
+ {
+ dump(context, "\n[__vulkanRayPayload]");
+ }
+ break;
+ case kIRDecorationOp_VulkanHitAttributes:
+ {
+ dump(context, "\n[__vulkanHitAttributes]");
+ }
+ break;
}
}
}
@@ -2857,9 +2881,6 @@ namespace Slang
IRDumpContext* context,
IRGlobalValueWithCode* code)
{
- // TODO: should apply this to all instructions
- dumpIRDecorations(context, code);
-
auto opInfo = getIROpInfo(code->op);
dump(context, "\n");
@@ -2920,9 +2941,6 @@ namespace Slang
IRDumpContext* context,
IRParentInst* inst)
{
- // TODO: should apply this to all instructions
- dumpIRDecorations(context, inst);
-
auto opInfo = getIROpInfo(inst->op);
dump(context, "\n");
@@ -2988,6 +3006,8 @@ namespace Slang
auto op = inst->op;
+ dumpIRDecorations(context, inst);
+
// There are several ops we want to special-case here,
// so that they will be more pleasant to look at.
//
@@ -4188,11 +4208,17 @@ namespace Slang
fieldKey));
case ScalarizedVal::Flavor::address:
- return ScalarizedVal::address(
- builder->emitFieldAddress(
- getFieldType(val.irValue->getDataType(), fieldKey),
- val.irValue,
- fieldKey));
+ {
+ auto ptrType = as<IRPtrTypeBase>(val.irValue->getDataType());
+ auto valType = ptrType->getValueType();
+ auto fieldType = getFieldType(valType, fieldKey);
+ auto fieldPtrType = builder->getPtrType(ptrType->op, fieldType);
+ return ScalarizedVal::address(
+ builder->emitFieldAddress(
+ fieldPtrType,
+ val.irValue,
+ fieldKey));
+ }
case ScalarizedVal::Flavor::tuple:
{
@@ -4536,6 +4562,308 @@ namespace Slang
return nullptr;
}
+ void legalizeRayTracingEntryPointParameterForGLSL(
+ GLSLLegalizationContext* context,
+ IRParam* pp,
+ VarLayout* paramLayout)
+ {
+ auto builder = context->getBuilder();
+ auto paramType = pp->getDataType();
+
+ if(auto paramPtrType = as<IROutTypeBase>(paramType) )
+ {
+ // This is either an `out` or `in out` parameter.
+ // We want to treat `out` parameters the same
+ // as `in out` for our purposes, since there are
+ // no pure `out` parameters defined for the ray
+ // tracing stages.
+
+ // Unlike the default legalization strategy for
+ // `out` and `in out` entry point parameters,
+ // we will not introduce an intermediate temporary.
+ //
+ // Instead we will simply create a global variable
+ // and replace uses of the parameter with uses
+ // of that global variable.
+
+ auto valueType = paramPtrType->getValueType();
+
+ auto globalVariable = addGlobalVariable(builder->getModule(), valueType);
+ builder->addLayoutDecoration(globalVariable, paramLayout);
+ moveValueBefore(globalVariable, builder->getFunc());
+
+ pp->replaceUsesWith(globalVariable);
+ }
+ else
+ {
+ // This is the `in` parameter case, so that the parameter
+ // was not a pointer. We will allocate a global variable
+ // to represent the parameter, and then perform a load
+ // form it at the start of the function.
+ //
+ auto valueType = paramType;
+ auto globalVariable = addGlobalVariable(builder->getModule(), valueType);
+ builder->addLayoutDecoration(globalVariable, paramLayout);
+ moveValueBefore(globalVariable, builder->getFunc());
+
+ auto irLoad = builder->emitLoad(globalVariable);
+ pp->replaceUsesWith(irLoad);
+ }
+
+ }
+
+ void legalizeEntryPointParameterForGLSL(
+ GLSLLegalizationContext* context,
+ IRFunc* func,
+ IRParam* pp,
+ VarLayout* paramLayout)
+ {
+ auto builder = context->getBuilder();
+ auto stage = context->getStage();
+
+ // We need to create a global variable that will replace the parameter.
+ // It seems superficially obvious that the variable should have
+ // the same type as the parameter.
+ // However, if the parameter was a pointer, in order to
+ // support `out` or `in out` parameter passing, we need
+ // to be sure to allocate a variable of the pointed-to
+ // type instead.
+ //
+ // We also need to replace uses of the parameter with
+ // uses of the variable, and the exact logic there
+ // will differ a bit between the pointer and non-pointer
+ // cases.
+ auto paramType = pp->getDataType();
+
+ // First we will special-case stage input/outputs that
+ // don't fit into the standard varying model.
+ // For right now we are only doing special-case handling
+ // of geometry shader output streams.
+ if( auto paramPtrType = as<IROutTypeBase>(paramType) )
+ {
+ auto valueType = paramPtrType->getValueType();
+ if( auto gsStreamType = as<IRHLSLStreamOutputType>(valueType) )
+ {
+ // An output stream type like `TriangleStream<Foo>` should
+ // more or less translate into `out Foo` (plus scalarization).
+
+ auto globalOutputVal = createGLSLGlobalVaryings(
+ context,
+ builder,
+ valueType,
+ paramLayout,
+ LayoutResourceKind::VaryingOutput,
+ stage);
+
+ // TODO: a GS output stream might be passed into other
+ // functions, so that we should really be modifying
+ // any function that has one of these in its parameter
+ // list (and in the limit we should be leagalizing any
+ // type that nests these...).
+ //
+ // For now we will just try to deal with `Append` calls
+ // directly in this function.
+
+
+
+ for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
+ {
+ for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
+ {
+ // Is it a call?
+ if(ii->op != kIROp_Call)
+ continue;
+
+ // Is it calling the append operation?
+ auto callee = ii->getOperand(0);
+ for(;;)
+ {
+ // If the instruction is `specialize(X,...)` then
+ // we want to look at `X`, and if it is `generic { ... return R; }`
+ // then we want to look at `R`. We handle this
+ // iteratively here.
+ //
+ // TODO: This idiom seems to come up enough that we
+ // should probably have a dedicated convenience routine
+ // for this.
+ //
+ // Alternatively, we could switch the IR encoding so
+ // that decorations are added to the generic instead of the
+ // value it returns.
+ //
+ switch(callee->op)
+ {
+ case kIROp_Specialize:
+ {
+ callee = cast<IRSpecialize>(callee)->getOperand(0);
+ continue;
+ }
+
+ case kIROp_Generic:
+ {
+ auto genericResult = findGenericReturnVal(cast<IRGeneric>(callee));
+ if(genericResult)
+ {
+ callee = genericResult;
+ continue;
+ }
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+ if(callee->op != kIROp_Func)
+ continue;
+
+ // HACK: we will identify the operation based
+ // on the target-intrinsic definition that was
+ // given to it.
+ auto decoration = findTargetIntrinsicDecoration(callee, "glsl");
+ if(!decoration)
+ continue;
+
+ if(StringRepresentation::asSlice(decoration->definition) != UnownedStringSlice::fromLiteral("EmitVertex()"))
+ {
+ continue;
+ }
+
+ // Okay, we have a declaration, and we want to modify it!
+
+ builder->setInsertBefore(ii);
+
+ assign(builder, globalOutputVal, ScalarizedVal::value(ii->getOperand(2)));
+ }
+ }
+
+ return;
+ }
+ }
+
+ // When we have an HLSL ray tracing shader entry point,
+ // we don't want to translate the inputs/outputs for GLSL/SPIR-V
+ // according to our default rules, for two reasons:
+ //
+ // 1. The input and output for these stages are expected to
+ // be packaged into `struct` types rather than be scalarized,
+ // so the usual scalarization approach we take here should
+ // not be applied.
+ //
+ // 2. An `in out` parameter isn't just sugar for a combination
+ // of an `in` and an `out` parameter, and instead represents the
+ // read/write "payload" that was passed in. It should legalize
+ // to a single variable, and we can lower reads/writes of it
+ // directly, rather than introduce an intermediate temporary.
+ //
+ switch( stage )
+ {
+ default:
+ break;
+
+ case Stage::AnyHit:
+ case Stage::Callable:
+ case Stage::ClosestHit:
+ case Stage::Intersection:
+ case Stage::Miss:
+ case Stage::RayGeneration:
+ legalizeRayTracingEntryPointParameterForGLSL(context, pp, paramLayout);
+ return;
+ }
+
+ // Is the parameter type a special pointer type
+ // that indicates the parameter is used for `out`
+ // or `inout` access?
+ if(auto paramPtrType = as<IROutTypeBase>(paramType) )
+ {
+ // Okay, we have the more interesting case here,
+ // where the parameter was being passed by reference.
+ // We are going to create a local variable of the appropriate
+ // type, which will replace the parameter, along with
+ // one or more global variables for the actual input/output.
+
+ auto valueType = paramPtrType->getValueType();
+
+ auto localVariable = builder->emitVar(valueType);
+ auto localVal = ScalarizedVal::address(localVariable);
+
+ if( auto inOutType = as<IRInOutType>(paramPtrType) )
+ {
+ // In the `in out` case we need to declare two
+ // sets of global variables: one for the `in`
+ // side and one for the `out` side.
+ auto globalInputVal = createGLSLGlobalVaryings(
+ context,
+ builder, valueType, paramLayout, LayoutResourceKind::VaryingInput, stage);
+
+ assign(builder, localVal, globalInputVal);
+ }
+
+ // Any places where the original parameter was used inside
+ // the function body should instead use the new local variable.
+ // Since the parameter was a pointer, we use the variable instruction
+ // itself (which is an `alloca`d pointer) directly:
+ pp->replaceUsesWith(localVariable);
+
+ // We also need one or more global variables to write the output to
+ // when the function is done. We create them here.
+ auto globalOutputVal = createGLSLGlobalVaryings(
+ context,
+ builder, valueType, paramLayout, LayoutResourceKind::VaryingOutput, stage);
+
+ // Now we need to iterate over all the blocks in the function looking
+ // for any `return*` instructions, so that we can write to the output variable
+ for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
+ {
+ auto terminatorInst = bb->getLastInst();
+ if(!terminatorInst)
+ continue;
+
+ switch( terminatorInst->op )
+ {
+ default:
+ continue;
+
+ case kIROp_ReturnVal:
+ case kIROp_ReturnVoid:
+ break;
+ }
+
+ // We dont' re-use `builder` here because we don't want to
+ // disrupt the source location it is using for inserting
+ // temporary variables at the top of the function.
+ //
+ IRBuilder terminatorBuilder;
+ terminatorBuilder.sharedBuilder = builder->sharedBuilder;
+ terminatorBuilder.setInsertBefore(terminatorInst);
+
+ // Assign from the local variabel to the global output
+ // variable before the actual `return` takes place.
+ assign(&terminatorBuilder, globalOutputVal, localVal);
+ }
+ }
+ else
+ {
+ // This is the "easy" case where the parameter wasn't
+ // being passed by reference. We start by just creating
+ // one or more global variables to represent the parameter,
+ // and attach the required layout information to it along
+ // the way.
+
+ auto globalValue = createGLSLGlobalVaryings(
+ context,
+ builder, paramType, paramLayout, LayoutResourceKind::VaryingInput, stage);
+
+ // Next we need to replace uses of the parameter with
+ // references to the variable(s). We are going to do that
+ // somewhat naively, by simply materializing the
+ // variables at the start.
+ IRInst* materialized = materializeValue(builder, globalValue);
+
+ pp->replaceUsesWith(materialized);
+ }
+ }
+
void legalizeEntryPointForGLSL(
Session* session,
IRModule* module,
@@ -4672,218 +5000,11 @@ namespace Slang
SLANG_ASSERT(entryPointLayout->fields.Count() > paramIndex);
auto paramLayout = entryPointLayout->fields[paramIndex];
- // We need to create a global variable that will replace the parameter.
- // It seems superficially obvious that the variable should have
- // the same type as the parameter.
- // However, if the parameter was a pointer, in order to
- // support `out` or `in out` parameter passing, we need
- // to be sure to allocate a variable of the pointed-to
- // type instead.
- //
- // We also need to replace uses of the parameter with
- // uses of the variable, and the exact logic there
- // will differ a bit between the pointer and non-pointer
- // cases.
- auto paramType = pp->getDataType();
-
- // First we will special-case stage input/outputs that
- // don't fit into the standard varying model.
- // For right now we are only doing special-case handling
- // of geometry shader output streams.
- if( auto paramPtrType = as<IROutTypeBase>(paramType) )
- {
- auto valueType = paramPtrType->getValueType();
- if( auto gsStreamType = as<IRHLSLStreamOutputType>(valueType) )
- {
- // An output stream type like `TriangleStream<Foo>` should
- // more or less translate into `out Foo` (plus scalarization).
-
- auto globalOutputVal = createGLSLGlobalVaryings(
- &context,
- &builder,
- valueType,
- paramLayout,
- LayoutResourceKind::VaryingOutput,
- stage);
-
- // TODO: a GS output stream might be passed into other
- // functions, so that we should really be modifying
- // any function that has one of these in its parameter
- // list (and in the limit we should be leagalizing any
- // type that nests these...).
- //
- // For now we will just try to deal with `Append` calls
- // directly in this function.
-
-
-
- for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
- {
- for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() )
- {
- // Is it a call?
- if(ii->op != kIROp_Call)
- continue;
-
- // Is it calling the append operation?
- auto callee = ii->getOperand(0);
- for(;;)
- {
- // If the instruction is `specialize(X,...)` then
- // we want to look at `X`, and if it is `generic { ... return R; }`
- // then we want to look at `R`. We handle this
- // iteratively here.
- //
- // TODO: This idiom seems to come up enough that we
- // should probably have a dedicated convenience routine
- // for this.
- //
- // Alternatively, we could switch the IR encoding so
- // that decorations are added to the generic instead of the
- // value it returns.
- //
- switch(callee->op)
- {
- case kIROp_Specialize:
- {
- callee = cast<IRSpecialize>(callee)->getOperand(0);
- continue;
- }
-
- case kIROp_Generic:
- {
- auto genericResult = findGenericReturnVal(cast<IRGeneric>(callee));
- if(genericResult)
- {
- callee = genericResult;
- continue;
- }
- }
-
- default:
- break;
- }
- break;
- }
- if(callee->op != kIROp_Func)
- continue;
-
- // HACK: we will identify the operation based
- // on the target-intrinsic definition that was
- // given to it.
- auto decoration = findTargetIntrinsicDecoration(callee, "glsl");
- if(!decoration)
- continue;
-
- if(StringRepresentation::asSlice(decoration->definition) != UnownedStringSlice::fromLiteral("EmitVertex()"))
- {
- continue;
- }
-
- // Okay, we have a declaration, and we want to modify it!
-
- builder.setInsertBefore(ii);
-
- assign(&builder, globalOutputVal, ScalarizedVal::value(ii->getOperand(2)));
- }
- }
-
- continue;
- }
- }
-
-
- // Is the parameter type a special pointer type
- // that indicates the parameter is used for `out`
- // or `inout` access?
- if(auto paramPtrType = as<IROutTypeBase>(paramType) )
- {
- // Okay, we have the more interesting case here,
- // where the parameter was being passed by reference.
- // We are going to create a local variable of the appropriate
- // type, which will replace the parameter, along with
- // one or more global variables for the actual input/output.
-
- auto valueType = paramPtrType->getValueType();
-
- auto localVariable = builder.emitVar(valueType);
- auto localVal = ScalarizedVal::address(localVariable);
-
- if( auto inOutType = as<IRInOutType>(paramPtrType) )
- {
- // In the `in out` case we need to declare two
- // sets of global variables: one for the `in`
- // side and one for the `out` side.
- auto globalInputVal = createGLSLGlobalVaryings(
- &context,
- &builder, valueType, paramLayout, LayoutResourceKind::VaryingInput, stage);
-
- assign(&builder, localVal, globalInputVal);
- }
-
- // Any places where the original parameter was used inside
- // the function body should instead use the new local variable.
- // Since the parameter was a pointer, we use the variable instruction
- // itself (which is an `alloca`d pointer) directly:
- pp->replaceUsesWith(localVariable);
-
- // We also need one or more global variabels to write the output to
- // when the function is done. We create them here.
- auto globalOutputVal = createGLSLGlobalVaryings(
- &context,
- &builder, valueType, paramLayout, LayoutResourceKind::VaryingOutput, stage);
-
- // Now we need to iterate over all the blocks in the function looking
- // for any `return*` instructions, so that we can write to the output variable
- for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() )
- {
- auto terminatorInst = bb->getLastInst();
- if(!terminatorInst)
- continue;
-
- switch( terminatorInst->op )
- {
- default:
- continue;
-
- case kIROp_ReturnVal:
- case kIROp_ReturnVoid:
- break;
- }
-
- // We dont' re-use `builder` here because we don't want to
- // disrupt the source location it is using for inserting
- // temporary variables at the top of the function.
- //
- IRBuilder terminatorBuilder;
- terminatorBuilder.sharedBuilder = builder.sharedBuilder;
- terminatorBuilder.setInsertBefore(terminatorInst);
-
- // Assign from the local variabel to the global output
- // variable before the actual `return` takes place.
- assign(&terminatorBuilder, globalOutputVal, localVal);
- }
- }
- else
- {
- // This is the "easy" case where the parameter wasn't
- // being passed by reference. We start by just creating
- // one or more global variables to represent the parameter,
- // and attach the required layout information to it along
- // the way.
-
- auto globalValue = createGLSLGlobalVaryings(
- &context,
- &builder, paramType, paramLayout, LayoutResourceKind::VaryingInput, stage);
-
- // Next we need to replace uses of the parameter with
- // references to the variable(s). We are going to do that
- // somewhat naively, by simply materializing the
- // variables at the start.
- IRInst* materialized = materializeValue(&builder, globalValue);
-
- pp->replaceUsesWith(materialized);
- }
+ legalizeEntryPointParameterForGLSL(
+ &context,
+ func,
+ pp,
+ paramLayout);
}
// At this point we should have eliminated all uses of the
@@ -5137,6 +5258,18 @@ namespace Slang
}
break;
+ case kIRDecorationOp_VulkanRayPayload:
+ {
+ context->builder->addDecoration<IRVulkanRayPayloadDecoration>(clonedValue);
+ }
+ break;
+
+ case kIRDecorationOp_VulkanHitAttributes:
+ {
+ context->builder->addDecoration<IRVulkanHitAttributesDecoration>(clonedValue);
+ }
+ break;
+
default:
// Don't clone any decorations we don't understand.
break;
@@ -5721,9 +5854,26 @@ namespace Slang
};
TargetSpecializationLevel getTargetSpecialiationLevel(
- IRGlobalValue* val,
+ IRGlobalValue* inVal,
String const& targetName)
{
+ // HACK: Currently the front-end is placing modifiers related
+ // to target specialization on nodes like functions, even when
+ // those functions are being returned by a generic. This
+ // means that we need to try and inspect the value being
+ // returned by the generic if we are looking at a generic.
+ IRInst* val = inVal;
+ while( auto genericVal = as<IRGeneric>(val) )
+ {
+ auto firstBlock = genericVal->getFirstBlock();
+ if(!firstBlock) break;
+
+ auto returnInst = as<IRReturnVal>(firstBlock->getLastInst());
+ if(!returnInst) break;
+
+ val = returnInst->getVal();
+ }
+
TargetSpecializationLevel result = TargetSpecializationLevel::notSpecialized;
for( auto dd = val->firstDecoration; dd; dd = dd->next )
{
@@ -5776,13 +5926,13 @@ namespace Slang
switch (val->op)
{
case kIROp_WitnessTable:
- case kIROp_GlobalVar:
case kIROp_GlobalConstant:
case kIROp_Func:
case kIROp_Generic:
return ((IRParentInst*)val)->getFirstChild() != nullptr;
case kIROp_StructType:
+ case kIROp_GlobalVar:
return true;
default: