diff options
| -rw-r--r-- | source/slang/core.meta.slang | 6 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 8 | ||||
| -rw-r--r-- | source/slang/slang-ir-lower-existential.cpp | 46 | ||||
| -rw-r--r-- | tests/compute/dynamic-dispatch-16.slang | 56 | ||||
| -rw-r--r-- | tests/compute/dynamic-dispatch-16.slang.expected.txt | 2 |
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 |
