Files
swift-mirror/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp
John McCall ab3f77baf2 Make SILInstruction no longer a subclass of ValueBase and
introduce a common superclass, SILNode.

This is in preparation for allowing instructions to have multiple
results.  It is also a somewhat more elegant representation for
instructions that have zero results.  Instructions that are known
to have exactly one result inherit from a class, SingleValueInstruction,
that subclasses both ValueBase and SILInstruction.  Some care must be
taken when working with SILNode pointers and testing for equality;
please see the comment on SILNode for more information.

A number of SIL passes needed to be updated in order to handle this
new distinction between SIL values and SIL instructions.

Note that the SIL parser is now stricter about not trying to assign
a result value from an instruction (like 'return' or 'strong_retain')
that does not produce any.
2017-09-25 02:06:26 -04:00

571 lines
21 KiB
C++

//===--- SILCombinerBuiltinVisitors.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::optimizeBuiltinCompareEq(BuiltinInst *BI,
bool NegateResult) {
// Canonicalize boolean comparisons.
if (auto OpTy = BI->getArguments()[0]->getType().getAs<BuiltinIntegerType>())
if (OpTy->isFixedWidth(1))
// cmp_eq %X, -1 -> xor (cmp_eq %X, 0), -1
if (!NegateResult) {
if (auto *ILOp = dyn_cast<IntegerLiteralInst>(BI->getArguments()[1]))
if (ILOp->getValue().isAllOnesValue()) {
auto X = BI->getArguments()[0];
SILValue One(ILOp);
SILValue Zero(
Builder.createIntegerLiteral(BI->getLoc(), BI->getType(), 0));
SILValue Inverted(Builder.createBuiltin(
BI->getLoc(), BI->getName(), BI->getType(), {}, {X, Zero}));
auto *Xor = Builder.createBuiltinBinaryFunction(
BI->getLoc(), "xor", BI->getType(), BI->getType(),
{Inverted, One});
replaceInstUsesWith(*BI, Xor);
return eraseInstFromFunction(*BI);
}
}
IsZeroKind LHS = isZeroValue(BI->getArguments()[0]);
IsZeroKind RHS = isZeroValue(BI->getArguments()[1]);
// Can't handle unknown values.
if (LHS == IsZeroKind::Unknown) {
return nullptr;
}
// Canonicalize i1_const == X to X == i1_const.
// Canonicalize i1_const != X to X != i1_const.
if (RHS == IsZeroKind::Unknown) {
auto *CanonI =
Builder.createBuiltin(BI->getLoc(), BI->getName(), BI->getType(), {},
{BI->getArguments()[1], BI->getArguments()[0]});
replaceInstUsesWith(*BI, CanonI);
return eraseInstFromFunction(*BI);
}
// Can't handle non-zero ptr values.
if (LHS == IsZeroKind::NotZero && RHS == IsZeroKind::NotZero)
return nullptr;
// Set to true if both sides are zero. Set to false if only one side is zero.
bool Val = (LHS == RHS) ^ NegateResult;
return Builder.createIntegerLiteral(BI->getLoc(), BI->getType(),
APInt(1, Val));
}
SILInstruction *SILCombiner::optimizeBuiltinCanBeObjCClass(BuiltinInst *BI) {
assert(BI->hasSubstitutions() && "Expected substitutions for canBeClass");
auto const &Subs = BI->getSubstitutions();
assert((Subs.size() == 1) &&
"Expected one substitution in call to canBeClass");
auto Ty = Subs[0].getReplacement()->getCanonicalType();
switch (Ty->canBeClass()) {
case TypeTraitResult::IsNot:
return Builder.createIntegerLiteral(BI->getLoc(), BI->getType(),
APInt(8, 0));
case TypeTraitResult::Is:
return Builder.createIntegerLiteral(BI->getLoc(), BI->getType(),
APInt(8, 1));
case TypeTraitResult::CanBe:
return nullptr;
}
llvm_unreachable("Unhandled TypeTraitResult in switch.");
}
static unsigned getTypeWidth(SILType Ty) {
if (auto BuiltinIntTy = Ty.getAs<BuiltinIntegerType>()) {
if (BuiltinIntTy->isFixedWidth()) {
return BuiltinIntTy->getFixedWidth();
}
}
return 0;
}
SILInstruction *SILCombiner::optimizeBuiltinTruncOrBitCast(BuiltinInst *I) {
assert(I->getBuiltinInfo().ID == BuiltinValueKind::TruncOrBitCast &&
"BuiltinInst is not Trunc");
SILValue Op = I->getArguments()[0];
SILValue Source;
if (match(Op, m_ZExtOrBitCast(m_SILValue(Source)))) {
SILType ResultType = I->getType();
SILType SourceType = Source->getType();
SILType SourceTargetType = Op->getType();
unsigned ResultTypeWidth = getTypeWidth(ResultType);
unsigned SourceTypeWidth = getTypeWidth(SourceType);
unsigned SourceTargetTypeWidth = getTypeWidth(SourceTargetType);
if (ResultTypeWidth == 0 || SourceTypeWidth == 0 ||
SourceTargetTypeWidth == 0) {
// Not all types involved have fixed width
return nullptr;
}
if (SourceTargetTypeWidth <= SourceTypeWidth) {
return nullptr;
}
if (ResultTypeWidth < SourceTypeWidth) {
return nullptr;
}
// Reach here if and only if:
// SourceTargetTypeWidth > SourceTypeWidth and ResultTypeWidth >=
// SourceTypeWidth
auto *NI = Builder.createBuiltinBinaryFunctionWithTwoOpTypes(
I->getLoc(), "zextOrBitCast", Source->getType(), ResultType, ResultType,
Source);
replaceInstUsesWith(*I, NI);
return eraseInstFromFunction(*I);
}
return nullptr;
}
SILInstruction *SILCombiner::optimizeBuiltinZextOrBitCast(BuiltinInst *I) {
assert(I->getBuiltinInfo().ID == BuiltinValueKind::ZExtOrBitCast &&
"BuiltinInst is not ZExt");
SILValue Op = I->getArguments()[0];
SILValue Source;
if (match(Op, m_ZExtOrBitCast(m_SILValue(Source)))) {
SILType ResultType = I->getType();
// We can't extend to a size *smaller* than the source.
// As such, this transformation will always maintain correctness
auto *NI = Builder.createBuiltinBinaryFunctionWithTwoOpTypes(
I->getLoc(), "zextOrBitCast", Source->getType(), ResultType, ResultType,
Source);
replaceInstUsesWith(*I, NI);
return eraseInstFromFunction(*I);
}
return nullptr;
}
/// Optimize builtins which receive the same value in their first and second
/// operand.
static SILInstruction *optimizeBuiltinWithSameOperands(SILBuilder &Builder,
BuiltinInst *I,
SILCombiner *C) {
// Handle all builtins which can be optimized.
// We have to take special care about floating point operations because of
// potential NaN values. E.g. ordered equal FCMP_OEQ(Nan, Nan) is not true.
switch (I->getBuiltinInfo().ID) {
// Replace the uses with one of the (identical) operands.
case BuiltinValueKind::And:
case BuiltinValueKind::Or: {
// We cannot just _return_ the operand because it is not necessarily an
// instruction. It can be an argument.
SILValue Op = I->getOperand(0);
C->replaceInstUsesWith(*I, Op);
break;
}
// Return 0 or false.
case BuiltinValueKind::Sub:
case BuiltinValueKind::SRem:
case BuiltinValueKind::URem:
case BuiltinValueKind::Xor:
case BuiltinValueKind::ICMP_NE:
case BuiltinValueKind::ICMP_SLT:
case BuiltinValueKind::ICMP_SGT:
case BuiltinValueKind::ICMP_ULT:
case BuiltinValueKind::ICMP_UGT:
case BuiltinValueKind::FCMP_ONE:
if (auto Ty = I->getType().getAs<BuiltinIntegerType>()) {
// FIXME: Should also use SILBuilderWithScope?
return Builder.createIntegerLiteral(I->getLoc(), I->getType(),
APInt(Ty->getGreatestWidth(), 0));
}
break;
// Return 1 or true.
case BuiltinValueKind::ICMP_EQ:
case BuiltinValueKind::ICMP_SLE:
case BuiltinValueKind::ICMP_SGE:
case BuiltinValueKind::ICMP_ULE:
case BuiltinValueKind::ICMP_UGE:
case BuiltinValueKind::FCMP_UEQ:
case BuiltinValueKind::FCMP_UGE:
case BuiltinValueKind::FCMP_ULE:
case BuiltinValueKind::SDiv:
case BuiltinValueKind::UDiv:
if (auto Ty = I->getType().getAs<BuiltinIntegerType>()) {
// FIXME: Should also use SILBuilderWithScope?
return Builder.createIntegerLiteral(I->getLoc(), I->getType(),
APInt(Ty->getGreatestWidth(), 1));
}
break;
// Return 0 in a tuple.
case BuiltinValueKind::SSubOver:
case BuiltinValueKind::USubOver: {
SILType Ty = I->getType();
SILType IntTy = Ty.getTupleElementType(0);
SILType BoolTy = Ty.getTupleElementType(1);
SILBuilderWithScope B(I);
SILValue Elements[] = {
B.createIntegerLiteral(I->getLoc(), IntTy, /* Result */ 0),
B.createIntegerLiteral(I->getLoc(), BoolTy, /* Overflow */ 0)
};
return B.createTuple(I->getLoc(), Ty, Elements);
}
default:
break;
}
return nullptr;
}
/// Match an index pointer that is fed by a sizeof(T)*Distance offset.
static IndexRawPointerInst *
matchSizeOfMultiplication(SILValue I, MetatypeInst *RequiredType,
BuiltinInst *&TruncOrBitCast, SILValue &Ptr,
SILValue &Distance) {
auto *Res = dyn_cast<IndexRawPointerInst>(I);
if (!Res)
return nullptr;
SILValue Dist;
MetatypeInst *StrideType;
if (match(
Res->getOperand(1),
m_ApplyInst(
BuiltinValueKind::TruncOrBitCast,
m_TupleExtractInst(
m_ApplyInst(
BuiltinValueKind::SMulOver, m_SILValue(Dist),
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(StrideType)))),
0))) ||
match(
Res->getOperand(1),
m_ApplyInst(
BuiltinValueKind::TruncOrBitCast,
m_TupleExtractInst(
m_ApplyInst(
BuiltinValueKind::SMulOver,
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(StrideType))),
m_SILValue(Dist)),
0)))) {
if (StrideType != RequiredType)
return nullptr;
TruncOrBitCast = cast<BuiltinInst>(Res->getOperand(1));
Distance = Dist;
Ptr = Res->getOperand(0);
return Res;
}
return nullptr;
}
/// Given an index_raw_pointer Ptr, size_of(Metatype) * Distance create an
/// address_to_pointer (index_addr ptr, Distance : $*Metatype) : $RawPointer
/// instruction.
static SILValue createIndexAddrFrom(IndexRawPointerInst *I,
MetatypeInst *Metatype,
BuiltinInst *TruncOrBitCast,
SILValue Ptr, SILValue Distance,
SILType RawPointerTy,
SILBuilder &Builder) {
Builder.setCurrentDebugScope(I->getDebugScope());
SILType InstanceType =
Metatype->getType().getMetatypeInstanceType(I->getModule());
// index_raw_pointer's address type is currently always strict.
auto *NewPTAI = Builder.createPointerToAddress(
I->getLoc(), Ptr, InstanceType.getAddressType(),
/*isStrict*/ true, /*isInvariant*/ false);
auto *DistanceAsWord =
Builder.createBuiltin(I->getLoc(), TruncOrBitCast->getName(),
TruncOrBitCast->getType(), {}, Distance);
auto *NewIAI = Builder.createIndexAddr(I->getLoc(), NewPTAI, DistanceAsWord);
auto *NewATPI =
Builder.createAddressToPointer(I->getLoc(), NewIAI, RawPointerTy);
return NewATPI;
}
/// Optimize an array operation that has (index_raw_pointer b, sizeof(T) * Dist)
/// operands into one that use index_addr as operands.
SILInstruction *optimizeBuiltinArrayOperation(BuiltinInst *I,
SILBuilder &Builder) {
if (I->getNumOperands() < 3)
return nullptr;
// Take something like this:
// %stride = Builtin.strideof(T) * %distance
// %ptr' = index_raw_pointer %ptr, %stride
// = builtin "takeArrayFrontToBack"<Int>(%metatype, %ptr', ...
// And convert it to this:
// %addr = pointer_to_address %ptr, strict $*T
// %result = index_addr %addr, %distance
// %ptr' = address_to_pointer result : $RawPointer
// = builtin "takeArrayFrontToBack"<Int>(%metatype, %ptr', ...
auto *Metatype = dyn_cast<MetatypeInst>(I->getOperand(0));
if (!Metatype)
return nullptr;
SILValue Ptr;
SILValue Distance;
BuiltinInst *TruncOrBitCast;
SILValue NewOp1 = I->getOperand(1), NewOp2 = I->getOperand(2);
// Try to replace the first pointer operand.
auto *IdxRawPtr1 = matchSizeOfMultiplication(I->getOperand(1), Metatype,
TruncOrBitCast, Ptr, Distance);
if (IdxRawPtr1)
NewOp1 = createIndexAddrFrom(IdxRawPtr1, Metatype, TruncOrBitCast, Ptr,
Distance, NewOp1->getType(), Builder);
// Try to replace the second pointer operand.
auto *IdxRawPtr2 = matchSizeOfMultiplication(I->getOperand(2), Metatype,
TruncOrBitCast, Ptr, Distance);
if (IdxRawPtr2)
NewOp2 = createIndexAddrFrom(IdxRawPtr2, Metatype, TruncOrBitCast, Ptr,
Distance, NewOp2->getType(), Builder);
if (NewOp1 != I->getOperand(1) || NewOp2 != I->getOperand(2)) {
SmallVector<SILValue, 5> NewOpds;
for (auto OldOpd : I->getArguments())
NewOpds.push_back(OldOpd);
NewOpds[1] = NewOp1;
NewOpds[2] = NewOp2;
return Builder.createBuiltin(I->getLoc(), I->getName(), I->getType(),
I->getSubstitutions(), NewOpds);
}
return nullptr;
}
/// Get operands of a binary bitop builtin where one operand is an integer
/// literal.
static bool getBitOpArgs(BuiltinInst *BI, SILValue &op, APInt &bits) {
OperandValueArrayRef Args = BI->getArguments();
if (auto *IL = dyn_cast<IntegerLiteralInst>(Args[0])) {
op = Args[1];
bits = IL->getValue();
return true;
}
if (auto *IL = dyn_cast<IntegerLiteralInst>(Args[1])) {
op = Args[0];
bits = IL->getValue();
return true;
}
return false;
}
/// Optimizes binary bit operations. Optimizations for "and":
/// x & 0 -> 0
/// x & ~0 -> x
/// (x & c1) & c2 -> x & (c1 & c2)
/// The same optimizations are done for "or" and "xor".
template <typename CombineFunc, typename NeutralFunc, typename ZeroFunc>
SILInstruction *optimizeBitOp(BuiltinInst *BI,
CombineFunc combine,
NeutralFunc isNeutral,
ZeroFunc isZero,
SILBuilder &Builder,
SILCombiner *C) {
SILValue firstOp;
APInt bits;
if (!getBitOpArgs(BI, firstOp, bits))
return nullptr;
// Combine all bits of consecutive bit operations, e.g. ((op & c1) & c2) & c3
SILValue op = firstOp;
BuiltinInst *Prev;
APInt prevBits;
while ((Prev = dyn_cast<BuiltinInst>(op)) &&
Prev->getBuiltinInfo().ID == BI->getBuiltinInfo().ID &&
getBitOpArgs(Prev, op, prevBits)) {
combine(bits, prevBits);
}
if (isNeutral(bits)) {
// The bit operation has no effect, e.g. x | 0 -> x
C->replaceInstUsesWith(*BI, op);
return BI;
}
if (isZero(bits))
// The bit operation yields to a constant, e.g. x & 0 -> 0
return Builder.createIntegerLiteral(BI->getLoc(), BI->getType(), bits);
if (op != firstOp) {
// We combined multiple bit operations to a single one,
// e.g. (x & c1) & c2 -> x & (c1 & c2)
auto *newLI = Builder.createIntegerLiteral(BI->getLoc(), BI->getType(),
bits);
return Builder.createBuiltin(BI->getLoc(), BI->getName(), BI->getType(),
BI->getSubstitutions(),
{ op, newLI });
}
return nullptr;
}
SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) {
if (I->getBuiltinInfo().ID == BuiltinValueKind::CanBeObjCClass)
return optimizeBuiltinCanBeObjCClass(I);
if (I->getBuiltinInfo().ID == BuiltinValueKind::TakeArrayFrontToBack ||
I->getBuiltinInfo().ID == BuiltinValueKind::TakeArrayBackToFront ||
I->getBuiltinInfo().ID == BuiltinValueKind::TakeArrayNoAlias ||
I->getBuiltinInfo().ID == BuiltinValueKind::CopyArray ||
I->getBuiltinInfo().ID == BuiltinValueKind::AssignCopyArrayNoAlias ||
I->getBuiltinInfo().ID == BuiltinValueKind::AssignCopyArrayFrontToBack ||
I->getBuiltinInfo().ID == BuiltinValueKind::AssignCopyArrayBackToFront ||
I->getBuiltinInfo().ID == BuiltinValueKind::AssignTakeArray)
return optimizeBuiltinArrayOperation(I, Builder);
if (I->getBuiltinInfo().ID == BuiltinValueKind::TruncOrBitCast) {
return optimizeBuiltinTruncOrBitCast(I);
}
if (I->getBuiltinInfo().ID == BuiltinValueKind::ZExtOrBitCast) {
return optimizeBuiltinZextOrBitCast(I);
}
if (I->getNumOperands() >= 2 && I->getOperand(0) == I->getOperand(1)) {
// It's a builtin which has the same value in its first and second operand.
auto *Replacement = optimizeBuiltinWithSameOperands(Builder, I, this);
if (Replacement)
return Replacement;
}
// Optimize this case for unsigned and equality comparisons:
// cmp_*_T . (zext U->T x, zext U->T y)
// => cmp_*_T (x, y)
switch (I->getBuiltinInfo().ID) {
case BuiltinValueKind::ICMP_ULT: {
if (auto *ILOp = dyn_cast<IntegerLiteralInst>(I->getArguments()[0])) {
if (ILOp->getValue().isMaxValue()) {
auto *Zero = Builder.createIntegerLiteral(I->getLoc(), I->getType(), 0);
replaceInstUsesWith(*I, Zero);
return eraseInstFromFunction(*I);
}
}
}
LLVM_FALLTHROUGH;
case BuiltinValueKind::ICMP_ULE:
case BuiltinValueKind::ICMP_UGE:
case BuiltinValueKind::ICMP_UGT:
case BuiltinValueKind::ICMP_EQ:
case BuiltinValueKind::ICMP_NE: {
SILValue LCast, RCast;
if (match(I->getArguments()[0],
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_SILValue(LCast))) &&
match(I->getArguments()[1],
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_SILValue(RCast))) &&
LCast->getType() == RCast->getType()) {
auto *NewCmp = Builder.createBuiltinBinaryFunction(
I->getLoc(), getBuiltinName(I->getBuiltinInfo().ID),
LCast->getType(), I->getType(), {LCast, RCast});
I->replaceAllUsesWith(NewCmp);
replaceInstUsesWith(*I, NewCmp);
return eraseInstFromFunction(*I);
}
break;
}
case BuiltinValueKind::And:
return optimizeBitOp(I,
[](APInt &left, const APInt &right) { left &= right; } /* combine */,
[](const APInt &i) -> bool { return i.isAllOnesValue(); } /* isNeutral */,
[](const APInt &i) -> bool { return i.isMinValue(); } /* isZero */,
Builder, this);
case BuiltinValueKind::Or:
return optimizeBitOp(I,
[](APInt &left, const APInt &right) { left |= right; } /* combine */,
[](const APInt &i) -> bool { return i.isMinValue(); } /* isNeutral */,
[](const APInt &i) -> bool { return i.isAllOnesValue(); } /* isZero */,
Builder, this);
case BuiltinValueKind::Xor:
return optimizeBitOp(I,
[](APInt &left, const APInt &right) { left ^= right; } /* combine */,
[](const APInt &i) -> bool { return i.isMinValue(); } /* isNeutral */,
[](const APInt &i) -> bool { return false; } /* isZero */,
Builder, this);
case BuiltinValueKind::DestroyArray: {
SubstitutionList Substs = I->getSubstitutions();
// Check if the element type is a trivial type.
if (Substs.size() == 1) {
Substitution Subst = Substs[0];
Type ElemType = Subst.getReplacement();
auto &SILElemTy = I->getModule().Types.getTypeLowering(ElemType);
// Destroying an array of trivial types is a no-op.
if (SILElemTy.isTrivial())
return eraseInstFromFunction(*I);
}
break;
}
default:
break;
}
if (I->getBuiltinInfo().ID == BuiltinValueKind::ICMP_EQ)
return optimizeBuiltinCompareEq(I, /*Negate Eq result*/ false);
if (I->getBuiltinInfo().ID == BuiltinValueKind::ICMP_NE)
return optimizeBuiltinCompareEq(I, /*Negate Eq result*/ true);
// Optimize sub(ptrtoint(index_raw_pointer(v, x)), ptrtoint(v)) -> x.
BuiltinInst *Bytes2;
IndexRawPointerInst *Indexraw;
if (I->getNumOperands() == 2 &&
match(I, m_BuiltinInst(BuiltinValueKind::Sub,
m_BuiltinInst(BuiltinValueKind::PtrToInt,
m_IndexRawPointerInst(Indexraw)),
m_BuiltinInst(Bytes2)))) {
if (match(Bytes2,
m_BuiltinInst(BuiltinValueKind::PtrToInt, m_ValueBase()))) {
if (Indexraw->getOperand(0) == Bytes2->getOperand(0) &&
Indexraw->getOperand(1)->getType() == I->getType()) {
replaceInstUsesWith(*I, Indexraw->getOperand(1));
return eraseInstFromFunction(*I);
}
}
}
// Canonicalize multiplication by a stride to be such that the stride is
// always the second argument.
if (I->getNumOperands() != 3)
return nullptr;
if (match(I, m_ApplyInst(BuiltinValueKind::SMulOver,
m_ApplyInst(BuiltinValueKind::Strideof),
m_ValueBase(), m_IntegerLiteralInst()))) {
I->swapOperands(0, 1);
return I;
}
return nullptr;
}