| //===--- 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.getReplacementTypes().size() == 1) && |
| "Expected one substitution in call to canBeClass"); |
| |
| auto Ty = Subs.getReplacementTypes()[0]->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: { |
| SubstitutionMap Substs = I->getSubstitutions(); |
| // Check if the element type is a trivial type. |
| if (Substs.getReplacementTypes().size() == 1) { |
| Type ElemType = Substs.getReplacementTypes()[0]; |
| 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; |
| } |