blob: d231d6beb4b33000a8164ff6e69fbcb5b06dfbd0 [file] [log] [blame]
//===--- SILCombinerCastVisitors.cpp --------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-combine"
#include "SILCombiner.h"
#include "swift/SIL/DynamicCasts.h"
#include "swift/SIL/PatternMatch.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/CFG.h"
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/DenseMap.h"
using namespace swift;
using namespace swift::PatternMatch;
SILInstruction *
SILCombiner::visitRefToRawPointerInst(RefToRawPointerInst *RRPI) {
// Ref to raw pointer consumption of other ref casts.
if (auto *URCI = dyn_cast<UncheckedRefCastInst>(RRPI->getOperand())) {
// (ref_to_raw_pointer (unchecked_ref_cast x))
// -> (ref_to_raw_pointer x)
if (URCI->getOperand()->getType().isAnyClassReferenceType()) {
RRPI->setOperand(URCI->getOperand());
return URCI->use_empty() ? eraseInstFromFunction(*URCI) : nullptr;
}
// (ref_to_raw_pointer (unchecked_ref_cast x))
// -> (unchecked_trivial_bit_cast x)
return Builder.createUncheckedTrivialBitCast(RRPI->getLoc(),
URCI->getOperand(),
RRPI->getType());
}
// (ref_to_raw_pointer (open_existential_ref (init_existential_ref x))) ->
// (ref_to_raw_pointer x)
if (auto *OER = dyn_cast<OpenExistentialRefInst>(RRPI->getOperand()))
if (auto *IER = dyn_cast<InitExistentialRefInst>(OER->getOperand()))
return Builder.createRefToRawPointer(RRPI->getLoc(), IER->getOperand(),
RRPI->getType());
return nullptr;
}
SILInstruction *SILCombiner::visitUpcastInst(UpcastInst *UCI) {
// Ref to raw pointer consumption of other ref casts.
//
// (upcast (upcast x)) -> (upcast x)
if (auto *Op = dyn_cast<UpcastInst>(UCI->getOperand())) {
UCI->setOperand(Op->getOperand());
return Op->use_empty() ? eraseInstFromFunction(*Op) : nullptr;
}
return nullptr;
}
SILInstruction *
SILCombiner::
visitPointerToAddressInst(PointerToAddressInst *PTAI) {
Builder.setCurrentDebugScope(PTAI->getDebugScope());
// If we reach this point, we know that the types must be different since
// otherwise simplifyInstruction would have handled the identity case. This is
// always legal to do since address-to-pointer pointer-to-address implies
// layout compatibility.
//
// (pointer-to-address strict (address-to-pointer %x))
// -> (unchecked_addr_cast %x)
if (PTAI->isStrict()) {
if (auto *ATPI = dyn_cast<AddressToPointerInst>(PTAI->getOperand())) {
return Builder.createUncheckedAddrCast(PTAI->getLoc(), ATPI->getOperand(),
PTAI->getType());
}
}
// Turn this also into an index_addr. We generate this pattern after switching
// the Word type to an explicit Int32 or Int64 in the stdlib.
//
// %101 = builtin "strideof"<Int>(%84 : $@thick Int.Type) :
// $Builtin.Word
// %102 = builtin "zextOrBitCast_Word_Int64"(%101 : $Builtin.Word) :
// $Builtin.Int64
// %111 = builtin "smul_with_overflow_Int64"(%108 : $Builtin.Int64,
// %102 : $Builtin.Int64, %20 : $Builtin.Int1) :
// $(Builtin.Int64, Builtin.Int1)
// %112 = tuple_extract %111 : $(Builtin.Int64, Builtin.Int1), 0
// %113 = builtin "truncOrBitCast_Int64_Word"(%112 : $Builtin.Int64) :
// $Builtin.Word
// %114 = index_raw_pointer %100 : $Builtin.RawPointer, %113 : $Builtin.Word
// %115 = pointer_to_address %114 : $Builtin.RawPointer to [strict] $*Int
SILValue Distance;
SILValue TruncOrBitCast;
MetatypeInst *Metatype;
IndexRawPointerInst *IndexRawPtr;
BuiltinInst *StrideMul;
if (match(
PTAI->getOperand(),
m_IndexRawPointerInst(IndexRawPtr))) {
SILValue Ptr = IndexRawPtr->getOperand(0);
SILValue TruncOrBitCast = IndexRawPtr->getOperand(1);
if (match(TruncOrBitCast,
m_ApplyInst(BuiltinValueKind::TruncOrBitCast,
m_TupleExtractInst(m_BuiltinInst(StrideMul), 0)))) {
if (match(StrideMul,
m_ApplyInst(
BuiltinValueKind::SMulOver, m_SILValue(Distance),
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(Metatype))))) ||
match(StrideMul,
m_ApplyInst(
BuiltinValueKind::SMulOver,
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(Metatype))),
m_SILValue(Distance)))) {
SILType InstanceType =
Metatype->getType().getMetatypeInstanceType(PTAI->getModule());
auto *Trunc = cast<BuiltinInst>(TruncOrBitCast);
// Make sure that the type of the metatype matches the type that we are
// casting to so we stride by the correct amount.
if (InstanceType.getAddressType() != PTAI->getType()) {
return nullptr;
}
auto *NewPTAI = Builder.createPointerToAddress(PTAI->getLoc(), Ptr,
PTAI->getType(),
PTAI->isStrict(),
PTAI->isInvariant());
auto DistanceAsWord = Builder.createBuiltin(
PTAI->getLoc(), Trunc->getName(), Trunc->getType(), {}, Distance);
return Builder.createIndexAddr(PTAI->getLoc(), NewPTAI, DistanceAsWord);
}
}
}
// Turn:
//
// %stride = Builtin.strideof(T) * %distance
// %ptr' = index_raw_pointer %ptr, %stride
// %result = pointer_to_address %ptr, [strict] $T'
//
// To:
//
// %addr = pointer_to_address %ptr, [strict] $T
// %result = index_addr %addr, %distance
//
BuiltinInst *Bytes;
if (match(PTAI->getOperand(),
m_IndexRawPointerInst(m_ValueBase(),
m_TupleExtractInst(m_BuiltinInst(Bytes),
0)))) {
if (match(Bytes, m_ApplyInst(BuiltinValueKind::SMulOver, m_ValueBase(),
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(Metatype)),
m_ValueBase()))) {
SILType InstanceType =
Metatype->getType().getMetatypeInstanceType(PTAI->getModule());
// Make sure that the type of the metatype matches the type that we are
// casting to so we stride by the correct amount.
if (InstanceType.getAddressType() != PTAI->getType())
return nullptr;
auto IRPI = cast<IndexRawPointerInst>(PTAI->getOperand());
SILValue Ptr = IRPI->getOperand(0);
SILValue Distance = Bytes->getArguments()[0];
auto *NewPTAI =
Builder.createPointerToAddress(PTAI->getLoc(), Ptr, PTAI->getType(),
PTAI->isStrict(), PTAI->isInvariant());
return Builder.createIndexAddr(PTAI->getLoc(), NewPTAI, Distance);
}
}
return nullptr;
}
SILInstruction *
SILCombiner::visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI) {
Builder.setCurrentDebugScope(UADCI->getDebugScope());
// (unchecked-addr-cast (unchecked-addr-cast x X->Y) Y->Z)
// ->
// (unchecked-addr-cast x X->Z)
if (auto *OtherUADCI = dyn_cast<UncheckedAddrCastInst>(UADCI->getOperand()))
return Builder.createUncheckedAddrCast(UADCI->getLoc(),
OtherUADCI->getOperand(),
UADCI->getType());
// (unchecked-addr-cast cls->superclass) -> (upcast cls->superclass)
if (UADCI->getType() != UADCI->getOperand()->getType() &&
UADCI->getType().isExactSuperclassOf(UADCI->getOperand()->getType()))
return Builder.createUpcast(UADCI->getLoc(), UADCI->getOperand(),
UADCI->getType());
return nullptr;
}
SILInstruction *
SILCombiner::visitUncheckedRefCastInst(UncheckedRefCastInst *URCI) {
// (unchecked-ref-cast (unchecked-ref-cast x X->Y) Y->Z)
// ->
// (unchecked-ref-cast x X->Z)
if (auto *OtherURCI = dyn_cast<UncheckedRefCastInst>(URCI->getOperand()))
return Builder.createUncheckedRefCast(URCI->getLoc(),
OtherURCI->getOperand(),
URCI->getType());
// (unchecked_ref_cast (upcast x X->Y) Y->Z) -> (unchecked_ref_cast x X->Z)
if (auto *UI = dyn_cast<UpcastInst>(URCI->getOperand()))
return Builder.createUncheckedRefCast(URCI->getLoc(),
UI->getOperand(),
URCI->getType());
if (URCI->getType() != URCI->getOperand()->getType() &&
URCI->getType().isExactSuperclassOf(URCI->getOperand()->getType()))
return Builder.createUpcast(URCI->getLoc(), URCI->getOperand(),
URCI->getType());
// (unchecked_ref_cast (open_existential_ref (init_existential_ref X))) ->
// (unchecked_ref_cast X)
if (auto *OER = dyn_cast<OpenExistentialRefInst>(URCI->getOperand()))
if (auto *IER = dyn_cast<InitExistentialRefInst>(OER->getOperand()))
return Builder.createUncheckedRefCast(URCI->getLoc(), IER->getOperand(),
URCI->getType());
return nullptr;
}
SILInstruction *
SILCombiner::visitBridgeObjectToRefInst(BridgeObjectToRefInst *BORI) {
// Fold noop casts through Builtin.BridgeObject.
// (bridge_object_to_ref (unchecked-ref-cast x BridgeObject) y)
// -> (unchecked-ref-cast x y)
if (auto URC = dyn_cast<UncheckedRefCastInst>(BORI->getOperand()))
return Builder.createUncheckedRefCast(BORI->getLoc(), URC->getOperand(),
BORI->getType());
return nullptr;
}
SILInstruction *
SILCombiner::visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI) {
SILType SrcTy = URCI->getSrc()->getType();
if (!SrcTy.isLoadable(URCI->getModule()))
return nullptr;
SILType DestTy = URCI->getDest()->getType();
if (!DestTy.isLoadable(URCI->getModule()))
return nullptr;
// After promoting unchecked_ref_cast_addr to unchecked_ref_cast, the SIL
// verifier will assert that the loadable source and dest type of reference
// castable. If the static types are invalid, simply avoid promotion, that way
// the runtime will then report a failure if this cast is ever executed.
if (!SILType::canRefCast(SrcTy.getObjectType(), DestTy.getObjectType(),
URCI->getModule()))
return nullptr;
SILLocation Loc = URCI->getLoc();
Builder.setCurrentDebugScope(URCI->getDebugScope());
LoadInst *load = Builder.createLoad(Loc, URCI->getSrc(),
LoadOwnershipQualifier::Unqualified);
auto *cast = Builder.tryCreateUncheckedRefCast(Loc, load,
DestTy.getObjectType());
assert(cast && "SILBuilder cannot handle reference-castable types");
Builder.createStore(Loc, cast, URCI->getDest(),
StoreOwnershipQualifier::Unqualified);
return eraseInstFromFunction(*URCI);
}
SILInstruction *
SILCombiner::
visitUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *UCCAI) {
if (CastOpt.optimizeUnconditionalCheckedCastAddrInst(UCCAI))
MadeChange = true;
return nullptr;
}
SILInstruction *
SILCombiner::
visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI) {
if (CastOpt.optimizeUnconditionalCheckedCastInst(UCCI)) {
MadeChange = true;
return nullptr;
}
// FIXME: rename from RemoveCondFails to RemoveRuntimeAsserts.
if (RemoveCondFails) {
auto LoweredTargetType = UCCI->getType();
auto Loc = UCCI->getLoc();
auto Op = UCCI->getOperand();
if (LoweredTargetType.isAddress()) {
// unconditional_checked_cast -> unchecked_addr_cast
return Builder.createUncheckedAddrCast(Loc, Op, LoweredTargetType);
} else if (LoweredTargetType.isHeapObjectReferenceType()) {
if (!(Op->getType().isHeapObjectReferenceType() ||
Op->getType().isClassExistentialType())) {
return nullptr;
}
// unconditional_checked_cast -> unchecked_ref_cast
return Builder.createUncheckedRefCast(Loc, Op, LoweredTargetType);
}
}
return nullptr;
}
SILInstruction *
SILCombiner::
visitRawPointerToRefInst(RawPointerToRefInst *RawToRef) {
// (raw_pointer_to_ref (ref_to_raw_pointer x X->Y) Y->Z)
// ->
// (unchecked_ref_cast X->Z)
if (auto *RefToRaw = dyn_cast<RefToRawPointerInst>(RawToRef->getOperand())) {
return Builder.createUncheckedRefCast(RawToRef->getLoc(),
RefToRaw->getOperand(),
RawToRef->getType());
}
return nullptr;
}
SILInstruction *
SILCombiner::
visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI) {
// (unchecked_trivial_bit_cast Y->Z
// (unchecked_trivial_bit_cast X->Y x))
// ->
// (unchecked_trivial_bit_cast X->Z x)
SILValue Op = UTBCI->getOperand();
if (auto *OtherUTBCI = dyn_cast<UncheckedTrivialBitCastInst>(Op)) {
return Builder.createUncheckedTrivialBitCast(UTBCI->getLoc(),
OtherUTBCI->getOperand(),
UTBCI->getType());
}
// (unchecked_trivial_bit_cast Y->Z
// (unchecked_ref_cast X->Y x))
// ->
// (unchecked_trivial_bit_cast X->Z x)
if (auto *URBCI = dyn_cast<UncheckedRefCastInst>(Op)) {
return Builder.createUncheckedTrivialBitCast(UTBCI->getLoc(),
URBCI->getOperand(),
UTBCI->getType());
}
return nullptr;
}
SILInstruction *
SILCombiner::
visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {
// (unchecked_bitwise_cast Y->Z (unchecked_bitwise_cast X->Y x))
// OR (unchecked_trivial_cast Y->Z (unchecked_bitwise_cast X->Y x))
// ->
// (unchecked_bitwise_cast X->Z x)
SILValue Oper;
if (match(UBCI->getOperand(),
m_CombineOr(m_UncheckedBitwiseCastInst(m_SILValue(Oper)),
m_UncheckedTrivialBitCastInst(m_SILValue(Oper))))) {
return Builder.createUncheckedBitwiseCast(UBCI->getLoc(), Oper,
UBCI->getType());
}
if (UBCI->getType().isTrivial(UBCI->getModule()))
return Builder.createUncheckedTrivialBitCast(UBCI->getLoc(),
UBCI->getOperand(),
UBCI->getType());
if (auto refCast = Builder.tryCreateUncheckedRefCast(
UBCI->getLoc(), UBCI->getOperand(), UBCI->getType()))
return refCast;
return nullptr;
}
SILInstruction *
SILCombiner::visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *TTOCMI) {
// Perform the following transformations:
// (thick_to_objc_metatype (metatype @thick)) ->
// (metatype @objc_metatype)
//
// (thick_to_objc_metatype (value_metatype @thick)) ->
// (value_metatype @objc_metatype)
//
// (thick_to_objc_metatype (existential_metatype @thick)) ->
// (existential_metatype @objc_metatype)
if (CastOpt.optimizeMetatypeConversion(TTOCMI, MetatypeRepresentation::Thick))
MadeChange = true;
return nullptr;
}
SILInstruction *
SILCombiner::visitObjCToThickMetatypeInst(ObjCToThickMetatypeInst *OCTTMI) {
// Perform the following transformations:
// (objc_to_thick_metatype (metatype @objc_metatype)) ->
// (metatype @thick)
//
// (objc_to_thick_metatype (value_metatype @objc_metatype)) ->
// (value_metatype @thick)
//
// (objc_to_thick_metatype (existential_metatype @objc_metatype)) ->
// (existential_metatype @thick)
if (CastOpt.optimizeMetatypeConversion(OCTTMI, MetatypeRepresentation::ObjC))
MadeChange = true;
return nullptr;
}
SILInstruction *
SILCombiner::visitCheckedCastBranchInst(CheckedCastBranchInst *CBI) {
if (CastOpt.optimizeCheckedCastBranchInst(CBI))
MadeChange = true;
return nullptr;
}
SILInstruction *
SILCombiner::
visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CCABI) {
if (CastOpt.optimizeCheckedCastAddrBranchInst(CCABI))
MadeChange = true;
return nullptr;
}
SILInstruction *SILCombiner::visitConvertEscapeToNoEscapeInst(
ConvertEscapeToNoEscapeInst *Cvt) {
auto *OrigThinToThick =
dyn_cast<ThinToThickFunctionInst>(Cvt->getConverted());
if (!OrigThinToThick)
return nullptr;
auto origFunType = OrigThinToThick->getType().getAs<SILFunctionType>();
auto NewTy = origFunType->getWithExtInfo(origFunType->getExtInfo().withNoEscape(true));
return Builder.createThinToThickFunction(
OrigThinToThick->getLoc(), OrigThinToThick->getOperand(),
SILType::getPrimitiveObjectType(NewTy));
}
/// Replace a convert_function that only has refcounting uses with its
/// operand.
SILInstruction *SILCombiner::visitConvertFunctionInst(ConvertFunctionInst *CFI) {
auto anyNonRefCountUse =
std::any_of(CFI->use_begin(),
CFI->use_end(),
[](Operand *Use) {
return !isa<RefCountingInst>(Use->getUser());
});
if (anyNonRefCountUse)
return nullptr;
// Replace all retain/releases on convert_function by retain/releases on
// its argument. This is required to preserve the lifetime of its argument,
// which could be e.g. a partial_apply instruction capturing some further
// arguments.
auto Converted = CFI->getConverted();
while (!CFI->use_empty()) {
auto *Use = *(CFI->use_begin());
assert(Use->getUser()->getResults().empty() &&
"Did not expect user with a result!");
Use->set(Converted);
}
eraseInstFromFunction(*CFI);
return nullptr;
}