mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Preparation to fix <rdar://problem/18151694> Add Builtin.checkUnique
to avoid lost Array copies.
This adds the following new builtins:
isUnique : <T> (inout T[?]) -> Int1
isUniqueOrPinned : <T> (inout T[?]) -> Int1
These builtins take an inout object reference and return a
boolean. Passing the reference inout forces the optimizer to preserve
a retain distinct from what’s required to maintain lifetime for any of
the reference's source-level copies, because the called function is
allowed to replace the reference, thereby releasing the referent.
Before this change, the API entry points for uniqueness checking
already took an inout reference. However, after full inlining, it was
possible for two source-level variables that reference the same object
to appear to be the same variable from the optimizer's perspective
because an address to the variable was longer taken at the point of
checking uniqueness. Consequently the optimizer could remove
"redundant" copies which were actually needed to implement
copy-on-write semantics. With a builtin, the variable whose reference
is being checked for uniqueness appears mutable at the level of an
individual SIL instruction.
The kind of reference count checking that Builtin.isUnique performs
depends on the argument type:
- Native object types are directly checked by reading the
strong reference count:
(Builtin.NativeObject, known native class reference)
- Objective-C object types require an additional check that the
dynamic object type uses native swift reference counting:
(Builtin.UnknownObject, unknown class reference, class existential)
- Bridged object types allow the dymanic object type check to be
bypassed based on the pointer encoding:
(Builtin.BridgeObject)
Any of the above types may also be wrapped in an optional. If the
static argument type is optional, then a null check is also performed.
Thus, isUnique only returns true for non-null, native swift object
references with a strong reference count of one.
isUniqueOrPinned has the same semantics as isUnique except that it
also returns true if the object is marked pinned regardless of the
reference count. This allows for simultaneous non-structural
modification of multiple subobjects.
In some cases, the standard library can dynamically determine that it
has a native reference even though the static type is a bridge or
unknown object. Unsafe variants of the builtin are available to allow
the additional pointer bit mask and dynamic class lookup to be
bypassed in these cases:
isUnique_native : <T> (inout T[?]) -> Int1
isUniqueOrPinned_native : <T> (inout T[?]) -> Int1
These builtins perform an implicit cast to NativeObject before
checking uniqueness. There’s no way at SIL level to cast the address
of a reference, so we need to encapsulate this operation as part of
the builtin.
Swift SVN r27887
476 lines
19 KiB
C++
476 lines
19 KiB
C++
//===--- GenEnum.h - Swift IR Generation For 'enum' Types -------* C++ *-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements IR generation for algebraic data types (ADTs,
|
|
// or 'enum' types) in Swift. This includes creating the IR type as
|
|
// well as emitting the basic access operations.
|
|
//
|
|
// The current scheme is that all such types with are represented
|
|
// with an initial word indicating the variant, followed by an enum
|
|
// of all the possibilities. This is obviously completely acceptable
|
|
// to everyone and will not benefit from further refinement.
|
|
//
|
|
// As a completely unimportant premature optimization, we do emit
|
|
// types with only a single variant as simple structs wrapping that
|
|
// variant.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef __SWIFT_IRGen_GenEnum_H__
|
|
#define __SWIFT_IRGen_GenEnum_H__
|
|
|
|
#include "TypeInfo.h"
|
|
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
|
|
namespace llvm {
|
|
class BasicBlock;
|
|
class ConstantInt;
|
|
class StructType;
|
|
class Type;
|
|
class Value;
|
|
}
|
|
|
|
namespace swift {
|
|
class EnumElementDecl;
|
|
|
|
namespace irgen {
|
|
class IRGenFunction;
|
|
class TypeConverter;
|
|
|
|
/// Utility class for packing enum payloads. The payload of a fixed-size, non-
|
|
/// trivial enum is represented as an LLVM integer type large enough to
|
|
/// hold the largest member of the enum. This class collects individual
|
|
/// scalar values, such as from an explosion, into an enum payload.
|
|
class PackEnumPayload {
|
|
IRGenFunction &IGF;
|
|
unsigned packedBits = 0;
|
|
unsigned bitSize;
|
|
llvm::Value *packedValue = nullptr;
|
|
|
|
public:
|
|
PackEnumPayload(IRGenFunction &IGF, unsigned bitSize);
|
|
|
|
/// Insert a value into the packed value after the previously inserted value,
|
|
/// or at offset zero if this is the first value.
|
|
void add(llvm::Value *v);
|
|
|
|
/// Make further insertions with 'add' start at the given offset.
|
|
void moveToOffset(unsigned bitOffset) {
|
|
assert(bitOffset < bitSize);
|
|
packedBits = bitOffset;
|
|
}
|
|
|
|
/// Insert a value into the packed value at a specific offset.
|
|
void addAtOffset(llvm::Value *v, unsigned bitOffset) {
|
|
moveToOffset(bitOffset);
|
|
add(v);
|
|
}
|
|
|
|
/// Combine a partially packed payload at a different offset into our packing.
|
|
void combine(llvm::Value *v);
|
|
|
|
/// Get the packed value.
|
|
llvm::Value *get();
|
|
|
|
/// Get an empty payload value of the given bit size.
|
|
static llvm::Value *getEmpty(IRGenModule &IGM, unsigned bitSize);
|
|
};
|
|
|
|
/// Utility class for packing enum payloads. The payload of a fixed-size, non-
|
|
/// trivial enum is represented as an LLVM integer type large enough to
|
|
/// hold the largest member of the enum. This class extracts individual
|
|
/// scalar values from an enum payload.
|
|
class UnpackEnumPayload {
|
|
IRGenFunction &IGF;
|
|
llvm::Value *packedValue;
|
|
unsigned unpackedBits = 0;
|
|
|
|
public:
|
|
UnpackEnumPayload(IRGenFunction &IGF, llvm::Value *packedValue);
|
|
|
|
/// Extract a value of the given type that was packed after the previously
|
|
/// claimed value, or at offset zero if this is the first claimed value.
|
|
llvm::Value *claim(llvm::Type *ty);
|
|
|
|
/// Extract further values from the given offset.
|
|
void moveToOffset(unsigned bitOffset) {
|
|
unpackedBits = bitOffset;
|
|
}
|
|
|
|
/// Claim a value at a specific offset inside the value.
|
|
llvm::Value *claimAtOffset(llvm::Type *ty, unsigned bitOffset) {
|
|
moveToOffset(bitOffset);
|
|
return claim(ty);
|
|
}
|
|
};
|
|
|
|
/// \brief Emit the dispatch branch(es) for an address-only enum.
|
|
void emitSwitchAddressOnlyEnumDispatch(IRGenFunction &IGF,
|
|
SILType enumTy,
|
|
Address enumAddr,
|
|
ArrayRef<std::pair<EnumElementDecl*,
|
|
llvm::BasicBlock*>> dests,
|
|
llvm::BasicBlock *defaultDest);
|
|
|
|
/// \brief Injects a case and its associated data, if any, into a loadable enum
|
|
/// value.
|
|
void emitInjectLoadableEnum(IRGenFunction &IGF,
|
|
SILType enumTy,
|
|
EnumElementDecl *theCase,
|
|
Explosion &data,
|
|
Explosion &out);
|
|
|
|
/// \brief Extracts the associated data for an enum case. This is an unchecked
|
|
/// operation; the input enum value must be of the given case.
|
|
void emitProjectLoadableEnum(IRGenFunction &IGF,
|
|
SILType enumTy,
|
|
Explosion &inData,
|
|
EnumElementDecl *theCase,
|
|
Explosion &out);
|
|
|
|
/// \brief Projects the address of the associated data for a case inside a
|
|
/// enum, to which a new data value can be stored.
|
|
Address emitProjectEnumAddressForStore(IRGenFunction &IGF,
|
|
SILType enumTy,
|
|
Address enumAddr,
|
|
EnumElementDecl *theCase);
|
|
|
|
/// \brief Projects the address of the associated data for a case inside a
|
|
/// enum, clearing any tag bits interleaved into the data area, so that the
|
|
/// value inside can be loaded. Does not check that the enum has a value of the
|
|
/// given case.
|
|
Address emitDestructiveProjectEnumAddressForLoad(IRGenFunction &IGF,
|
|
SILType enumTy,
|
|
Address enumAddr,
|
|
EnumElementDecl *theCase);
|
|
|
|
/// \brief Stores the tag bits for an enum case to the given address, overlaying
|
|
/// the data (if any) stored there.
|
|
void emitStoreEnumTagToAddress(IRGenFunction &IGF,
|
|
SILType enumTy,
|
|
Address enumAddr,
|
|
EnumElementDecl *theCase);
|
|
|
|
/// Interleave the occupiedValue and spareValue bits, taking a bit from one
|
|
/// or the other at each position based on the spareBits mask.
|
|
APInt
|
|
interleaveSpareBits(IRGenModule &IGM, const SpareBitVector &spareBits,
|
|
unsigned bits, unsigned spareValue, unsigned occupiedValue);
|
|
|
|
/// Gather spare bits into the low bits of a smaller integer value.
|
|
llvm::Value *emitGatherSpareBits(IRGenFunction &IGF,
|
|
const SpareBitVector &spareBitMask,
|
|
llvm::Value *spareBits,
|
|
unsigned resultLowBit,
|
|
unsigned resultBitWidth);
|
|
/// Scatter spare bits from the low bits of a smaller integer value.
|
|
llvm::Value *emitScatterSpareBits(IRGenFunction &IGF,
|
|
const SpareBitVector &spareBitMask,
|
|
llvm::Value *packedBits,
|
|
unsigned packedLowBit);
|
|
|
|
/// An implementation strategy for an enum, which handles how the enum is
|
|
/// laid out and how to perform TypeInfo operations on values of the enum.
|
|
class EnumImplStrategy {
|
|
public:
|
|
struct Element {
|
|
EnumElementDecl *decl;
|
|
const TypeInfo *ti;
|
|
};
|
|
|
|
enum TypeInfoKind {
|
|
Opaque, ///< The enum has a NonFixedTypeInfo.
|
|
Fixed, ///< The enum has a FixedTypeInfo.
|
|
Loadable, ///< The enum has a LoadableTypeInfo.
|
|
};
|
|
|
|
protected:
|
|
std::vector<Element> ElementsWithPayload;
|
|
std::vector<Element> ElementsWithRecursivePayload;
|
|
std::vector<Element> ElementsWithNoPayload;
|
|
const TypeInfo *TI = nullptr;
|
|
TypeInfoKind TIK;
|
|
unsigned NumElements;
|
|
|
|
EnumImplStrategy(IRGenModule &IGM,
|
|
TypeInfoKind tik,
|
|
unsigned NumElements,
|
|
std::vector<Element> &&ElementsWithPayload,
|
|
std::vector<Element> &&ElementsWithRecursivePayload,
|
|
std::vector<Element> &&ElementsWithNoPayload)
|
|
: ElementsWithPayload(std::move(ElementsWithPayload)),
|
|
ElementsWithRecursivePayload(std::move(ElementsWithRecursivePayload)),
|
|
ElementsWithNoPayload(std::move(ElementsWithNoPayload)),
|
|
TIK(tik),
|
|
NumElements(NumElements)
|
|
{}
|
|
|
|
/// Save the TypeInfo created for the enum.
|
|
TypeInfo *registerEnumTypeInfo(TypeInfo *mutableTI) {
|
|
TI = mutableTI;
|
|
return mutableTI;
|
|
}
|
|
|
|
/// Constructs a TypeInfo for an enum of the best possible kind for its
|
|
/// layout, FixedEnumTypeInfo or LoadableEnumTypeInfo.
|
|
TypeInfo *getFixedEnumTypeInfo(llvm::StructType *T, Size S, SpareBitVector SB,
|
|
Alignment A, IsPOD_t isPOD,
|
|
IsBitwiseTakable_t isBT);
|
|
|
|
public:
|
|
virtual ~EnumImplStrategy() { }
|
|
|
|
/// Construct a layout strategy appropriate to the enum type.
|
|
static EnumImplStrategy *get(TypeConverter &TC,
|
|
SILType Type,
|
|
EnumDecl *theEnum);
|
|
|
|
/// Given an incomplete StructType for the enum, completes layout of the
|
|
/// storage type, calculates its size and alignment, and produces the
|
|
/// TypeInfo for the enum.
|
|
virtual TypeInfo *completeEnumTypeLayout(TypeConverter &TC,
|
|
SILType Type,
|
|
EnumDecl *theEnum,
|
|
llvm::StructType *enumTy) = 0;
|
|
|
|
const TypeInfo &getTypeInfo() const {
|
|
assert(TI);
|
|
return *TI;
|
|
}
|
|
|
|
llvm::StructType *getStorageType() const {
|
|
return cast<llvm::StructType>(getTypeInfo().getStorageType());
|
|
}
|
|
|
|
IsPOD_t isPOD(ResilienceScope scope) const {
|
|
return getTypeInfo().isPOD(scope);
|
|
}
|
|
|
|
/// \group Query enum layout
|
|
///
|
|
/// These APIs assume a fixed layout; they will not work on generic
|
|
/// enum types that have not been fully instantiated.
|
|
|
|
/// Return the list of cases determined to have a payload.
|
|
/// The payloads are discriminated using the bits in the mask returned by
|
|
/// getTagBits().
|
|
ArrayRef<Element> getElementsWithPayload() const {
|
|
return ElementsWithPayload;
|
|
}
|
|
|
|
/// Return the list of cases determined to have no payload.
|
|
/// Each no-payload case is represented by a specific bit pattern which can
|
|
/// be queried using getBitPatternForNoPayloadElement.
|
|
ArrayRef<Element> getElementsWithNoPayload() const {
|
|
return ElementsWithNoPayload;
|
|
}
|
|
|
|
/// Map the given element to the appropriate index in the
|
|
/// discriminator type.
|
|
/// Returns -1 if this is not supported by the enum implementation.
|
|
virtual int64_t getDiscriminatorIndex(EnumElementDecl *target) const {
|
|
return -1;
|
|
}
|
|
|
|
/// \brief Return the bits used for discriminators for payload cases.
|
|
///
|
|
/// These bits are populated in increasing value according to the order of
|
|
/// the getElementsWithPayload() array, starting from zero for the first
|
|
/// element with payload.
|
|
virtual ClusteredBitVector getTagBitsForPayloads(IRGenModule &IGM) const = 0;
|
|
|
|
/// Return the bit pattern used for the given no-payload case.
|
|
virtual ClusteredBitVector
|
|
getBitPatternForNoPayloadElement(IRGenModule &IGM,
|
|
EnumElementDecl *theCase) const = 0;
|
|
|
|
/// Return the bit mask used to test for no-payload cases.
|
|
virtual ClusteredBitVector
|
|
getBitMaskForNoPayloadElements(IRGenModule &IGM) const = 0;
|
|
|
|
/// \group Indirect enum operations
|
|
|
|
/// Project the address of the data for a case. Does not check or modify
|
|
/// the referenced enum value.
|
|
/// Corresponds to the SIL 'init_enum_data_addr' instruction.
|
|
virtual Address projectDataForStore(IRGenFunction &IGF,
|
|
EnumElementDecl *elt,
|
|
Address enumAddr) const = 0;
|
|
|
|
/// Overlay the tag value for a case onto a data value in memory.
|
|
/// Corresponds to the SIL 'inject_enum_addr' instruction.
|
|
virtual void storeTag(IRGenFunction &IGF,
|
|
EnumElementDecl *elt,
|
|
Address enumAddr,
|
|
SILType T) const = 0;
|
|
|
|
/// Clears tag bits from within the payload of an enum in memory and
|
|
/// projects the address of the data for a case. Does not check
|
|
/// the referenced enum value.
|
|
/// Performs the block argument binding for a SIL
|
|
/// 'switch_enum_addr' instruction.
|
|
virtual Address destructiveProjectDataForLoad(IRGenFunction &IGF,
|
|
EnumElementDecl *elt,
|
|
Address enumAddr) const = 0;
|
|
|
|
/// Return an i1 value that indicates whether the specified indirect enum
|
|
/// value holds the specified case. This is a light-weight form of a switch.
|
|
virtual llvm::Value *emitIndirectCaseTest(IRGenFunction &IGF,
|
|
SILType T,
|
|
Address enumAddr,
|
|
EnumElementDecl *Case) const = 0;
|
|
|
|
/// Emit a branch on the case contained by an enum explosion.
|
|
/// Performs the branching for a SIL 'switch_enum_addr'
|
|
/// instruction.
|
|
virtual void emitIndirectSwitch(IRGenFunction &IGF,
|
|
SILType T,
|
|
Address enumAddr,
|
|
ArrayRef<std::pair<EnumElementDecl*,
|
|
llvm::BasicBlock*>> dests,
|
|
llvm::BasicBlock *defaultDest) const = 0;
|
|
|
|
/// Emit code to extract the discriminator as an integer value.
|
|
/// Returns null if this is not supported by the enum implementation.
|
|
virtual llvm::Value *emitExtractDiscriminator(IRGenFunction &IGF,
|
|
Explosion &value) const {
|
|
return nullptr;
|
|
}
|
|
|
|
/// \group Loadable enum operations
|
|
|
|
/// Emit the construction sequence for an enum case into an explosion.
|
|
/// Corresponds to the SIL 'enum' instruction.
|
|
virtual void emitValueInjection(IRGenFunction &IGF,
|
|
EnumElementDecl *elt,
|
|
Explosion ¶ms,
|
|
Explosion &out) const = 0;
|
|
|
|
/// Return an i1 value that indicates whether the specified loadable enum
|
|
/// value holds the specified case. This is a light-weight form of a switch.
|
|
virtual llvm::Value *emitValueCaseTest(IRGenFunction &IGF,
|
|
Explosion &value,
|
|
EnumElementDecl *Case) const = 0;
|
|
|
|
/// Emit a branch on the case contained by an enum explosion.
|
|
/// Performs the branching for a SIL 'switch_enum' instruction.
|
|
virtual void emitValueSwitch(IRGenFunction &IGF,
|
|
Explosion &value,
|
|
ArrayRef<std::pair<EnumElementDecl*,
|
|
llvm::BasicBlock*>> dests,
|
|
llvm::BasicBlock *defaultDest) const = 0;
|
|
|
|
/// Project a case value out of an enum explosion. This does not check that
|
|
/// the explosion actually contains a value of the given case.
|
|
/// Performs the block argument binding for a SIL 'switch_enum'
|
|
/// instruction.
|
|
virtual void emitValueProject(IRGenFunction &IGF,
|
|
Explosion &inEnum,
|
|
EnumElementDecl *theCase,
|
|
Explosion &out) const = 0;
|
|
|
|
/// \group Delegated TypeInfo operations
|
|
|
|
virtual void getSchema(ExplosionSchema &schema) const = 0;
|
|
virtual void destroy(IRGenFunction &IGF, Address addr, SILType T) const = 0;
|
|
|
|
virtual bool isIndirectArgument() const {
|
|
return TIK < Loadable;
|
|
}
|
|
|
|
virtual void initializeFromParams(IRGenFunction &IGF, Explosion ¶ms,
|
|
Address dest, SILType T) const;
|
|
|
|
virtual void assignWithCopy(IRGenFunction &IGF, Address dest,
|
|
Address src, SILType T) const = 0;
|
|
virtual void assignWithTake(IRGenFunction &IGF, Address dest,
|
|
Address src, SILType T) const = 0;
|
|
virtual void initializeWithCopy(IRGenFunction &IGF, Address dest,
|
|
Address src, SILType T) const = 0;
|
|
virtual void initializeWithTake(IRGenFunction &IGF, Address dest,
|
|
Address src, SILType T) const = 0;
|
|
|
|
virtual void initializeMetadata(IRGenFunction &IGF,
|
|
llvm::Value *metadata,
|
|
llvm::Value *vwtable,
|
|
SILType T) const = 0;
|
|
|
|
virtual bool mayHaveExtraInhabitants(IRGenModule &IGM) const = 0;
|
|
|
|
virtual llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF,
|
|
Address src,
|
|
SILType T) const = 0;
|
|
virtual void storeExtraInhabitant(IRGenFunction &IGF,
|
|
llvm::Value *index,
|
|
Address dest,
|
|
SILType T) const = 0;
|
|
|
|
/// \group Delegated FixedTypeInfo operations
|
|
|
|
virtual unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const = 0;
|
|
|
|
virtual llvm::ConstantInt *
|
|
getFixedExtraInhabitantValue(IRGenModule &IGM,
|
|
unsigned bits,
|
|
unsigned index) const = 0;
|
|
|
|
virtual llvm::Value *
|
|
maskFixedExtraInhabitant(IRGenFunction &IGF,
|
|
llvm::Value *payload) const = 0;
|
|
|
|
/// \group Delegated LoadableTypeInfo operations
|
|
|
|
virtual unsigned getExplosionSize() const = 0;
|
|
virtual void loadAsCopy(IRGenFunction &IGF, Address addr,
|
|
Explosion &e) const = 0;
|
|
virtual void loadAsTake(IRGenFunction &IGF, Address addr,
|
|
Explosion &e) const = 0;
|
|
virtual void assign(IRGenFunction &IGF, Explosion &e,
|
|
Address addr) const = 0;
|
|
virtual void initialize(IRGenFunction &IGF, Explosion &e,
|
|
Address addr) const = 0;
|
|
virtual void reexplode(IRGenFunction &IGF, Explosion &src,
|
|
Explosion &dest) const = 0;
|
|
virtual void copy(IRGenFunction &IGF, Explosion &src,
|
|
Explosion &dest) const = 0;
|
|
virtual void consume(IRGenFunction &IGF, Explosion &src) const = 0;
|
|
virtual void fixLifetime(IRGenFunction &IGF, Explosion &src) const = 0;
|
|
virtual llvm::Value *packEnumPayload(IRGenFunction &IGF,
|
|
Explosion &in,
|
|
unsigned bitWidth,
|
|
unsigned offset) const = 0;
|
|
|
|
virtual void unpackEnumPayload(IRGenFunction &IGF,
|
|
llvm::Value *payload,
|
|
Explosion &dest,
|
|
unsigned offset) const = 0;
|
|
|
|
virtual bool needsPayloadSizeInMetadata() const = 0;
|
|
|
|
virtual llvm::Value *loadRefcountedPtr(IRGenFunction &IGF, SourceLoc loc,
|
|
Address addr) const;
|
|
|
|
private:
|
|
EnumImplStrategy(const EnumImplStrategy &) = delete;
|
|
EnumImplStrategy &operator=(const EnumImplStrategy &) = delete;
|
|
};
|
|
|
|
/// Get the EnumImplStrategy for an enum type.
|
|
const EnumImplStrategy &getEnumImplStrategy(IRGenModule &IGM, CanType ty);
|
|
const EnumImplStrategy &getEnumImplStrategy(IRGenModule &IGM, SILType ty);
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|