Skip to content

Commit 65a5a03

Browse files
committed
IRGen: Add a new destructiveInjectEnumTag value witness function
This value witness function takes an address of an enum value where the payload has already been initialized, together with a case index, and forms the enum value. The formal behavior can be thought of as satisfying an identity in relation to the existing two enum value witnesses. For any enum value, the following is to leave the value unchanged: tag = getEnumTag(value) destructiveProjectEnumData(value) destructiveInjectEnumData(value, tag) This is the last missing piece for the inject_enum_addr SIL instruction to handle resilient enums, allowing the implementation of an enum to be decoupled from its uses. Also, it should be useful for dynamically constructing enum cases with write reflection, once we get around to doing such a thing. The body of the value witness is emitted by a new emitStoreTag() method on EnumImplStrategy. This is similar to the existing storeTag(), except the case index is a value instead of a contant. This is implemented as follows for the different enum strategies: 1) For enums consisting of a single case, this is trivial. 2) For enums where all cases are empty, stores the case index into the payload area. 3) For enums with a single payload case, emits a call to a runtime function. Note that for non-generic single payload enums, this could be open-coded more efficiently, but the function still has the correct behavior since it supports extra inhabitants and so on. A follow-up patch will make this more efficient. 4) For multi-payload enums, there are two cases: a) If one of the payloads is generic or resilient, the enum is dynamically-sized, and a call to a runtime function is emitted. b) If the entire enum is fixed-size, the value witness checks if the case is empty or not. If the case has a payload, the case index is swizzled into spare bits of the payload, if any, with remaining bits going into the extra tag area. If the case is empty, the case index is swizzled into the spare bits of the payload, the remaining bits of the payload, and the extra tag area. The implementations of emitStoreTag() duplicate existing logic in the enum strategies, in particular case 4)b) is rather complicated. Code cleanups are welcome here!
1 parent 218c960 commit 65a5a03

File tree

12 files changed

+1011
-185
lines changed

12 files changed

+1011
-185
lines changed

docs/ABI.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,7 @@ TODO: document these
10891089
value-witness-kind ::= 'tT' // initializeArrayWithTakeBackToFront
10901090
value-witness-kind ::= 'ug' // getEnumTag
10911091
value-witness-kind ::= 'up' // destructiveProjectEnumData
1092+
value-witness-kind ::= 'ui' // destructiveInjectEnumTag
10921093

10931094
``<value-witness-kind>`` differentiates the kinds of value
10941095
witness functions for a type.

include/swift/Runtime/Metadata.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,13 @@ typedef unsigned getEnumTag(const OpaqueValue *src,
545545
typedef void destructiveProjectEnumData(OpaqueValue *src,
546546
const Metadata *self);
547547

548+
/// Given a valid object of an enum case payload's type, destructively add
549+
/// the tag bits for the given case, leaving behind a fully-formed value of
550+
/// the enum type. If the enum case does not have a payload, the initial
551+
/// state of the value can be undefined.
552+
typedef void destructiveInjectEnumTag(OpaqueValue *src,
553+
unsigned tag,
554+
const Metadata *self);
548555

549556
} // end namespace value_witness_types
550557

@@ -702,18 +709,22 @@ struct ExtraInhabitantsValueWitnessTable : ValueWitnessTable {
702709
struct EnumValueWitnessTable : ExtraInhabitantsValueWitnessTable {
703710
value_witness_types::getEnumTag *getEnumTag;
704711
value_witness_types::destructiveProjectEnumData *destructiveProjectEnumData;
712+
value_witness_types::destructiveInjectEnumTag *destructiveInjectEnumTag;
705713

706714
constexpr EnumValueWitnessTable()
707715
: ExtraInhabitantsValueWitnessTable(),
708716
getEnumTag(nullptr),
709-
destructiveProjectEnumData(nullptr) {}
717+
destructiveProjectEnumData(nullptr),
718+
destructiveInjectEnumTag(nullptr) {}
710719
constexpr EnumValueWitnessTable(
711720
const ExtraInhabitantsValueWitnessTable &base,
712721
value_witness_types::getEnumTag *getEnumTag,
713-
value_witness_types::destructiveProjectEnumData *destructiveProjectEnumData)
722+
value_witness_types::destructiveProjectEnumData *destructiveProjectEnumData,
723+
value_witness_types::destructiveInjectEnumTag *destructiveInjectEnumTag)
714724
: ExtraInhabitantsValueWitnessTable(base),
715725
getEnumTag(getEnumTag),
716-
destructiveProjectEnumData(destructiveProjectEnumData) {}
726+
destructiveProjectEnumData(destructiveProjectEnumData),
727+
destructiveInjectEnumTag(destructiveInjectEnumTag) {}
717728

718729
static bool classof(const ValueWitnessTable *table) {
719730
return table->flags.hasEnumWitnesses();
@@ -1070,6 +1081,9 @@ struct Metadata {
10701081
void vw_destructiveProjectEnumData(OpaqueValue *value) const {
10711082
getValueWitnesses()->_asEVWT()->destructiveProjectEnumData(value, this);
10721083
}
1084+
void vw_destructiveInjectEnumTag(OpaqueValue *value, unsigned tag) const {
1085+
getValueWitnesses()->_asEVWT()->destructiveInjectEnumTag(value, tag, this);
1086+
}
10731087

10741088
/// Get the nominal type descriptor if this metadata describes a nominal type,
10751089
/// or return null if it does not.

lib/IRGen/EnumPayload.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,39 @@ EnumPayload::emitApplyOrMask(IRGenFunction &IGF, APInt mask) {
635635
}
636636
}
637637

638+
void
639+
EnumPayload::emitApplyOrMask(IRGenFunction &IGF,
640+
EnumPayload mask) {
641+
unsigned count = PayloadValues.size();
642+
assert(count == mask.PayloadValues.size());
643+
644+
auto &DL = IGF.IGM.DataLayout;
645+
for (unsigned i = 0; i < count; i++ ) {
646+
auto payloadTy = getPayloadType(PayloadValues[i]);
647+
unsigned size = DL.getTypeSizeInBits(payloadTy);
648+
649+
auto payloadIntTy = llvm::IntegerType::get(IGF.IGM.getLLVMContext(), size);
650+
651+
if (mask.PayloadValues[i].is<llvm::Type *>()) {
652+
// We're ORing with zero, do nothing
653+
} else if (PayloadValues[i].is<llvm::Type *>()) {
654+
PayloadValues[i] = mask.PayloadValues[i];
655+
} else {
656+
auto v1 = IGF.Builder.CreateBitOrPointerCast(
657+
PayloadValues[i].get<llvm::Value *>(),
658+
payloadIntTy);
659+
660+
auto v2 = IGF.Builder.CreateBitOrPointerCast(
661+
mask.PayloadValues[i].get<llvm::Value *>(),
662+
payloadIntTy);
663+
664+
PayloadValues[i] = IGF.Builder.CreateBitOrPointerCast(
665+
IGF.Builder.CreateOr(v1, v2),
666+
payloadTy);
667+
}
668+
}
669+
}
670+
638671
llvm::Value *
639672
EnumPayload::emitGatherSpareBits(IRGenFunction &IGF,
640673
const SpareBitVector &spareBits,

lib/IRGen/EnumPayload.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ class EnumPayload {
166166
/// Apply an OR mask to the payload.
167167
void emitApplyOrMask(IRGenFunction &IGF, APInt mask);
168168

169+
/// Apply an OR mask to the payload.
170+
void emitApplyOrMask(IRGenFunction &IGF, EnumPayload mask);
171+
169172
/// Gather bits from an enum payload based on a spare bit mask.
170173
llvm::Value *emitGatherSpareBits(IRGenFunction &IGF,
171174
const SpareBitVector &spareBits,

0 commit comments

Comments
 (0)