Skip to content

Commit 33f7e15

Browse files
authored
Add createDynamicObject stdlib function. (shader-slang#1923)
This function takes a user provided `typeID` and arbitrary typed value, and turns them into an existential value whose `witnessTableID` is `typeID` and whose `anyValue` is the user provided value. This allows the users to pack the runtime type id info in arbitrary way.
1 parent 3b0b920 commit 33f7e15

6 files changed

+121
-0
lines changed

source/slang/core.meta.slang

+6
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,12 @@ __generic<T, U>
19011901
__intrinsic_op($(kIROp_BitCast))
19021902
T bit_cast(U value);
19031903

1904+
// Create Existential object
1905+
__generic<T, U>
1906+
[__unsafeForceInlineEarly]
1907+
__intrinsic_op($(kIROp_CreateExistentialObject))
1908+
T createDynamicObject(uint typeId, U value);
1909+
19041910
// Specialized function
19051911

19061912
/// Given a string returns an integer hash of that string.

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

+3
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,9 @@ INST(MakeExistential, makeExistential, 2, 0)
644644
// but with the type of `v` being an explict operand.
645645
INST(MakeExistentialWithRTTI, makeExistentialWithRTTI, 3, 0)
646646

647+
// A 'CreateExistentialObject<I>(typeID, T)` packs user-provided `typeID` and a
648+
// value of any type, and constructs an existential value of type `I`.
649+
INST(CreateExistentialObject, createExistentialObject, 2, 0)
647650

648651
// A `wrapExistential(v, T0,w0, T1,w0) : T` instruction is similar to `makeExistential`.
649652
// but applies to a value `v` that is of type `BindExistentials(T, T0,w0, ...)`. The

source/slang/slang-ir-insts.h

+8
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,14 @@ struct IRMakeExistentialWithRTTI : IRInst
17261726
IR_LEAF_ISA(MakeExistentialWithRTTI)
17271727
};
17281728

1729+
struct IRCreateExistentialObject : IRInst
1730+
{
1731+
IRInst* getTypeID() { return getOperand(0); }
1732+
IRInst* getValue() { return getOperand(1); }
1733+
1734+
IR_LEAF_ISA(CreateExistentialObject)
1735+
};
1736+
17291737
/// Generalizes `IRMakeExistential` by allowing a type with existential sub-fields to be boxed
17301738
struct IRWrapExistential : IRInst
17311739
{

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

+46
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,48 @@ namespace Slang
4646
inst->removeAndDeallocate();
4747
}
4848

49+
// Translates `createExistentialObject` insts, which takes a user defined
50+
// type id and user defined value and turns into an existential value,
51+
// into a `makeTuple` inst that makes the tuple representing the lowered
52+
// existential value.
53+
void processCreateExistentialObject(IRCreateExistentialObject* inst)
54+
{
55+
IRBuilder builderStorage;
56+
auto builder = &builderStorage;
57+
builder->sharedBuilder = &sharedContext->sharedBuilderStorage;
58+
builder->setInsertBefore(inst);
59+
60+
// The result type of this `createExistentialObject` inst should already
61+
// be lowered into a `TupleType(rttiType, WitnessTableIDType, AnyValueType)`
62+
// in the previous `lowerGenericType` pass.
63+
auto tupleType = inst->getDataType();
64+
auto witnessTableIdType = cast<IRWitnessTableIDType>(tupleType->getOperand(1));
65+
auto anyValueType = cast<IRAnyValueType>(tupleType->getOperand(2));
66+
67+
// Create a null value for `rttiObject` for now since it will not be used.
68+
IRInst* rttiObject = builder->getIntValue(builder->getIntType(), 0);
69+
70+
// Pack the user provided value into `AnyValue`.
71+
IRInst* packedValue = inst->getValue();
72+
if (packedValue->getDataType()->getOp() != kIROp_AnyValueType)
73+
packedValue = builder->emitPackAnyValue(anyValueType, packedValue);
74+
75+
// Use the user provided `typeID` value as the witness table ID field in the
76+
// newly constructed tuple.
77+
// All `WitnessTableID` types are lowered into `uint2`s, so we need to create
78+
// a `uint2` value from `typeID` to stay consistent with the convention.
79+
IRInst* vectorArgs[2] = {
80+
inst->getTypeID(), builder->getIntValue(builder->getUIntType(), 0)};
81+
auto uint2Type = builder->getVectorType(
82+
builder->getUIntType(), builder->getIntValue(builder->getIntType(), 2));
83+
IRInst* typeIdValue = builder->emitMakeVector(uint2Type, 2, vectorArgs);
84+
typeIdValue = builder->emitBitCast(witnessTableIdType, typeIdValue);
85+
IRInst* tupleArgs[] = {rttiObject, typeIdValue, packedValue};
86+
auto tuple = builder->emitMakeTuple(tupleType, 3, tupleArgs);
87+
inst->replaceUsesWith(tuple);
88+
inst->removeAndDeallocate();
89+
}
90+
4991
IRInst* extractTupleElement(IRBuilder* builder, IRInst* value, UInt index)
5092
{
5193
auto tupleType = cast<IRTupleType>(sharedContext->lowerType(builder, value->getDataType()));
@@ -138,6 +180,10 @@ namespace Slang
138180
{
139181
processMakeExistential(makeExistential);
140182
}
183+
else if (auto createExistentialObject = as<IRCreateExistentialObject>(inst))
184+
{
185+
processCreateExistentialObject(createExistentialObject);
186+
}
141187
else if (auto getValueFromBoundInterface = as<IRGetValueFromBoundInterface>(inst))
142188
{
143189
processGetValueFromBoundInterface(getValueFromBoundInterface);
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Test packing a user provided value and typeID into an existential value.
2+
3+
//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -vk -output-using-type
4+
//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil -output-using-type
5+
//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx11 -profile sm_5_0 -output-using-type
6+
7+
[anyValueSize(16)]
8+
interface IInterface
9+
{
10+
float run();
11+
}
12+
13+
struct UserDefinedPackedType
14+
{
15+
float3 val;
16+
uint flags;
17+
};
18+
19+
//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=gOutputBuffer
20+
RWStructuredBuffer<float> gOutputBuffer;
21+
22+
//TEST_INPUT: set gObj = new StructuredBuffer<UserDefinedPackedType>[new UserDefinedPackedType{[1.0, 0.0, 0.0], 0}, new UserDefinedPackedType{[2.0, 3.0, 4.0], 1}];
23+
RWStructuredBuffer<UserDefinedPackedType> gObj;
24+
25+
[numthreads(1, 1, 1)]
26+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
27+
{
28+
float result = 0.0;
29+
for (int i = 0; i < 2; i++)
30+
{
31+
var rawObj = gObj.Load(i);
32+
IInterface dynamicObj = createDynamicObject<IInterface, UserDefinedPackedType>(rawObj.flags, rawObj);
33+
result += dynamicObj.run();
34+
}
35+
gOutputBuffer[0] = result;
36+
}
37+
38+
// Type must be marked `public` to ensure it is visible in the generated DLL.
39+
public struct FloatVal : IInterface
40+
{
41+
float val;
42+
float run()
43+
{
44+
return val;
45+
}
46+
};
47+
interface ISomething{void g();}
48+
struct Float4Struct : ISomething { float4 val; void g() {} }
49+
public struct Float4Val : IInterface
50+
{
51+
Float4Struct val;
52+
float run()
53+
{
54+
return val.val.x + val.val.y;
55+
}
56+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
type: float
2+
6.0

0 commit comments

Comments
 (0)