Skip to content

Commit 8c6e02b

Browse files
committed
Dynamic dipatch non-static functions.
1 parent d1a8cd2 commit 8c6e02b

9 files changed

+293
-10
lines changed

source/slang/slang-emit-cpp.cpp

+144-3
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,24 @@ UnownedStringSlice CPPSourceEmitter::_getFuncName(const HLSLIntrinsic* specOp)
14461446
return m_slicePool.getSlice(handle);
14471447
}
14481448

1449+
UnownedStringSlice CPPSourceEmitter::_getWitnessTableWrapperFuncName(IRFunc* func)
1450+
{
1451+
StringSlicePool::Handle handle = StringSlicePool::kNullHandle;
1452+
if (m_witnessTableWrapperFuncNameMap.TryGetValue(func, handle))
1453+
{
1454+
return m_slicePool.getSlice(handle);
1455+
}
1456+
1457+
StringBuilder builder;
1458+
builder << getName(func) << "_wtwrapper";
1459+
1460+
handle = m_slicePool.add(builder);
1461+
m_witnessTableWrapperFuncNameMap.Add(func, handle);
1462+
1463+
SLANG_ASSERT(handle != StringSlicePool::kNullHandle);
1464+
return m_slicePool.getSlice(handle);
1465+
}
1466+
14491467
SlangResult CPPSourceEmitter::calcFuncName(const HLSLIntrinsic* specOp, StringBuilder& outBuilder)
14501468
{
14511469
typedef HLSLIntrinsic::Op Op;
@@ -1591,6 +1609,83 @@ void CPPSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable)
15911609
pendingWitnessTableDefinitions.add(witnessTable);
15921610
}
15931611

1612+
void CPPSourceEmitter::_emitWitnessTableWrappers()
1613+
{
1614+
for (auto witnessTable : pendingWitnessTableDefinitions)
1615+
{
1616+
for (auto child : witnessTable->getChildren())
1617+
{
1618+
if (auto entry = as<IRWitnessTableEntry>(child))
1619+
{
1620+
if (auto funcVal = as<IRFunc>(entry->getSatisfyingVal()))
1621+
{
1622+
emitType(funcVal->getResultType());
1623+
m_writer->emit(" ");
1624+
m_writer->emit(_getWitnessTableWrapperFuncName(funcVal));
1625+
m_writer->emit("(");
1626+
// Emit parameter list.
1627+
{
1628+
bool isFirst = true;
1629+
for (auto param : funcVal->getParams())
1630+
{
1631+
if (as<IRTypeType>(param->getFullType()))
1632+
continue;
1633+
1634+
if (isFirst)
1635+
isFirst = false;
1636+
else
1637+
m_writer->emit(",");
1638+
1639+
if (param->findDecoration<IRThisPointerDecoration>())
1640+
{
1641+
m_writer->emit("void* ");
1642+
m_writer->emit(getName(param));
1643+
continue;
1644+
}
1645+
emitSimpleFuncParamImpl(param);
1646+
}
1647+
}
1648+
m_writer->emit(")\n{\n");
1649+
m_writer->indent();
1650+
m_writer->emit("return ");
1651+
m_writer->emit(getName(funcVal));
1652+
m_writer->emit("(");
1653+
// Emit argument list.
1654+
{
1655+
bool isFirst = true;
1656+
for (auto param : funcVal->getParams())
1657+
{
1658+
if (as<IRTypeType>(param->getFullType()))
1659+
continue;
1660+
1661+
if (isFirst)
1662+
isFirst = false;
1663+
else
1664+
m_writer->emit(", ");
1665+
1666+
if (param->findDecoration<IRThisPointerDecoration>())
1667+
{
1668+
m_writer->emit("*static_cast<");
1669+
emitType(param->getFullType());
1670+
m_writer->emit("*>(");
1671+
m_writer->emit(getName(param));
1672+
m_writer->emit(")");
1673+
}
1674+
else
1675+
{
1676+
m_writer->emit(getName(param));
1677+
}
1678+
}
1679+
}
1680+
m_writer->emit(");\n");
1681+
m_writer->dedent();
1682+
m_writer->emit("}\n");
1683+
}
1684+
}
1685+
}
1686+
}
1687+
}
1688+
15941689
void CPPSourceEmitter::_emitWitnessTableDefinitions()
15951690
{
15961691
for (auto witnessTable : pendingWitnessTableDefinitions)
@@ -1613,7 +1708,7 @@ void CPPSourceEmitter::_emitWitnessTableDefinitions()
16131708
else
16141709
isFirstEntry = false;
16151710
m_writer->emit("&Context::");
1616-
m_writer->emit(getName(funcVal));
1711+
m_writer->emit(_getWitnessTableWrapperFuncName(funcVal));
16171712
}
16181713
else
16191714
{
@@ -1671,7 +1766,13 @@ void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition(
16711766
m_writer->emit(", ");
16721767
else
16731768
isFirstParam = false;
1674-
emitParamType(param->getFullType(), getName(param));
1769+
if (param->findDecoration<IRThisPointerDecoration>())
1770+
{
1771+
m_writer->emit("void* ");
1772+
m_writer->emit(getName(param));
1773+
continue;
1774+
}
1775+
emitSimpleFuncParamImpl(param);
16751776
}
16761777
m_writer->emit(");\n");
16771778
}
@@ -1681,7 +1782,7 @@ void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition(
16811782
}
16821783
}
16831784
m_writer->dedent();
1684-
m_writer->emit("\n};\n");
1785+
m_writer->emit("};\n");
16851786
}
16861787

16871788
bool CPPSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType)
@@ -1877,6 +1978,31 @@ void CPPSourceEmitter::emitSimpleValueImpl(IRInst* inst)
18771978
}
18781979
}
18791980

1981+
static bool isVoidPtrType(IRType* type)
1982+
{
1983+
auto ptrType = as<IRPtrType>(type);
1984+
if (!ptrType) return false;
1985+
return ptrType->getValueType()->op == kIROp_VoidType;
1986+
}
1987+
1988+
void CPPSourceEmitter::emitSimpleFuncParamImpl(IRParam* param)
1989+
{
1990+
// Polymorphic types are already translated to void* type in
1991+
// lower-generics pass. However, the current emitting logic will
1992+
// emit "void&" instead of "void*" for pointer types.
1993+
// In the future, we will handle pointer types more properly,
1994+
// and this override logic will not be necessary.
1995+
// For now we special-case this scenario.
1996+
if (param->findDecoration<IRPolymorphicDecoration>() &&
1997+
isVoidPtrType(param->getDataType()))
1998+
{
1999+
m_writer->emit("void* ");
2000+
m_writer->emit(getName(param));
2001+
return;
2002+
}
2003+
CLikeSourceEmitter::emitSimpleFuncParamImpl(param);
2004+
}
2005+
18802006
void CPPSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount)
18812007
{
18822008
emitSimpleType(m_typeSet.addVectorType(elementType, int(elementCount)));
@@ -2102,6 +2228,16 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut
21022228
m_writer->emit(")");
21032229
return true;
21042230
}
2231+
case kIROp_getAddr:
2232+
{
2233+
// Once we clean up the pointer emitting logic, we can
2234+
// just use GetElementAddress instruction in place of
2235+
// getAddr instruction, and this case can be removed.
2236+
m_writer->emit("(&(");
2237+
emitInstExpr(inst->getOperand(0), EmitOpInfo::get(EmitOp::General));
2238+
m_writer->emit("))");
2239+
return true;
2240+
}
21052241
}
21062242
}
21072243

@@ -2658,6 +2794,11 @@ void CPPSourceEmitter::emitModuleImpl(IRModule* module)
26582794
}
26592795
}
26602796

2797+
// Emit wrapper functions for each witness table entry.
2798+
// These wrapper functions takes an abstract type parameter (void*)
2799+
// in the place of `this` parameter.
2800+
_emitWitnessTableWrappers();
2801+
26612802
m_writer->dedent();
26622803
m_writer->emit("};\n\n");
26632804
}

source/slang/slang-emit-cpp.h

+13
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class CPPSourceEmitter: public CLikeSourceEmitter
7171
virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE;
7272
virtual void emitPreprocessorDirectivesImpl() SLANG_OVERRIDE;
7373
virtual void emitSimpleValueImpl(IRInst* value) SLANG_OVERRIDE;
74+
virtual void emitSimpleFuncParamImpl(IRParam* param) SLANG_OVERRIDE;
7475
virtual void emitModuleImpl(IRModule* module) SLANG_OVERRIDE;
7576
virtual void emitSimpleFuncImpl(IRFunc* func) SLANG_OVERRIDE;
7677
virtual void emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPrec) SLANG_OVERRIDE;
@@ -117,6 +118,10 @@ class CPPSourceEmitter: public CLikeSourceEmitter
117118

118119
UnownedStringSlice _getFuncName(const HLSLIntrinsic* specOp);
119120

121+
// Returns a StringSlice representing the mangled name of a witness table
122+
// wrapper function.
123+
UnownedStringSlice _getWitnessTableWrapperFuncName(IRFunc* func);
124+
120125
UnownedStringSlice _getTypeName(IRType* type);
121126

122127
SlangResult _calcCPPTextureTypeName(IRTextureTypeBase* texType, StringBuilder& outName);
@@ -134,12 +139,20 @@ class CPPSourceEmitter: public CLikeSourceEmitter
134139
// of all the witness table objects in `pendingWitnessTableDefinitions`.
135140
void _emitWitnessTableDefinitions();
136141

142+
// Emit wrapper functions that are referenced in witness tables.
143+
// Wrapper functions wraps the actual member function, and takes a `void*`
144+
// as the `this` parameter instead of the actual object type, so that
145+
// their signature is agnostic to the object type.
146+
void _emitWitnessTableWrappers();
147+
137148
HLSLIntrinsic* _addIntrinsic(HLSLIntrinsic::Op op, IRType* returnType, IRType*const* argTypes, Index argTypeCount);
138149

139150
static bool _isVariable(IROp op);
140151

141152
Dictionary<IRType*, StringSlicePool::Handle> m_typeNameMap;
142153
Dictionary<const HLSLIntrinsic*, StringSlicePool::Handle> m_intrinsicNameMap;
154+
Dictionary<IRFunc*, StringSlicePool::Handle> m_witnessTableWrapperFuncNameMap;
155+
143156

144157
IRTypeSet m_typeSet;
145158
RefPtr<HLSLIntrinsicOpLookup> m_opLookup;

source/slang/slang-ir-inst-defs.h

+11
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ INST(FieldAddress, get_field_addr, 2, 0)
236236

237237
INST(getElement, getElement, 2, 0)
238238
INST(getElementPtr, getElementPtr, 2, 0)
239+
INST(getAddr, getAddr, 1, 0)
239240

240241
// "Subscript" an image at a pixel coordinate to get pointer
241242
INST(ImageSubscript, imageSubscript, 2, 0)
@@ -506,6 +507,16 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0)
506507

507508
INST(BindExistentialSlotsDecoration, bindExistentialSlots, 0, 0)
508509

510+
/// A `[polymorphic]` decoration marks a function parameter that should translate to an abstract type
511+
/// e.g. (void*) that are casted to actual type before use. For example, a parameter of generic type
512+
/// is marked `[polymorphic]`, so that the code gen logic can emit it as a `void*` parameter,
513+
/// allowing the function to be used at sites that are agnostic of the actual object type.
514+
INST(PolymorphicDecoration, polymorphic, 0, 0)
515+
516+
/// A `[this_ptr]` decoration marks a function parameter that serves as `this` pointer.
517+
INST(ThisPointerDecoration, this_ptr, 0, 0)
518+
519+
509520
/// A `[format(f)]` decoration specifies that the format of an image should be `f`
510521
INST(FormatDecoration, format, 1, 0)
511522

source/slang/slang-ir-insts.h

+23
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ IR_SIMPLE_DECORATION(VulkanCallablePayloadDecoration)
166166
/// to it.
167167
IR_SIMPLE_DECORATION(VulkanHitAttributesDecoration)
168168

169+
IR_SIMPLE_DECORATION(PolymorphicDecoration)
170+
IR_SIMPLE_DECORATION(ThisPointerDecoration)
171+
172+
169173
struct IRRequireGLSLVersionDecoration : IRDecoration
170174
{
171175
enum { kOp = kIROp_RequireGLSLVersionDecoration };
@@ -1145,6 +1149,11 @@ struct IRFieldAddress : IRInst
11451149
IRInst* getField() { return field.get(); }
11461150
};
11471151

1152+
struct IRGetAddress : IRInst
1153+
{
1154+
IR_LEAF_ISA(getAddr);
1155+
};
1156+
11481157
// Terminators
11491158

11501159
struct IRReturn : IRTerminatorInst
@@ -1894,6 +1903,10 @@ struct IRBuilder
18941903
IRInst* basePtr,
18951904
IRInst* index);
18961905

1906+
IRInst* emitGetAddress(
1907+
IRType* type,
1908+
IRInst* value);
1909+
18971910
IRInst* emitSwizzle(
18981911
IRType* type,
18991912
IRInst* base,
@@ -2147,6 +2160,16 @@ struct IRBuilder
21472160
addDecoration(value, kIROp_LoopControlDecoration, getIntValue(getIntType(), IRIntegerValue(mode)));
21482161
}
21492162

2163+
void addPolymorphicDecoration(IRInst* value)
2164+
{
2165+
addDecoration(value, kIROp_PolymorphicDecoration);
2166+
}
2167+
2168+
void addThisPointerDecoration(IRInst* value)
2169+
{
2170+
addDecoration(value, kIROp_ThisPointerDecoration);
2171+
}
2172+
21502173
void addSemanticDecoration(IRInst* value, UnownedStringSlice const& text, int index = 0)
21512174
{
21522175
addDecoration(value, kIROp_SemanticDecoration, getStringValue(text), getIntValue(getIntType(), index));

source/slang/slang-ir-lower-generics.cpp

+30-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ namespace Slang
7979
block->addParam(as<IRParam>(param));
8080
}
8181
loweredGenericFunctions[genericValue] = loweredFunc;
82+
// Turn generic parameters into void pointers.
83+
for (auto param : cast<IRFunc>(loweredFunc)->getParams())
84+
{
85+
if (param->findDecoration<IRPolymorphicDecoration>())
86+
{
87+
param->setFullType(builder.getPtrType(builder.getVoidType()));
88+
}
89+
}
8290
addToWorkList(loweredFunc);
8391
return loweredFunc;
8492
}
@@ -103,8 +111,29 @@ namespace Slang
103111
builder->sharedBuilder = &sharedBuilderStorage;
104112
builder->setInsertBefore(inst);
105113
List<IRInst*> args;
114+
auto pp = as<IRFunc>(loweredFunc)->getParams().begin();
115+
auto voidPtrType = builder->getPtrType(builder->getVoidType());
106116
for (UInt i = 0; i < callInst->getArgCount(); i++)
107-
args.add(callInst->getArg(i));
117+
{
118+
auto arg = callInst->getArg(i);
119+
if ((*pp)->getDataType() == voidPtrType &&
120+
arg->getDataType() != voidPtrType)
121+
{
122+
// We are calling a generic function that with an argument of
123+
// concrete type. We need to convert this argument o void*.
124+
125+
// Ideally this should just be a GetElementAddress inst.
126+
// However the current code emitting logic for this instruction
127+
// doesn't truly respect the pointerness and does not produce
128+
// what we needed. For now we use another instruction here
129+
// to keep changes minimal.
130+
arg = builder->emitGetAddress(
131+
voidPtrType,
132+
arg);
133+
}
134+
args.add(arg);
135+
++pp;
136+
}
108137
for (UInt i = 0; i < specializeInst->getArgCount(); i++)
109138
args.add(specializeInst->getArg(i));
110139
auto newCall = builder->emitCallInst(callInst->getFullType(), loweredFunc, args);

source/slang/slang-ir.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -3083,6 +3083,20 @@ namespace Slang
30833083
return inst;
30843084
}
30853085

3086+
IRInst* IRBuilder::emitGetAddress(
3087+
IRType* type,
3088+
IRInst* value)
3089+
{
3090+
auto inst = createInst<IRGetAddress>(
3091+
this,
3092+
kIROp_getAddr,
3093+
type,
3094+
value);
3095+
3096+
addInst(inst);
3097+
return inst;
3098+
}
3099+
30863100
IRInst* IRBuilder::emitSwizzle(
30873101
IRType* type,
30883102
IRInst* base,

0 commit comments

Comments
 (0)