Skip to content

Commit cd7f01b

Browse files
authored
Generate dynamic C++ code for the minimal test case. (shader-slang#1391)
* Add IR pass to lower generics into ordinary functions. * Fix project files * Emit dynamic C++ code for simple generics and witness tables. Fixes shader-slang#1386. * Remove -dump-ir flag. * Fixups.
1 parent ca503d4 commit cd7f01b

17 files changed

+505
-41
lines changed

prelude/slang-cpp-prelude.h

+2
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@
3939
#endif
4040

4141
#endif
42+
43+
struct Context;

source/slang/slang-emit-c-like.cpp

+79-16
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,42 @@ void CLikeSourceEmitter::emitSimpleType(IRType* type)
217217
outNumThreads[i] = decor ? Int(getIntVal(decor->getOperand(i))) : 1;
218218
}
219219
return decor;
220-
}
221-
220+
}
221+
222+
List<IRWitnessTableEntry*> CLikeSourceEmitter::getSortedWitnessTableEntries(IRWitnessTable* witnessTable)
223+
{
224+
List<IRWitnessTableEntry*> sortedWitnessTableEntries;
225+
auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0));
226+
auto witnessTableItems = witnessTable->getChildren();
227+
// Build a dictionary of witness table entries for fast lookup.
228+
Dictionary<IRInst*, IRWitnessTableEntry*> witnessTableEntryDictionary;
229+
for (auto item : witnessTableItems)
230+
{
231+
if (auto entry = as<IRWitnessTableEntry>(item))
232+
{
233+
witnessTableEntryDictionary[entry->getRequirementKey()] = entry;
234+
}
235+
}
236+
// Get a sorted list of entries using RequirementKeys defined in `interfaceType`.
237+
for (UInt i = 0; i < interfaceType->getOperandCount(); i++)
238+
{
239+
auto reqKey = cast<IRStructKey>(interfaceType->getOperand(i));
240+
bool matchingEntryFound = false;
241+
IRWitnessTableEntry* entry = nullptr;
242+
if (witnessTableEntryDictionary.TryGetValue(reqKey, entry))
243+
{
244+
if (entry->requirementKey.get() == reqKey)
245+
{
246+
matchingEntryFound = true;
247+
sortedWitnessTableEntries.add(entry);
248+
break;
249+
}
250+
}
251+
SLANG_ASSERT(matchingEntryFound);
252+
}
253+
return sortedWitnessTableEntries;
254+
}
255+
222256
void CLikeSourceEmitter::_emitArrayType(IRArrayType* arrayType, EDeclarator* declarator)
223257
{
224258
EDeclarator arrayDeclarator;
@@ -271,6 +305,12 @@ void CLikeSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable)
271305
SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "Unimplemented emit: IROpWitnessTable.");
272306
}
273307

308+
void CLikeSourceEmitter::emitInterface(IRInterfaceType* interfaceType)
309+
{
310+
SLANG_UNUSED(interfaceType);
311+
SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "Unimplemented emit: IROpInterfaceType.");
312+
}
313+
274314
void CLikeSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameAndLoc)
275315
{
276316
if (nameAndLoc)
@@ -890,6 +930,7 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst)
890930
case kIROp_FieldAddress:
891931
case kIROp_getElementPtr:
892932
case kIROp_Specialize:
933+
case kIROp_lookup_interface_method:
893934
return true;
894935
}
895936

@@ -1849,6 +1890,31 @@ void CLikeSourceEmitter::emitIntrinsicCallExprImpl(
18491890
}
18501891
}
18511892

1893+
void CLikeSourceEmitter::_emitCallArgList(IRCall* inst)
1894+
{
1895+
bool isFirstArg = true;
1896+
m_writer->emit("(");
1897+
UInt argCount = inst->getOperandCount();
1898+
for (UInt aa = 1; aa < argCount; ++aa)
1899+
{
1900+
auto operand = inst->getOperand(aa);
1901+
if (as<IRVoidType>(operand->getDataType()))
1902+
continue;
1903+
1904+
// TODO: [generate dynamic dispatch code for generics]
1905+
// Pass RTTI object here. Ignore type argument for now.
1906+
if (as<IRType>(operand))
1907+
continue;
1908+
1909+
if (!isFirstArg)
1910+
m_writer->emit(", ");
1911+
else
1912+
isFirstArg = false;
1913+
emitOperand(inst->getOperand(aa), getInfo(EmitOp::General));
1914+
}
1915+
m_writer->emit(")");
1916+
}
1917+
18521918
void CLikeSourceEmitter::emitCallExpr(IRCall* inst, EmitOpInfo outerPrec)
18531919
{
18541920
auto funcValue = inst->getOperand(0);
@@ -1868,18 +1934,7 @@ void CLikeSourceEmitter::emitCallExpr(IRCall* inst, EmitOpInfo outerPrec)
18681934
bool needClose = maybeEmitParens(outerPrec, prec);
18691935

18701936
emitOperand(funcValue, leftSide(outerPrec, prec));
1871-
m_writer->emit("(");
1872-
UInt argCount = inst->getOperandCount();
1873-
for( UInt aa = 1; aa < argCount; ++aa )
1874-
{
1875-
auto operand = inst->getOperand(aa);
1876-
if (as<IRVoidType>(operand->getDataType()))
1877-
continue;
1878-
if(aa != 1) m_writer->emit(", ");
1879-
emitOperand(inst->getOperand(aa), getInfo(EmitOp::General));
1880-
}
1881-
m_writer->emit(")");
1882-
1937+
_emitCallArgList(inst);
18831938
maybeCloseParens(needClose);
18841939
}
18851940
}
@@ -3522,6 +3577,10 @@ void CLikeSourceEmitter::emitGlobalInst(IRInst* inst)
35223577
emitStruct(cast<IRStructType>(inst));
35233578
break;
35243579

3580+
case kIROp_InterfaceType:
3581+
emitInterface(cast<IRInterfaceType>(inst));
3582+
break;
3583+
35253584
case kIROp_WitnessTable:
35263585
emitWitnessTable(cast<IRWitnessTable>(inst));
35273586
break;
@@ -3576,11 +3635,15 @@ void CLikeSourceEmitter::ensureInstOperandsRec(ComputeEmitActionsContext* ctx, I
35763635

35773636
void CLikeSourceEmitter::ensureGlobalInst(ComputeEmitActionsContext* ctx, IRInst* inst, EmitAction::Level requiredLevel)
35783637
{
3579-
// Skip certain instructions, since they
3580-
// don't affect output.
3638+
// Skip certain instructions that don't affect output.
35813639
switch(inst->op)
35823640
{
35833641
case kIROp_WitnessTable:
3642+
// Only skip witness tables when we are generating
3643+
// static code.
3644+
if (!m_compileRequest->allowDynamicCode)
3645+
return;
3646+
break;
35843647
case kIROp_Generic:
35853648
return;
35863649

source/slang/slang-emit-c-like.h

+7
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ class CLikeSourceEmitter: public RefObject
341341
virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) = 0;
342342

343343
virtual void emitWitnessTable(IRWitnessTable* witnessTable);
344+
virtual void emitInterface(IRInterfaceType* interfaceType);
344345

345346
virtual void handleCallExprDecorationsImpl(IRInst* funcValue) { SLANG_UNUSED(funcValue); }
346347

@@ -351,6 +352,12 @@ class CLikeSourceEmitter: public RefObject
351352
void _emitUnsizedArrayType(IRUnsizedArrayType* arrayType, EDeclarator* declarator);
352353
void _emitType(IRType* type, EDeclarator* declarator);
353354
void _emitInst(IRInst* inst);
355+
356+
// Emit the argument list (including paranthesis) in a `CallInst`
357+
void _emitCallArgList(IRCall* call);
358+
359+
// Sort witnessTable entries according to the order defined in the witnessed interface type.
360+
List<IRWitnessTableEntry*> getSortedWitnessTableEntries(IRWitnessTable* witnessTable);
354361

355362
BackEndCompileRequest* m_compileRequest = nullptr;
356363

source/slang/slang-emit-cpp.cpp

+153-1
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,16 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S
488488
out << ">";
489489
return SLANG_OK;
490490
}
491+
case kIROp_WitnessTableType:
492+
{
493+
// A witness table typed value translates to a pointer to the
494+
// struct of function pointers corresponding to the interface type.
495+
auto witnessTableType = static_cast<IRWitnessTableType*>(type);
496+
auto baseType = cast<IRType>(witnessTableType->getOperand(0));
497+
emitType(baseType);
498+
out << "*";
499+
return SLANG_OK;
500+
}
491501
default:
492502
{
493503
if (isNominalOp(type->op))
@@ -1561,7 +1571,117 @@ void CPPSourceEmitter::emitParamTypeImpl(IRType* type, String const& name)
15611571

15621572
void CPPSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable)
15631573
{
1564-
SLANG_UNUSED(witnessTable);
1574+
auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0));
1575+
auto witnessTableItems = witnessTable->getChildren();
1576+
List<IRWitnessTableEntry*> sortedWitnessTableEntries = getSortedWitnessTableEntries(witnessTable);
1577+
_maybeEmitWitnessTableTypeDefinition(interfaceType, sortedWitnessTableEntries);
1578+
1579+
// Define a global variable for the witness table.
1580+
m_writer->emit("extern ");
1581+
emitSimpleType(interfaceType);
1582+
m_writer->emit(" ");
1583+
m_writer->emit(getName(witnessTable));
1584+
m_writer->emit(";\n");
1585+
1586+
// The actual definition of this witness table global variable
1587+
// is deferred until the entire `Context` class is emitted, so
1588+
// that the member functions are available for reference.
1589+
// The witness table definition emission logic is defined in the
1590+
// `_emitWitnessTableDefinitions` function.
1591+
pendingWitnessTableDefinitions.add(witnessTable);
1592+
}
1593+
1594+
void CPPSourceEmitter::_emitWitnessTableDefinitions()
1595+
{
1596+
for (auto witnessTable : pendingWitnessTableDefinitions)
1597+
{
1598+
auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0));
1599+
List<IRWitnessTableEntry*> sortedWitnessTableEntries = getSortedWitnessTableEntries(witnessTable);
1600+
emitSimpleType(interfaceType);
1601+
m_writer->emit(" ");
1602+
m_writer->emit(getName(witnessTable));
1603+
m_writer->emit(" = {\n");
1604+
m_writer->indent();
1605+
bool isFirstEntry = true;
1606+
for (Index i = 0; i < sortedWitnessTableEntries.getCount(); i++)
1607+
{
1608+
auto entry = sortedWitnessTableEntries[i];
1609+
if (auto funcVal = as<IRFunc>(entry->satisfyingVal.get()))
1610+
{
1611+
if (!isFirstEntry)
1612+
m_writer->emit(",\n");
1613+
else
1614+
isFirstEntry = false;
1615+
m_writer->emit("&Context::");
1616+
m_writer->emit(getName(funcVal));
1617+
}
1618+
else
1619+
{
1620+
// TODO: handle other witness table entry types.
1621+
}
1622+
}
1623+
m_writer->dedent();
1624+
m_writer->emit("\n};\n");
1625+
}
1626+
}
1627+
1628+
void CPPSourceEmitter::emitInterface(IRInterfaceType* interfaceType)
1629+
{
1630+
// The current IRInterfaceType defintion does not contain
1631+
// sufficient info for emitting a witness table struct by itself
1632+
// Instead, it defines the order of entries in a witness table.
1633+
// Therefore, we emit a forward declaration here, and actual definition
1634+
// for the witness table type during emitWitnessTable.
1635+
SLANG_UNUSED(interfaceType);
1636+
m_writer->emit("struct ");
1637+
emitSimpleType(interfaceType);
1638+
m_writer->emit(";\n");
1639+
}
1640+
1641+
/// Emits witness table type definition given a sorted list of witness tables
1642+
/// acoording to the order defined by `interfaceType`.
1643+
///
1644+
void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition(
1645+
IRInterfaceType* interfaceType,
1646+
const List<IRWitnessTableEntry*>& sortedWitnessTableEntries)
1647+
{
1648+
m_writer->emit("struct ");
1649+
emitSimpleType(interfaceType);
1650+
m_writer->emit("\n{\n");
1651+
m_writer->indent();
1652+
bool isFirstEntry = true;
1653+
for (Index i = 0; i < sortedWitnessTableEntries.getCount(); i++)
1654+
{
1655+
auto entry = sortedWitnessTableEntries[i];
1656+
if (auto funcVal = as<IRFunc>(entry->satisfyingVal.get()))
1657+
{
1658+
if (!isFirstEntry)
1659+
m_writer->emit(",\n");
1660+
else
1661+
isFirstEntry = false;
1662+
emitType(funcVal->getResultType());
1663+
m_writer->emit(" (Context::*");
1664+
m_writer->emit(getName(entry->requirementKey.get()));
1665+
m_writer->emit(")");
1666+
m_writer->emit("(");
1667+
bool isFirstParam = true;
1668+
for (auto param : funcVal->getParams())
1669+
{
1670+
if (!isFirstParam)
1671+
m_writer->emit(", ");
1672+
else
1673+
isFirstParam = false;
1674+
emitParamType(param->getFullType(), getName(param));
1675+
}
1676+
m_writer->emit(");\n");
1677+
}
1678+
else
1679+
{
1680+
// TODO: handle other witness table entry types.
1681+
}
1682+
}
1683+
m_writer->dedent();
1684+
m_writer->emit("\n};\n");
15651685
}
15661686

15671687
bool CPPSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType)
@@ -1673,6 +1793,12 @@ void CPPSourceEmitter::emitSimpleFuncImpl(IRFunc* func)
16731793
auto firstParam = func->getFirstParam();
16741794
for (auto pp = firstParam; pp; pp = pp->getNextParam())
16751795
{
1796+
// Ingore TypeType-typed parameters for now.
1797+
// In the future we will pass around runtime type info
1798+
// for TypeType parameters.
1799+
if (as<IRTypeType>(pp->getFullType()))
1800+
continue;
1801+
16761802
if (pp != firstParam)
16771803
m_writer->emit(", ");
16781804

@@ -1950,9 +2076,32 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut
19502076
// Does this function declare any requirements.
19512077
handleCallExprDecorationsImpl(funcValue);
19522078

2079+
if (funcValue->op == kIROp_lookup_interface_method)
2080+
{
2081+
m_writer->emit("(this->*(");
2082+
emitOperand(funcValue, EmitOpInfo());
2083+
m_writer->emit("))");
2084+
_emitCallArgList(as<IRCall>(inst));
2085+
return true;
2086+
}
2087+
19532088
// try doing automatically
19542089
return _tryEmitInstExprAsIntrinsic(inst, inOuterPrec);
19552090
}
2091+
case kIROp_lookup_interface_method:
2092+
{
2093+
emitInstExpr(inst->getOperand(0), inOuterPrec);
2094+
m_writer->emit("->");
2095+
m_writer->emit(getName(inst->getOperand(1)));
2096+
return true;
2097+
}
2098+
case kIROp_WitnessTable:
2099+
{
2100+
m_writer->emit("(&");
2101+
m_writer->emit(getName(inst));
2102+
m_writer->emit(")");
2103+
return true;
2104+
}
19562105
}
19572106
}
19582107

@@ -2513,6 +2662,9 @@ void CPPSourceEmitter::emitModuleImpl(IRModule* module)
25132662
m_writer->emit("};\n\n");
25142663
}
25152664

2665+
// Emit all witness table definitions.
2666+
_emitWitnessTableDefinitions();
2667+
25162668
// Finally we need to output dll entry points
25172669

25182670
for (auto action : actions)

0 commit comments

Comments
 (0)