mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
This should eliminate pointless operations that get added to our inline cost itinerary. rdar://15567647 rdar://16762768 rdar://16832529 Swift SVN r17644
743 lines
22 KiB
C++
743 lines
22 KiB
C++
//===--- SILValue.h - Value base class for SIL ------------------*- 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 defines the SILValue class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_SILVALUE_H
|
|
#define SWIFT_SIL_SILVALUE_H
|
|
|
|
#include "swift/Basic/Range.h"
|
|
#include "swift/SIL/SILType.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/PointerUnion.h"
|
|
#include "llvm/ADT/Hashing.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
namespace swift {
|
|
class SILTypeList;
|
|
class Operand;
|
|
class SILValue;
|
|
class ValueBaseUseIterator;
|
|
class ValueUseIterator;
|
|
class SILInstruction;
|
|
|
|
enum class ValueKind {
|
|
#define VALUE(Id, Parent) Id,
|
|
#define VALUE_RANGE(Id, FirstId, LastId) \
|
|
First_##Id = FirstId, Last_##Id = LastId,
|
|
#include "swift/SIL/SILNodes.def"
|
|
};
|
|
|
|
/// ValueBase - This is the base class of the SIL value hierarchy, which
|
|
/// represents a runtime computed value. Things like SILInstruction derive
|
|
/// from this.
|
|
class alignas(8) ValueBase : public SILAllocated<ValueBase> {
|
|
PointerUnion<SILType, SILTypeList*> TypeOrTypeList;
|
|
Operand *FirstUse = nullptr;
|
|
friend class Operand;
|
|
friend class SILValue;
|
|
|
|
const ValueKind Kind;
|
|
|
|
ValueBase(const ValueBase &) = delete;
|
|
ValueBase &operator=(const ValueBase &) = delete;
|
|
|
|
protected:
|
|
ValueBase(ValueKind Kind, SILTypeList *TypeList = 0)
|
|
: TypeOrTypeList(TypeList), Kind(Kind) {}
|
|
ValueBase(ValueKind Kind, SILType Ty)
|
|
: TypeOrTypeList(Ty), Kind(Kind) {}
|
|
|
|
public:
|
|
~ValueBase() {
|
|
assert(use_empty() && "Cannot destroy a value that still has uses!");
|
|
}
|
|
|
|
|
|
ValueKind getKind() const { return Kind; }
|
|
|
|
ArrayRef<SILType> getTypes() const;
|
|
|
|
/// True if the "value" is actually a value that can be used by other
|
|
/// instructions.
|
|
bool hasValue() const { return !TypeOrTypeList.isNull(); }
|
|
|
|
SILType getType(unsigned i) const {
|
|
if (TypeOrTypeList.is<SILType>()) {
|
|
assert(i == 0);
|
|
return TypeOrTypeList.get<SILType>();
|
|
}
|
|
return getTypes()[i];
|
|
}
|
|
|
|
unsigned getNumTypes() const {
|
|
if (TypeOrTypeList.isNull())
|
|
return 0;
|
|
if (TypeOrTypeList.is<SILType>())
|
|
return 1;
|
|
return getTypes().size();
|
|
}
|
|
|
|
/// Replace every use of a result of this instruction with the corresponding
|
|
/// result from RHS. The method assumes that both instructions have the same
|
|
/// number of results. To replace just one result use
|
|
/// SILValue::replaceAllUsesWith.
|
|
void replaceAllUsesWith(ValueBase *RHS);
|
|
|
|
bool use_empty() const { return FirstUse == nullptr; }
|
|
inline ValueBaseUseIterator use_begin();
|
|
inline ValueBaseUseIterator use_end();
|
|
inline Range<ValueBaseUseIterator> getUses();
|
|
|
|
inline bool hasOneUse();
|
|
|
|
/// Pretty-print the value.
|
|
void dump() const;
|
|
void print(raw_ostream &OS) const;
|
|
|
|
/// Pretty-print the value in context, preceded by its operands (if the
|
|
/// value represents the result of an instruction) and followed by its
|
|
/// users.
|
|
void dumpInContext() const;
|
|
void printInContext(raw_ostream &OS) const;
|
|
|
|
static bool classof(const ValueBase *V) { return true; }
|
|
};
|
|
|
|
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
|
|
const ValueBase &V) {
|
|
V.print(OS);
|
|
return OS;
|
|
}
|
|
} // end namespace swift
|
|
|
|
namespace llvm {
|
|
// ValueBase* is always at least eight-byte aligned; make the three tag bits
|
|
// available through PointerLikeTypeTraits.
|
|
template<>
|
|
class PointerLikeTypeTraits<swift::ValueBase*> {
|
|
public:
|
|
static inline void *getAsVoidPointer(swift::ValueBase *I) {
|
|
return (void*)I;
|
|
}
|
|
static inline swift::ValueBase *getFromVoidPointer(void *P) {
|
|
return (swift::ValueBase*)P;
|
|
}
|
|
enum { NumLowBitsAvailable = 3 };
|
|
};
|
|
} // end namespace llvm
|
|
|
|
namespace swift {
|
|
|
|
enum {
|
|
/// The number of bits required to store a ResultNumber.
|
|
/// This is primarily here as a way to allow everything that
|
|
/// depends on it to be easily grepped.
|
|
ValueResultNumberBits = 2
|
|
};
|
|
|
|
/// SILValue - A SILValue is a use of a specific result of an ValueBase. As
|
|
/// such, it is a pair of the ValueBase and the result number being referenced.
|
|
class SILValue {
|
|
llvm::PointerIntPair<ValueBase*, ValueResultNumberBits> ValueAndResultNumber;
|
|
|
|
explicit SILValue(void *p) {
|
|
ValueAndResultNumber =
|
|
decltype(ValueAndResultNumber)::getFromOpaqueValue(p);
|
|
}
|
|
|
|
public:
|
|
SILValue(const ValueBase *V = 0, unsigned ResultNumber = 0)
|
|
: ValueAndResultNumber((ValueBase*)V, ResultNumber) {
|
|
assert(ResultNumber == getResultNumber() && "Overflow");
|
|
}
|
|
|
|
ValueBase *getDef() const {
|
|
return ValueAndResultNumber.getPointer();
|
|
}
|
|
ValueBase *operator->() const { return getDef(); }
|
|
ValueBase &operator*() const { return *getDef(); }
|
|
unsigned getResultNumber() const { return ValueAndResultNumber.getInt(); }
|
|
|
|
SILType getType() const {
|
|
return getDef()->getType(getResultNumber());
|
|
}
|
|
|
|
// Comparison.
|
|
bool operator==(SILValue RHS) const {
|
|
return ValueAndResultNumber == RHS.ValueAndResultNumber;
|
|
}
|
|
bool operator!=(SILValue RHS) const { return !(*this == RHS); }
|
|
// Ordering (for std::map).
|
|
bool operator<(SILValue RHS) const {
|
|
return ValueAndResultNumber.getOpaqueValue() <
|
|
RHS.ValueAndResultNumber.getOpaqueValue();
|
|
}
|
|
|
|
inline bool use_empty() const;
|
|
inline ValueUseIterator use_begin();
|
|
inline ValueUseIterator use_end();
|
|
inline Range<ValueUseIterator> getUses();
|
|
|
|
inline bool hasOneUse();
|
|
|
|
// Return the underlying SILValue after stripping off all casts from the
|
|
// current SILValue.
|
|
SILValue stripCasts();
|
|
|
|
// Return the underlying SILValue after stripping off all casts and
|
|
// address projection instructions.
|
|
//
|
|
// An address projection instruction is one of one of ref_element_addr,
|
|
// struct_element_addr, tuple_element_addr.
|
|
SILValue stripAddressProjections();
|
|
|
|
// Return the underlying SILValue after stripping off all aggregate projection
|
|
// instructions.
|
|
//
|
|
// An aggregate projection instruction is either a struct_extract or a
|
|
// tuple_extract instruction.
|
|
SILValue stripAggregateProjections();
|
|
|
|
// Return the underlying SILValue after stripping off all indexing
|
|
// instructions.
|
|
//
|
|
// An indexing inst is either index_addr or index_raw_pointer.
|
|
SILValue stripIndexingInsts();
|
|
|
|
void replaceAllUsesWith(SILValue V);
|
|
|
|
void dump() const;
|
|
void print(raw_ostream &os) const;
|
|
|
|
// Check validity.
|
|
bool isValid() const { return getDef() != nullptr; }
|
|
explicit operator bool() const { return getDef() != nullptr; }
|
|
|
|
// Use as a pointer-like type.
|
|
void *getOpaqueValue() const {
|
|
return ValueAndResultNumber.getOpaqueValue();
|
|
}
|
|
static SILValue getFromOpaqueValue(void *p) {
|
|
return SILValue(p);
|
|
}
|
|
|
|
enum {
|
|
NumLowBitsAvailable =
|
|
llvm::PointerLikeTypeTraits<decltype(ValueAndResultNumber)>::
|
|
NumLowBitsAvailable
|
|
};
|
|
};
|
|
|
|
/// A formal SIL reference to a value, suitable for use as a stored
|
|
/// operand.
|
|
class Operand {
|
|
/// The value used as this operand.
|
|
SILValue TheValue;
|
|
|
|
/// The next operand in the use-chain. Note that the chain holds
|
|
/// every use of the current ValueBase, not just those of the
|
|
/// designated result.
|
|
Operand *NextUse = nullptr;
|
|
|
|
/// A back-pointer in the use-chain, required for fast patching
|
|
/// of use-chains.
|
|
Operand **Back = nullptr;
|
|
|
|
/// The owner of this operand.
|
|
/// FIXME: this could be space-compressed.
|
|
SILInstruction *Owner;
|
|
|
|
Operand(SILInstruction *owner) : Owner(owner) {}
|
|
Operand(SILInstruction *owner, SILValue theValue)
|
|
: TheValue(theValue), Owner(owner) {
|
|
insertIntoCurrent();
|
|
}
|
|
template<unsigned N> friend class FixedOperandList;
|
|
template<unsigned N> friend class TailAllocatedOperandList;
|
|
|
|
public:
|
|
// Operands are not copyable.
|
|
Operand(const Operand &use) = delete;
|
|
Operand &operator=(const Operand &use) = delete;
|
|
|
|
/// Return the current value being used by this operand.
|
|
SILValue get() const { return TheValue; }
|
|
|
|
/// Set the current value being used by this operand.
|
|
void set(SILValue newValue) {
|
|
// It's probably not worth optimizing for the case of switching
|
|
// operands on a single value.
|
|
removeFromCurrent();
|
|
assert(cast<ValueBase>(Owner) != newValue.getDef() &&
|
|
"Cannot add a value as an operand of the instruction that defines it!");
|
|
TheValue = newValue;
|
|
insertIntoCurrent();
|
|
}
|
|
|
|
/// Swap the given operand with the current one.
|
|
void swap(Operand &Op) {
|
|
SILValue OtherV = Op.get();
|
|
Op.set(get());
|
|
set(OtherV);
|
|
}
|
|
|
|
/// \brief Remove this use of the operand.
|
|
void drop() {
|
|
removeFromCurrent();
|
|
TheValue = SILValue();
|
|
NextUse = nullptr;
|
|
Back = nullptr;
|
|
Owner = nullptr;
|
|
}
|
|
|
|
~Operand() {
|
|
removeFromCurrent();
|
|
}
|
|
|
|
/// Return the user that owns this use.
|
|
SILInstruction *getUser() { return Owner; }
|
|
const SILInstruction *getUser() const { return Owner; }
|
|
|
|
/// getOperandNumber - Return which operand this is in the operand list of the
|
|
/// using instruction.
|
|
unsigned getOperandNumber() const;
|
|
|
|
private:
|
|
void removeFromCurrent() {
|
|
if (!Back) return;
|
|
*Back = NextUse;
|
|
if (NextUse) NextUse->Back = Back;
|
|
}
|
|
|
|
void insertIntoCurrent() {
|
|
Back = &TheValue->FirstUse;
|
|
NextUse = TheValue->FirstUse;
|
|
if (NextUse) NextUse->Back = &NextUse;
|
|
TheValue->FirstUse = this;
|
|
}
|
|
|
|
friend class ValueBaseUseIterator;
|
|
friend class ValueUseIterator;
|
|
template <unsigned N> friend class FixedOperandList;
|
|
template <unsigned N> friend class TailAllocatedOperandList;
|
|
};
|
|
|
|
/// A class which adapts an array of Operands into an array of Values.
|
|
///
|
|
/// The intent is that this should basically act exactly like
|
|
/// ArrayRef except projecting away the Operand-ness.
|
|
class OperandValueArrayRef {
|
|
ArrayRef<Operand> Operands;
|
|
public:
|
|
explicit OperandValueArrayRef(ArrayRef<Operand> operands)
|
|
: Operands(operands) {}
|
|
|
|
/// A simple iterator adapter.
|
|
class iterator {
|
|
const Operand *Ptr;
|
|
public:
|
|
iterator(const Operand *ptr) : Ptr(ptr) {}
|
|
SILValue operator*() const { assert(Ptr); return Ptr->get(); }
|
|
SILValue operator->() const { return operator*(); }
|
|
iterator &operator++() { ++Ptr; return *this; }
|
|
iterator operator++(int) { iterator copy = *this; ++Ptr; return copy; }
|
|
|
|
friend bool operator==(iterator lhs, iterator rhs) {
|
|
return lhs.Ptr == rhs.Ptr;
|
|
}
|
|
friend bool operator!=(iterator lhs, iterator rhs) {
|
|
return lhs.Ptr != rhs.Ptr;
|
|
}
|
|
};
|
|
|
|
iterator begin() const { return iterator(Operands.begin()); }
|
|
iterator end() const { return iterator(Operands.end()); }
|
|
size_t size() const { return Operands.size(); }
|
|
bool empty() const { return Operands.empty(); }
|
|
|
|
SILValue front() const { return Operands.front().get(); }
|
|
SILValue back() const { return Operands.back().get(); }
|
|
|
|
SILValue operator[](unsigned i) const { return Operands[i].get(); }
|
|
OperandValueArrayRef slice(unsigned begin, unsigned length) const {
|
|
return OperandValueArrayRef(Operands.slice(begin, length));
|
|
}
|
|
OperandValueArrayRef slice(unsigned begin) const {
|
|
return OperandValueArrayRef(Operands.slice(begin));
|
|
}
|
|
};
|
|
|
|
/// An iterator over all uses of a ValueBase.
|
|
class ValueBaseUseIterator : public std::iterator<std::forward_iterator_tag,
|
|
Operand*, ptrdiff_t> {
|
|
Operand *Cur;
|
|
public:
|
|
ValueBaseUseIterator() = default;
|
|
explicit ValueBaseUseIterator(Operand *cur) : Cur(cur) {}
|
|
Operand *operator->() const { return Cur; }
|
|
Operand *operator*() const { return Cur; }
|
|
|
|
SILInstruction *getUser() const {
|
|
return Cur->getUser();
|
|
}
|
|
|
|
ValueBaseUseIterator &operator++() {
|
|
assert(Cur && "incrementing past end()!");
|
|
Cur = Cur->NextUse;
|
|
return *this;
|
|
}
|
|
|
|
ValueBaseUseIterator operator++(int unused) {
|
|
ValueBaseUseIterator copy = *this;
|
|
++*this;
|
|
return copy;
|
|
}
|
|
|
|
friend bool operator==(ValueBaseUseIterator lhs,
|
|
ValueBaseUseIterator rhs) {
|
|
return lhs.Cur == rhs.Cur;
|
|
}
|
|
friend bool operator!=(ValueBaseUseIterator lhs,
|
|
ValueBaseUseIterator rhs) {
|
|
return !(lhs == rhs);
|
|
}
|
|
};
|
|
inline ValueBaseUseIterator ValueBase::use_begin() {
|
|
return ValueBaseUseIterator(FirstUse);
|
|
}
|
|
inline ValueBaseUseIterator ValueBase::use_end() {
|
|
return ValueBaseUseIterator(nullptr);
|
|
}
|
|
inline Range<ValueBaseUseIterator> ValueBase::getUses() {
|
|
return { use_begin(), use_end() };
|
|
}
|
|
inline bool ValueBase::hasOneUse() {
|
|
auto I = use_begin(), E = use_end();
|
|
if (I == E) return false;
|
|
return ++I == E;
|
|
}
|
|
|
|
|
|
/// An iterator over all uses of a specific result of a ValueBase.
|
|
class ValueUseIterator : public std::iterator<std::forward_iterator_tag,
|
|
Operand*, ptrdiff_t>
|
|
{
|
|
llvm::PointerIntPair<Operand*, ValueResultNumberBits> CurAndResultNumber;
|
|
public:
|
|
ValueUseIterator() = default;
|
|
explicit ValueUseIterator(Operand *cur, unsigned resultNumber) {
|
|
// Skip past uses with different result numbers.
|
|
while (cur && cur->get().getResultNumber() != resultNumber)
|
|
cur = cur->NextUse;
|
|
|
|
CurAndResultNumber.setPointerAndInt(cur, resultNumber);
|
|
}
|
|
|
|
Operand *operator*() const { return CurAndResultNumber.getPointer(); }
|
|
Operand *operator->() const { return operator*(); }
|
|
|
|
SILInstruction *getUser() const {
|
|
return CurAndResultNumber.getPointer()->getUser();
|
|
}
|
|
|
|
ValueUseIterator &operator++() {
|
|
Operand *next = CurAndResultNumber.getPointer();
|
|
assert(next && "incrementing past end()!");
|
|
|
|
// Skip past uses with different result numbers.
|
|
while ((next = next->NextUse)) {
|
|
if (next->get().getResultNumber() == CurAndResultNumber.getInt())
|
|
break;
|
|
}
|
|
|
|
CurAndResultNumber.setPointer(next);
|
|
return *this;
|
|
}
|
|
|
|
ValueUseIterator operator++(int unused) {
|
|
ValueUseIterator copy = *this;
|
|
++*this;
|
|
return copy;
|
|
}
|
|
|
|
friend bool operator==(ValueUseIterator lhs, ValueUseIterator rhs) {
|
|
return lhs.CurAndResultNumber.getPointer()
|
|
== rhs.CurAndResultNumber.getPointer();
|
|
}
|
|
friend bool operator!=(ValueUseIterator lhs, ValueUseIterator rhs) {
|
|
return !(lhs == rhs);
|
|
}
|
|
};
|
|
inline ValueUseIterator SILValue::use_begin() {
|
|
return ValueUseIterator((*this)->FirstUse, getResultNumber());
|
|
}
|
|
inline ValueUseIterator SILValue::use_end() {
|
|
return ValueUseIterator(nullptr, 0);
|
|
}
|
|
inline Range<ValueUseIterator> SILValue::getUses() {
|
|
return { use_begin(), use_end() };
|
|
}
|
|
inline bool SILValue::use_empty() const {
|
|
SILValue *mthis = const_cast<SILValue*>(this);
|
|
return mthis->use_begin() == mthis->use_end();
|
|
}
|
|
inline bool SILValue::hasOneUse() {
|
|
auto I = use_begin(), E = use_end();
|
|
if (I == E) return false;
|
|
return ++I == E;
|
|
}
|
|
|
|
|
|
/// A constant-size list of the operands of an instruction.
|
|
template <unsigned N> class FixedOperandList {
|
|
Operand Buffer[N];
|
|
|
|
FixedOperandList(const FixedOperandList &) = delete;
|
|
FixedOperandList &operator=(const FixedOperandList &) = delete;
|
|
|
|
public:
|
|
template <class... T> FixedOperandList(SILInstruction *user, T&&...args)
|
|
: Buffer{ { user, std::forward<T>(args) }... } {
|
|
static_assert(sizeof...(args) == N, "wrong number of initializers");
|
|
}
|
|
|
|
/// Returns the full list of operands.
|
|
MutableArrayRef<Operand> asArray() {
|
|
return MutableArrayRef<Operand>(Buffer, N);
|
|
}
|
|
ArrayRef<Operand> asArray() const {
|
|
return ArrayRef<Operand>(Buffer, N);
|
|
}
|
|
|
|
/// Returns the full list of operand values.
|
|
OperandValueArrayRef asValueArray() const {
|
|
return OperandValueArrayRef(asArray());
|
|
}
|
|
|
|
/// Indexes into the full list of operands.
|
|
Operand &operator[](unsigned i) { return asArray()[i]; }
|
|
const Operand &operator[](unsigned i) const { return asArray()[i]; }
|
|
};
|
|
|
|
/// An operator list with a fixed number of known operands
|
|
/// (possibly zero) and a dynamically-determined set of extra
|
|
/// operands (also possibly zero). The number of dynamic operands
|
|
/// is permanently set at initialization time.
|
|
///
|
|
/// 'N' is the number of static operands.
|
|
///
|
|
/// This class assumes that a number of bytes of extra storage have
|
|
/// been allocated immediately after it. This means that this class
|
|
/// must always be the final data member in a class.
|
|
template <unsigned N> class TailAllocatedOperandList {
|
|
unsigned NumExtra;
|
|
Operand Buffer[N];
|
|
|
|
TailAllocatedOperandList(const TailAllocatedOperandList &) = delete;
|
|
TailAllocatedOperandList &operator=(const TailAllocatedOperandList &) =delete;
|
|
|
|
public:
|
|
/// Given the number of dynamic operands required, returns the
|
|
/// number of bytes of extra storage to allocate.
|
|
static size_t getExtraSize(unsigned numExtra) {
|
|
return sizeof(Operand) * numExtra;
|
|
}
|
|
|
|
/// Initialize this operand list.
|
|
///
|
|
/// The dynamic operands are actually out of order: logically they
|
|
/// will placed after the fixed operands, not before them. But
|
|
/// the variadic arguments have to come last.
|
|
template <class... T>
|
|
TailAllocatedOperandList(SILInstruction *user,
|
|
ArrayRef<SILValue> dynamicArgs,
|
|
T&&... fixedArgs)
|
|
: NumExtra(dynamicArgs.size()),
|
|
Buffer{ { user, std::forward<T>(fixedArgs) }... } {
|
|
static_assert(sizeof...(fixedArgs) == N, "wrong number of initializers");
|
|
|
|
Operand *dynamicSlot = Buffer + N;
|
|
for (auto value : dynamicArgs) {
|
|
new (dynamicSlot++) Operand(user, value);
|
|
}
|
|
}
|
|
|
|
~TailAllocatedOperandList() {
|
|
for (auto &op : getDynamicAsArray()) {
|
|
op.~Operand();
|
|
}
|
|
}
|
|
|
|
/// Returns the full list of operands.
|
|
MutableArrayRef<Operand> asArray() {
|
|
return MutableArrayRef<Operand>(Buffer, N+NumExtra);
|
|
}
|
|
ArrayRef<Operand> asArray() const {
|
|
return ArrayRef<Operand>(Buffer, N+NumExtra);
|
|
}
|
|
|
|
/// Returns the full list of operand values.
|
|
OperandValueArrayRef asValueArray() const {
|
|
return OperandValueArrayRef(asArray());
|
|
}
|
|
|
|
/// Returns the list of the dynamic operands.
|
|
MutableArrayRef<Operand> getDynamicAsArray() {
|
|
return MutableArrayRef<Operand>(Buffer+N, NumExtra);
|
|
}
|
|
ArrayRef<Operand> getDynamicAsArray() const {
|
|
return ArrayRef<Operand>(Buffer+N, NumExtra);
|
|
}
|
|
|
|
/// Returns the list of the dynamic operand values.
|
|
OperandValueArrayRef getDynamicValuesAsArray() const {
|
|
return OperandValueArrayRef(getDynamicAsArray());
|
|
}
|
|
|
|
unsigned size() const { return N+NumExtra; }
|
|
|
|
/// Indexes into the full list of operands.
|
|
Operand &operator[](unsigned i) { return asArray()[i]; }
|
|
const Operand &operator[](unsigned i) const { return asArray()[i]; }
|
|
};
|
|
|
|
/// A specialization of TailAllocatedOperandList for zero static operands.
|
|
template<> class TailAllocatedOperandList<0> {
|
|
unsigned NumExtra;
|
|
union { // suppress value semantics
|
|
Operand Buffer[1];
|
|
};
|
|
|
|
TailAllocatedOperandList(const TailAllocatedOperandList &) = delete;
|
|
TailAllocatedOperandList &operator=(const TailAllocatedOperandList &) =delete;
|
|
|
|
public:
|
|
static size_t getExtraSize(unsigned numExtra) {
|
|
return sizeof(Operand) * (numExtra > 0 ? numExtra - 1 : 0);
|
|
}
|
|
|
|
TailAllocatedOperandList(SILInstruction *user, ArrayRef<SILValue> dynamicArgs)
|
|
: NumExtra(dynamicArgs.size()) {
|
|
|
|
Operand *dynamicSlot = Buffer;
|
|
for (auto value : dynamicArgs) {
|
|
new (dynamicSlot++) Operand(user, value);
|
|
}
|
|
}
|
|
|
|
~TailAllocatedOperandList() {
|
|
for (auto &op : getDynamicAsArray()) {
|
|
op.~Operand();
|
|
}
|
|
}
|
|
|
|
/// Returns the full list of operands.
|
|
MutableArrayRef<Operand> asArray() {
|
|
return MutableArrayRef<Operand>(Buffer, NumExtra);
|
|
}
|
|
ArrayRef<Operand> asArray() const {
|
|
return ArrayRef<Operand>(Buffer, NumExtra);
|
|
}
|
|
|
|
/// Returns the full list of operand values.
|
|
OperandValueArrayRef asValueArray() const {
|
|
return OperandValueArrayRef(asArray());
|
|
}
|
|
|
|
/// Returns the list of the dynamic operands.
|
|
MutableArrayRef<Operand> getDynamicAsArray() {
|
|
return MutableArrayRef<Operand>(Buffer, NumExtra);
|
|
}
|
|
ArrayRef<Operand> getDynamicAsArray() const {
|
|
return ArrayRef<Operand>(Buffer, NumExtra);
|
|
}
|
|
|
|
/// Returns the list of the dynamic operand values.
|
|
OperandValueArrayRef getDynamicValuesAsArray() const {
|
|
return OperandValueArrayRef(getDynamicAsArray());
|
|
}
|
|
|
|
unsigned size() const { return NumExtra; }
|
|
|
|
/// Indexes into the full list of operands.
|
|
Operand &operator[](unsigned i) { return asArray()[i]; }
|
|
const Operand &operator[](unsigned i) const { return asArray()[i]; }
|
|
};
|
|
|
|
/// SILValue hashes just like a pointer.
|
|
static inline llvm::hash_code hash_value(SILValue V) {
|
|
return llvm::hash_value(V.getDef());
|
|
}
|
|
|
|
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SILValue V) {
|
|
OS << "(" << V.getResultNumber() << "): ";
|
|
V.print(OS);
|
|
return OS;
|
|
}
|
|
|
|
} // end namespace swift
|
|
|
|
|
|
namespace llvm {
|
|
// A SILValue casts like a ValueBase*.
|
|
template<> struct simplify_type<const ::swift::SILValue> {
|
|
typedef ::swift::ValueBase *SimpleType;
|
|
static SimpleType getSimplifiedValue(::swift::SILValue Val) {
|
|
return Val.getDef();
|
|
}
|
|
};
|
|
template<> struct simplify_type< ::swift::SILValue>
|
|
: public simplify_type<const ::swift::SILValue> {};
|
|
|
|
// Values hash just like pointers.
|
|
template<> struct DenseMapInfo<swift::SILValue> {
|
|
static swift::SILValue getEmptyKey() {
|
|
return swift::SILValue::getFromOpaqueValue(
|
|
llvm::DenseMapInfo<void*>::getEmptyKey());
|
|
}
|
|
static swift::SILValue getTombstoneKey() {
|
|
return swift::SILValue::getFromOpaqueValue(
|
|
llvm::DenseMapInfo<void*>::getTombstoneKey());
|
|
}
|
|
static unsigned getHashValue(swift::SILValue V) {
|
|
auto ResultNumHash =
|
|
DenseMapInfo<unsigned>::getHashValue(V.getResultNumber());
|
|
auto ValueBaseHash =
|
|
DenseMapInfo<swift::ValueBase*>::getHashValue(V.getDef());
|
|
return hash_combine(ResultNumHash, ValueBaseHash);
|
|
}
|
|
static bool isEqual(swift::SILValue LHS, swift::SILValue RHS) {
|
|
return LHS == RHS;
|
|
}
|
|
};
|
|
|
|
// SILValue is a PointerLikeType.
|
|
template<> class PointerLikeTypeTraits<::swift::SILValue> {
|
|
using SILValue = ::swift::SILValue;
|
|
public:
|
|
static void *getAsVoidPointer(SILValue v) {
|
|
return v.getOpaqueValue();
|
|
}
|
|
static SILValue getFromVoidPointer(void *p) {
|
|
return SILValue::getFromOpaqueValue(p);
|
|
}
|
|
|
|
enum { NumLowBitsAvailable = swift::SILValue::NumLowBitsAvailable };
|
|
};
|
|
|
|
} // end namespace llvm
|
|
|
|
#endif
|