summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2020-06-17 13:08:27 -0700
committerGitHub <noreply@github.com>2020-06-17 13:08:27 -0700
commitcd7f01b63a52eaaad00088524801e502bcb0f168 (patch)
treea04b6d9f2f7f85466b537a8aedeb3795339fae71 /source
parentca503d48bff31d3990d4740751d5f6a4a48bfe5a (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.cpp95
-rw-r--r--source/slang/slang-emit-c-like.h7
-rw-r--r--source/slang/slang-emit-cpp.cpp154
-rw-r--r--source/slang/slang-emit-cpp.h14
-rw-r--r--source/slang/slang-emit.cpp27
-rw-r--r--source/slang/slang-ir-inst-defs.h9
-rw-r--r--source/slang/slang-ir-insts.h7
-rw-r--r--source/slang/slang-ir-lower-generics.cpp158
-rw-r--r--source/slang/slang-ir-lower-generics.h13
-rw-r--r--source/slang/slang-ir.cpp14
-rw-r--r--source/slang/slang-ir.h10
-rw-r--r--source/slang/slang-lower-to-ir.cpp17
-rw-r--r--source/slang/slang.vcxproj2
-rw-r--r--source/slang/slang.vcxproj.filters6
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>