summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2021-08-25 10:27:22 -0700
committerGitHub <noreply@github.com>2021-08-25 10:27:22 -0700
commit33f7e1599cbecb32c23787b37b2bf3b34bdd5c84 (patch)
tree1fcdadfa1d03a21668606439402e80e6a754162c /source
parent3b0b920608928f8cb39dc9116043d5a8644149c3 (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.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
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);