Skip to content

Commit 8f96289

Browse files
authored
Allow mixing unspecialized and specialized existential parameters. (shader-slang#1533)
* Allow mixing unspecialized and specialized existential parameters. * Fixes.
1 parent 5e10f1b commit 8f96289

24 files changed

+252
-23
lines changed

source/slang/core.meta.slang

+5
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,11 @@ struct Ref
313313
__magic_type(StringType)
314314
__intrinsic_type($(kIROp_StringType))
315315
struct String
316+
{};
317+
318+
__magic_type(DynamicType)
319+
__intrinsic_type($(kIROp_DynamicType))
320+
struct __Dynamic
316321
{};
317322

318323
/// An `N` component vector with elements of type `T`.

source/slang/slang-ast-builder.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ Type* SharedASTBuilder::getEnumTypeType()
100100
return m_enumTypeType;
101101
}
102102

103+
Type* SharedASTBuilder::getDynamicType()
104+
{
105+
if (!m_dynamicType)
106+
{
107+
auto dynamicTypeDecl = findMagicDecl("DynamicType");
108+
m_dynamicType = DeclRefType::create(m_astBuilder, makeDeclRef<Decl>(dynamicTypeDecl));
109+
}
110+
return m_dynamicType;
111+
}
112+
103113
SharedASTBuilder::~SharedASTBuilder()
104114
{
105115
// Release built in types..

source/slang/slang-ast-builder.h

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class SharedASTBuilder : public RefObject
2525
Type* getStringType();
2626
/// Get the enum type type
2727
Type* getEnumTypeType();
28+
/// Get the __Dynamic type
29+
Type* getDynamicType();
2830

2931
const ReflectClassInfo* findClassInfo(Name* name);
3032
SyntaxClass<NodeBase> findSyntaxClass(Name* name);
@@ -62,6 +64,7 @@ class SharedASTBuilder : public RefObject
6264
//
6365
Type* m_stringType = nullptr;
6466
Type* m_enumTypeType = nullptr;
67+
Type* m_dynamicType = nullptr;
6568

6669
Type* m_builtinTypes[Index(BaseType::CountOf)];
6770

source/slang/slang-ast-type.h

+5
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,11 @@ class StringType : public BuiltinType
466466
SLANG_CLASS(StringType)
467467
};
468468

469+
// The built-in `__Dynamic` type
470+
class DynamicType : public BuiltinType
471+
{
472+
SLANG_CLASS(DynamicType)
473+
};
469474

470475
// Type built-in `__EnumType` type
471476
class EnumTypeType : public BuiltinType

source/slang/slang-ast-val.h

+7
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,11 @@ class ThisTypeSubtypeWitness : public SubtypeWitness
206206
SLANG_CLASS(ThisTypeSubtypeWitness)
207207
};
208208

209+
/// A witness of the fact that a user provided "__Dynamic" type argument is a
210+
/// subtype to the existential type parameter.
211+
class DynamicSubtypeWitness : public SubtypeWitness
212+
{
213+
SLANG_CLASS(DynamicSubtypeWitness)
214+
};
215+
209216
} // namespace Slang

source/slang/slang-check-conformance.cpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,16 @@ namespace Slang
180180
}
181181
return true;
182182
}
183-
184-
if( auto aggTypeDeclRef = declRef.as<AggTypeDecl>() )
183+
if (auto dynamicType = as<DynamicType>(subType))
184+
{
185+
// A __Dynamic type always conforms to the interface via its witness table.
186+
if (outWitness)
187+
{
188+
*outWitness = m_astBuilder->create<DynamicSubtypeWitness>();
189+
}
190+
return true;
191+
}
192+
else if( auto aggTypeDeclRef = declRef.as<AggTypeDecl>() )
185193
{
186194
ensureDecl(aggTypeDeclRef, DeclCheckState::CanEnumerateBases);
187195

@@ -363,7 +371,6 @@ namespace Slang
363371
}
364372
return true;
365373
}
366-
367374
// default is failure
368375
return false;
369376
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,7 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst)
942942
case kIROp_getElementPtr:
943943
case kIROp_Specialize:
944944
case kIROp_lookup_interface_method:
945+
case kIROp_GetValueFromExistentialBox:
945946
return true;
946947
}
947948

@@ -2388,6 +2389,7 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
23882389
break;
23892390

23902391
case kIROp_GlobalConstant:
2392+
case kIROp_GetValueFromExistentialBox:
23912393
emitOperand(inst->getOperand(0), outerPrec);
23922394
break;
23932395

source/slang/slang-emit.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ Result linkAndOptimizeIR(
314314
// generics / interface types to ordinary functions and types using
315315
// function pointers.
316316
dumpIRIfEnabled(compileRequest, irModule, "BEFORE-LOWER-GENERICS");
317-
lowerGenerics(irModule, sink);
317+
lowerGenerics(targetRequest, irModule, sink);
318318
dumpIRIfEnabled(compileRequest, irModule, "LOWER-GENERICS");
319319
break;
320320
default:

source/slang/slang-ir-collect-global-uniforms.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ struct CollectGlobalUniformParametersContext
5050
IRModule* module;
5151
IRVarLayout* globalScopeVarLayout;
5252

53+
IRGlobalParam* _getGlobalParamFromLayoutFieldKey(IRInst* key)
54+
{
55+
switch (key->op)
56+
{
57+
case kIROp_GlobalParam:
58+
return cast<IRGlobalParam>(key);
59+
case kIROp_MakeExistential:
60+
case kIROp_WrapExistential:
61+
return as<IRGlobalParam>(key->getOperand(0));
62+
default:
63+
return nullptr;
64+
}
65+
}
66+
5367
// This is a relatively simple pass, and it is all driven
5468
// by a single subroutine.
5569
//
@@ -158,7 +172,7 @@ struct CollectGlobalUniformParametersContext
158172
// layout so that the "key" for the field is the corresponding
159173
// global shader parameter.
160174
//
161-
auto globalParam = as<IRGlobalParam>(fieldLayoutAttr->getFieldKey());
175+
auto globalParam = _getGlobalParamFromLayoutFieldKey(fieldLayoutAttr->getFieldKey());
162176
SLANG_ASSERT(globalParam);
163177

164178
auto globalParamLayout = fieldLayoutAttr->getLayout();

source/slang/slang-ir-generics-lowering-context.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ namespace Slang
197197
SLANG_ASSERT(reqVal && reqVal->op == kIROp_AssociatedType);
198198
return lowerType(builder, reqVal, typeMapping);
199199
}
200+
case kIROp_ExistentialBoxType:
201+
{
202+
auto existentialBoxType = static_cast<IRExistentialBoxType*>(paramType);
203+
return lowerType(builder, existentialBoxType->getInterfaceType(), typeMapping);
204+
}
200205
default:
201206
{
202207
bool translated = false;

source/slang/slang-ir-generics-lowering-context.h

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace Slang
1616
// we are processing.
1717
IRModule* module;
1818

19+
TargetRequest* targetReq;
20+
1921
DiagnosticSink* sink;
2022

2123
// RTTI objects for each type used to call a generic function.

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

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ INST(Nop, nop, 0, 0)
2626

2727
INST(StringType, String, 0, 0)
2828

29+
INST(DynamicType, DynamicType, 0, 0)
30+
2931
INST(AnyValueType, AnyValueType, 1, 0)
3032

3133
INST(RawPointerType, RawPointerType, 0, 0)
@@ -587,6 +589,10 @@ INST(MakeExistentialWithRTTI, makeExistentialWithRTTI, 3, 0)
587589
//
588590
INST(WrapExistential, wrapExistential, 2, 0)
589591

592+
// A `GetValueFromExistentialBox` takes a `ExistentialBox` value and returns the value wrapped by
593+
// the existential box.
594+
INST(GetValueFromExistentialBox, getValueFromExistentialBox, 1, 0)
595+
590596
INST(ExtractExistentialValue, extractExistentialValue, 1, 0)
591597
INST(ExtractExistentialType, extractExistentialType, 1, 0)
592598
INST(ExtractExistentialWitnessTable, extractExistentialWitnessTable, 1, 0)

source/slang/slang-ir-insts.h

+11
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,11 @@ struct IRWrapExistential : IRInst
16221622
IR_LEAF_ISA(WrapExistential)
16231623
};
16241624

1625+
struct IRGetValueFromExistentialBox : IRInst
1626+
{
1627+
IR_LEAF_ISA(GetValueFromExistentialBox);
1628+
};
1629+
16251630
struct IRExtractExistentialValue : IRInst
16261631
{
16271632
IR_LEAF_ISA(ExtractExistentialValue);
@@ -1745,13 +1750,15 @@ struct IRBuilder
17451750
IRBasicType* getIntType();
17461751
IRBasicType* getUIntType();
17471752
IRStringType* getStringType();
1753+
17481754
IRAssociatedType* getAssociatedType(ArrayView<IRInterfaceType*> constraintTypes);
17491755
IRThisType* getThisType(IRInterfaceType* interfaceType);
17501756
IRRawPointerType* getRawPointerType();
17511757
IRRTTIPointerType* getRTTIPointerType(IRInst* rttiPtr);
17521758
IRRTTIType* getRTTIType();
17531759
IRAnyValueType* getAnyValueType(IRIntegerValue size);
17541760
IRAnyValueType* getAnyValueType(IRInst* size);
1761+
IRDynamicType* getDynamicType();
17551762

17561763
IRTupleType* getTupleType(UInt count, IRType* const* types);
17571764
IRTupleType* getTupleType(IRType* type0, IRType* type1);
@@ -1770,6 +1777,7 @@ struct IRBuilder
17701777
IRInOutType* getInOutType(IRType* valueType);
17711778
IRRefType* getRefType(IRType* valueType);
17721779
IRPtrTypeBase* getPtrType(IROp op, IRType* valueType);
1780+
IRExistentialBoxType* getExistentialBoxType(IRType* concreteType, IRType* interfaceType);
17731781

17741782
IRArrayTypeBase* getArrayTypeBase(
17751783
IROp op,
@@ -1838,6 +1846,9 @@ struct IRBuilder
18381846
// its rate, if any.
18391847
void setDataType(IRInst* inst, IRType* dataType);
18401848

1849+
/// Extract the value wrapped inside an existential box.
1850+
IRInst* emitGetValueFromExistentialBox(IRType* type, IRInst* existentialBox);
1851+
18411852
/// Given an existential value, extract the underlying "real" value
18421853
IRInst* emitExtractExistentialValue(
18431854
IRType* type,

source/slang/slang-ir-legalize-types.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,9 @@ static LegalVal legalizeInst(
12101210
case kIROp_Load:
12111211
return legalizeLoad(context, args[0]);
12121212

1213+
case kIROp_GetValueFromExistentialBox:
1214+
return args[0];
1215+
12131216
case kIROp_FieldAddress:
12141217
return legalizeFieldAddress(context, type, args[0], args[1]);
12151218

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

+31
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
namespace Slang
99
{
10+
bool isCPUTarget(TargetRequest* targetReq);
11+
bool isCUDATarget(TargetRequest* targetReq);
12+
1013
struct ExistentialLoweringContext
1114
{
1215
SharedGenericsLoweringContext* sharedContext;
@@ -79,12 +82,40 @@ namespace Slang
7982
processExtractExistentialElement(inst, 0);
8083
}
8184

85+
void processGetValueFromExistentialBox(IRGetValueFromExistentialBox* inst)
86+
{
87+
// Currently we do not translate on HLSL/GLSL targets,
88+
// since we don't attempt to actually layout the value inside an fixed sized
89+
// existential box for these targets.
90+
if (isCPUTarget(sharedContext->targetReq) || isCUDATarget(sharedContext->targetReq))
91+
{
92+
IRBuilder builderStorage;
93+
auto builder = &builderStorage;
94+
builder->sharedBuilder = &sharedContext->sharedBuilderStorage;
95+
builder->setInsertBefore(inst);
96+
97+
auto element = extractTupleElement(builder, inst->getOperand(0), 2);
98+
// TODO: it is not technically sound to use `getAddress` on a temporary value.
99+
// We probably need to develop a mechanism to allow a temporary value to be used
100+
// in the place of a pointer.
101+
auto elementAddr =
102+
builder->emitGetAddress(builder->getPtrType(element->getDataType()), element);
103+
auto reinterpretAddr = builder->emitBitCast(inst->getDataType(), elementAddr);
104+
inst->replaceUsesWith(reinterpretAddr);
105+
inst->removeAndDeallocate();
106+
}
107+
}
108+
82109
void processInst(IRInst* inst)
83110
{
84111
if (auto makeExistential = as<IRMakeExistentialWithRTTI>(inst))
85112
{
86113
processMakeExistential(makeExistential);
87114
}
115+
else if (auto getExistentialValue = as<IRGetValueFromExistentialBox>(inst))
116+
{
117+
processGetValueFromExistentialBox(getExistentialValue);
118+
}
88119
else if (auto extractExistentialVal = as<IRExtractExistentialValue>(inst))
89120
{
90121
processExtractExistentialValue(extractExistentialVal);

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

+2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
namespace Slang
1616
{
1717
void lowerGenerics(
18+
TargetRequest* targetReq,
1819
IRModule* module,
1920
DiagnosticSink* sink)
2021
{
2122
SharedGenericsLoweringContext sharedContext;
23+
sharedContext.targetReq = targetReq;
2224
sharedContext.module = module;
2325
sharedContext.sink = sink;
2426

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

+2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ namespace Slang
77
{
88
struct IRModule;
99
class DiagnosticSink;
10+
class TargetRequest;
1011

1112
/// Lower generic and interface-based code to ordinary types and functions using
1213
/// dynamic dispatch mechanisms.
1314
void lowerGenerics(
15+
TargetRequest* targetReq,
1416
IRModule* module,
1517
DiagnosticSink* sink);
1618

source/slang/slang-ir-specialize.cpp

+32-1
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,33 @@ struct SpecializationContext
611611
return nullptr;
612612
}
613613

614+
void maybeInsertGetExistentialValue(IRInst* inst)
615+
{
616+
// If inst has `ExistentialBox` type, we need to make sure
617+
// all uses are through `GetValueFromExistentialBox`.
618+
if (auto existentialBoxType = as<IRExistentialBoxType>(inst->getDataType()))
619+
{
620+
ShortList<IRUse*> usesToReplace;
621+
for (auto use = inst->firstUse; use; use = use->nextUse)
622+
{
623+
if (use->getUser()->op != kIROp_GetValueFromExistentialBox)
624+
usesToReplace.add(use);
625+
}
626+
for (auto use : usesToReplace)
627+
{
628+
auto user = use->getUser();
629+
IRBuilder builderStorage;
630+
auto builder = &builderStorage;
631+
builder->sharedBuilder = &sharedBuilderStorage;
632+
builder->setInsertBefore(user);
633+
auto getValueInst = builder->emitGetValueFromExistentialBox(
634+
builder->getPtrType(existentialBoxType->getValueType()), inst);
635+
use->set(getValueInst);
636+
addToWorkList(getValueInst);
637+
}
638+
}
639+
}
640+
614641
// All of the machinery for generic specialization
615642
// has been defined above, so we will now walk
616643
// through the flow of the overall specialization pass.
@@ -678,6 +705,10 @@ struct SpecializationContext
678705
workListSet.Remove(inst);
679706
cleanInsts.Add(inst);
680707

708+
// If inst represents a value of ExistentialBox type, all its uses
709+
// must be through a `GetValueFromExistentialBox` inst.
710+
maybeInsertGetExistentialValue(inst);
711+
681712
// For each instruction we process, we want to perform
682713
// a few steps.
683714
//
@@ -1640,7 +1671,7 @@ struct SpecializationContext
16401671
if(slotOperandCount <= 1) return;
16411672

16421673
auto concreteType = (IRType*) type->getExistentialArg(0);
1643-
auto newVal = builder.getPtrType(kIROp_ExistentialBoxType, concreteType);
1674+
auto newVal = builder.getExistentialBoxType(concreteType, baseInterfaceType);
16441675

16451676
addUsersToWorkList(type);
16461677
type->replaceUsesWith(newVal);

0 commit comments

Comments
 (0)