diff options
| author | Yong He <yonghe@outlook.com> | 2021-08-25 10:27:22 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-25 10:27:22 -0700 |
| commit | 33f7e1599cbecb32c23787b37b2bf3b34bdd5c84 (patch) | |
| tree | 1fcdadfa1d03a21668606439402e80e6a754162c /source | |
| parent | 3b0b920608928f8cb39dc9116043d5a8644149c3 (diff) | |
Add `createDynamicObject` stdlib function. (#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.
Diffstat (limited to 'source')
| -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 |
4 files changed, 63 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); |
