diff options
| author | Yong He <yonghe@outlook.com> | 2020-06-15 09:04:53 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-15 09:04:53 -0700 |
| commit | 90444f8366255f274993ce4699738d9ab7cf4ee1 (patch) | |
| tree | 6fa9364535f210698b0ee13894ab956b8dd12c7b | |
| parent | 36a06f1289c9a68a261920ef5d34f075f2a43219 (diff) | |
Generate IRType for interfaces, and reference them as `operand[0]` in IRWitnessTable values (#1387)
* Generate IRType for interfaces, and use them as the type of IRWitnessTable values.
This results the following IR for the included test case:
```
[export("_S3tu010IInterface7Computep1pii")]
let %1 : _ = key
[export("_ST3tu010IInterface")]
[nameHint("IInterface")]
interface %IInterface : _(%1);
[export("_S3tu04Impl7Computep1pii")]
[nameHint("Impl.Compute")]
func %Implx5FCompute : Func(Int, Int)
{
block %2(
[nameHint("inVal")]
param %inVal : Int):
let %3 : Int = mul(%inVal, %inVal)
return_val(%3)
}
[export("_SW3tu04Impl3tu010IInterface")]
witness_table %4 : %IInterface
{
witness_table_entry(%1,%Implx5FCompute)
}
```
* Fixes per code review comments.
Moved interface type reference in IRWitnessTable from their type to operand[0].
* Fix typo in comment.
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 14 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 15 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 82 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 20 | ||||
| -rw-r--r-- | tests/compute/dynamic-generics-simple.slang | 36 | ||||
| -rw-r--r-- | tests/compute/dynamic-generics-simple.slang.expected.txt | 4 |
13 files changed, 144 insertions, 51 deletions
diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 759930aac..ef6589ca4 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -1285,6 +1285,9 @@ namespace Slang struct WitnessTable : RefObject { RequirementDictionary requirementDictionary; + + // The type that the witness table witnesses conformance to (e.g. an Interface) + Type* baseType; }; typedef Dictionary<unsigned int, NodeBase*> AttributeArgumentValueDict; diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 3aeca23cd..ab22108b9 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -1581,6 +1581,7 @@ namespace Slang if(!witnessTable) { witnessTable = new WitnessTable(); + witnessTable->baseType = DeclRefType::create(m_astBuilder, interfaceDeclRef); } context->mapInterfaceToWitnessTable.Add(interfaceDeclRef, witnessTable); @@ -2137,6 +2138,7 @@ namespace Slang // let them define a tag value with the name `__Tag`). // RefPtr<WitnessTable> witnessTable = new WitnessTable(); + witnessTable->baseType = enumConformanceDecl->base.type; enumConformanceDecl->witnessTable = witnessTable; Name* tagAssociatedTypeName = getSession()->getNameObj("__Tag"); diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 9d23cec43..911928e2a 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -217,8 +217,8 @@ void CLikeSourceEmitter::emitSimpleType(IRType* type) outNumThreads[i] = decor ? Int(getIntVal(decor->getOperand(i))) : 1; } return decor; -} - +}
+
void CLikeSourceEmitter::_emitArrayType(IRArrayType* arrayType, EDeclarator* declarator) { EDeclarator arrayDeclarator; @@ -265,6 +265,12 @@ void CLikeSourceEmitter::_emitType(IRType* type, EDeclarator* declarator) } +void CLikeSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) +{ + SLANG_UNUSED(witnessTable); + SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "Unimplemented emit: IROpWitnessTable."); +} + void CLikeSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameAndLoc) { if (nameAndLoc) @@ -3516,6 +3522,10 @@ void CLikeSourceEmitter::emitGlobalInst(IRInst* inst) emitStruct(cast<IRStructType>(inst)); break; + case kIROp_WitnessTable: + emitWitnessTable(cast<IRWitnessTable>(inst)); + break; + default: // We have an "ordinary" instruction at the global // scope, and we should therefore emit it using the diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index e1106ea76..2253e7ea8 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -340,6 +340,8 @@ public: // Again necessary for & prefix intrinsics. May be removable in the future virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) = 0; + virtual void emitWitnessTable(IRWitnessTable* witnessTable); + virtual void handleCallExprDecorationsImpl(IRInst* funcValue) { SLANG_UNUSED(funcValue); } virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); SLANG_UNUSED(varType); return false; } diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index bc82475f4..d7636e5a8 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -1559,6 +1559,11 @@ void CPPSourceEmitter::emitParamTypeImpl(IRType* type, String const& name) emitType(type, name); } +void CPPSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) +{ + SLANG_UNUSED(witnessTable); +} + bool CPPSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index aeef30804..2f95c8da5 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -75,7 +75,7 @@ protected: virtual void emitSimpleFuncImpl(IRFunc* func) SLANG_OVERRIDE; virtual void emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPrec) SLANG_OVERRIDE; virtual void emitParamTypeImpl(IRType* type, String const& name) SLANG_OVERRIDE; - + virtual void emitWitnessTable(IRWitnessTable* witnessTable) SLANG_OVERRIDE; virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE; virtual void emitIntrinsicCallExprImpl(IRCall* inst, IRTargetIntrinsicDecoration* targetIntrinsic, EmitOpInfo const& inOuterPrec) SLANG_OVERRIDE; diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 3e0d6ae26..2212d5d5a 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -698,6 +698,9 @@ SlangResult emitEntryPointSourceFromIR( // // TODO: do we want to emit directly from IR, or translate the // IR back into AST for emission? +#if 0 + dumpIR(compileRequest, irModule, "PRE-EMIT"); +#endif sourceEmitter->emitModule(irModule); } diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 0ae05311b..09f78707d 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -1790,7 +1790,10 @@ struct IRBuilder IRType* valueType); IRGlobalParam* createGlobalParam( IRType* valueType); - IRWitnessTable* createWitnessTable(); + + /// Creates an IRWitnessTable value. + /// @param baseType: The comformant-to type of this witness. + IRWitnessTable* createWitnessTable(IRType* baseType); IRWitnessTableEntry* createWitnessTableEntry( IRWitnessTable* witnessTable, IRInst* requirementKey, @@ -1800,7 +1803,7 @@ struct IRBuilder IRStructType* createStructType(); // Create an empty `interface` type. - IRInterfaceType* createInterfaceType(); + IRInterfaceType* createInterfaceType(UInt operandCount, IRInst* const* operands); // Create a global "key" to use for indexing into a `struct` type. IRStructKey* createStructKey(); diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 904a88d78..3f51aa876 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -567,7 +567,12 @@ IRWitnessTable* cloneWitnessTableImpl( IRWitnessTable* dstTable = nullptr, bool registerValue = true) { - auto clonedTable = dstTable ? dstTable : builder->createWitnessTable(); + IRWitnessTable* clonedTable = dstTable; + if (!clonedTable) + { + auto clonedBaseType = cloneType(context, as<IRType>(originalTable->getOperand(0))); + clonedTable = builder->createWitnessTable(clonedBaseType); + } cloneSimpleGlobalValueImpl(context, originalTable, originalValues, clonedTable, registerValue); return clonedTable; } @@ -599,7 +604,13 @@ IRInterfaceType* cloneInterfaceTypeImpl( IRInterfaceType* originalInterface, IROriginalValuesForClone const& originalValues) { - auto clonedInterface = builder->createInterfaceType(); + auto clonedInterface = builder->createInterfaceType(originalInterface->getOperandCount(), nullptr); + for (UInt i = 0; i < originalInterface->getOperandCount(); i++) + { + auto clonedKey = findClonedValue(context, originalInterface->getOperand(i)); + SLANG_ASSERT(clonedKey); + clonedInterface->setOperand(i, clonedKey); + } cloneSimpleGlobalValueImpl(context, originalInterface, originalValues, clonedInterface); return clonedInterface; } diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 136bf322f..4d507de41 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2770,12 +2770,13 @@ namespace Slang return inst; } - IRWitnessTable* IRBuilder::createWitnessTable() + IRWitnessTable* IRBuilder::createWitnessTable(IRType* baseType) { IRWitnessTable* witnessTable = createInst<IRWitnessTable>( this, kIROp_WitnessTable, - nullptr); + nullptr, + baseType); addGlobalValue(this, witnessTable); return witnessTable; } @@ -2810,12 +2811,14 @@ namespace Slang return structType; } - IRInterfaceType* IRBuilder::createInterfaceType() + IRInterfaceType* IRBuilder::createInterfaceType(UInt operandCount, IRInst* const* operands) { IRInterfaceType* interfaceType = createInst<IRInterfaceType>( this, kIROp_InterfaceType, - nullptr); + nullptr, + operandCount, + operands); addGlobalValue(this, interfaceType); return interfaceType; } @@ -4209,6 +4212,42 @@ namespace Slang dump(context, "}"); } + static void dumpInstOperandList( + IRDumpContext* context, + IRInst* inst) + { + UInt argCount = inst->getOperandCount(); + + if (argCount == 0) + return; + + UInt ii = 0; + + // Special case: make printing of `call` a bit + // nicer to look at + if (inst->op == kIROp_Call && argCount > 0) + { + dump(context, " "); + auto argVal = inst->getOperand(ii++); + dumpOperand(context, argVal); + } + + bool first = true; + dump(context, "("); + for (; ii < argCount; ++ii) + { + if (!first) + dump(context, ", "); + + auto argVal = inst->getOperand(ii); + + dumpOperand(context, argVal); + + first = false; + } + + dump(context, ")"); + } void dumpIRWitnessTableEntry( IRDumpContext* context, @@ -4234,6 +4273,8 @@ namespace Slang dumpInstTypeClause(context, inst->getFullType()); + dumpInstOperandList(context, inst); + if (!inst->getFirstChild()) { // Empty. @@ -4321,38 +4362,7 @@ namespace Slang dump(context, opInfo.name); - UInt argCount = inst->getOperandCount(); - - if(argCount == 0) - return; - - UInt ii = 0; - - // Special case: make printing of `call` a bit - // nicer to look at - if (inst->op == kIROp_Call && argCount > 0) - { - dump(context, " "); - auto argVal = inst->getOperand(ii++); - dumpOperand(context, argVal); - } - - bool first = true; - dump(context, "("); - for (; ii < argCount; ++ii) - { - if (!first) - dump(context, ", "); - - auto argVal = inst->getOperand(ii); - - dumpOperand(context, argVal); - - first = false; - } - - dump(context, ")"); - + dumpInstOperandList(context, inst); } static void dumpInstBody( diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index a1089b537..42f53ef22 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -1105,7 +1105,8 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower UNREACHABLE_RETURN(LoweredValInfo()); } - auto irWitnessTable = getBuilder()->createWitnessTable(); + auto irWitnessTableBaseType = lowerType(context, supDeclRefType); + auto irWitnessTable = getBuilder()->createWitnessTable(irWitnessTableBaseType); // Now we will iterate over the requirements (members) of the // interface and try to synthesize an appropriate value for each. @@ -4524,7 +4525,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> if(!mapASTToIRWitnessTable.TryGetValue(astReqWitnessTable, irSatisfyingWitnessTable)) { // Need to construct a sub-witness-table - irSatisfyingWitnessTable = subBuilder->createWitnessTable(); + auto irWitnessTableBaseType = lowerType(subContext, astReqWitnessTable->baseType); + irSatisfyingWitnessTable = subBuilder->createWitnessTable(irWitnessTableBaseType); // Recursively lower the sub-table. lowerWitnessTable( @@ -4637,10 +4639,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // and we need those parameters to lower as references to // the parameters of our IR-level generic. // - lowerType(subContext, superType); + auto irWitnessTableBaseType = lowerType(subContext, superType); // Create the IR-level witness table - auto irWitnessTable = subBuilder->createWitnessTable(); + auto irWitnessTable = subBuilder->createWitnessTable(irWitnessTableBaseType); addLinkageDecoration(context, irWitnessTable, inheritanceDecl, mangledName.getUnownedSlice()); // Register the value now, rather than later, to avoid any possible infinite recursion. @@ -5243,9 +5245,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // a witness table for the interface type's conformance // to its own interface. // + List<IRStructKey*> requirementKeys; for (auto requirementDecl : decl->members) { - getInterfaceRequirementKey(requirementDecl); + requirementKeys.add(getInterfaceRequirementKey(requirementDecl)); // As a special case, any type constraints placed // on an associated type will *also* need to be turned @@ -5254,7 +5257,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { for (auto constraintDecl : associatedTypeDecl->getMembersOfType<TypeConstraintDecl>()) { - getInterfaceRequirementKey(constraintDecl); + requirementKeys.add(getInterfaceRequirementKey(constraintDecl)); } } } @@ -5267,11 +5270,12 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // Emit any generics that should wrap the actual type. emitOuterGenerics(subContext, decl, decl); - IRInterfaceType* irInterface = subBuilder->createInterfaceType(); + IRInterfaceType* irInterface = subBuilder->createInterfaceType( + requirementKeys.getCount(), + reinterpret_cast<IRInst**>(requirementKeys.getBuffer())); addNameHint(context, irInterface, decl); addLinkageDecoration(context, irInterface, decl); subBuilder->setInsertInto(irInterface); - // TODO: are there any interface members that should be // nested inside the interface type itself? diff --git a/tests/compute/dynamic-generics-simple.slang b/tests/compute/dynamic-generics-simple.slang new file mode 100644 index 000000000..bb009204e --- /dev/null +++ b/tests/compute/dynamic-generics-simple.slang @@ -0,0 +1,36 @@ +//TEST_IGNORE_FILE +//TEST(compute):COMPARE_COMPUTE:-cpu -xslang -allow-dynamic-code -xslang -dump-ir + +// Test basic dynamic dispatch code gen + +interface IInterface +{ + static int Compute(int inVal); +}; + +int GenericCompute<T:IInterface>(int inVal) +{ + return T.Compute(inVal); +} + +struct Impl : IInterface +{ + static int Compute(int inVal) { return inVal * inVal; } +}; + +int test(int inVal) +{ + return GenericCompute<Impl>(inVal); +} + +//TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer : register(u0); + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + int inVal = outputBuffer[tid]; + int outVal = test(inVal); + outputBuffer[tid] = outVal; +}
\ No newline at end of file diff --git a/tests/compute/dynamic-generics-simple.slang.expected.txt b/tests/compute/dynamic-generics-simple.slang.expected.txt new file mode 100644 index 000000000..bf72fb434 --- /dev/null +++ b/tests/compute/dynamic-generics-simple.slang.expected.txt @@ -0,0 +1,4 @@ +0 +1 +4 +9
\ No newline at end of file |
