Skip to content

Commit

Permalink
IRGen: Add a new destructiveInjectEnumTag value witness function
Browse files Browse the repository at this point in the history
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!
slavapestov committed Dec 8, 2015
1 parent 218c960 commit 65a5a03
Showing 12 changed files with 1,011 additions and 185 deletions.
1 change: 1 addition & 0 deletions docs/ABI.rst
Original file line number Diff line number Diff line change
@@ -1089,6 +1089,7 @@ TODO: document these
value-witness-kind ::= 'tT' // initializeArrayWithTakeBackToFront
value-witness-kind ::= 'ug' // getEnumTag
value-witness-kind ::= 'up' // destructiveProjectEnumData
value-witness-kind ::= 'ui' // destructiveInjectEnumTag

``<value-witness-kind>`` differentiates the kinds of value
witness functions for a type.
20 changes: 17 additions & 3 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
@@ -545,6 +545,13 @@ typedef unsigned getEnumTag(const OpaqueValue *src,
typedef void destructiveProjectEnumData(OpaqueValue *src,
const Metadata *self);

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

} // end namespace value_witness_types

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

constexpr EnumValueWitnessTable()
: ExtraInhabitantsValueWitnessTable(),
getEnumTag(nullptr),
destructiveProjectEnumData(nullptr) {}
destructiveProjectEnumData(nullptr),
destructiveInjectEnumTag(nullptr) {}
constexpr EnumValueWitnessTable(
const ExtraInhabitantsValueWitnessTable &base,
value_witness_types::getEnumTag *getEnumTag,
value_witness_types::destructiveProjectEnumData *destructiveProjectEnumData)
value_witness_types::destructiveProjectEnumData *destructiveProjectEnumData,
value_witness_types::destructiveInjectEnumTag *destructiveInjectEnumTag)
: ExtraInhabitantsValueWitnessTable(base),
getEnumTag(getEnumTag),
destructiveProjectEnumData(destructiveProjectEnumData) {}
destructiveProjectEnumData(destructiveProjectEnumData),
destructiveInjectEnumTag(destructiveInjectEnumTag) {}

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

/// Get the nominal type descriptor if this metadata describes a nominal type,
/// or return null if it does not.
33 changes: 33 additions & 0 deletions lib/IRGen/EnumPayload.cpp
Original file line number Diff line number Diff line change
@@ -635,6 +635,39 @@ EnumPayload::emitApplyOrMask(IRGenFunction &IGF, APInt mask) {
}
}

void
EnumPayload::emitApplyOrMask(IRGenFunction &IGF,
EnumPayload mask) {
unsigned count = PayloadValues.size();
assert(count == mask.PayloadValues.size());

auto &DL = IGF.IGM.DataLayout;
for (unsigned i = 0; i < count; i++ ) {
auto payloadTy = getPayloadType(PayloadValues[i]);
unsigned size = DL.getTypeSizeInBits(payloadTy);

auto payloadIntTy = llvm::IntegerType::get(IGF.IGM.getLLVMContext(), size);

if (mask.PayloadValues[i].is<llvm::Type *>()) {
// We're ORing with zero, do nothing
} else if (PayloadValues[i].is<llvm::Type *>()) {
PayloadValues[i] = mask.PayloadValues[i];
} else {
auto v1 = IGF.Builder.CreateBitOrPointerCast(
PayloadValues[i].get<llvm::Value *>(),
payloadIntTy);

auto v2 = IGF.Builder.CreateBitOrPointerCast(
mask.PayloadValues[i].get<llvm::Value *>(),
payloadIntTy);

PayloadValues[i] = IGF.Builder.CreateBitOrPointerCast(
IGF.Builder.CreateOr(v1, v2),
payloadTy);
}
}
}

llvm::Value *
EnumPayload::emitGatherSpareBits(IRGenFunction &IGF,
const SpareBitVector &spareBits,
3 changes: 3 additions & 0 deletions lib/IRGen/EnumPayload.h
Original file line number Diff line number Diff line change
@@ -166,6 +166,9 @@ class EnumPayload {
/// Apply an OR mask to the payload.
void emitApplyOrMask(IRGenFunction &IGF, APInt mask);

/// Apply an OR mask to the payload.
void emitApplyOrMask(IRGenFunction &IGF, EnumPayload mask);

/// Gather bits from an enum payload based on a spare bit mask.
llvm::Value *emitGatherSpareBits(IRGenFunction &IGF,
const SpareBitVector &spareBits,
Loading
Oops, something went wrong.

0 comments on commit 65a5a03

Please sign in to comment.