mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The field's ordinal value is used by the Projection abstraction, which is
the basis of efficiently comparing and sorting access paths in SIL. It must
be cached before it is used by any SIL passes, including the verifier, or it
causes widespread quadratic complexity.
Fixes <rdar://problem/50353228> Swift compile time regression with optimizations enabled
In production code, a file that was taking 40 minutes to compile now
takes 1 minute, with more than half of the time in LLVM.
Here's a short script that reproduces the problem. It used to take 30s
and now takes 0.06s:
// swift genlazyinit.swift > lazyinit.sil
// sil-opt ./lazyinit.sil --access-enforcement-opts
var NumProperties = 300
print("""
sil_stage canonical
import Builtin
import Swift
import SwiftShims
public class LazyProperties {
""")
for i in 0..<NumProperties {
print("""
// public lazy var i\(i): Int { get set }
@_hasStorage @_hasInitialValue final var __lazy_storage__i\(i): Int? { get set }
""")
}
print("""
}
// LazyProperties.init()
sil @$s4lazy14LazyPropertiesCACycfc : $@convention(method) (@owned LazyProperties) -> @owned LazyProperties {
bb0(%0 : $LazyProperties):
%enum = enum $Optional<Int>, #Optional.none!enumelt
""")
for i in 0..<NumProperties {
let adr = (i*4) + 2
let access = adr + 1
print("""
%\(adr) = ref_element_addr %0 : $LazyProperties, #LazyProperties.__lazy_storage__i\(i)
%\(access) = begin_access [modify] [dynamic] %\(adr) : $*Optional<Int>
store %enum to %\(access) : $*Optional<Int>
end_access %\(access) : $*Optional<Int>
""")
}
print("""
return %0 : $LazyProperties
} // end sil function '$s4lazy14LazyPropertiesCACycfc'
""")
576 lines
21 KiB
C++
576 lines
21 KiB
C++
//===--- SILNode.h - Node base class for SIL --------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the SILNode class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_SILNODE_H
|
|
#define SWIFT_SIL_SILNODE_H
|
|
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/ADT/DenseMapInfo.h"
|
|
#include "swift/Basic/InlineBitfield.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include <type_traits>
|
|
|
|
namespace swift {
|
|
|
|
class SILBasicBlock;
|
|
class SILFunction;
|
|
class SILInstruction;
|
|
class SILModule;
|
|
class SingleValueInstruction;
|
|
class ValueBase;
|
|
|
|
/// An enumeration which contains values for all the nodes in SILNodes.def.
|
|
/// Other enumerators, like ValueKind and SILInstructionKind, ultimately
|
|
/// take their values from this enumerator.
|
|
enum class SILNodeKind {
|
|
#define NODE(ID, PARENT) \
|
|
ID,
|
|
#define NODE_RANGE(ID, FIRST, LAST) \
|
|
First_##ID = FIRST, \
|
|
Last_##ID = LAST,
|
|
#include "swift/SIL/SILNodes.def"
|
|
};
|
|
|
|
enum { NumSILNodeKindBits =
|
|
countBitsUsed(static_cast<unsigned>(SILNodeKind::Last_SILNode)) };
|
|
|
|
enum class SILInstructionKind : std::underlying_type<SILNodeKind>::type;
|
|
|
|
/// A SILNode is a node in the use-def graph of a SILFunction. It is
|
|
/// either an instruction or a defined value which can be used by an
|
|
/// instruction. A defined value may be an instruction result, a basic
|
|
/// block argument, or the special 'undef' value.
|
|
///
|
|
/// The 'node' intuition is slightly imprecise because a single instruction
|
|
/// may be composed of multiple SILNodes: one for the instruction itself
|
|
/// and one for each value it produces. When an instruction kind always
|
|
/// produces exactly one value, the cast machinery (isa, cast, and dyn_cast)
|
|
/// works to make both nodes appear to be the same object: there is a value
|
|
/// kind exactly equal to the instruction kind and the value node can be
|
|
/// directly cast to the instruction's class. When an instruction kind
|
|
/// never produces values, it has no corresponding value kind, and it is
|
|
/// a compile-time error to attempt to cast a value node to the instruction
|
|
/// class. When an instruction kind can have multiple values (not yet
|
|
/// implemented), its value nodes have a different kind from the
|
|
/// instruction kind and it is a static error to attempt to cast a value
|
|
/// node to the instruction kind.
|
|
///
|
|
/// Another way of interpreting SILNode is that there is a SILNode for
|
|
/// everything that can be numbered in SIL assembly (plus 'undef', which
|
|
/// is not conventionally numbered). Instructions without results are
|
|
/// still numbered in SIL in order to describe the users lists of an
|
|
/// instruction or argument. Instructions with multiple results are
|
|
/// numbered using their first result.
|
|
///
|
|
/// SILNode is a base class of both SILInstruction and ValueBase.
|
|
/// Because there can be multiple SILNodes within a single instruction
|
|
/// object, some care must be taken when working with SILNode pointers.
|
|
/// These precautions only apply to SILNode* and not its subclasses.
|
|
///
|
|
/// - There may have multiple SILNode* values that refer to the same
|
|
/// instruction. Data structures and algorithms that rely on uniqueness of a
|
|
/// SILNode* should generally make sure that they're working with the
|
|
/// representative SILNode*; see getRepresentativeSILNodeInObject().
|
|
///
|
|
/// - Do not use builtin C++ casts to downcast a SILNode*. A static_cast
|
|
/// from SILNode* to SILInstruction* only works if the referenced
|
|
/// SILNode is the base subobject of the object's SILInstruction
|
|
/// subobject. If the SILNode is actually the base subobject of a
|
|
/// ValueBase subobject, the cast will yield a corrupted value.
|
|
/// Always use the LLVM casts (cast<>, dyn_cast<>, etc.) instead.
|
|
class alignas(8) SILNode {
|
|
public:
|
|
enum { NumVOKindBits = 3 };
|
|
enum { NumStoreOwnershipQualifierBits = 2 };
|
|
enum { NumLoadOwnershipQualifierBits = 2 };
|
|
enum { NumAssignOwnershipQualifierBits = 2 };
|
|
enum { NumSILAccessKindBits = 2 };
|
|
enum { NumSILAccessEnforcementBits = 2 };
|
|
|
|
protected:
|
|
union { uint64_t OpaqueBits;
|
|
|
|
SWIFT_INLINE_BITFIELD_BASE(SILNode, bitmax(NumSILNodeKindBits,8)+1+1,
|
|
Kind : bitmax(NumSILNodeKindBits,8),
|
|
StorageLoc : 1,
|
|
IsRepresentativeNode : 1
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD_EMPTY(ValueBase, SILNode);
|
|
|
|
SWIFT_INLINE_BITFIELD(SILArgument, ValueBase, NumVOKindBits,
|
|
VOKind : NumVOKindBits
|
|
);
|
|
|
|
// No MultipleValueInstructionResult subclass needs inline bits right now,
|
|
// therefore let's naturally align and size the Index for speed.
|
|
SWIFT_INLINE_BITFIELD_FULL(MultipleValueInstructionResult, ValueBase,
|
|
NumVOKindBits+32,
|
|
VOKind : NumVOKindBits,
|
|
: NumPadBits,
|
|
Index : 32
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD_EMPTY(SILInstruction, SILNode);
|
|
|
|
// Special handling for UnaryInstructionWithTypeDependentOperandsBase
|
|
SWIFT_INLINE_BITFIELD(IBWTO, SILNode, 64-NumSILNodeBits,
|
|
// DO NOT allocate bits at the front!
|
|
// IBWTO is a template, and templates must allocate bits from back to front
|
|
// so that "normal" subclassing can allocate bits from front to back.
|
|
// If you update this, then you must update the IBWTO_BITFIELD macros.
|
|
|
|
/*pad*/ : 32-NumSILNodeBits,
|
|
|
|
// Total number of operands of this instruction.
|
|
// It is number of type dependent operands + 1.
|
|
NumOperands : 32;
|
|
template<SILInstructionKind Kind, typename, typename, typename...>
|
|
friend class InstructionBaseWithTrailingOperands
|
|
);
|
|
|
|
#define IBWTO_BITFIELD(T, U, C, ...) \
|
|
SWIFT_INLINE_BITFIELD_TEMPLATE(T, U, (C), 32, __VA_ARGS__)
|
|
#define IBWTO_BITFIELD_EMPTY(T, U) \
|
|
SWIFT_INLINE_BITFIELD_EMPTY(T, U)
|
|
|
|
#define UIWTDOB_BITFIELD(T, U, C, ...) \
|
|
IBWTO_BITFIELD(T, U, (C), __VA_ARGS__)
|
|
#define UIWTDOB_BITFIELD_EMPTY(T, U) \
|
|
IBWTO_BITFIELD_EMPTY(T, U)
|
|
|
|
SWIFT_INLINE_BITFIELD_EMPTY(SingleValueInstruction, SILInstruction);
|
|
SWIFT_INLINE_BITFIELD_EMPTY(DeallocationInst, SILInstruction);
|
|
SWIFT_INLINE_BITFIELD_EMPTY(LiteralInst, SingleValueInstruction);
|
|
SWIFT_INLINE_BITFIELD_EMPTY(AllocationInst, SingleValueInstruction);
|
|
|
|
// Ensure that StructInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(StructInst, SingleValueInstruction);
|
|
|
|
// Ensure that TupleInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(TupleInst, SingleValueInstruction);
|
|
|
|
IBWTO_BITFIELD(ObjectInst, SingleValueInstruction,
|
|
32-NumSingleValueInstructionBits,
|
|
NumBaseElements : 32-NumSingleValueInstructionBits
|
|
);
|
|
|
|
IBWTO_BITFIELD(SelectEnumInstBase, SingleValueInstruction, 1,
|
|
HasDefault : 1
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD_FULL(IntegerLiteralInst, LiteralInst, 32,
|
|
: NumPadBits,
|
|
numBits : 32
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD_FULL(FloatLiteralInst, LiteralInst, 32,
|
|
: NumPadBits,
|
|
numBits : 32
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD_FULL(StringLiteralInst, LiteralInst, 2+32,
|
|
TheEncoding : 2,
|
|
: NumPadBits,
|
|
Length : 32
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD(DeallocRefInst, DeallocationInst, 1,
|
|
OnStack : 1
|
|
);
|
|
|
|
// Ensure that AllocBoxInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(AllocBoxInst, AllocationInst);
|
|
// Ensure that AllocExistentialBoxInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(AllocExistentialBoxInst, AllocationInst);
|
|
SWIFT_INLINE_BITFIELD_FULL(AllocStackInst, AllocationInst,
|
|
64-NumAllocationInstBits,
|
|
NumOperands : 32-NumAllocationInstBits,
|
|
VarInfo : 32
|
|
);
|
|
IBWTO_BITFIELD(AllocRefInstBase, AllocationInst, 32-NumAllocationInstBits,
|
|
ObjC : 1,
|
|
OnStack : 1,
|
|
NumTailTypes : 32-1-1-NumAllocationInstBits
|
|
);
|
|
static_assert(32-1-1-NumAllocationInstBits >= 16, "Reconsider bitfield use?");
|
|
|
|
UIWTDOB_BITFIELD_EMPTY(AllocValueBufferInst, AllocationInst);
|
|
|
|
// TODO: Sort the following in SILNodes.def order
|
|
|
|
SWIFT_INLINE_BITFIELD_EMPTY(NonValueInstruction, SILInstruction);
|
|
SWIFT_INLINE_BITFIELD(RefCountingInst, NonValueInstruction, 1,
|
|
atomicity : 1
|
|
);
|
|
|
|
// Ensure that BindMemoryInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(BindMemoryInst, NonValueInstruction);
|
|
|
|
// Ensure that MarkFunctionEscapeInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(MarkFunctionEscapeInst, NonValueInstruction);
|
|
|
|
// Ensure that MetatypeInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(MetatypeInst, SingleValueInstruction);
|
|
|
|
SWIFT_INLINE_BITFIELD(CopyAddrInst, NonValueInstruction, 1+1,
|
|
/// IsTakeOfSrc - True if ownership will be taken from the value at the
|
|
/// source memory location.
|
|
IsTakeOfSrc : 1,
|
|
|
|
/// IsInitializationOfDest - True if this is the initialization of the
|
|
/// uninitialized destination memory location.
|
|
IsInitializationOfDest : 1
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD(LoadReferenceInstBaseT, NonValueInstruction, 1,
|
|
IsTake : 1;
|
|
template<SILInstructionKind K>
|
|
friend class LoadReferenceInstBase
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD(StoreReferenceInstBaseT, NonValueInstruction, 1,
|
|
IsInitializationOfDest : 1;
|
|
template<SILInstructionKind K>
|
|
friend class StoreReferenceInstBase
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD(BeginAccessInst, SingleValueInstruction,
|
|
NumSILAccessKindBits+NumSILAccessEnforcementBits
|
|
+ 1 + 1,
|
|
AccessKind : NumSILAccessKindBits,
|
|
Enforcement : NumSILAccessEnforcementBits,
|
|
NoNestedConflict : 1,
|
|
FromBuiltin : 1
|
|
);
|
|
SWIFT_INLINE_BITFIELD(BeginUnpairedAccessInst, NonValueInstruction,
|
|
NumSILAccessKindBits + NumSILAccessEnforcementBits
|
|
+ 1 + 1,
|
|
AccessKind : NumSILAccessKindBits,
|
|
Enforcement : NumSILAccessEnforcementBits,
|
|
NoNestedConflict : 1,
|
|
FromBuiltin : 1);
|
|
|
|
SWIFT_INLINE_BITFIELD(EndAccessInst, NonValueInstruction, 1,
|
|
Aborting : 1
|
|
);
|
|
SWIFT_INLINE_BITFIELD(EndUnpairedAccessInst, NonValueInstruction,
|
|
NumSILAccessEnforcementBits + 1 + 1,
|
|
Enforcement : NumSILAccessEnforcementBits,
|
|
Aborting : 1,
|
|
FromBuiltin : 1);
|
|
|
|
SWIFT_INLINE_BITFIELD(StoreInst, NonValueInstruction,
|
|
NumStoreOwnershipQualifierBits,
|
|
OwnershipQualifier : NumStoreOwnershipQualifierBits
|
|
);
|
|
SWIFT_INLINE_BITFIELD(LoadInst, SingleValueInstruction,
|
|
NumLoadOwnershipQualifierBits,
|
|
OwnershipQualifier : NumLoadOwnershipQualifierBits
|
|
);
|
|
SWIFT_INLINE_BITFIELD(AssignInst, NonValueInstruction,
|
|
NumAssignOwnershipQualifierBits,
|
|
OwnershipQualifier : NumAssignOwnershipQualifierBits
|
|
);
|
|
SWIFT_INLINE_BITFIELD(AssignByDelegateInst, NonValueInstruction,
|
|
NumAssignOwnershipQualifierBits,
|
|
OwnershipQualifier : NumAssignOwnershipQualifierBits
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD(UncheckedOwnershipConversionInst,SingleValueInstruction,
|
|
NumVOKindBits,
|
|
Kind : NumVOKindBits
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD_FULL(TupleExtractInst, SingleValueInstruction, 32,
|
|
: NumPadBits,
|
|
FieldNo : 32
|
|
);
|
|
SWIFT_INLINE_BITFIELD_FULL(TupleElementAddrInst, SingleValueInstruction, 32,
|
|
: NumPadBits,
|
|
FieldNo : 32
|
|
);
|
|
|
|
SWIFT_INLINE_BITFIELD_FULL(FieldIndexCacheBase, SingleValueInstruction, 32,
|
|
: NumPadBits,
|
|
FieldIndex : 32);
|
|
|
|
SWIFT_INLINE_BITFIELD_EMPTY(MethodInst, SingleValueInstruction);
|
|
// Ensure that WitnessMethodInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(WitnessMethodInst, MethodInst);
|
|
UIWTDOB_BITFIELD_EMPTY(ObjCMethodInst, MethodInst);
|
|
|
|
SWIFT_INLINE_BITFIELD_EMPTY(ConversionInst, SingleValueInstruction);
|
|
SWIFT_INLINE_BITFIELD(PointerToAddressInst, ConversionInst, 1+1,
|
|
IsStrict : 1,
|
|
IsInvariant : 1
|
|
);
|
|
|
|
UIWTDOB_BITFIELD(ConvertFunctionInst, ConversionInst, 1,
|
|
WithoutActuallyEscaping : 1);
|
|
UIWTDOB_BITFIELD_EMPTY(PointerToThinFunctionInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(UnconditionalCheckedCastInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(UpcastInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(UncheckedRefCastInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(UncheckedAddrCastInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(UncheckedTrivialBitCastInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(UncheckedBitwiseCastInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(ThinToThickFunctionInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(UnconditionalCheckedCastValueInst, ConversionInst);
|
|
UIWTDOB_BITFIELD_EMPTY(InitExistentialAddrInst, SingleValueInstruction);
|
|
UIWTDOB_BITFIELD_EMPTY(InitExistentialValueInst, SingleValueInstruction);
|
|
UIWTDOB_BITFIELD_EMPTY(InitExistentialRefInst, SingleValueInstruction);
|
|
UIWTDOB_BITFIELD_EMPTY(InitExistentialMetatypeInst, SingleValueInstruction);
|
|
|
|
SWIFT_INLINE_BITFIELD_EMPTY(TermInst, SILInstruction);
|
|
UIWTDOB_BITFIELD_EMPTY(CheckedCastBranchInst, SingleValueInstruction);
|
|
UIWTDOB_BITFIELD_EMPTY(CheckedCastValueBranchInst, SingleValueInstruction);
|
|
|
|
// Ensure that BranchInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(BranchInst, TermInst);
|
|
// Ensure that YieldInst bitfield does not overflow.
|
|
IBWTO_BITFIELD_EMPTY(YieldInst, TermInst);
|
|
IBWTO_BITFIELD(CondBranchInst, TermInst, 32-NumTermInstBits,
|
|
NumTrueArgs : 32-NumTermInstBits
|
|
);
|
|
IBWTO_BITFIELD(SwitchValueInst, TermInst, 1,
|
|
HasDefault : 1
|
|
);
|
|
SWIFT_INLINE_BITFIELD_FULL(SwitchEnumInstBase, TermInst, 1+32,
|
|
HasDefault : 1,
|
|
: NumPadBits,
|
|
NumCases : 32
|
|
);
|
|
|
|
} Bits;
|
|
|
|
enum class SILNodeStorageLocation : uint8_t { Value, Instruction };
|
|
|
|
enum class IsRepresentative : bool {
|
|
No = false,
|
|
Yes = true,
|
|
};
|
|
|
|
private:
|
|
|
|
SILNodeStorageLocation getStorageLoc() const {
|
|
return SILNodeStorageLocation(Bits.SILNode.StorageLoc);
|
|
}
|
|
|
|
const SILNode *getRepresentativeSILNodeSlowPath() const;
|
|
|
|
protected:
|
|
SILNode(SILNodeKind kind, SILNodeStorageLocation storageLoc,
|
|
IsRepresentative isRepresentative) {
|
|
Bits.OpaqueBits = 0;
|
|
Bits.SILNode.Kind = unsigned(kind);
|
|
Bits.SILNode.StorageLoc = unsigned(storageLoc);
|
|
Bits.SILNode.IsRepresentativeNode = unsigned(isRepresentative);
|
|
}
|
|
|
|
public:
|
|
/// Does the given kind of node inherit from multiple multiple SILNode base
|
|
/// classes?
|
|
///
|
|
/// This enables one to know if their is a diamond in the inheritence
|
|
/// hierarchy for this SILNode.
|
|
static bool hasMultipleSILNodeBases(SILNodeKind kind) {
|
|
// Currently only SingleValueInstructions. Note that multi-result
|
|
// instructions shouldn't return true for this.
|
|
return kind >= SILNodeKind::First_SingleValueInstruction &&
|
|
kind <= SILNodeKind::Last_SingleValueInstruction;
|
|
}
|
|
|
|
/// Is this SILNode the representative SILNode subobject in this object?
|
|
bool isRepresentativeSILNodeInObject() const {
|
|
return Bits.SILNode.IsRepresentativeNode;
|
|
}
|
|
|
|
/// Return a pointer to the representative SILNode subobject in this object.
|
|
SILNode *getRepresentativeSILNodeInObject() {
|
|
if (isRepresentativeSILNodeInObject())
|
|
return this;
|
|
return const_cast<SILNode *>(getRepresentativeSILNodeSlowPath());
|
|
}
|
|
|
|
const SILNode *getRepresentativeSILNodeInObject() const {
|
|
if (isRepresentativeSILNodeInObject())
|
|
return this;
|
|
return getRepresentativeSILNodeSlowPath();
|
|
}
|
|
|
|
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
|
SILNodeKind getKind() const {
|
|
return SILNodeKind(Bits.SILNode.Kind);
|
|
}
|
|
|
|
/// Return the SILNodeKind of this node's representative SILNode.
|
|
SILNodeKind getKindOfRepresentativeSILNodeInObject() const {
|
|
return getRepresentativeSILNodeInObject()->getKind();
|
|
}
|
|
|
|
/// If this is a SILArgument or a SILInstruction get its parent basic block,
|
|
/// otherwise return null.
|
|
SILBasicBlock *getParentBlock() const;
|
|
|
|
/// If this is a SILArgument or a SILInstruction get its parent function,
|
|
/// otherwise return null.
|
|
SILFunction *getFunction() const;
|
|
|
|
/// If this is a SILArgument or a SILInstruction get its parent module,
|
|
/// otherwise return null.
|
|
SILModule *getModule() const;
|
|
|
|
/// Pretty-print the node. If the node is an instruction, the output
|
|
/// will be valid SIL assembly; otherwise, it will be an arbitrary
|
|
/// format suitable for debugging.
|
|
void print(raw_ostream &OS) const;
|
|
void dump() const;
|
|
|
|
/// Pretty-print the node in context, preceded by its operands (if the
|
|
/// value represents the result of an instruction) and followed by its
|
|
/// users.
|
|
void printInContext(raw_ostream &OS) const;
|
|
void dumpInContext() const;
|
|
|
|
// Cast to SingleValueInstruction. This is an implementation detail
|
|
// of the cast machinery. At a high level, all you need to know is to
|
|
// never use static_cast to downcast a SILNode.
|
|
SingleValueInstruction *castToSingleValueInstruction();
|
|
const SingleValueInstruction *castToSingleValueInstruction() const {
|
|
return const_cast<SILNode*>(this)->castToSingleValueInstruction();
|
|
}
|
|
|
|
static bool classof(const SILNode *node) {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
|
|
const SILNode &node) {
|
|
node.print(OS);
|
|
return OS;
|
|
}
|
|
|
|
template <class To> struct cast_sil_node_is_unambiguous {
|
|
// The only ambiguity right now is between the value and instruction
|
|
// nodes on a SingleValueInstruction.
|
|
static constexpr bool value =
|
|
// If the destination type isn't a subclass of ValueBase or
|
|
// SILInstruction, there's no ambiguity.
|
|
(!std::is_base_of<SILInstruction, To>::value &&
|
|
!std::is_base_of<ValueBase, To>::value)
|
|
|
|
// If the destination type is a proper subclass of ValueBase
|
|
// that isn't a subclass of SILInstruction, there's no ambiguity.
|
|
|| (std::is_base_of<ValueBase, To>::value &&
|
|
!std::is_same<ValueBase, To>::value &&
|
|
!std::is_base_of<SILInstruction, To>::value)
|
|
|
|
// If the destination type is a proper subclass of SILInstruction
|
|
// that isn't a subclass of ValueBase, there's no ambiguity.
|
|
|| (std::is_base_of<SILInstruction, To>::value &&
|
|
!std::is_same<SILInstruction, To>::value &&
|
|
!std::is_base_of<ValueBase, To>::value);
|
|
};
|
|
|
|
template <class To,
|
|
bool IsSingleValueInstruction =
|
|
std::is_base_of<SingleValueInstruction, To>::value,
|
|
bool IsKnownUnambiguous =
|
|
cast_sil_node_is_unambiguous<To>::value>
|
|
struct cast_sil_node;
|
|
|
|
// If all complete objects of the destination type are known to only
|
|
// contain a single node, we can just use a static_cast.
|
|
template <class To>
|
|
struct cast_sil_node<To, /*single value*/ false, /*unambiguous*/ true> {
|
|
static To *doit(SILNode *node) {
|
|
return &static_cast<To&>(*node);
|
|
}
|
|
};
|
|
|
|
// If we're casting to a subclass of SingleValueInstruction, we don't
|
|
// need to dynamically check whether the node is an SVI. In fact,
|
|
// we can't, because the static_cast will be ambiguous.
|
|
template <class To>
|
|
struct cast_sil_node<To, /*single value*/ true, /*unambiguous*/ false> {
|
|
static To *doit(SILNode *node) {
|
|
auto svi = node->castToSingleValueInstruction();
|
|
return &static_cast<To&>(*svi);
|
|
}
|
|
};
|
|
|
|
// Otherwise, we need to dynamically check which case we're in.
|
|
template <class To>
|
|
struct cast_sil_node<To, /*single value*/ false, /*unambiguous*/ false> {
|
|
static To *doit(SILNode *node) {
|
|
// If the node isn't dynamically a SingleValueInstruction, then this
|
|
// is indeed the SILNode subobject that's statically observable in To.
|
|
if (!SILNode::hasMultipleSILNodeBases(node->getKind())) {
|
|
return &static_cast<To&>(*node);
|
|
}
|
|
|
|
auto svi = node->castToSingleValueInstruction();
|
|
return &static_cast<To&>(*svi);
|
|
}
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
namespace llvm {
|
|
|
|
/// Completely take over cast<>'ing from SILNode*. A static_cast to
|
|
/// ValueBase* or SILInstruction* can be quite wrong.
|
|
template <class To>
|
|
struct cast_convert_val<To, swift::SILNode*, swift::SILNode*> {
|
|
using ret_type = typename cast_retty<To, swift::SILNode*>::ret_type;
|
|
static ret_type doit(swift::SILNode *node) {
|
|
return swift::cast_sil_node<To>::doit(node);
|
|
}
|
|
};
|
|
template <class To>
|
|
struct cast_convert_val<To, const swift::SILNode *, const swift::SILNode *> {
|
|
using ret_type = typename cast_retty<To, const swift::SILNode*>::ret_type;
|
|
static ret_type doit(const swift::SILNode *node) {
|
|
return swift::cast_sil_node<To>::doit(const_cast<swift::SILNode*>(node));
|
|
}
|
|
};
|
|
|
|
// We don't support casting from SILNode references yet.
|
|
template <class To, class From>
|
|
struct cast_convert_val<To, swift::SILNode, From>;
|
|
template <class To, class From>
|
|
struct cast_convert_val<To, const swift::SILNode, From>;
|
|
|
|
/// ValueBase * is always at least eight-byte aligned; make the three tag bits
|
|
/// available through PointerLikeTypeTraits.
|
|
template<>
|
|
struct PointerLikeTypeTraits<swift::SILNode *> {
|
|
public:
|
|
static inline void *getAsVoidPointer(swift::SILNode *I) {
|
|
return (void*)I;
|
|
}
|
|
static inline swift::SILNode *getFromVoidPointer(void *P) {
|
|
return (swift::SILNode *)P;
|
|
}
|
|
enum { NumLowBitsAvailable = 3 };
|
|
};
|
|
|
|
} // end namespace llvm
|
|
|
|
#endif
|