summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/slang/core.meta.slang6
-rw-r--r--source/slang/slang-ir-inst-defs.h3
-rw-r--r--source/slang/slang-ir-insts.h8
-rw-r--r--source/slang/slang-ir-lower-existential.cpp46
-rw-r--r--tests/compute/dynamic-dispatch-16.slang56
-rw-r--r--tests/compute/dynamic-dispatch-16.slang.expected.txt2
6 files changed, 121 insertions, 0 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index d1dc25cc7..28fd1a545 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -1901,6 +1901,12 @@ __generic<T, U>
__intrinsic_op($(kIROp_BitCast))
T bit_cast(U value);
+// Create Existential object
+__generic<T, U>
+[__unsafeForceInlineEarly]
+__intrinsic_op($(kIROp_CreateExistentialObject))
+T createDynamicObject(uint typeId, U value);
+
// Specialized function
/// Given a string returns an integer hash of that string.
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index d81e6868c..797d9f69c 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -644,6 +644,9 @@ INST(MakeExistential, makeExistential, 2, 0)
// but with the type of `v` being an explict operand.
INST(MakeExistentialWithRTTI, makeExistentialWithRTTI, 3, 0)
+// A 'CreateExistentialObject<I>(typeID, T)` packs user-provided `typeID` and a
+// value of any type, and constructs an existential value of type `I`.
+INST(CreateExistentialObject, createExistentialObject, 2, 0)
// A `wrapExistential(v, T0,w0, T1,w0) : T` instruction is similar to `makeExistential`.
// but applies to a value `v` that is of type `BindExistentials(T, T0,w0, ...)`. The
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 25313d2f5..0a1f3be8d 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -1726,6 +1726,14 @@ struct IRMakeExistentialWithRTTI : IRInst
IR_LEAF_ISA(MakeExistentialWithRTTI)
};
+struct IRCreateExistentialObject : IRInst
+{
+ IRInst* getTypeID() { return getOperand(0); }
+ IRInst* getValue() { return getOperand(1); }
+
+ IR_LEAF_ISA(CreateExistentialObject)
+};
+
/// Generalizes `IRMakeExistential` by allowing a type with existential sub-fields to be boxed
struct IRWrapExistential : IRInst
{
diff --git a/source/slang/slang-ir-lower-existential.cpp b/source/slang/slang-ir-lower-existential.cpp
index c85d7af63..ac46fa46f 100644
--- a/source/slang/slang-ir-lower-existential.cpp
+++ b/source/slang/slang-ir-lower-existential.cpp
@@ -46,6 +46,48 @@ namespace Slang
inst->removeAndDeallocate();
}
+ // Translates `createExistentialObject` insts, which takes a user defined
+ // type id and user defined value and turns into an existential value,
+ // into a `makeTuple` inst that makes the tuple representing the lowered
+ // existential value.
+ void processCreateExistentialObject(IRCreateExistentialObject* inst)
+ {
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+ builder->sharedBuilder = &sharedContext->sharedBuilderStorage;
+ builder->setInsertBefore(inst);
+
+ // The result type of this `createExistentialObject` inst should already
+ // be lowered into a `TupleType(rttiType, WitnessTableIDType, AnyValueType)`
+ // in the previous `lowerGenericType` pass.
+ auto tupleType = inst->getDataType();
+ auto witnessTableIdType = cast<IRWitnessTableIDType>(tupleType->getOperand(1));
+ auto anyValueType = cast<IRAnyValueType>(tupleType->getOperand(2));
+
+ // Create a null value for `rttiObject` for now since it will not be used.
+ IRInst* rttiObject = builder->getIntValue(builder->getIntType(), 0);
+
+ // Pack the user provided value into `AnyValue`.
+ IRInst* packedValue = inst->getValue();
+ if (packedValue->getDataType()->getOp() != kIROp_AnyValueType)
+ packedValue = builder->emitPackAnyValue(anyValueType, packedValue);
+
+ // Use the user provided `typeID` value as the witness table ID field in the
+ // newly constructed tuple.
+ // All `WitnessTableID` types are lowered into `uint2`s, so we need to create
+ // a `uint2` value from `typeID` to stay consistent with the convention.
+ IRInst* vectorArgs[2] = {
+ inst->getTypeID(), builder->getIntValue(builder->getUIntType(), 0)};
+ auto uint2Type = builder->getVectorType(
+ builder->getUIntType(), builder->getIntValue(builder->getIntType(), 2));
+ IRInst* typeIdValue = builder->emitMakeVector(uint2Type, 2, vectorArgs);
+ typeIdValue = builder->emitBitCast(witnessTableIdType, typeIdValue);
+ IRInst* tupleArgs[] = {rttiObject, typeIdValue, packedValue};
+ auto tuple = builder->emitMakeTuple(tupleType, 3, tupleArgs);
+ inst->replaceUsesWith(tuple);
+ inst->removeAndDeallocate();
+ }
+
IRInst* extractTupleElement(IRBuilder* builder, IRInst* value, UInt index)
{
auto tupleType = cast<IRTupleType>(sharedContext->lowerType(builder, value->getDataType()));
@@ -138,6 +180,10 @@ namespace Slang
{
processMakeExistential(makeExistential);
}
+ else if (auto createExistentialObject = as<IRCreateExistentialObject>(inst))
+ {
+ processCreateExistentialObject(createExistentialObject);
+ }
else if (auto getValueFromBoundInterface = as<IRGetValueFromBoundInterface>(inst))
{
processGetValueFromBoundInterface(getValueFromBoundInterface);
diff --git a/tests/compute/dynamic-dispatch-16.slang b/tests/compute/dynamic-dispatch-16.slang
new file mode 100644
index 000000000..5ceb75cd7
--- /dev/null
+++ b/tests/compute/dynamic-dispatch-16.slang
@@ -0,0 +1,56 @@
+// Test packing a user provided value and typeID into an existential value.
+
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -vk -output-using-type
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil -output-using-type
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx11 -profile sm_5_0 -output-using-type
+
+[anyValueSize(16)]
+interface IInterface
+{
+ float run();
+}
+
+struct UserDefinedPackedType
+{
+ float3 val;
+ uint flags;
+};
+
+//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=gOutputBuffer
+RWStructuredBuffer<float> gOutputBuffer;
+
+//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}];
+RWStructuredBuffer<UserDefinedPackedType> gObj;
+
+[numthreads(1, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ float result = 0.0;
+ for (int i = 0; i < 2; i++)
+ {
+ var rawObj = gObj.Load(i);
+ IInterface dynamicObj = createDynamicObject<IInterface, UserDefinedPackedType>(rawObj.flags, rawObj);
+ result += dynamicObj.run();
+ }
+ gOutputBuffer[0] = result;
+}
+
+// Type must be marked `public` to ensure it is visible in the generated DLL.
+public struct FloatVal : IInterface
+{
+ float val;
+ float run()
+ {
+ return val;
+ }
+};
+interface ISomething{void g();}
+struct Float4Struct : ISomething { float4 val; void g() {} }
+public struct Float4Val : IInterface
+{
+ Float4Struct val;
+ float run()
+ {
+ return val.val.x + val.val.y;
+ }
+};
diff --git a/tests/compute/dynamic-dispatch-16.slang.expected.txt b/tests/compute/dynamic-dispatch-16.slang.expected.txt
new file mode 100644
index 000000000..253df0793
--- /dev/null
+++ b/tests/compute/dynamic-dispatch-16.slang.expected.txt
@@ -0,0 +1,2 @@
+type: float
+6.0