mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
685 lines
23 KiB
C++
685 lines
23 KiB
C++
//===--- SimplifyInstruction.cpp - Fold instructions ----------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "sil-simplify"
|
|
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
|
|
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
|
|
#include "swift/SILOptimizer/Utils/Local.h"
|
|
#include "swift/SIL/PatternMatch.h"
|
|
#include "swift/SIL/SILVisitor.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::PatternMatch;
|
|
|
|
namespace swift {
|
|
class ASTContext;
|
|
} // end namespace swift
|
|
|
|
namespace {
|
|
class InstSimplifier : public SILInstructionVisitor<InstSimplifier, SILValue>{
|
|
public:
|
|
SILValue visitSILInstruction(SILInstruction *I) { return SILValue(); }
|
|
|
|
SILValue visitTupleExtractInst(TupleExtractInst *TEI);
|
|
SILValue visitStructExtractInst(StructExtractInst *SEI);
|
|
SILValue visitEnumInst(EnumInst *EI);
|
|
SILValue visitSelectEnumInst(SelectEnumInst *SEI);
|
|
SILValue visitUncheckedEnumDataInst(UncheckedEnumDataInst *UEDI);
|
|
SILValue visitAddressToPointerInst(AddressToPointerInst *ATPI);
|
|
SILValue visitPointerToAddressInst(PointerToAddressInst *PTAI);
|
|
SILValue visitRefToRawPointerInst(RefToRawPointerInst *RRPI);
|
|
SILValue
|
|
visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI);
|
|
SILValue visitUncheckedRefCastInst(UncheckedRefCastInst *OPRI);
|
|
SILValue visitUncheckedAddrCastInst(UncheckedAddrCastInst *UACI);
|
|
SILValue visitStructInst(StructInst *SI);
|
|
SILValue visitTupleInst(TupleInst *SI);
|
|
SILValue visitBuiltinInst(BuiltinInst *AI);
|
|
SILValue visitUpcastInst(UpcastInst *UI);
|
|
SILValue visitRefToUnownedInst(RefToUnownedInst *RUI);
|
|
SILValue visitUnownedToRefInst(UnownedToRefInst *URI);
|
|
SILValue visitRefToUnmanagedInst(RefToUnmanagedInst *RUI);
|
|
SILValue visitUnmanagedToRefInst(UnmanagedToRefInst *URI);
|
|
SILValue visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI);
|
|
SILValue
|
|
visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI);
|
|
SILValue visitThinFunctionToPointerInst(ThinFunctionToPointerInst *TFTPI);
|
|
SILValue visitPointerToThinFunctionInst(PointerToThinFunctionInst *PTTFI);
|
|
|
|
SILValue simplifyOverflowBuiltin(BuiltinInst *BI);
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
SILValue InstSimplifier::visitStructInst(StructInst *SI) {
|
|
// Ignore empty structs.
|
|
if (SI->getNumOperands() < 1)
|
|
return SILValue();
|
|
|
|
// Optimize structs that are generated from struct_extract instructions
|
|
// from the same struct.
|
|
if (auto *Ex0 = dyn_cast<StructExtractInst>(SI->getOperand(0))) {
|
|
// Check that the constructed struct and the extracted struct are of the
|
|
// same type.
|
|
if (SI->getType() != Ex0->getOperand()->getType())
|
|
return SILValue();
|
|
|
|
// Check that all of the operands are extracts of the correct kind.
|
|
for (unsigned i = 0, e = SI->getNumOperands(); i < e; i++) {
|
|
auto *Ex = dyn_cast<StructExtractInst>(SI->getOperand(i));
|
|
// Must be an extract.
|
|
if (!Ex)
|
|
return SILValue();
|
|
|
|
// Extract from the same struct as the first extract_inst.
|
|
if (Ex0->getOperand() != Ex->getOperand())
|
|
return SILValue();
|
|
|
|
// And the order of the field must be identical to the construction order.
|
|
if (Ex->getFieldNo() != i)
|
|
return SILValue();
|
|
}
|
|
|
|
return Ex0->getOperand();
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitTupleInst(TupleInst *TI) {
|
|
// Ignore empty tuples.
|
|
if (TI->getNumOperands() < 1)
|
|
return SILValue();
|
|
|
|
// Optimize tuples that are generated from tuple_extract instructions
|
|
// from the same tuple.
|
|
if (auto *Ex0 = dyn_cast<TupleExtractInst>(TI->getOperand(0))) {
|
|
// Check that the constructed tuple and the extracted tuple are of the
|
|
// same type.
|
|
if (TI->getType() != Ex0->getOperand()->getType())
|
|
return SILValue();
|
|
|
|
// Check that all of the operands are extracts of the correct kind.
|
|
for (unsigned i = 0, e = TI->getNumOperands(); i < e; i++) {
|
|
auto *Ex = dyn_cast<TupleExtractInst>(TI->getOperand(i));
|
|
// Must be an extract.
|
|
if (!Ex)
|
|
return SILValue();
|
|
|
|
// Extract from the same struct as the first extract_inst.
|
|
if (Ex0->getOperand() != Ex->getOperand())
|
|
return SILValue();
|
|
|
|
// And the order of the field must be identical to the construction order.
|
|
if (Ex->getFieldNo() != i)
|
|
return SILValue();
|
|
}
|
|
|
|
return Ex0->getOperand();
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitTupleExtractInst(TupleExtractInst *TEI) {
|
|
// tuple_extract(tuple(x, y), 0) -> x
|
|
if (TupleInst *TheTuple = dyn_cast<TupleInst>(TEI->getOperand()))
|
|
return TheTuple->getElement(TEI->getFieldNo());
|
|
|
|
// tuple_extract(apply([add|sub|...]overflow(x,y)), 0) -> x
|
|
// tuple_extract(apply(checked_trunc(ext(x))), 0) -> x
|
|
if (TEI->getFieldNo() == 0)
|
|
if (BuiltinInst *BI = dyn_cast<BuiltinInst>(TEI->getOperand()))
|
|
return simplifyOverflowBuiltin(BI);
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitStructExtractInst(StructExtractInst *SEI) {
|
|
// struct_extract(struct(x, y), x) -> x
|
|
if (StructInst *Struct = dyn_cast<StructInst>(SEI->getOperand()))
|
|
return Struct->getFieldValue(SEI->getField());
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::
|
|
visitUncheckedEnumDataInst(UncheckedEnumDataInst *UEDI) {
|
|
// (unchecked_enum_data (enum payload)) -> payload
|
|
if (EnumInst *EI = dyn_cast<EnumInst>(UEDI->getOperand())) {
|
|
if (EI->getElement() != UEDI->getElement())
|
|
return SILValue();
|
|
|
|
assert(EI->hasOperand() &&
|
|
"Should only get data from an enum with payload.");
|
|
return EI->getOperand();
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
// Simplify
|
|
// %1 = unchecked_enum_data %0 : $Optional<C>, #Optional.Some!enumelt.1 // user: %27
|
|
// %2 = enum $Optional<C>, #Optional.Some!enumelt.1, %1 : $C // user: %28
|
|
// to %0 since we are building the same enum.
|
|
static SILValue simplifyEnumFromUncheckedEnumData(EnumInst *EI) {
|
|
assert(EI->hasOperand() && "Expected an enum with an operand!");
|
|
|
|
auto *UEDI = dyn_cast<UncheckedEnumDataInst>(EI->getOperand());
|
|
if (!UEDI || UEDI->getElement() != EI->getElement())
|
|
return SILValue();
|
|
|
|
SILValue EnumOp = UEDI->getOperand();
|
|
|
|
// Same enum elements don't necessarily imply same enum types.
|
|
// Enum types may be different if the enum is generic, e.g.
|
|
// E<Int>.Case and E<Double>.Case.
|
|
SILType OriginalEnum = EnumOp->getType();
|
|
SILType NewEnum = EI->getType();
|
|
|
|
if (OriginalEnum != NewEnum)
|
|
return SILValue();
|
|
|
|
return EnumOp;
|
|
}
|
|
|
|
SILValue InstSimplifier::visitSelectEnumInst(SelectEnumInst *SEI) {
|
|
|
|
auto *EI = dyn_cast<EnumInst>(SEI->getEnumOperand());
|
|
if (EI && EI->getType() == SEI->getEnumOperand()->getType()) {
|
|
// Simplify a select_enum on an enum instruction.
|
|
// %27 = enum $Optional<Int>, #Optional.Some!enumelt.1, %20 : $Int
|
|
// %28 = integer_literal $Builtin.Int1, -1
|
|
// %29 = integer_literal $Builtin.Int1, 0
|
|
// %30 = select_enum %27 : $Optional<Int>, case #Optional.None!enumelt: %28,
|
|
// case #Optional.Some!enumelt.1: %29
|
|
// We will return %29.
|
|
return SEI->getCaseResult(EI->getElement());
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitEnumInst(EnumInst *EI) {
|
|
if (EI->hasOperand()) {
|
|
auto Result = simplifyEnumFromUncheckedEnumData(EI);
|
|
if (Result)
|
|
return Result;
|
|
|
|
// switch_enum %e : $EnumTy, case %casex: bbX,...
|
|
// bbX(%arg):
|
|
// enum $EnumTy, EnumTy::casex, %arg
|
|
// ->
|
|
// replace enum $EnumTy, EnumTy::casex, %arg by %e
|
|
|
|
auto Op = EI->getOperand();
|
|
|
|
auto *EnumArg = dyn_cast<SILArgument>(Op);
|
|
if (!EnumArg)
|
|
return SILValue();
|
|
|
|
SILBasicBlock *EnumBlock = EI->getParent();
|
|
if (EnumArg->getParent() != EnumBlock)
|
|
return SILValue();
|
|
|
|
auto *Pred = EnumBlock->getSinglePredecessor();
|
|
if (!Pred)
|
|
return SILValue();
|
|
|
|
auto *SEI = dyn_cast<SwitchEnumInst>(Pred->getTerminator());
|
|
if (!SEI)
|
|
return SILValue();
|
|
|
|
auto Case = SEI->getUniqueCaseForDestination(EI->getParent());
|
|
|
|
if (Case && Case.getPtrOrNull() == EI->getElement() &&
|
|
SEI->getOperand()->getType() == EI->getType()) {
|
|
return SEI->getOperand();
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
// Simplify enum insts to the value from a switch_enum when possible, e.g.
|
|
// for
|
|
// switch_enum %0 : $Bool, case #Bool.true!enumelt: bb1
|
|
// bb1:
|
|
// %1 = enum $Bool, #Bool.true!enumelt
|
|
//
|
|
// we'll return %0
|
|
auto *BB = EI->getParent();
|
|
auto *Pred = BB->getSinglePredecessor();
|
|
if (!Pred)
|
|
return SILValue();
|
|
|
|
if (auto *SEI = dyn_cast<SwitchEnumInst>(Pred->getTerminator())) {
|
|
if (EI->getType() != SEI->getOperand()->getType())
|
|
return SILValue();
|
|
|
|
if (EI->getElement() == SEI->getUniqueCaseForDestination(BB).getPtrOrNull())
|
|
return SEI->getOperand();
|
|
}
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitAddressToPointerInst(AddressToPointerInst *ATPI) {
|
|
// (address_to_pointer (pointer_to_address x)) -> x
|
|
if (auto *PTAI = dyn_cast<PointerToAddressInst>(ATPI->getOperand()))
|
|
if (PTAI->getType() == ATPI->getOperand()->getType())
|
|
return PTAI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitPointerToAddressInst(PointerToAddressInst *PTAI) {
|
|
// (pointer_to_address (address_to_pointer x)) -> x
|
|
if (auto *ATPI = dyn_cast<AddressToPointerInst>(PTAI->getOperand()))
|
|
if (ATPI->getOperand()->getType() == PTAI->getType())
|
|
return ATPI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitRefToRawPointerInst(RefToRawPointerInst *RefToRaw) {
|
|
// Perform the following simplification:
|
|
//
|
|
// (ref_to_raw_pointer (raw_pointer_to_ref x)) -> x
|
|
//
|
|
// *NOTE* We don't need to check types here.
|
|
if (auto *RawToRef = dyn_cast<RawPointerToRefInst>(&*RefToRaw->getOperand()))
|
|
return RawToRef->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::
|
|
visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI) {
|
|
// (UCCI downcast (upcast x #type1 to #type2) #type2 to #type1) -> x
|
|
if (auto *upcast = dyn_cast<UpcastInst>(UCCI->getOperand()))
|
|
if (UCCI->getType() == upcast->getOperand()->getType())
|
|
return upcast->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::
|
|
visitUncheckedRefCastInst(UncheckedRefCastInst *OPRI) {
|
|
// (unchecked-ref-cast Y->X (unchecked-ref-cast x X->Y)) -> x
|
|
if (auto *ROPI = dyn_cast<UncheckedRefCastInst>(&*OPRI->getOperand()))
|
|
if (ROPI->getOperand()->getType() == OPRI->getType())
|
|
return ROPI->getOperand();
|
|
|
|
// (unchecked-ref-cast Y->X (upcast x X->Y)) -> x
|
|
if (auto *UI = dyn_cast<UpcastInst>(OPRI->getOperand()))
|
|
if (UI->getOperand()->getType() == OPRI->getType())
|
|
return UI->getOperand();
|
|
|
|
// (unchecked-ref-cast Y->X (open_existential_ref x X->Y)) -> x
|
|
if (auto *OER = dyn_cast<OpenExistentialRefInst>(OPRI->getOperand()))
|
|
if (OER->getOperand()->getType() == OPRI->getType())
|
|
return OER->getOperand();
|
|
|
|
// (unchecked-ref-cast X->X x) -> x
|
|
if (OPRI->getOperand()->getType() == OPRI->getType())
|
|
return OPRI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::
|
|
visitUncheckedAddrCastInst(UncheckedAddrCastInst *UACI) {
|
|
// (unchecked-addr-cast Y->X (unchecked-addr-cast x X->Y)) -> x
|
|
if (auto *OtherUACI = dyn_cast<UncheckedAddrCastInst>(&*UACI->getOperand()))
|
|
if (OtherUACI->getOperand()->getType() == UACI->getType())
|
|
return OtherUACI->getOperand();
|
|
|
|
// (unchecked-addr-cast X->X x) -> x
|
|
if (UACI->getOperand()->getType() == UACI->getType())
|
|
return UACI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitUpcastInst(UpcastInst *UI) {
|
|
// (upcast Y->X (unchecked-ref-cast x X->Y)) -> x
|
|
if (auto *URCI = dyn_cast<UncheckedRefCastInst>(UI->getOperand()))
|
|
if (URCI->getOperand()->getType() == UI->getType())
|
|
return URCI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::visitRefToUnownedInst(RefToUnownedInst *RUI) {
|
|
if (auto *URI = dyn_cast<UnownedToRefInst>(RUI->getOperand()))
|
|
if (URI->getOperand()->getType() == RUI->getType())
|
|
return URI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::visitUnownedToRefInst(UnownedToRefInst *URI) {
|
|
if (auto *RUI = dyn_cast<RefToUnownedInst>(URI->getOperand()))
|
|
if (RUI->getOperand()->getType() == URI->getType())
|
|
return RUI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::visitRefToUnmanagedInst(RefToUnmanagedInst *RUI) {
|
|
if (auto *URI = dyn_cast<UnmanagedToRefInst>(RUI->getOperand()))
|
|
if (URI->getOperand()->getType() == RUI->getType())
|
|
return URI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::visitUnmanagedToRefInst(UnmanagedToRefInst *URI) {
|
|
if (auto *RUI = dyn_cast<RefToUnmanagedInst>(URI->getOperand()))
|
|
if (RUI->getOperand()->getType() == URI->getType())
|
|
return RUI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::
|
|
visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI) {
|
|
// (unchecked_trivial_bit_cast X->X x) -> x
|
|
if (UTBCI->getOperand()->getType() == UTBCI->getType())
|
|
return UTBCI->getOperand();
|
|
|
|
// (unchecked_trivial_bit_cast Y->X (unchecked_trivial_bit_cast X->Y x)) -> x
|
|
if (auto *Op = dyn_cast<UncheckedTrivialBitCastInst>(UTBCI->getOperand()))
|
|
if (Op->getOperand()->getType() == UTBCI->getType())
|
|
return Op->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue
|
|
InstSimplifier::
|
|
visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {
|
|
// (unchecked_bitwise_cast X->X x) -> x
|
|
if (UBCI->getOperand()->getType() == UBCI->getType())
|
|
return UBCI->getOperand();
|
|
|
|
// A round-trip cast implies X and Y have the same size:
|
|
// (unchecked_bitwise_cast Y->X (unchecked_bitwise_cast X->Y x)) -> x
|
|
if (auto *Op = dyn_cast<UncheckedBitwiseCastInst>(UBCI->getOperand()))
|
|
if (Op->getOperand()->getType() == UBCI->getType())
|
|
return Op->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitThinFunctionToPointerInst(ThinFunctionToPointerInst *TFTPI) {
|
|
// (thin_function_to_pointer (pointer_to_thin_function x)) -> x
|
|
if (auto *PTTFI = dyn_cast<PointerToThinFunctionInst>(TFTPI->getOperand()))
|
|
if (PTTFI->getOperand()->getType() == TFTPI->getType())
|
|
return PTTFI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
SILValue InstSimplifier::visitPointerToThinFunctionInst(PointerToThinFunctionInst *PTTFI) {
|
|
// (pointer_to_thin_function (thin_function_to_pointer x)) -> x
|
|
if (auto *TFTPI = dyn_cast<ThinFunctionToPointerInst>(PTTFI->getOperand()))
|
|
if (TFTPI->getOperand()->getType() == PTTFI->getType())
|
|
return TFTPI->getOperand();
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
static SILValue simplifyBuiltin(BuiltinInst *BI) {
|
|
const IntrinsicInfo &Intrinsic = BI->getIntrinsicInfo();
|
|
|
|
switch (Intrinsic.ID) {
|
|
default:
|
|
// TODO: Handle some of the llvm intrinsics here.
|
|
return SILValue();
|
|
case llvm::Intrinsic::not_intrinsic:
|
|
break;
|
|
case llvm::Intrinsic::expect:
|
|
// If we have an expect optimizer hint with a constant value input,
|
|
// there is nothing left to expect so propagate the input, i.e.,
|
|
//
|
|
// apply(expect, constant, _) -> constant.
|
|
if (auto *Literal = dyn_cast<IntegerLiteralInst>(BI->getArguments()[0]))
|
|
return Literal;
|
|
return SILValue();
|
|
}
|
|
|
|
// Otherwise, it should be one of the builtin functions.
|
|
OperandValueArrayRef Args = BI->getArguments();
|
|
const BuiltinInfo &Builtin = BI->getBuiltinInfo();
|
|
|
|
switch (Builtin.ID) {
|
|
default: break;
|
|
|
|
case BuiltinValueKind::TruncOrBitCast: {
|
|
const SILValue &Op = Args[0];
|
|
SILValue Result;
|
|
// trunc(extOrBitCast(x)) -> x
|
|
if (match(Op, m_ExtOrBitCast(m_SILValue(Result)))) {
|
|
// Truncated back to the same bits we started with.
|
|
if (Result->getType() == BI->getType())
|
|
return Result;
|
|
}
|
|
|
|
// trunc(tuple_extract(conversion(extOrBitCast(x))))) -> x
|
|
if (match(Op, m_TupleExtractInst(
|
|
m_CheckedConversion(
|
|
m_ExtOrBitCast(m_SILValue(Result))), 0))) {
|
|
// If the top bit of Result is known to be 0, then
|
|
// it is safe to replace the whole pattern by original bits of x
|
|
if (Result->getType() == BI->getType()) {
|
|
if (auto signBit = computeSignBit(Result))
|
|
if (!signBit.getValue())
|
|
return Result;
|
|
}
|
|
}
|
|
return SILValue();
|
|
}
|
|
|
|
case BuiltinValueKind::Xor: {
|
|
SILValue val1, val2, val3;
|
|
// xor (xor (val1, val2), val3) == val1
|
|
if (BI->getNumOperands() == 2 &&
|
|
(match(BI,
|
|
m_BuiltinInst(BuiltinValueKind::Xor,
|
|
m_BuiltinInst(BuiltinValueKind::Xor,
|
|
m_SILValue(val1), m_SILValue(val2)),
|
|
m_SILValue(val3))) ||
|
|
match(BI, m_BuiltinInst(BuiltinValueKind::Xor, m_SILValue(val3),
|
|
m_BuiltinInst(BuiltinValueKind::Xor,
|
|
m_SILValue(val1),
|
|
m_SILValue(val2)))))) {
|
|
|
|
if (val2 == val3)
|
|
return val1;
|
|
if (val1 == val3)
|
|
return val2;
|
|
if (val1 == val2)
|
|
return val3;
|
|
}
|
|
}
|
|
}
|
|
return SILValue();
|
|
}
|
|
|
|
/// Simplify an apply of the builtin canBeClass to either 0 or 1
|
|
/// when we can statically determine the result.
|
|
SILValue InstSimplifier::visitBuiltinInst(BuiltinInst *BI) {
|
|
return simplifyBuiltin(BI);
|
|
}
|
|
|
|
/// \brief Simplify arithmetic intrinsics with overflow and known identity
|
|
/// constants such as 0 and 1.
|
|
/// If this returns a value other than SILValue() then the instruction was
|
|
/// simplified to a value which doesn't overflow. The overflow case is handled
|
|
/// in SILCombine.
|
|
static SILValue simplifyBinaryWithOverflow(BuiltinInst *BI,
|
|
llvm::Intrinsic::ID ID) {
|
|
OperandValueArrayRef Args = BI->getArguments();
|
|
assert(Args.size() >= 2);
|
|
|
|
const SILValue &Op1 = Args[0];
|
|
const SILValue &Op2 = Args[1];
|
|
|
|
IntegerLiteralInst *IntOp1 = dyn_cast<IntegerLiteralInst>(Op1);
|
|
IntegerLiteralInst *IntOp2 = dyn_cast<IntegerLiteralInst>(Op2);
|
|
|
|
// If both ops are not constants, we cannot do anything.
|
|
// FIXME: Add cases where we can do something, eg, (x - x) -> 0
|
|
if (!IntOp1 && !IntOp2)
|
|
return SILValue();
|
|
|
|
// Calculate the result.
|
|
|
|
switch (ID) {
|
|
default: llvm_unreachable("Invalid case");
|
|
case llvm::Intrinsic::sadd_with_overflow:
|
|
case llvm::Intrinsic::uadd_with_overflow:
|
|
// 0 + X -> X
|
|
if (match(Op1, m_Zero()))
|
|
return Op2;
|
|
// X + 0 -> X
|
|
if (match(Op2, m_Zero()))
|
|
return Op1;
|
|
return SILValue();
|
|
case llvm::Intrinsic::ssub_with_overflow:
|
|
case llvm::Intrinsic::usub_with_overflow:
|
|
// X - 0 -> X
|
|
if (match(Op2, m_Zero()))
|
|
return Op1;
|
|
return SILValue();
|
|
case llvm::Intrinsic::smul_with_overflow:
|
|
case llvm::Intrinsic::umul_with_overflow:
|
|
// 0 * X -> 0
|
|
if (match(Op1, m_Zero()))
|
|
return Op1;
|
|
// X * 0 -> 0
|
|
if (match(Op2, m_Zero()))
|
|
return Op2;
|
|
// 1 * X -> X
|
|
if (match(Op1, m_One()))
|
|
return Op2;
|
|
// X * 1 -> X
|
|
if (match(Op2, m_One()))
|
|
return Op1;
|
|
return SILValue();
|
|
}
|
|
return SILValue();
|
|
}
|
|
|
|
/// Simplify operations that may overflow. All such operations return a tuple.
|
|
/// This function simplifies such operations, but returns only the first
|
|
/// element of a tuple. It looks strange at the first glance, but this
|
|
/// is OK, because this function is invoked only internally when processing
|
|
/// tuple_extract instructions. Therefore the result of this function
|
|
/// is used for simplifications like tuple_extract(x, 0) -> simplified(x)
|
|
SILValue InstSimplifier::simplifyOverflowBuiltin(BuiltinInst *BI) {
|
|
const IntrinsicInfo &Intrinsic = BI->getIntrinsicInfo();
|
|
|
|
// If it's an llvm intrinsic, fold the intrinsic.
|
|
switch (Intrinsic.ID) {
|
|
default:
|
|
return SILValue();
|
|
case llvm::Intrinsic::not_intrinsic:
|
|
break;
|
|
case llvm::Intrinsic::sadd_with_overflow:
|
|
case llvm::Intrinsic::uadd_with_overflow:
|
|
case llvm::Intrinsic::ssub_with_overflow:
|
|
case llvm::Intrinsic::usub_with_overflow:
|
|
case llvm::Intrinsic::smul_with_overflow:
|
|
case llvm::Intrinsic::umul_with_overflow:
|
|
return simplifyBinaryWithOverflow(BI, Intrinsic.ID);
|
|
}
|
|
|
|
// Otherwise, it should be one of the builtin functions.
|
|
const BuiltinInfo &Builtin = BI->getBuiltinInfo();
|
|
|
|
switch (Builtin.ID) {
|
|
default: break;
|
|
|
|
case BuiltinValueKind::SUCheckedConversion:
|
|
case BuiltinValueKind::USCheckedConversion: {
|
|
OperandValueArrayRef Args = BI->getArguments();
|
|
const SILValue &Op = Args[0];
|
|
if (auto signBit = computeSignBit(Op))
|
|
if (!signBit.getValue())
|
|
return Op;
|
|
SILValue Result;
|
|
// CheckedConversion(ExtOrBitCast(x)) -> x
|
|
if (match(BI, m_CheckedConversion(m_ExtOrBitCast(m_SILValue(Result)))))
|
|
if (Result->getType() == BI->getType().getTupleElementType(0)) {
|
|
assert (!computeSignBit(Result).getValue() && "Sign bit should be 0");
|
|
return Result;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BuiltinValueKind::UToSCheckedTrunc:
|
|
case BuiltinValueKind::UToUCheckedTrunc:
|
|
case BuiltinValueKind::SToUCheckedTrunc:
|
|
case BuiltinValueKind::SToSCheckedTrunc: {
|
|
SILValue Result;
|
|
// CheckedTrunc(Ext(x)) -> x
|
|
if (match(BI, m_CheckedTrunc(m_Ext(m_SILValue(Result)))))
|
|
if (Result->getType() == BI->getType().getTupleElementType(0))
|
|
if (auto signBit = computeSignBit(Result))
|
|
if (!signBit.getValue())
|
|
return Result;
|
|
}
|
|
break;
|
|
|
|
// Check and simplify binary arithmetic with overflow.
|
|
#define BUILTIN(id, name, Attrs)
|
|
#define BUILTIN_BINARY_OPERATION_WITH_OVERFLOW(id, name, _, attrs, overload) \
|
|
case BuiltinValueKind::id:
|
|
#include "swift/AST/Builtins.def"
|
|
return simplifyBinaryWithOverflow(BI,
|
|
getLLVMIntrinsicIDForBuiltinWithOverflow(Builtin.ID));
|
|
|
|
}
|
|
return SILValue();
|
|
}
|
|
|
|
/// \brief Try to simplify the specified instruction, performing local
|
|
/// analysis of the operands of the instruction, without looking at its uses
|
|
/// (e.g. constant folding). If a simpler result can be found, it is
|
|
/// returned, otherwise a null SILValue is returned.
|
|
///
|
|
SILValue swift::simplifyInstruction(SILInstruction *I) {
|
|
return InstSimplifier().visit(I);
|
|
}
|
|
|
|
/// Simplify invocations of builtin operations that may overflow.
|
|
/// All such operations return a tuple (result, overflow_flag).
|
|
/// This function try to simplify such operations, but returns only a
|
|
/// simplified first element of a tuple. The overflow flag is not returned
|
|
/// explicitly, because this simplification is only possible if there is
|
|
/// no overflow. Therefore the overflow flag is known to have a value of 0 if
|
|
/// simplification was successful.
|
|
/// In case when a simplification is not possible, a null SILValue is returned.
|
|
SILValue swift::simplifyOverflowBuiltinInstruction(BuiltinInst *BI) {
|
|
return InstSimplifier().simplifyOverflowBuiltin(BI);
|
|
}
|