diff options
| author | Yong He <yonghe@outlook.com> | 2020-06-17 13:08:27 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-17 13:08:27 -0700 |
| commit | cd7f01b63a52eaaad00088524801e502bcb0f168 (patch) | |
| tree | a04b6d9f2f7f85466b537a8aedeb3795339fae71 /source | |
| parent | ca503d48bff31d3990d4740751d5f6a4a48bfe5a (diff) | |
Generate dynamic C++ code for the minimal test case. (#1391)
* Add IR pass to lower generics into ordinary functions.
* Fix project files
* Emit dynamic C++ code for simple generics and witness tables.
Fixes #1386.
* Remove -dump-ir flag.
* Fixups.
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 95 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 154 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 14 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 27 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 9 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-ir-lower-generics.cpp | 158 | ||||
| -rw-r--r-- | source/slang/slang-ir-lower-generics.h | 13 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 14 | ||||
| -rw-r--r-- | source/slang/slang-ir.h | 10 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 17 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 2 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 6 |
14 files changed, 498 insertions, 35 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 911928e2a..eae3bdedc 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -217,8 +217,42 @@ void CLikeSourceEmitter::emitSimpleType(IRType* type) outNumThreads[i] = decor ? Int(getIntVal(decor->getOperand(i))) : 1; } return decor; -}
-
+} + +List<IRWitnessTableEntry*> CLikeSourceEmitter::getSortedWitnessTableEntries(IRWitnessTable* witnessTable) +{ + List<IRWitnessTableEntry*> sortedWitnessTableEntries; + auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0)); + auto witnessTableItems = witnessTable->getChildren(); + // Build a dictionary of witness table entries for fast lookup. + Dictionary<IRInst*, IRWitnessTableEntry*> witnessTableEntryDictionary; + for (auto item : witnessTableItems) + { + if (auto entry = as<IRWitnessTableEntry>(item)) + { + witnessTableEntryDictionary[entry->getRequirementKey()] = entry; + } + } + // Get a sorted list of entries using RequirementKeys defined in `interfaceType`. + for (UInt i = 0; i < interfaceType->getOperandCount(); i++) + { + auto reqKey = cast<IRStructKey>(interfaceType->getOperand(i)); + bool matchingEntryFound = false; + IRWitnessTableEntry* entry = nullptr; + if (witnessTableEntryDictionary.TryGetValue(reqKey, entry)) + { + if (entry->requirementKey.get() == reqKey) + { + matchingEntryFound = true; + sortedWitnessTableEntries.add(entry); + break; + } + } + SLANG_ASSERT(matchingEntryFound); + } + return sortedWitnessTableEntries; +} + void CLikeSourceEmitter::_emitArrayType(IRArrayType* arrayType, EDeclarator* declarator) { EDeclarator arrayDeclarator; @@ -271,6 +305,12 @@ void CLikeSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "Unimplemented emit: IROpWitnessTable."); } +void CLikeSourceEmitter::emitInterface(IRInterfaceType* interfaceType) +{ + SLANG_UNUSED(interfaceType); + SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "Unimplemented emit: IROpInterfaceType."); +} + void CLikeSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameAndLoc) { if (nameAndLoc) @@ -890,6 +930,7 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst) case kIROp_FieldAddress: case kIROp_getElementPtr: case kIROp_Specialize: + case kIROp_lookup_interface_method: return true; } @@ -1849,6 +1890,31 @@ void CLikeSourceEmitter::emitIntrinsicCallExprImpl( } } +void CLikeSourceEmitter::_emitCallArgList(IRCall* inst) +{ + bool isFirstArg = true; + m_writer->emit("("); + UInt argCount = inst->getOperandCount(); + for (UInt aa = 1; aa < argCount; ++aa) + { + auto operand = inst->getOperand(aa); + if (as<IRVoidType>(operand->getDataType())) + continue; + + // TODO: [generate dynamic dispatch code for generics] + // Pass RTTI object here. Ignore type argument for now. + if (as<IRType>(operand)) + continue; + + if (!isFirstArg) + m_writer->emit(", "); + else + isFirstArg = false; + emitOperand(inst->getOperand(aa), getInfo(EmitOp::General)); + } + m_writer->emit(")"); +} + void CLikeSourceEmitter::emitCallExpr(IRCall* inst, EmitOpInfo outerPrec) { auto funcValue = inst->getOperand(0); @@ -1868,18 +1934,7 @@ void CLikeSourceEmitter::emitCallExpr(IRCall* inst, EmitOpInfo outerPrec) bool needClose = maybeEmitParens(outerPrec, prec); emitOperand(funcValue, leftSide(outerPrec, prec)); - m_writer->emit("("); - UInt argCount = inst->getOperandCount(); - for( UInt aa = 1; aa < argCount; ++aa ) - { - auto operand = inst->getOperand(aa); - if (as<IRVoidType>(operand->getDataType())) - continue; - if(aa != 1) m_writer->emit(", "); - emitOperand(inst->getOperand(aa), getInfo(EmitOp::General)); - } - m_writer->emit(")"); - + _emitCallArgList(inst); maybeCloseParens(needClose); } } @@ -3522,6 +3577,10 @@ void CLikeSourceEmitter::emitGlobalInst(IRInst* inst) emitStruct(cast<IRStructType>(inst)); break; + case kIROp_InterfaceType: + emitInterface(cast<IRInterfaceType>(inst)); + break; + case kIROp_WitnessTable: emitWitnessTable(cast<IRWitnessTable>(inst)); break; @@ -3576,11 +3635,15 @@ void CLikeSourceEmitter::ensureInstOperandsRec(ComputeEmitActionsContext* ctx, I void CLikeSourceEmitter::ensureGlobalInst(ComputeEmitActionsContext* ctx, IRInst* inst, EmitAction::Level requiredLevel) { - // Skip certain instructions, since they - // don't affect output. + // Skip certain instructions that don't affect output. switch(inst->op) { case kIROp_WitnessTable: + // Only skip witness tables when we are generating + // static code. + if (!m_compileRequest->allowDynamicCode) + return; + break; case kIROp_Generic: return; diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 2253e7ea8..bead26db3 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -341,6 +341,7 @@ public: virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) = 0; virtual void emitWitnessTable(IRWitnessTable* witnessTable); + virtual void emitInterface(IRInterfaceType* interfaceType); virtual void handleCallExprDecorationsImpl(IRInst* funcValue) { SLANG_UNUSED(funcValue); } @@ -351,6 +352,12 @@ public: void _emitUnsizedArrayType(IRUnsizedArrayType* arrayType, EDeclarator* declarator); void _emitType(IRType* type, EDeclarator* declarator); void _emitInst(IRInst* inst); + + // Emit the argument list (including paranthesis) in a `CallInst` + void _emitCallArgList(IRCall* call); + + // Sort witnessTable entries according to the order defined in the witnessed interface type. + List<IRWitnessTableEntry*> getSortedWitnessTableEntries(IRWitnessTable* witnessTable); BackEndCompileRequest* m_compileRequest = nullptr; diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index d7636e5a8..c31ef3bc7 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -488,6 +488,16 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S out << ">"; return SLANG_OK; } + case kIROp_WitnessTableType: + { + // A witness table typed value translates to a pointer to the + // struct of function pointers corresponding to the interface type. + auto witnessTableType = static_cast<IRWitnessTableType*>(type); + auto baseType = cast<IRType>(witnessTableType->getOperand(0)); + emitType(baseType); + out << "*"; + return SLANG_OK; + } default: { if (isNominalOp(type->op)) @@ -1561,7 +1571,117 @@ void CPPSourceEmitter::emitParamTypeImpl(IRType* type, String const& name) void CPPSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) { - SLANG_UNUSED(witnessTable); + auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0)); + auto witnessTableItems = witnessTable->getChildren(); + List<IRWitnessTableEntry*> sortedWitnessTableEntries = getSortedWitnessTableEntries(witnessTable); + _maybeEmitWitnessTableTypeDefinition(interfaceType, sortedWitnessTableEntries); + + // Define a global variable for the witness table. + m_writer->emit("extern "); + emitSimpleType(interfaceType); + m_writer->emit(" "); + m_writer->emit(getName(witnessTable)); + m_writer->emit(";\n"); + + // The actual definition of this witness table global variable + // is deferred until the entire `Context` class is emitted, so + // that the member functions are available for reference. + // The witness table definition emission logic is defined in the + // `_emitWitnessTableDefinitions` function. + pendingWitnessTableDefinitions.add(witnessTable); +} + +void CPPSourceEmitter::_emitWitnessTableDefinitions() +{ + for (auto witnessTable : pendingWitnessTableDefinitions) + { + auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0)); + List<IRWitnessTableEntry*> sortedWitnessTableEntries = getSortedWitnessTableEntries(witnessTable); + emitSimpleType(interfaceType); + m_writer->emit(" "); + m_writer->emit(getName(witnessTable)); + m_writer->emit(" = {\n"); + m_writer->indent(); + bool isFirstEntry = true; + for (Index i = 0; i < sortedWitnessTableEntries.getCount(); i++) + { + auto entry = sortedWitnessTableEntries[i]; + if (auto funcVal = as<IRFunc>(entry->satisfyingVal.get())) + { + if (!isFirstEntry) + m_writer->emit(",\n"); + else + isFirstEntry = false; + m_writer->emit("&Context::"); + m_writer->emit(getName(funcVal)); + } + else + { + // TODO: handle other witness table entry types. + } + } + m_writer->dedent(); + m_writer->emit("\n};\n"); + } +} + +void CPPSourceEmitter::emitInterface(IRInterfaceType* interfaceType) +{ + // The current IRInterfaceType defintion does not contain + // sufficient info for emitting a witness table struct by itself + // Instead, it defines the order of entries in a witness table. + // Therefore, we emit a forward declaration here, and actual definition + // for the witness table type during emitWitnessTable. + SLANG_UNUSED(interfaceType); + m_writer->emit("struct "); + emitSimpleType(interfaceType); + m_writer->emit(";\n"); +} + + /// Emits witness table type definition given a sorted list of witness tables + /// acoording to the order defined by `interfaceType`. + /// +void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( + IRInterfaceType* interfaceType, + const List<IRWitnessTableEntry*>& sortedWitnessTableEntries) +{ + m_writer->emit("struct "); + emitSimpleType(interfaceType); + m_writer->emit("\n{\n"); + m_writer->indent(); + bool isFirstEntry = true; + for (Index i = 0; i < sortedWitnessTableEntries.getCount(); i++) + { + auto entry = sortedWitnessTableEntries[i]; + if (auto funcVal = as<IRFunc>(entry->satisfyingVal.get())) + { + if (!isFirstEntry) + m_writer->emit(",\n"); + else + isFirstEntry = false; + emitType(funcVal->getResultType()); + m_writer->emit(" (Context::*"); + m_writer->emit(getName(entry->requirementKey.get())); + m_writer->emit(")"); + m_writer->emit("("); + bool isFirstParam = true; + for (auto param : funcVal->getParams()) + { + if (!isFirstParam) + m_writer->emit(", "); + else + isFirstParam = false; + emitParamType(param->getFullType(), getName(param)); + } + m_writer->emit(");\n"); + } + else + { + // TODO: handle other witness table entry types. + } + } + m_writer->dedent(); + m_writer->emit("\n};\n"); } bool CPPSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) @@ -1673,6 +1793,12 @@ void CPPSourceEmitter::emitSimpleFuncImpl(IRFunc* func) auto firstParam = func->getFirstParam(); for (auto pp = firstParam; pp; pp = pp->getNextParam()) { + // Ingore TypeType-typed parameters for now. + // In the future we will pass around runtime type info + // for TypeType parameters. + if (as<IRTypeType>(pp->getFullType())) + continue; + if (pp != firstParam) m_writer->emit(", "); @@ -1950,9 +2076,32 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut // Does this function declare any requirements. handleCallExprDecorationsImpl(funcValue); + if (funcValue->op == kIROp_lookup_interface_method) + { + m_writer->emit("(this->*("); + emitOperand(funcValue, EmitOpInfo()); + m_writer->emit("))"); + _emitCallArgList(as<IRCall>(inst)); + return true; + } + // try doing automatically return _tryEmitInstExprAsIntrinsic(inst, inOuterPrec); } + case kIROp_lookup_interface_method: + { + emitInstExpr(inst->getOperand(0), inOuterPrec); + m_writer->emit("->"); + m_writer->emit(getName(inst->getOperand(1))); + return true; + } + case kIROp_WitnessTable: + { + m_writer->emit("(&"); + m_writer->emit(getName(inst)); + m_writer->emit(")"); + return true; + } } } @@ -2513,6 +2662,9 @@ void CPPSourceEmitter::emitModuleImpl(IRModule* module) m_writer->emit("};\n\n"); } + // Emit all witness table definitions. + _emitWitnessTableDefinitions(); + // Finally we need to output dll entry points for (auto action : actions) diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index 2f95c8da5..b8afc6a76 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -76,6 +76,7 @@ protected: 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 void emitInterface(IRInterfaceType* interfaceType) SLANG_OVERRIDE; virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE; virtual void emitIntrinsicCallExprImpl(IRCall* inst, IRTargetIntrinsicDecoration* targetIntrinsic, EmitOpInfo const& inOuterPrec) SLANG_OVERRIDE; @@ -85,7 +86,9 @@ protected: virtual SlangResult calcTypeName(IRType* type, CodeGenTarget target, StringBuilder& out); virtual SlangResult calcFuncName(const HLSLIntrinsic* specOp, StringBuilder& out); virtual SlangResult calcScalarFuncName(HLSLIntrinsic::Op op, IRBasicType* type, StringBuilder& outBuilder); - + + // Emits a struct of function pointers defined in `interfaceType`. + void _maybeEmitWitnessTableTypeDefinition(IRInterfaceType* interfaceType, const List<IRWitnessTableEntry*>& sortedWitnessTableEntries); void _maybeEmitSpecializedOperationDefinition(const HLSLIntrinsic* specOp); void _emitForwardDeclarations(const List<EmitAction>& actions); @@ -127,6 +130,10 @@ protected: bool _tryEmitInstExprAsIntrinsic(IRInst* inst, const EmitOpInfo& inOuterPrec); + // Emit the actual definition (including intializer list) + // of all the witness table objects in `pendingWitnessTableDefinitions`. + void _emitWitnessTableDefinitions(); + HLSLIntrinsic* _addIntrinsic(HLSLIntrinsic::Op op, IRType* returnType, IRType*const* argTypes, Index argTypeCount); static bool _isVariable(IROp op); @@ -143,6 +150,11 @@ protected: StringSlicePool m_slicePool; SemanticUsedFlags m_semanticUsedFlags; + + // Witness tables pending for emitting their definitions. + // They must be emitted last, after the entire `Context` class so those member functions defined + // in `Context` may be referenced. + List<IRWitnessTable*> pendingWitnessTableDefinitions; }; } diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 2212d5d5a..260a862ae 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -11,6 +11,7 @@ #include "slang-ir-glsl-legalize.h" #include "slang-ir-insts.h" #include "slang-ir-link.h" +#include "slang-ir-lower-generics.h" #include "slang-ir-restructure.h" #include "slang-ir-restructure-scoping.h" #include "slang-ir-specialize.h" @@ -273,6 +274,18 @@ Result linkAndOptimizeIR( if (!compileRequest->allowDynamicCode) specializeModule(irModule); + switch (target) + { + case CodeGenTarget::CPPSource: + // For targets that supports dynamic dispatch, we need to lower the + // generics / interface types to ordinary functions and types using + // function pointers. + lowerGenerics(irModule); + break; + default: + break; + } + // Debugging code for IR transformations... #if 0 dumpIRIfEnabled(compileRequest, irModule, "SPECIALIZED"); @@ -389,6 +402,7 @@ Result linkAndOptimizeIR( #if 0 dumpIRIfEnabled(compileRequest, irModule, "AFTER RESOURCE SPECIALIZATION"); #endif + validateIRModuleIfEnabled(compileRequest, irModule); // For HLSL (and fxc/dxc) only, we need to "wrap" any @@ -558,11 +572,14 @@ Result linkAndOptimizeIR( break; } - // For all targets that don't support true dynamic dispatch through - // witness tables (that is all targets at present), we need - // to eliminate witness tables from the IR so that they - // don't keep symbols live that we don't actually need. - stripWitnessTables(irModule); + if (!compileRequest->allowDynamicCode) + { + // For all targets that don't support true dynamic dispatch through + // witness tables, we need to eliminate witness tables from the IR so + // that they don't keep symbols live that we don't actually need. + stripWitnessTables(irModule); + } + #if 0 dumpIRIfEnabled(compileRequest, irModule, "AFTER STRIP WITNESS TABLES"); #endif diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index b07703503..f5127d0fa 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -166,7 +166,14 @@ INST(Nop, nop, 0, 0) INST(StructType, struct, 0, PARENT) INST(InterfaceType, interface, 0, PARENT) -INST_RANGE(Type, VoidType, InterfaceType) +// A TypeType-typed IRValue represents a IRType. +// It is used to represent a type parameter/argument in a generics. +INST(TypeType, type_t, 0, 0) + +// An `IRWitnessTable` has type `WitnessTableType`. +INST(WitnessTableType, witness_table_t, 0, 0) + +INST_RANGE(Type, VoidType, WitnessTableType) /*IRGlobalValueWithCode*/ /* IRGlobalValueWithParams*/ diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 09f78707d..3aab4c323 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -1564,7 +1564,8 @@ struct IRBuilder IRStringType* getStringType(); IRBasicBlockType* getBasicBlockType(); - IRType* getWitnessTableType() { return nullptr; } + IRWitnessTableType* getWitnessTableType(IRType* baseType); + IRType* getTypeType() { return getType(IROp::kIROp_TypeType); } IRType* getKeyType() { return nullptr; } IRTypeKind* getTypeKind(); @@ -1993,9 +1994,9 @@ struct IRBuilder return emitGlobalGenericParam(getTypeKind()); } - IRGlobalGenericParam* emitGlobalGenericWitnessTableParam() + IRGlobalGenericParam* emitGlobalGenericWitnessTableParam(IRType* comformanceType) { - return emitGlobalGenericParam(getWitnessTableType()); + return emitGlobalGenericParam(getWitnessTableType(comformanceType)); } IRBindGlobalGenericParam* emitBindGlobalGenericParam( diff --git a/source/slang/slang-ir-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp new file mode 100644 index 000000000..4378d396f --- /dev/null +++ b/source/slang/slang-ir-lower-generics.cpp @@ -0,0 +1,158 @@ +// slang-ir-lower-generics.cpp +#include "slang-ir-lower-generics.h" + +#include "slang-ir.h" +#include "slang-ir-clone.h" +#include "slang-ir-insts.h" + +namespace Slang +{ + struct GenericsLoweringContext; + + struct GenericsLoweringContext + { + // For convenience, we will keep a pointer to the module + // we are processing. + IRModule* module; + + Dictionary<IRInst*, IRInst*> loweredGenericFunctions; + + SharedIRBuilder sharedBuilderStorage; + + // We will use a single work list of instructions that need + // to be considered for lowering. + // + List<IRInst*> workList; + HashSet<IRInst*> workListSet; + + void addToWorkList( + IRInst* inst) + { + // We will ignore any code that is nested under a generic, + // because they will be recursively processed through specialized + // call sites. + // + for (auto ii = inst->getParent(); ii; ii = ii->getParent()) + { + if (as<IRGeneric>(ii)) + return; + } + + if (workListSet.Contains(inst)) + return; + + workList.add(inst); + workListSet.Add(inst); + } + + IRInst* lowerGenericFunction(IRInst* genericValue) + { + IRInst* result = nullptr; + if (loweredGenericFunctions.TryGetValue(genericValue, result)) + return result; + auto genericParent = as<IRGeneric>(genericValue); + SLANG_ASSERT(genericParent); + auto func = as<IRFunc>(findGenericReturnVal(genericParent)); + SLANG_ASSERT(func); + if (!func->isDefinition()) + { + loweredGenericFunctions[genericValue] = genericValue; + return genericValue; + } + IRCloneEnv cloneEnv; + IRBuilder builder; + builder.sharedBuilder = &sharedBuilderStorage; + builder.setInsertBefore(genericParent); + auto loweredFunc = cloneInstAndOperands(&cloneEnv, &builder, func); + List<IRInst*> clonedParams; + for (auto genericParam : genericParent->getParams()) + { + auto clonedParam = cloneInst(&cloneEnv, &builder, genericParam); + cloneEnv.mapOldValToNew[genericParam] = clonedParam; + clonedParams.add(clonedParam); + } + cloneInstDecorationsAndChildren(&cloneEnv, &sharedBuilderStorage, func, loweredFunc); + auto block = as<IRBlock>(loweredFunc->getFirstChild()); + for (auto param : clonedParams) + { + param->removeFromParent(); + block->addParam(as<IRParam>(param)); + } + loweredGenericFunctions[genericValue] = loweredFunc; + addToWorkList(loweredFunc); + return loweredFunc; + } + + void processInst(IRInst* inst) + { + if (auto callInst = as<IRCall>(inst)) + { + // If we see a call(specialize(gFunc, Targs), args), + // translate it into call(gFunc, args, Targs). + auto funcOperand = callInst->getOperand(0); + if (auto specializeInst = as<IRSpecialize>(funcOperand)) + { + auto loweredFunc = lowerGenericFunction(specializeInst->getOperand(0)); + if (loweredFunc == specializeInst->getOperand(0)) + { + // This is an intrinsic function, don't transform. + return; + } + IRBuilder builderStorage; + auto builder = &builderStorage; + builder->sharedBuilder = &sharedBuilderStorage; + builder->setInsertBefore(inst); + List<IRInst*> args; + for (UInt i = 0; i < callInst->getArgCount(); i++) + args.add(callInst->getArg(i)); + for (UInt i = 0; i < specializeInst->getArgCount(); i++) + args.add(specializeInst->getArg(i)); + auto newCall = builder->emitCallInst(callInst->getFullType(), loweredFunc, args); + callInst->replaceUsesWith(newCall); + callInst->removeAndDeallocate(); + } + } + } + + void processModule() + { + // We start by initializing our shared IR building state, + // since we will re-use that state for any code we + // generate along the way. + // + SharedIRBuilder* sharedBuilder = &sharedBuilderStorage; + sharedBuilder->module = module; + sharedBuilder->session = module->session; + + addToWorkList(module->getModuleInst()); + + while (workList.getCount() != 0) + { + // We will then iterate until our work list goes dry. + // + while (workList.getCount() != 0) + { + IRInst* inst = workList.getLast(); + + workList.removeLast(); + workListSet.Remove(inst); + + processInst(inst); + + for (auto child = inst->getLastChild(); child; child = child->getPrevInst()) + { + addToWorkList(child); + } + } + } + } + }; + + void lowerGenerics( + IRModule* module) + { + GenericsLoweringContext context; + context.module = module; + context.processModule(); + } +} // namespace Slang diff --git a/source/slang/slang-ir-lower-generics.h b/source/slang/slang-ir-lower-generics.h new file mode 100644 index 000000000..ed9e58c8f --- /dev/null +++ b/source/slang/slang-ir-lower-generics.h @@ -0,0 +1,13 @@ +// slang-ir-lower-generics.h
+#pragma once
+
+namespace Slang
+{
+ struct IRModule;
+
+ /// Lower generic and interface-based code to ordinary types and functions using
+ /// dynamic dispatch mechanisms.
+ void lowerGenerics(
+ IRModule* module);
+
+}
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 4d507de41..34ea23b85 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2307,6 +2307,16 @@ namespace Slang (IRInst* const*) paramTypes); } + IRWitnessTableType* IRBuilder::getWitnessTableType( + IRType* baseType) + { + return (IRWitnessTableType*)findOrEmitHoistableInst( + nullptr, + kIROp_WitnessTableType, + 1, + (IRInst* const*)&baseType); + } + IRConstantBufferType* IRBuilder::getConstantBufferType(IRType* elementType) { IRInst* operands[] = { elementType }; @@ -2465,7 +2475,7 @@ namespace Slang IRInst* IRBuilder::emitExtractExistentialWitnessTable( IRInst* existentialValue) { - auto type = getWitnessTableType(); + auto type = getWitnessTableType(existentialValue->getDataType()); auto inst = createInst<IRInst>( this, kIROp_ExtractExistentialWitnessTable, @@ -2775,7 +2785,7 @@ namespace Slang IRWitnessTable* witnessTable = createInst<IRWitnessTable>( this, kIROp_WitnessTable, - nullptr, + getWitnessTableType(baseType), baseType); addGlobalValue(this, witnessTable); return witnessTable; diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index dc0606644..3c9a15650 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1200,6 +1200,16 @@ struct IRTaggedUnionType : IRType IR_LEAF_ISA(TaggedUnionType) }; +struct IRTypeType : IRType +{ + IR_LEAF_ISA(TypeType); +}; + +struct IRWitnessTableType : IRType +{ + IR_LEAF_ISA(WitnessTableType); +}; + struct IRBindExistentialsType : IRType { IR_LEAF_ISA(BindExistentialsType) diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index fa23b3307..01bd0e972 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -1034,7 +1034,8 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower LoweredValInfo visitDeclaredSubtypeWitness(DeclaredSubtypeWitness* val) { return emitDeclRef(context, val->declRef, - context->irBuilder->getWitnessTableType()); + context->irBuilder->getWitnessTableType( + lowerType(context, DeclRefType::create(context->astBuilder, val->declRef)))); } LoweredValInfo visitTransitiveSubtypeWitness( @@ -4480,8 +4481,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { // This is a constraint on a global generic type parameters, // and so it should lower as a parameter of its own. - - auto inst = getBuilder()->emitGlobalGenericWitnessTableParam(); + auto supType = lowerType(context, decl->getSup().type); + auto inst = getBuilder()->emitGlobalGenericWitnessTableParam(supType); addLinkageDecoration(context, inst, decl); return LoweredValInfo::simple(inst); } @@ -5728,7 +5729,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { // TODO: use a `TypeKind` to represent the // classifier of the parameter. - auto param = subBuilder->emitParam(nullptr); + auto param = subBuilder->emitParam(subBuilder->getTypeType()); addNameHint(context, param, typeParamDecl); setValue(subContext, typeParamDecl, LoweredValInfo::simple(param)); } @@ -5748,7 +5749,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { // TODO: use a `WitnessTableKind` to represent the // classifier of the parameter. - auto param = subBuilder->emitParam(nullptr); + auto param = subBuilder->emitParam(subBuilder->getWitnessTableType( + lowerType(context, constraintDecl->sup.type))); addNameHint(context, param, constraintDecl); setValue(subContext, constraintDecl, LoweredValInfo::simple(param)); } @@ -6590,12 +6592,15 @@ IRInst* lowerSubstitutionArg( else if (auto declaredSubtypeWitness = as<DeclaredSubtypeWitness>(val)) { // We need to look up the IR-level representation of the witness (which will be a witness table). + auto supType = lowerType( + context, + DeclRefType::create(context->astBuilder, declaredSubtypeWitness->declRef)); auto irWitnessTable = getSimpleVal( context, emitDeclRef( context, declaredSubtypeWitness->declRef, - context->irBuilder->getWitnessTableType())); + context->irBuilder->getWitnessTableType(supType))); return irWitnessTable; } else diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index 353da833d..f6b321b7d 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -232,6 +232,7 @@ <ClInclude Include="slang-ir-insts.h" /> <ClInclude Include="slang-ir-layout.h" /> <ClInclude Include="slang-ir-link.h" /> + <ClInclude Include="slang-ir-lower-generics.h" /> <ClInclude Include="slang-ir-missing-return.h" /> <ClInclude Include="slang-ir-restructure-scoping.h" /> <ClInclude Include="slang-ir-restructure.h" /> @@ -319,6 +320,7 @@ <ClCompile Include="slang-ir-layout.cpp" /> <ClCompile Include="slang-ir-legalize-types.cpp" /> <ClCompile Include="slang-ir-link.cpp" /> + <ClCompile Include="slang-ir-lower-generics.cpp" /> <ClCompile Include="slang-ir-missing-return.cpp" /> <ClCompile Include="slang-ir-restructure-scoping.cpp" /> <ClCompile Include="slang-ir-restructure.cpp" /> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 589bd374c..a636e338a 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -147,6 +147,9 @@ <ClInclude Include="slang-ir-link.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="slang-ir-lower-generics.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="slang-ir-missing-return.h"> <Filter>Header Files</Filter> </ClInclude> @@ -404,6 +407,9 @@ <ClCompile Include="slang-ir-link.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="slang-ir-lower-generics.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="slang-ir-missing-return.cpp"> <Filter>Source Files</Filter> </ClCompile> |
