blob: fc305944db91681c8e2000e13970d5ce382fbae7 [file] [log] [blame]
//===--- SILInstruction.cpp - Instructions for SIL code -------------------===//
//
// 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 high-level SILInstruction classes used for SIL code.
//
//===----------------------------------------------------------------------===//
#include "swift/SIL/SILInstruction.h"
#include "swift/Basic/type_traits.h"
#include "swift/Basic/Unicode.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILCloner.h"
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/Basic/AssertImplements.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/SILModule.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/ErrorHandling.h"
using namespace swift;
using namespace Lowering;
//===----------------------------------------------------------------------===//
// Instruction-specific properties on SILValue
//===----------------------------------------------------------------------===//
SILLocation SILInstruction::getLoc() const { return Location.getLocation(); }
const SILDebugScope *SILInstruction::getDebugScope() const {
return Location.getScope();
}
void SILInstruction::setDebugScope(const SILDebugScope *DS) {
if (getDebugScope() && getDebugScope()->InlinedCallSite)
assert(DS->InlinedCallSite && "throwing away inlined scope info");
assert(DS->getParentFunction() == getFunction() &&
"scope belongs to different function");
Location = SILDebugLocation(getLoc(), DS);
}
//===----------------------------------------------------------------------===//
// ilist_traits<SILInstruction> Implementation
//===----------------------------------------------------------------------===//
// The trait object is embedded into a basic block. Use dirty hacks to
// reconstruct the BB from the 'self' pointer of the trait.
SILBasicBlock *llvm::ilist_traits<SILInstruction>::getContainingBlock() {
size_t Offset(
size_t(&((SILBasicBlock *)nullptr->*SILBasicBlock::getSublistAccess())));
iplist<SILInstruction> *Anchor(static_cast<iplist<SILInstruction> *>(this));
return reinterpret_cast<SILBasicBlock *>(reinterpret_cast<char *>(Anchor) -
Offset);
}
void llvm::ilist_traits<SILInstruction>::addNodeToList(SILInstruction *I) {
assert(I->ParentBB == nullptr && "Already in a list!");
I->ParentBB = getContainingBlock();
}
void llvm::ilist_traits<SILInstruction>::removeNodeFromList(SILInstruction *I) {
// When an instruction is removed from a BB, clear the parent pointer.
assert(I->ParentBB && "Not in a list!");
I->ParentBB = nullptr;
}
void llvm::ilist_traits<SILInstruction>::
transferNodesFromList(llvm::ilist_traits<SILInstruction> &L2,
instr_iterator first, instr_iterator last) {
// If transferring instructions within the same basic block, no reason to
// update their parent pointers.
SILBasicBlock *ThisParent = getContainingBlock();
if (ThisParent == L2.getContainingBlock()) return;
// Update the parent fields in the instructions.
for (; first != last; ++first) {
SWIFT_FUNC_STAT_NAMED("sil");
first->ParentBB = ThisParent;
}
}
//===----------------------------------------------------------------------===//
// SILInstruction Implementation
//===----------------------------------------------------------------------===//
// Assert that all subclasses of ValueBase implement classof.
#define NODE(CLASS, PARENT) \
ASSERT_IMPLEMENTS_STATIC(CLASS, PARENT, classof, bool(const SILNode*));
#include "swift/SIL/SILNodes.def"
SILFunction *SILInstruction::getFunction() {
return getParent()->getParent();
}
const SILFunction *SILInstruction::getFunction() const {
return getParent()->getParent();
}
SILModule &SILInstruction::getModule() const {
return getFunction()->getModule();
}
/// eraseFromParent - This method unlinks 'self' from the containing basic
/// block and deletes it.
///
void SILInstruction::eraseFromParent() {
#ifndef NDEBUG
for (auto result : getResults()) {
assert(result->use_empty() && "Uses of SILInstruction remain at deletion.");
}
#endif
getParent()->erase(this);
}
void SILInstruction::moveFront(SILBasicBlock *Block) {
getParent()->remove(this);
Block->push_front(this);
}
/// Unlink this instruction from its current basic block and insert it into
/// the basic block that Later lives in, right before Later.
void SILInstruction::moveBefore(SILInstruction *Later) {
if (this == Later)
return;
getParent()->remove(this);
Later->getParent()->insert(Later, this);
}
/// Unlink this instruction from its current basic block and insert it into
/// the basic block that Earlier lives in, right after Earlier.
void SILInstruction::moveAfter(SILInstruction *Earlier) {
// Since MovePos is an instruction, we know that there is always a valid
// iterator after it.
auto Later = std::next(SILBasicBlock::iterator(Earlier));
moveBefore(&*Later);
}
void SILInstruction::dropAllReferences() {
MutableArrayRef<Operand> PossiblyDeadOps = getAllOperands();
for (auto OpI = PossiblyDeadOps.begin(),
OpE = PossiblyDeadOps.end(); OpI != OpE; ++OpI) {
OpI->drop();
}
// If we have a function ref inst, we need to especially drop its function
// argument so that it gets a proper ref decrement.
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(this)) {
if (!FRI->getReferencedFunction())
return;
FRI->dropReferencedFunction();
return;
}
// If we have a KeyPathInst, drop its pattern reference so that we can
// decrement refcounts on referenced functions.
if (auto *KPI = dyn_cast<KeyPathInst>(this)) {
if (!KPI->hasPattern())
return;
KPI->dropReferencedPattern();
return;
}
}
namespace {
class AllResultsAccessor
: public SILInstructionVisitor<AllResultsAccessor,
SILInstructionResultArray> {
public:
// Make sure we hit a linker error if we ever miss an instruction.
#define INST(ID, NAME) SILInstructionResultArray visit##ID(ID *I);
#define NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
SILInstructionResultArray visit##ID(ID *I) { \
return SILInstructionResultArray(); \
}
#define SINGLE_VALUE_INST(ID, TEXTUALNAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
SILInstructionResultArray visit##ID(ID *I) { \
return SILInstructionResultArray( \
static_cast<SingleValueInstruction *>(I)); \
}
#define MULTIPLE_VALUE_INST(ID, TEXTUALNAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
SILInstructionResultArray visit##ID(ID *I) { \
return SILInstructionResultArray(I->getAllResults()); \
}
#include "swift/SIL/SILNodes.def"
};
} // end anonymous namespace
SILInstructionResultArray SILInstruction::getResultsImpl() const {
return AllResultsAccessor().visit(const_cast<SILInstruction *>(this));
}
// Initialize the static members of SILInstruction.
int SILInstruction::NumCreatedInstructions = 0;
int SILInstruction::NumDeletedInstructions = 0;
/// Map a SILInstruction name to its SILInstructionKind.
SILInstructionKind swift::getSILInstructionKind(StringRef name) {
#define FULL_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
if (name == #NAME) \
return SILInstructionKind::ID;
#include "swift/SIL/SILNodes.def"
#ifdef NDEBUG
llvm::errs() << "Unknown SIL instruction name\n";
abort();
#endif
llvm_unreachable("Unknown SIL insruction name");
}
/// Map SILInstructionKind to a corresponding SILInstruction name.
StringRef swift::getSILInstructionName(SILInstructionKind kind) {
switch (kind) {
#define FULL_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
case SILInstructionKind::ID: \
return #NAME;
#include "swift/SIL/SILNodes.def"
}
llvm_unreachable("bad kind");
}
void SILInstruction::replaceAllUsesOfAllResultsWithUndef() {
for (auto result : getResults()) {
result->replaceAllUsesWithUndef();
}
}
void SILInstruction::replaceAllUsesPairwiseWith(SILInstruction *other) {
auto results = getResults();
// If we don't have any results, fast-path out without asking the other
// instruction for its results.
if (results.empty()) {
assert(other->getResults().empty());
return;
}
// Replace values with the corresponding values of the other instruction.
auto otherResults = other->getResults();
assert(results.size() == otherResults.size());
for (auto i : indices(results)) {
results[i]->replaceAllUsesWith(otherResults[i]);
}
}
void SILInstruction::replaceAllUsesPairwiseWith(
const llvm::SmallVectorImpl<SILValue> &NewValues) {
auto Results = getResults();
// If we don't have any results, fast-path out without asking the other
// instruction for its results.
if (Results.empty()) {
assert(NewValues.empty());
return;
}
// Replace values with the corresponding values of the list. Make sure they
// are all the same type.
assert(Results.size() == NewValues.size());
for (unsigned i : indices(Results)) {
assert(Results[i]->getType() == NewValues[i]->getType() &&
"Can only replace results with new values of the same type");
Results[i]->replaceAllUsesWith(NewValues[i]);
}
}
namespace {
class InstructionDestroyer
: public SILInstructionVisitor<InstructionDestroyer> {
public:
#define INST(CLASS, PARENT) \
void visit##CLASS(CLASS *I) { I->~CLASS(); }
#include "swift/SIL/SILNodes.def"
};
} // end anonymous namespace
void SILInstruction::destroy(SILInstruction *I) {
InstructionDestroyer().visit(I);
}
namespace {
/// Given a pair of instructions that are already known to have the same kind,
/// type, and operands check any special state in the two instructions that
/// could disrupt equality.
class InstructionIdentityComparer :
public SILInstructionVisitor<InstructionIdentityComparer, bool> {
public:
InstructionIdentityComparer(const SILInstruction *L) : LHS(L) { }
/// Make sure we only process instructions we know how to process.
bool visitSILInstruction(const SILInstruction *RHS) {
return false;
}
bool visitInjectEnumAddrInst(const InjectEnumAddrInst *RHS) {
auto *X = cast<InjectEnumAddrInst>(LHS);
return X->getElement() == RHS->getElement();
}
bool visitDestroyAddrInst(const DestroyAddrInst *RHS) {
return true;
}
bool visitReleaseValueInst(const ReleaseValueInst *RHS) {
return true;
}
bool visitReleaseValueAddrInst(const ReleaseValueAddrInst *RHS) {
return true;
}
bool visitRetainValueInst(const RetainValueInst *RHS) {
return true;
}
bool visitRetainValueAddrInst(const RetainValueAddrInst *RHS) {
return true;
}
bool visitDeallocStackInst(const DeallocStackInst *RHS) {
return true;
}
bool visitAllocStackInst(const AllocStackInst *RHS) {
return true;
}
bool visitDeallocBoxInst(const DeallocBoxInst *RHS) {
return true;
}
bool visitAllocBoxInst(const AllocBoxInst *RHS) {
return true;
}
bool visitDeallocRefInst(const DeallocRefInst *RHS) {
return true;
}
bool visitDeallocPartialRefInst(const DeallocPartialRefInst *RHS) {
return true;
}
bool visitAllocRefInst(const AllocRefInst *RHS) {
auto *LHSInst = cast<AllocRefInst>(LHS);
auto LHSTypes = LHSInst->getTailAllocatedTypes();
auto RHSTypes = RHS->getTailAllocatedTypes();
unsigned NumTypes = LHSTypes.size();
assert(NumTypes == RHSTypes.size());
for (unsigned Idx = 0; Idx < NumTypes; ++Idx) {
if (LHSTypes[Idx] != RHSTypes[Idx])
return false;
}
return true;
}
bool visitAllocRefDynamicInst(const AllocRefDynamicInst *RHS) {
return true;
}
bool visitProjectValueBufferInst(const ProjectValueBufferInst *RHS) {
auto *X = cast<ProjectValueBufferInst>(LHS);
return X->getValueType() == RHS->getValueType();
}
bool visitProjectBoxInst(const ProjectBoxInst *RHS) {
return true;
}
bool visitProjectExistentialBoxInst(const ProjectExistentialBoxInst *RHS) {
return true;
}
bool visitBeginAccessInst(const BeginAccessInst *right) {
auto left = cast<BeginAccessInst>(LHS);
return left->getAccessKind() == right->getAccessKind()
&& left->getEnforcement() == right->getEnforcement()
&& left->hasNoNestedConflict() == right->hasNoNestedConflict()
&& left->isFromBuiltin() == right->isFromBuiltin();
}
bool visitEndAccessInst(const EndAccessInst *right) {
auto left = cast<EndAccessInst>(LHS);
return left->isAborting() == right->isAborting();
}
bool visitBeginUnpairedAccessInst(const BeginUnpairedAccessInst *right) {
auto left = cast<BeginUnpairedAccessInst>(LHS);
return left->getAccessKind() == right->getAccessKind()
&& left->getEnforcement() == right->getEnforcement()
&& left->hasNoNestedConflict() == right->hasNoNestedConflict()
&& left->isFromBuiltin() == right->isFromBuiltin();
}
bool visitEndUnpairedAccessInst(const EndUnpairedAccessInst *right) {
auto left = cast<EndUnpairedAccessInst>(LHS);
return left->getEnforcement() == right->getEnforcement()
&& left->isAborting() == right->isAborting()
&& left->isFromBuiltin() == right->isFromBuiltin();
}
bool visitStrongReleaseInst(const StrongReleaseInst *RHS) {
return true;
}
bool visitStrongRetainInst(const StrongRetainInst *RHS) {
return true;
}
bool visitLoadInst(const LoadInst *RHS) {
auto LHSQualifier = cast<LoadInst>(LHS)->getOwnershipQualifier();
return LHSQualifier == RHS->getOwnershipQualifier();
}
bool visitLoadBorrowInst(const LoadBorrowInst *RHS) { return true; }
bool visitEndBorrowInst(const EndBorrowInst *RHS) { return true; }
bool visitBeginBorrowInst(const BeginBorrowInst *BBI) { return true; }
bool visitStoreBorrowInst(const StoreBorrowInst *RHS) {
auto *X = cast<StoreBorrowInst>(LHS);
return X->getSrc() == RHS->getSrc() && X->getDest() == RHS->getDest();
}
bool visitStoreInst(const StoreInst *RHS) {
auto *X = cast<StoreInst>(LHS);
return X->getSrc() == RHS->getSrc() && X->getDest() == RHS->getDest() &&
X->getOwnershipQualifier() == RHS->getOwnershipQualifier();
}
bool visitBindMemoryInst(const BindMemoryInst *RHS) {
auto *X = cast<BindMemoryInst>(LHS);
return X->getBoundType() == RHS->getBoundType();
}
bool visitFunctionRefInst(const FunctionRefInst *RHS) {
auto *X = cast<FunctionRefInst>(LHS);
return X->getReferencedFunction() == RHS->getReferencedFunction();
}
bool visitDynamicFunctionRefInst(const DynamicFunctionRefInst *RHS) {
auto *X = cast<DynamicFunctionRefInst>(LHS);
return X->getReferencedFunction() == RHS->getReferencedFunction();
}
bool visitPreviousDynamicFunctionRefInst(
const PreviousDynamicFunctionRefInst *RHS) {
auto *X = cast<PreviousDynamicFunctionRefInst>(LHS);
return X->getReferencedFunction() == RHS->getReferencedFunction();
}
bool visitAllocGlobalInst(const AllocGlobalInst *RHS) {
auto *X = cast<AllocGlobalInst>(LHS);
return X->getReferencedGlobal() == RHS->getReferencedGlobal();
}
bool visitGlobalAddrInst(const GlobalAddrInst *RHS) {
auto *X = cast<GlobalAddrInst>(LHS);
return X->getReferencedGlobal() == RHS->getReferencedGlobal();
}
bool visitIntegerLiteralInst(const IntegerLiteralInst *RHS) {
APInt X = cast<IntegerLiteralInst>(LHS)->getValue();
APInt Y = RHS->getValue();
return X.getBitWidth() == Y.getBitWidth() &&
X == Y;
}
bool visitFloatLiteralInst(const FloatLiteralInst *RHS) {
// Avoid floating point comparison issues by doing a bitwise comparison.
APInt X = cast<FloatLiteralInst>(LHS)->getBits();
APInt Y = RHS->getBits();
return X.getBitWidth() == Y.getBitWidth() &&
X == Y;
}
bool visitStringLiteralInst(const StringLiteralInst *RHS) {
auto LHS_ = cast<StringLiteralInst>(LHS);
return LHS_->getEncoding() == RHS->getEncoding()
&& LHS_->getValue().equals(RHS->getValue());
}
bool visitStructInst(const StructInst *RHS) {
// We have already checked the operands. Make sure that the StructDecls
// match up.
StructDecl *S1 = cast<StructInst>(LHS)->getStructDecl();
return S1 == RHS->getStructDecl();
}
bool visitStructExtractInst(const StructExtractInst *RHS) {
// We have already checked that the operands of our struct_extracts
// match. Thus we need to check the field/struct decl which are not
// operands.
auto *X = cast<StructExtractInst>(LHS);
if (X->getStructDecl() != RHS->getStructDecl())
return false;
if (X->getField() != RHS->getField())
return false;
return true;
}
bool visitRefElementAddrInst(RefElementAddrInst *RHS) {
auto *X = cast<RefElementAddrInst>(LHS);
if (X->getField() != RHS->getField())
return false;
if (X->getOperand() != RHS->getOperand())
return false;
return true;
}
bool visitRefTailAddrInst(RefTailAddrInst *RHS) {
auto *X = cast<RefTailAddrInst>(LHS);
return X->getTailType() == RHS->getTailType();
}
bool visitStructElementAddrInst(const StructElementAddrInst *RHS) {
// We have already checked that the operands of our struct_element_addrs
// match. Thus we only need to check the field/struct decl which are not
// operands.
auto *X = cast<StructElementAddrInst>(LHS);
if (X->getStructDecl() != RHS->getStructDecl())
return false;
if (X->getField() != RHS->getField())
return false;
return true;
}
bool visitTupleInst(const TupleInst *RHS) {
// We have already checked the operands. Make sure that the tuple types
// match up.
TupleType *TT1 = cast<TupleInst>(LHS)->getTupleType();
return TT1 == RHS->getTupleType();
}
bool visitTupleExtractInst(const TupleExtractInst *RHS) {
// We have already checked that the operands match. Thus we only need to
// check the field no and tuple type which are not represented as operands.
auto *X = cast<TupleExtractInst>(LHS);
if (X->getTupleType() != RHS->getTupleType())
return false;
if (X->getFieldNo() != RHS->getFieldNo())
return false;
return true;
}
bool visitTupleElementAddrInst(const TupleElementAddrInst *RHS) {
// We have already checked that the operands match. Thus we only need to
// check the field no and tuple type which are not represented as operands.
auto *X = cast<TupleElementAddrInst>(LHS);
if (X->getTupleType() != RHS->getTupleType())
return false;
if (X->getFieldNo() != RHS->getFieldNo())
return false;
return true;
}
bool visitMetatypeInst(const MetatypeInst *RHS) {
// We have already compared the operands/types, so we should have equality
// at this point.
return true;
}
bool visitValueMetatypeInst(const ValueMetatypeInst *RHS) {
// We have already compared the operands/types, so we should have equality
// at this point.
return true;
}
bool visitExistentialMetatypeInst(const ExistentialMetatypeInst *RHS) {
// We have already compared the operands/types, so we should have equality
// at this point.
return true;
}
bool visitIndexRawPointerInst(IndexRawPointerInst *RHS) {
// We have already compared the operands/types, so we should have equality
// at this point.
return true;
}
bool visitIndexAddrInst(IndexAddrInst *RHS) {
// We have already compared the operands/types, so we should have equality
// at this point.
return true;
}
bool visitTailAddrInst(TailAddrInst *RHS) {
auto *X = cast<TailAddrInst>(LHS);
return X->getTailType() == RHS->getTailType();
}
bool visitCondFailInst(CondFailInst *RHS) {
// We have already compared the operands/types, so we should have equality
// at this point.
return true;
}
bool visitApplyInst(ApplyInst *RHS) {
auto *X = cast<ApplyInst>(LHS);
return X->getSubstitutionMap() == RHS->getSubstitutionMap();
}
bool visitBuiltinInst(BuiltinInst *RHS) {
auto *X = cast<BuiltinInst>(LHS);
if (X->getName() != RHS->getName())
return false;
return X->getSubstitutions() == RHS->getSubstitutions();
}
bool visitEnumInst(EnumInst *RHS) {
// We already checked operands and types. Only thing we need to check is
// that the element is the same.
auto *X = cast<EnumInst>(LHS);
return X->getElement() == RHS->getElement();
}
bool visitUncheckedEnumDataInst(UncheckedEnumDataInst *RHS) {
// We already checked operands and types. Only thing we need to check is
// that the element is the same.
auto *X = cast<UncheckedEnumDataInst>(LHS);
return X->getElement() == RHS->getElement();
}
bool visitSelectEnumInstBase(const SelectEnumInstBase *RHS) {
// Check that the instructions match cases in the same order.
auto *X = cast<SelectEnumInstBase>(LHS);
if (X->getNumCases() != RHS->getNumCases())
return false;
if (X->hasDefault() != RHS->hasDefault())
return false;
for (unsigned i = 0, e = X->getNumCases(); i < e; ++i) {
if (X->getCase(i).first != RHS->getCase(i).first)
return false;
}
return true;
}
bool visitSelectEnumInst(const SelectEnumInst *RHS) {
return visitSelectEnumInstBase(RHS);
}
bool visitSelectEnumAddrInst(const SelectEnumAddrInst *RHS) {
return visitSelectEnumInstBase(RHS);
}
bool visitSelectValueInst(const SelectValueInst *RHS) {
// Check that the instructions match cases in the same order.
auto *X = cast<SelectValueInst>(LHS);
if (X->getNumCases() != RHS->getNumCases())
return false;
if (X->hasDefault() != RHS->hasDefault())
return false;
for (unsigned i = 0, e = X->getNumCases(); i < e; ++i) {
if (X->getCase(i).first != RHS->getCase(i).first)
return false;
if (X->getCase(i).second != RHS->getCase(i).second)
return false;
}
return true;
}
// Conversion instructions.
// All of these just return true as they have already had their
// operands and types checked
bool visitUncheckedRefCastInst(UncheckedRefCastInst *RHS) {
return true;
}
bool visitUncheckedAddrCastInst(UncheckedAddrCastInst *RHS) {
return true;
}
bool visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *RHS) {
return true;
}
bool visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *RHS) {
return true;
}
bool visitUpcastInst(UpcastInst *RHS) {
return true;
}
bool visitAddressToPointerInst(AddressToPointerInst *RHS) {
return true;
}
bool visitPointerToAddressInst(PointerToAddressInst *RHS) {
return cast<PointerToAddressInst>(LHS)->isStrict() == RHS->isStrict();
}
bool visitRefToRawPointerInst(RefToRawPointerInst *RHS) {
return true;
}
bool visitRawPointerToRefInst(RawPointerToRefInst *RHS) {
return true;
}
#define LOADABLE_REF_STORAGE_HELPER(Name) \
bool visit##Name##ToRefInst(Name##ToRefInst *RHS) { return true; } \
bool visitRefTo##Name##Inst(RefTo##Name##Inst *RHS) { return true; }
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
LOADABLE_REF_STORAGE_HELPER(Name) \
bool visitStrongRetain##Name##Inst(const StrongRetain##Name##Inst *RHS) { \
return true; \
}
#define UNCHECKED_REF_STORAGE(Name, ...) \
LOADABLE_REF_STORAGE_HELPER(Name)
#include "swift/AST/ReferenceStorage.def"
#undef LOADABLE_REF_STORAGE_HELPER
bool visitThinToThickFunctionInst(ThinToThickFunctionInst *RHS) {
return true;
}
bool visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *RHS) {
return true;
}
bool visitObjCToThickMetatypeInst(ObjCToThickMetatypeInst *RHS) {
return true;
}
bool visitConvertFunctionInst(ConvertFunctionInst *RHS) {
return true;
}
bool visitConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *RHS) {
return true;
}
bool visitObjCMetatypeToObjectInst(ObjCMetatypeToObjectInst *RHS) {
return true;
}
bool visitObjCExistentialMetatypeToObjectInst(ObjCExistentialMetatypeToObjectInst *RHS) {
return true;
}
bool visitProjectBlockStorageInst(ProjectBlockStorageInst *RHS) {
return true;
}
bool visitBridgeObjectToRefInst(BridgeObjectToRefInst *X) {
return true;
}
bool visitValueToBridgeObjectInst(ValueToBridgeObjectInst *i) {
return true;
}
bool visitBridgeObjectToWordInst(BridgeObjectToWordInst *X) {
return true;
}
bool visitRefToBridgeObjectInst(RefToBridgeObjectInst *X) {
return true;
}
bool visitClassifyBridgeObjectInst(ClassifyBridgeObjectInst *X) {
return true;
}
bool visitThinFunctionToPointerInst(ThinFunctionToPointerInst *X) {
return true;
}
bool visitPointerToThinFunctionInst(PointerToThinFunctionInst *X) {
return true;
}
bool visitObjCProtocolInst(ObjCProtocolInst *RHS) {
auto *X = cast<ObjCProtocolInst>(LHS);
return X->getProtocol() == RHS->getProtocol();
}
bool visitClassMethodInst(ClassMethodInst *RHS) {
auto *X = cast<ClassMethodInst>(LHS);
return X->getMember() == RHS->getMember() &&
X->getOperand() == RHS->getOperand() &&
X->getType() == RHS->getType();
}
bool visitSuperMethodInst(SuperMethodInst *RHS) {
auto *X = cast<SuperMethodInst>(LHS);
return X->getMember() == RHS->getMember() &&
X->getOperand() == RHS->getOperand() &&
X->getType() == RHS->getType();
}
bool visitObjCMethodInst(ObjCMethodInst *RHS) {
auto *X = cast<ObjCMethodInst>(LHS);
return X->getMember() == RHS->getMember() &&
X->getOperand() == RHS->getOperand() &&
X->getType() == RHS->getType();
}
bool visitObjCSuperMethodInst(ObjCSuperMethodInst *RHS) {
auto *X = cast<ObjCSuperMethodInst>(LHS);
return X->getMember() == RHS->getMember() &&
X->getOperand() == RHS->getOperand() &&
X->getType() == RHS->getType();
}
bool visitWitnessMethodInst(const WitnessMethodInst *RHS) {
auto *X = cast<WitnessMethodInst>(LHS);
if (X->getMember() != RHS->getMember())
return false;
if (X->getLookupType() != RHS->getLookupType())
return false;
if (X->getConformance() != RHS->getConformance())
return false;
return true;
}
bool visitMarkDependenceInst(const MarkDependenceInst *RHS) {
return true;
}
bool visitOpenExistentialRefInst(const OpenExistentialRefInst *RHS) {
return true;
}
private:
const SILInstruction *LHS;
};
} // end anonymous namespace
bool SILInstruction::hasIdenticalState(const SILInstruction *RHS) const {
SILInstruction *UnconstRHS = const_cast<SILInstruction *>(RHS);
return InstructionIdentityComparer(this).visit(UnconstRHS);
}
namespace {
class AllOperandsAccessor : public SILInstructionVisitor<AllOperandsAccessor,
ArrayRef<Operand> > {
public:
#define INST(CLASS, PARENT) \
ArrayRef<Operand> visit##CLASS(const CLASS *I) { \
ASSERT_IMPLEMENTS(CLASS, SILInstruction, getAllOperands, \
ArrayRef<Operand>() const); \
return I->getAllOperands(); \
}
#include "swift/SIL/SILNodes.def"
};
class AllOperandsMutableAccessor
: public SILInstructionVisitor<AllOperandsMutableAccessor,
MutableArrayRef<Operand> > {
public:
#define INST(CLASS, PARENT) \
MutableArrayRef<Operand> visit##CLASS(CLASS *I) { \
ASSERT_IMPLEMENTS(CLASS, SILInstruction, getAllOperands, \
MutableArrayRef<Operand>()); \
return I->getAllOperands(); \
}
#include "swift/SIL/SILNodes.def"
};
#define IMPLEMENTS_METHOD(DerivedClass, BaseClass, MemberName, ExpectedType) \
(!::std::is_same<BaseClass, GET_IMPLEMENTING_CLASS(DerivedClass, MemberName,\
ExpectedType)>::value)
class TypeDependentOperandsAccessor
: public SILInstructionVisitor<TypeDependentOperandsAccessor,
ArrayRef<Operand>> {
public:
#define INST(CLASS, PARENT) \
ArrayRef<Operand> visit##CLASS(const CLASS *I) { \
if (!IMPLEMENTS_METHOD(CLASS, SILInstruction, getTypeDependentOperands, \
ArrayRef<Operand>() const)) \
return {}; \
return I->getTypeDependentOperands(); \
}
#include "swift/SIL/SILNodes.def"
};
class TypeDependentOperandsMutableAccessor
: public SILInstructionVisitor<TypeDependentOperandsMutableAccessor,
MutableArrayRef<Operand> > {
public:
#define INST(CLASS, PARENT) \
MutableArrayRef<Operand> visit##CLASS(CLASS *I) { \
if (!IMPLEMENTS_METHOD(CLASS, SILInstruction, getTypeDependentOperands, \
MutableArrayRef<Operand>())) \
return {}; \
return I->getTypeDependentOperands(); \
}
#include "swift/SIL/SILNodes.def"
};
} // end anonymous namespace
ArrayRef<Operand> SILInstruction::getAllOperands() const {
return AllOperandsAccessor().visit(const_cast<SILInstruction *>(this));
}
MutableArrayRef<Operand> SILInstruction::getAllOperands() {
return AllOperandsMutableAccessor().visit(this);
}
ArrayRef<Operand> SILInstruction::getTypeDependentOperands() const {
return TypeDependentOperandsAccessor().visit(
const_cast<SILInstruction *>(this));
}
MutableArrayRef<Operand> SILInstruction::getTypeDependentOperands() {
return TypeDependentOperandsMutableAccessor().visit(this);
}
/// getOperandNumber - Return which operand this is in the operand list of the
/// using instruction.
unsigned Operand::getOperandNumber() const {
return this - &cast<SILInstruction>(getUser())->getAllOperands()[0];
}
SILInstruction::MemoryBehavior SILInstruction::getMemoryBehavior() const {
if (auto *BI = dyn_cast<BuiltinInst>(this)) {
// Handle Swift builtin functions.
const BuiltinInfo &BInfo = BI->getBuiltinInfo();
if (BInfo.ID != BuiltinValueKind::None)
return BInfo.isReadNone() ? MemoryBehavior::None
: MemoryBehavior::MayHaveSideEffects;
// Handle LLVM intrinsic functions.
const IntrinsicInfo &IInfo = BI->getIntrinsicInfo();
if (IInfo.ID != llvm::Intrinsic::not_intrinsic) {
// Read-only.
if (IInfo.hasAttribute(llvm::Attribute::ReadOnly) &&
IInfo.hasAttribute(llvm::Attribute::NoUnwind))
return MemoryBehavior::MayRead;
// Read-none?
return IInfo.hasAttribute(llvm::Attribute::ReadNone) &&
IInfo.hasAttribute(llvm::Attribute::NoUnwind)
? MemoryBehavior::None
: MemoryBehavior::MayHaveSideEffects;
}
}
// Handle full apply sites that have a resolvable callee function with an
// effects attribute.
if (isa<FullApplySite>(this)) {
FullApplySite Site(const_cast<SILInstruction *>(this));
if (auto *F = Site.getCalleeFunction()) {
return F->getEffectsKind() == EffectsKind::ReadNone
? MemoryBehavior::None
: MemoryBehavior::MayHaveSideEffects;
}
}
switch (getKind()) {
#define FULL_INST(CLASS, TEXTUALNAME, PARENT, MEMBEHAVIOR, RELEASINGBEHAVIOR) \
case SILInstructionKind::CLASS: \
return MemoryBehavior::MEMBEHAVIOR;
#include "swift/SIL/SILNodes.def"
}
llvm_unreachable("We've just exhausted the switch.");
}
SILInstruction::ReleasingBehavior SILInstruction::getReleasingBehavior() const {
switch (getKind()) {
#define FULL_INST(CLASS, TEXTUALNAME, PARENT, MEMBEHAVIOR, RELEASINGBEHAVIOR) \
case SILInstructionKind::CLASS: \
return ReleasingBehavior::RELEASINGBEHAVIOR;
#include "swift/SIL/SILNodes.def"
}
llvm_unreachable("We've just exhausted the switch.");
}
bool SILInstruction::mayHaveSideEffects() const {
// If this instruction traps then it must have side effects.
if (mayTrap())
return true;
MemoryBehavior B = getMemoryBehavior();
return B == MemoryBehavior::MayWrite ||
B == MemoryBehavior::MayReadWrite ||
B == MemoryBehavior::MayHaveSideEffects;
}
bool SILInstruction::mayRelease() const {
if (getReleasingBehavior() ==
SILInstruction::ReleasingBehavior::DoesNotRelease)
return false;
switch (getKind()) {
default:
llvm_unreachable("Unhandled releasing instruction!");
case SILInstructionKind::ApplyInst:
case SILInstructionKind::TryApplyInst:
case SILInstructionKind::BeginApplyInst:
case SILInstructionKind::AbortApplyInst:
case SILInstructionKind::EndApplyInst:
case SILInstructionKind::YieldInst:
case SILInstructionKind::DestroyAddrInst:
case SILInstructionKind::StrongReleaseInst:
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case SILInstructionKind::Name##ReleaseInst:
#include "swift/AST/ReferenceStorage.def"
case SILInstructionKind::ReleaseValueInst:
case SILInstructionKind::ReleaseValueAddrInst:
return true;
case SILInstructionKind::DestroyValueInst:
assert(!SILModuleConventions(getModule()).useLoweredAddresses());
return true;
case SILInstructionKind::UnconditionalCheckedCastAddrInst:
case SILInstructionKind::UnconditionalCheckedCastValueInst:
return true;
case SILInstructionKind::CheckedCastAddrBranchInst: {
// Failing casts with take_always can release.
auto *Cast = cast<CheckedCastAddrBranchInst>(this);
return Cast->getConsumptionKind() == CastConsumptionKind::TakeAlways;
}
case SILInstructionKind::CopyAddrInst: {
auto *CopyAddr = cast<CopyAddrInst>(this);
// copy_addr without initialization can cause a release.
return CopyAddr->isInitializationOfDest() ==
IsInitialization_t::IsNotInitialization;
}
case SILInstructionKind::BuiltinInst: {
auto *BI = cast<BuiltinInst>(this);
// Builtins without side effects also do not release.
if (!BI->mayHaveSideEffects())
return false;
// If this is a builtin which might have side effect, but its side
// effects do not cause reference counts to be decremented, return false.
if (auto Kind = BI->getBuiltinKind()) {
switch (Kind.getValue()) {
case BuiltinValueKind::CopyArray:
return false;
default:
break;
}
}
if (auto ID = BI->getIntrinsicID()) {
switch (ID.getValue()) {
case llvm::Intrinsic::memcpy:
case llvm::Intrinsic::memmove:
case llvm::Intrinsic::memset:
return false;
default:
break;
}
}
return true;
}
}
}
bool SILInstruction::mayReleaseOrReadRefCount() const {
switch (getKind()) {
case SILInstructionKind::IsUniqueInst:
case SILInstructionKind::IsEscapingClosureInst:
return true;
default:
return mayRelease();
}
}
namespace {
class TrivialCloner : public SILCloner<TrivialCloner> {
friend class SILCloner<TrivialCloner>;
friend class SILInstructionVisitor<TrivialCloner>;
SILInstruction *Result = nullptr;
TrivialCloner(SILFunction *F) : SILCloner(*F) {}
public:
static SILInstruction *doIt(SILInstruction *I) {
TrivialCloner TC(I->getFunction());
TC.visit(I);
return TC.Result;
}
void postProcess(SILInstruction *Orig, SILInstruction *Cloned) {
assert(Orig->getFunction() == &getBuilder().getFunction() &&
"cloning between functions is not supported");
Result = Cloned;
SILCloner<TrivialCloner>::postProcess(Orig, Cloned);
}
SILValue getMappedValue(SILValue Value) {
return Value;
}
SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; }
};
} // end anonymous namespace
bool SILInstruction::isAllocatingStack() const {
if (isa<AllocStackInst>(this))
return true;
if (auto *ARI = dyn_cast<AllocRefInst>(this)) {
if (ARI->canAllocOnStack())
return true;
}
if (auto *PA = dyn_cast<PartialApplyInst>(this))
return PA->isOnStack();
return false;
}
bool SILInstruction::isDeallocatingStack() const {
if (isa<DeallocStackInst>(this))
return true;
if (auto *DRI = dyn_cast<DeallocRefInst>(this)) {
if (DRI->canAllocOnStack())
return true;
}
return false;
}
/// Create a new copy of this instruction, which retains all of the operands
/// and other information of this one. If an insertion point is specified,
/// then the new instruction is inserted before the specified point, otherwise
/// the new instruction is returned without a parent.
SILInstruction *SILInstruction::clone(SILInstruction *InsertPt) {
SILInstruction *NewInst = TrivialCloner::doIt(this);
if (NewInst && InsertPt)
InsertPt->getParent()->insert(InsertPt, NewInst);
return NewInst;
}
/// Returns true if the instruction can be duplicated without any special
/// additional handling. It is important to know this information when
/// you perform such optimizations like e.g. jump-threading.
bool SILInstruction::isTriviallyDuplicatable() const {
if (isa<AllocStackInst>(this) || isa<DeallocStackInst>(this)) {
return false;
}
if (auto *ARI = dyn_cast<AllocRefInst>(this)) {
if (ARI->canAllocOnStack())
return false;
}
if (isa<OpenExistentialAddrInst>(this) || isa<OpenExistentialRefInst>(this) ||
isa<OpenExistentialMetatypeInst>(this) ||
isa<OpenExistentialValueInst>(this) || isa<OpenExistentialBoxInst>(this) ||
isa<OpenExistentialBoxValueInst>(this)) {
// Don't know how to duplicate these properly yet. Inst.clone() per
// instruction does not work. Because the follow-up instructions need to
// reuse the same archetype uuid which would only work if we used a
// cloner.
return false;
}
if (auto *MI = dyn_cast<MethodInst>(this)) {
// We can't build SSA for method values that lower to objc methods.
if (MI->getMember().isForeign)
return false;
}
if (isa<ThrowInst>(this))
return false;
// BeginAccess defines the access scope entry point. All associated EndAccess
// instructions must directly operate on the BeginAccess.
if (isa<BeginAccessInst>(this))
return false;
// begin_apply creates a token that has to be directly used by the
// corresponding end_apply and abort_apply.
if (isa<BeginApplyInst>(this))
return false;
// dynamic_method_br is not duplicatable because IRGen does not support phi
// nodes of objc_method type.
if (isa<DynamicMethodBranchInst>(this))
return false;
if (auto *PA = dyn_cast<PartialApplyInst>(this))
return !PA->isOnStack();
// If you add more cases here, you should also update SILLoop:canDuplicate.
return true;
}
bool SILInstruction::mayTrap() const {
switch(getKind()) {
case SILInstructionKind::CondFailInst:
case SILInstructionKind::UnconditionalCheckedCastInst:
case SILInstructionKind::UnconditionalCheckedCastAddrInst:
return true;
default:
return false;
}
}
bool SILInstruction::isMetaInstruction() const {
// Every instruction that implements getVarInfo() should be in this list.
switch (getKind()) {
case SILInstructionKind::AllocBoxInst:
case SILInstructionKind::AllocStackInst:
case SILInstructionKind::DebugValueInst:
case SILInstructionKind::DebugValueAddrInst:
return true;
default:
return false;
}
llvm_unreachable("Instruction not handled in isMetaInstruction()!");
}
//===----------------------------------------------------------------------===//
// Utilities
//===----------------------------------------------------------------------===//
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS,
SILInstruction::MemoryBehavior B) {
switch (B) {
case SILInstruction::MemoryBehavior::None:
return OS << "None";
case SILInstruction::MemoryBehavior::MayRead:
return OS << "MayRead";
case SILInstruction::MemoryBehavior::MayWrite:
return OS << "MayWrite";
case SILInstruction::MemoryBehavior::MayReadWrite:
return OS << "MayReadWrite";
case SILInstruction::MemoryBehavior::MayHaveSideEffects:
return OS << "MayHaveSideEffects";
}
llvm_unreachable("Unhandled MemoryBehavior in switch.");
}
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS,
SILInstruction::ReleasingBehavior B) {
switch (B) {
case SILInstruction::ReleasingBehavior::DoesNotRelease:
return OS << "DoesNotRelease";
case SILInstruction::ReleasingBehavior::MayRelease:
return OS << "MayRelease";
}
llvm_unreachable("Unhandled ReleasingBehavior in switch.");
}
//===----------------------------------------------------------------------===//
// SILInstructionResultArray
//===----------------------------------------------------------------------===//
SILInstructionResultArray::SILInstructionResultArray(
const SingleValueInstruction *SVI)
: Pointer(), Size(1) {
// Make sure that even though we are munging things, we are able to get back
// the original value, types, and operands.
SILValue originalValue(SVI);
SILType originalType = SVI->getType();
(void)originalValue;
(void)originalType;
// *PLEASE READ BEFORE CHANGING*
//
// Since SingleValueInstruction is both a ValueBase and a SILInstruction, but
// SILInstruction is the first parent, we need to ensure that our ValueBase *
// pointer is properly offset. by first static casting to ValueBase and then
// going back to a uint8_t *.
auto *Value = static_cast<const ValueBase *>(SVI);
assert(uintptr_t(Value) != uintptr_t(SVI) &&
"Expected value to be offset from SVI since it is not the first "
"multi-inheritence parent");
Pointer = reinterpret_cast<const uint8_t *>(Value);
#ifndef NDEBUG
assert(originalValue == (*this)[0] &&
"Wrong value returned for single result");
assert(originalType == (*this)[0]->getType());
auto ValueRange = getValues();
assert(1 == std::distance(ValueRange.begin(), ValueRange.end()));
assert(originalValue == *ValueRange.begin());
auto TypedRange = getTypes();
assert(1 == std::distance(TypedRange.begin(), TypedRange.end()));
assert(originalType == *TypedRange.begin());
SILInstructionResultArray Copy = *this;
assert(Copy.hasSameTypes(*this));
assert(Copy == *this);
#endif
}
SILInstructionResultArray::SILInstructionResultArray(
ArrayRef<MultipleValueInstructionResult> MVResults)
: Pointer(nullptr), Size(MVResults.size()) {
// We are assuming here that MultipleValueInstructionResult when static_cast
// is not offset.
if (Size)
Pointer = reinterpret_cast<const uint8_t *>(&MVResults[0]);
#ifndef NDEBUG
// Verify our invariants.
assert(size() == MVResults.size());
auto ValueRange = getValues();
auto VRangeBegin = ValueRange.begin();
auto VRangeIter = VRangeBegin;
auto VRangeEnd = ValueRange.end();
assert(MVResults.size() == unsigned(std::distance(VRangeBegin, VRangeEnd)));
auto TypedRange = getTypes();
auto TRangeBegin = TypedRange.begin();
auto TRangeIter = TRangeBegin;
auto TRangeEnd = TypedRange.end();
assert(MVResults.size() == unsigned(std::distance(TRangeBegin, TRangeEnd)));
for (unsigned i : indices(MVResults)) {
assert(SILValue(&MVResults[i]) == (*this)[i]);
assert(SILValue(&MVResults[i])->getType() == (*this)[i]->getType());
assert(SILValue(&MVResults[i]) == (*VRangeIter));
assert(SILValue(&MVResults[i])->getType() == (*VRangeIter)->getType());
assert(SILValue(&MVResults[i])->getType() == *TRangeIter);
++VRangeIter;
++TRangeIter;
}
SILInstructionResultArray Copy = *this;
assert(Copy.hasSameTypes(*this));
assert(Copy == *this);
#endif
}
SILValue SILInstructionResultArray::operator[](size_t Index) const {
assert(Index < Size && "Index out of bounds");
// *NOTE* In the case where we have a single instruction, Index will always
// necessarily be 0 implying that it is safe for us to just multiple Index by
// sizeof(MultipleValueInstructionResult).
size_t Offset = sizeof(MultipleValueInstructionResult) * Index;
return SILValue(reinterpret_cast<const ValueBase *>(&Pointer[Offset]));
}
bool SILInstructionResultArray::hasSameTypes(
const SILInstructionResultArray &rhs) {
auto &lhs = *this;
if (lhs.size() != rhs.size())
return false;
for (unsigned i : indices(lhs)) {
if (lhs[i]->getType() != rhs[i]->getType())
return false;
}
return true;
}
bool SILInstructionResultArray::
operator==(const SILInstructionResultArray &other) {
if (size() != other.size())
return false;
for (auto i : indices(*this))
if ((*this)[i] != other[i])
return false;
return true;
}
SILInstructionResultArray::type_range
SILInstructionResultArray::getTypes() const {
SILType (*F)(SILValue) = [](SILValue V) -> SILType {
return V->getType();
};
return {llvm::map_iterator(begin(), F), llvm::map_iterator(end(), F)};
}
const ValueBase *SILInstructionResultArray::front() const {
assert(size() && "Can not access front of an empty result array");
return *begin();
}
const ValueBase *SILInstructionResultArray::back() const {
assert(size() && "Can not access back of an empty result array");
if (std::next(begin()) == end()) {
return *begin();
}
return *std::prev(end());
}
//===----------------------------------------------------------------------===//
// Multiple Value Instruction
//===----------------------------------------------------------------------===//
Optional<unsigned>
MultipleValueInstruction::getIndexOfResult(SILValue Target) const {
// First make sure we actually have one of our instruction results.
auto *MVIR = dyn_cast<MultipleValueInstructionResult>(Target);
if (!MVIR || MVIR->getParent() != this)
return None;
return MVIR->getIndex();
}
MultipleValueInstructionResult::MultipleValueInstructionResult(
ValueKind valueKind, unsigned index, SILType type,
ValueOwnershipKind ownershipKind)
: ValueBase(valueKind, type, IsRepresentative::No) {
setOwnershipKind(ownershipKind);
setIndex(index);
}
void MultipleValueInstructionResult::setOwnershipKind(
ValueOwnershipKind NewKind) {
Bits.MultipleValueInstructionResult.VOKind = unsigned(NewKind);
}
void MultipleValueInstructionResult::setIndex(unsigned NewIndex) {
// We currently use 32 bits to store the Index. A previous comment wrote
// that "500k fields is probably enough".
Bits.MultipleValueInstructionResult.Index = NewIndex;
}
ValueOwnershipKind MultipleValueInstructionResult::getOwnershipKind() const {
return ValueOwnershipKind(Bits.MultipleValueInstructionResult.VOKind);
}
MultipleValueInstruction *MultipleValueInstructionResult::getParent() {
char *Ptr = reinterpret_cast<char *>(
const_cast<MultipleValueInstructionResult *>(this));
// We know that we are in a trailing objects array with an extra prefix
// element that contains the pointer to our parent SILNode. So grab the
// address of the beginning of the array.
Ptr -= getIndex() * sizeof(MultipleValueInstructionResult);
// We may have some bytes of padding depending on our platform. Move past
// those bytes if we need to.
static_assert(alignof(MultipleValueInstructionResult) >=
alignof(MultipleValueInstruction *),
"We assume this relationship in between the alignments");
Ptr -= alignof(MultipleValueInstructionResult) -
alignof(MultipleValueInstruction *);
// Then subtract the size of MultipleValueInstruction.
Ptr -= sizeof(MultipleValueInstruction *);
// Now that we have the correct address of our parent instruction, grab it and
// return it avoiding type punning.
uintptr_t value;
memcpy(&value, Ptr, sizeof(value));
return reinterpret_cast<MultipleValueInstruction *>(value);
}
#ifndef NDEBUG
//---
// Static verification of multiple value properties.
//
// Make sure that all subclasses of MultipleValueInstruction implement
// getAllResults()
#define MULTIPLE_VALUE_INST(ID, TEXTUALNAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
static_assert(IMPLEMENTS_METHOD(ID, PARENT, getAllResults, \
SILInstructionResultArray() const), \
#ID " does not implement SILInstructionResultArray " \
"getAllResults() const?!");
// Check that all subclasses of MultipleValueInstructionResult are the same size
// as MultipleValueInstructionResult.
//
// If this changes, we just need to expand the size fo SILInstructionResultArray
// to contain a stride. But we assume this now so we should enforce it.
#define MULTIPLE_VALUE_INST_RESULT(ID, PARENT) \
static_assert( \
sizeof(ID) == sizeof(PARENT) && alignof(ID) == alignof(PARENT), \
"Expected all multiple value inst result to be the same size?!");
#include "swift/SIL/SILNodes.def"
#endif