//===--- ConstantFolding.cpp - Utils for SIL constant folding -------------===// // // 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 // //===----------------------------------------------------------------------===// #include "swift/SILOptimizer/Utils/ConstantFolding.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Expr.h" #include "swift/AST/SemanticAttrs.h" #include "swift/Basic/Assertions.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/PatternMatch.h" #include "swift/SIL/SILBuilder.h" #include "swift/SILOptimizer/Utils/CastOptimizer.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/InstructionDeleter.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/Intrinsics.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "sil-constant-folding" using namespace swift; APInt swift::constantFoldBitOperation(APInt lhs, APInt rhs, BuiltinValueKind ID) { switch (ID) { default: llvm_unreachable("Not all cases are covered!"); case BuiltinValueKind::And: return lhs & rhs; case BuiltinValueKind::AShr: return lhs.ashr(rhs); case BuiltinValueKind::LShr: return lhs.lshr(rhs); case BuiltinValueKind::Or: return lhs | rhs; case BuiltinValueKind::Shl: return lhs.shl(rhs); case BuiltinValueKind::Xor: return lhs ^ rhs; } } APInt swift::constantFoldComparisonFloat(APFloat lhs, APFloat rhs, BuiltinValueKind ID) { bool result; bool isOrdered = !lhs.isNaN() && !rhs.isNaN(); switch (ID) { default: llvm_unreachable("Invalid float compare kind"); // Ordered comparisons case BuiltinValueKind::FCMP_OEQ: result = isOrdered && lhs == rhs; break; case BuiltinValueKind::FCMP_OGT: result = isOrdered && lhs > rhs; break; case BuiltinValueKind::FCMP_OGE: result = isOrdered && lhs >= rhs; break; case BuiltinValueKind::FCMP_OLT: result = isOrdered && lhs < rhs; break; case BuiltinValueKind::FCMP_OLE: result = isOrdered && lhs <= rhs; break; case BuiltinValueKind::FCMP_ONE: result = isOrdered && lhs != rhs; break; case BuiltinValueKind::FCMP_ORD: result = isOrdered; break; // Unordered comparisons case BuiltinValueKind::FCMP_UEQ: result = !isOrdered || lhs == rhs; break; case BuiltinValueKind::FCMP_UGT: result = !isOrdered || lhs > rhs; break; case BuiltinValueKind::FCMP_UGE: result = !isOrdered || lhs >= rhs; break; case BuiltinValueKind::FCMP_ULT: result = !isOrdered || lhs < rhs; break; case BuiltinValueKind::FCMP_ULE: result = !isOrdered || lhs <= rhs; break; case BuiltinValueKind::FCMP_UNE: result = !isOrdered || lhs != rhs; break; case BuiltinValueKind::FCMP_UNO: result = !isOrdered; break; } return APInt(1, result); } APInt swift::constantFoldComparisonInt(APInt lhs, APInt rhs, BuiltinValueKind ID) { bool result; switch (ID) { default: llvm_unreachable("Invalid integer compare kind"); case BuiltinValueKind::ICMP_EQ: result = lhs == rhs; break; case BuiltinValueKind::ICMP_NE: result = lhs != rhs; break; case BuiltinValueKind::ICMP_SLT: result = lhs.slt(rhs); break; case BuiltinValueKind::ICMP_SGT: result = lhs.sgt(rhs); break; case BuiltinValueKind::ICMP_SLE: result = lhs.sle(rhs); break; case BuiltinValueKind::ICMP_SGE: result = lhs.sge(rhs); break; case BuiltinValueKind::ICMP_ULT: result = lhs.ult(rhs); break; case BuiltinValueKind::ICMP_UGT: result = lhs.ugt(rhs); break; case BuiltinValueKind::ICMP_ULE: result = lhs.ule(rhs); break; case BuiltinValueKind::ICMP_UGE: result = lhs.uge(rhs); break; } return APInt(1, result); } APInt swift::constantFoldBinaryWithOverflow(APInt lhs, APInt rhs, bool &Overflow, llvm::Intrinsic::ID ID) { switch (ID) { default: llvm_unreachable("Invalid case"); case llvm::Intrinsic::sadd_with_overflow: return lhs.sadd_ov(rhs, Overflow); case llvm::Intrinsic::uadd_with_overflow: return lhs.uadd_ov(rhs, Overflow); case llvm::Intrinsic::ssub_with_overflow: return lhs.ssub_ov(rhs, Overflow); case llvm::Intrinsic::usub_with_overflow: return lhs.usub_ov(rhs, Overflow); case llvm::Intrinsic::smul_with_overflow: return lhs.smul_ov(rhs, Overflow); case llvm::Intrinsic::umul_with_overflow: return lhs.umul_ov(rhs, Overflow); } } APInt swift::constantFoldDiv(APInt lhs, APInt rhs, bool &Overflow, BuiltinValueKind ID) { assert(rhs != 0 && "division by zero"); switch (ID) { default : llvm_unreachable("Invalid case"); case BuiltinValueKind::SDiv: return lhs.sdiv_ov(rhs, Overflow); case BuiltinValueKind::SRem: { // Check for overflow APInt Div = lhs.sdiv_ov(rhs, Overflow); (void)Div; return lhs.srem(rhs); } case BuiltinValueKind::UDiv: Overflow = false; return lhs.udiv(rhs); case BuiltinValueKind::URem: Overflow = false; return lhs.urem(rhs); } } APInt swift::constantFoldCast(APInt val, const BuiltinInfo &BI) { // Get the cast result. Type SrcTy = BI.Types[0]; Type DestTy = BI.Types.size() == 2 ? BI.Types[1] : Type(); uint32_t SrcBitWidth = SrcTy->castTo()->getGreatestWidth(); uint32_t DestBitWidth = DestTy->castTo()->getGreatestWidth(); APInt CastResV; if (SrcBitWidth == DestBitWidth) { return val; } else switch (BI.ID) { default : llvm_unreachable("Invalid case."); case BuiltinValueKind::Trunc: case BuiltinValueKind::TruncOrBitCast: return val.trunc(DestBitWidth); case BuiltinValueKind::ZExt: case BuiltinValueKind::ZExtOrBitCast: return val.zext(DestBitWidth); break; case BuiltinValueKind::SExt: case BuiltinValueKind::SExtOrBitCast: return val.sext(DestBitWidth); } } //===----------------------------------------------------------------------===// // ConstantFolder //===----------------------------------------------------------------------===// STATISTIC(NumInstFolded, "Number of constant folded instructions"); template static InFlightDiagnostic diagnose(ASTContext &Context, SourceLoc loc, Diag diag, U &&...args) { return Context.Diags.diagnose(loc, diag, std::forward(args)...); } /// Construct (int, overflow) result tuple. static SILValue constructResultWithOverflowTuple(BuiltinInst *BI, APInt Res, bool Overflow) { // Get the SIL subtypes of the returned tuple type. SILType FuncResType = BI->getType(); assert(FuncResType.castTo()->getNumElements() == 2); SILType ResTy1 = FuncResType.getTupleElementType(0); SILType ResTy2 = FuncResType.getTupleElementType(1); // Construct the folded instruction - a tuple of two literals, the // result and overflow. SILBuilderWithScope B(BI); SILLocation Loc = BI->getLoc(); SILValue Result[] = { B.createIntegerLiteral(Loc, ResTy1, Res), B.createIntegerLiteral(Loc, ResTy2, Overflow) }; return B.createTuple(Loc, FuncResType, Result); } /// Fold arithmetic intrinsics with overflow. static SILValue constantFoldBinaryWithOverflow(BuiltinInst *BI, llvm::Intrinsic::ID ID, bool ReportOverflow, std::optional &ResultsInError) { OperandValueArrayRef Args = BI->getArguments(); assert(Args.size() >= 2); auto *Op1 = dyn_cast(Args[0]); auto *Op2 = dyn_cast(Args[1]); // If either Op1 or Op2 is not a literal, we cannot do anything. if (!Op1 || !Op2) return nullptr; // Calculate the result. APInt LHSInt = Op1->getValue(); APInt RHSInt = Op2->getValue(); bool Overflow; APInt Res = constantFoldBinaryWithOverflow(LHSInt, RHSInt, Overflow, ID); // If we can statically determine that the operation overflows, // warn about it if warnings are not disabled by ResultsInError being null. if (ResultsInError.has_value() && Overflow && ReportOverflow) { if (BI->getFunction()->isSpecialization()) { // Do not report any constant propagation issues in specializations, // because they are eventually not present in the original function. return nullptr; } // Try to infer the type of the constant expression that the user operates // on. If the intrinsic was lowered from a call to a function that takes // two arguments of the same type, use the type of the LHS argument. // This would detect '+'/'+=' and such. Type OpType; SILLocation Loc = BI->getLoc(); const ApplyExpr *CE = Loc.getAsASTNode(); SourceRange LHSRange, RHSRange; if (CE) { const auto *Args = CE->getArgs(); if (Args->size() == 2) { // Look through inout types in order to handle += well. CanType LHSTy = Args->getExpr(0)->getType()->getInOutObjectType()-> getCanonicalType(); CanType RHSTy = Args->getExpr(1)->getType()->getCanonicalType(); if (LHSTy == RHSTy) OpType = Args->getExpr(1)->getType(); LHSRange = Args->getExpr(0)->getSourceRange(); RHSRange = Args->getExpr(1)->getSourceRange(); } } bool Signed = false; StringRef Operator = "+"; switch (ID) { default: llvm_unreachable("Invalid case"); case llvm::Intrinsic::sadd_with_overflow: Signed = true; break; case llvm::Intrinsic::uadd_with_overflow: break; case llvm::Intrinsic::ssub_with_overflow: Operator = "-"; Signed = true; break; case llvm::Intrinsic::usub_with_overflow: Operator = "-"; break; case llvm::Intrinsic::smul_with_overflow: Operator = "*"; Signed = true; break; case llvm::Intrinsic::umul_with_overflow: Operator = "*"; break; } SmallString<10> LhsStr; SmallString<10> RhsStr; LHSInt.toString(LhsStr, /*Radix*/ 10, Signed); RHSInt.toString(RhsStr, /*Radix*/ 10, Signed); if (!OpType.isNull()) { diagnose(BI->getModule().getASTContext(), Loc.getSourceLoc(), diag::arithmetic_operation_overflow, LhsStr, Operator, RhsStr, OpType) .highlight(LHSRange) .highlight(RHSRange); } else { // If we cannot get the type info in an expected way, describe the type. diagnose(BI->getModule().getASTContext(), Loc.getSourceLoc(), diag::arithmetic_operation_overflow_generic_type, LhsStr, Operator, RhsStr, Signed, LHSInt.getBitWidth()) .highlight(LHSRange) .highlight(RHSRange); } ResultsInError = std::optional(true); } return constructResultWithOverflowTuple(BI, Res, Overflow); } static SILValue constantFoldBinaryWithOverflow(BuiltinInst *BI, BuiltinValueKind ID, std::optional &ResultsInError) { OperandValueArrayRef Args = BI->getArguments(); auto *ShouldReportFlag = dyn_cast(Args[2]); return constantFoldBinaryWithOverflow(BI, getLLVMIntrinsicIDForBuiltinWithOverflow(ID), ShouldReportFlag && (ShouldReportFlag->getValue() == 1), ResultsInError); } /// Constant fold a cttz or ctlz builtin inst of an integer literal. /// If \p countLeadingZeros is set to true, then we assume \p bi must be ctlz. /// If false, \p bi must be cttz. /// /// NOTE: We assert that \p bi is either cttz or ctlz. static SILValue constantFoldCountLeadingOrTrialingZeroIntrinsic(BuiltinInst *bi, bool countLeadingZeros) { assert((bi->getIntrinsicID() == (llvm::Intrinsic::ID)llvm::Intrinsic::ctlz || bi->getIntrinsicID() == (llvm::Intrinsic::ID)llvm::Intrinsic::cttz) && "Invalid Intrinsic - expected Ctlz/Cllz"); OperandValueArrayRef args = bi->getArguments(); // Fold for integer constant arguments. auto *lhs = dyn_cast(args[0]); if (!lhs) { return nullptr; } APInt lhsi = lhs->getValue(); unsigned lz = [&] { if (lhsi == 0) { // Check corner-case of source == zero return lhsi.getBitWidth(); } if (countLeadingZeros) { return lhsi.countLeadingZeros(); } return lhsi.countTrailingZeros(); }(); APInt lzAsAPInt = APInt(lhsi.getBitWidth(), lz); SILBuilderWithScope builder(bi); return builder.createIntegerLiteral(bi->getLoc(), lhs->getType(), lzAsAPInt); } static SILValue constantFoldIntrinsic(BuiltinInst *BI, llvm::Intrinsic::ID ID, std::optional &ResultsInError) { switch (ID) { default: break; case llvm::Intrinsic::expect: { // An expect of an integral constant is the constant itself. assert(BI->getArguments().size() == 2 && "Expect should have 2 args."); auto *Op1 = dyn_cast(BI->getArguments()[0]); if (!Op1) return nullptr; return Op1; } case llvm::Intrinsic::ctlz: { return constantFoldCountLeadingOrTrialingZeroIntrinsic( BI, true /*countLeadingZeros*/); } case llvm::Intrinsic::cttz: { return constantFoldCountLeadingOrTrialingZeroIntrinsic( BI, false /*countLeadingZeros*/); } 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 constantFoldBinaryWithOverflow(BI, ID, /* ReportOverflow */ false, ResultsInError); case llvm::Intrinsic::rint: if (auto *floatLiteral = dyn_cast(BI->getArguments()[0])) { SILBuilderWithScope builder(BI); APFloat result = floatLiteral->getValue(); // The following code is taken from LLVM's constant folder. result.roundToIntegral(APFloat::rmNearestTiesToEven); return builder.createFloatLiteral(BI->getLoc(), BI->getType(), result); } } return nullptr; } static bool isNanLiteral(SILValue v) { if (auto *literal = dyn_cast(v)) { return literal->getValue().isNaN(); } return false; } static SILValue constantFoldCompareFloat(BuiltinInst *BI, BuiltinValueKind ID) { OperandValueArrayRef Args = BI->getArguments(); // Fold for floating point constant arguments. auto *LHS = dyn_cast(Args[0]); auto *RHS = dyn_cast(Args[1]); if (LHS && RHS) { APInt Res = constantFoldComparisonFloat(LHS->getValue(), RHS->getValue(), ID); SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), Res); } if (isNanLiteral(Args[0]) || isNanLiteral(Args[1])) { switch (BI->getBuiltinInfo().ID) { // Ordered comparisons with NaN always return false case BuiltinValueKind::FCMP_OEQ: // == case BuiltinValueKind::FCMP_OGT: // >= case BuiltinValueKind::FCMP_OGE: // < case BuiltinValueKind::FCMP_OLT: // < case BuiltinValueKind::FCMP_OLE: // <= case BuiltinValueKind::FCMP_ONE: { // != SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt(1, 0)); } // Unordered comparisons with NaN always return true case BuiltinValueKind::FCMP_UEQ: // == case BuiltinValueKind::FCMP_UGT: // >= case BuiltinValueKind::FCMP_UGE: // < case BuiltinValueKind::FCMP_ULT: // < case BuiltinValueKind::FCMP_ULE: // <= case BuiltinValueKind::FCMP_UNE: { // != SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt(1, 1)); } default: break; } } return nullptr; } static SILValue constantFoldCompareInt(BuiltinInst *BI, BuiltinValueKind ID) { OperandValueArrayRef Args = BI->getArguments(); // Fold for integer constant arguments. auto *LHS = dyn_cast(Args[0]); auto *RHS = dyn_cast(Args[1]); if (LHS && RHS) { APInt Res = constantFoldComparisonInt(LHS->getValue(), RHS->getValue(), ID); SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), Res); } using namespace swift::PatternMatch; // Comparisons of an unsigned value with 0. SILValue Other; auto MatchNonNegative = m_BuiltinInst(BuiltinValueKind::AssumeNonNegative, m_ValueBase()); if (match(BI, m_CombineOr(m_BuiltinInst(BuiltinValueKind::ICMP_ULT, m_SILValue(Other), m_Zero()), m_BuiltinInst(BuiltinValueKind::ICMP_UGT, m_Zero(), m_SILValue(Other)))) || match(BI, m_CombineOr(m_BuiltinInst(BuiltinValueKind::ICMP_SLT, MatchNonNegative, m_Zero()), m_BuiltinInst(BuiltinValueKind::ICMP_SGT, m_Zero(), MatchNonNegative)))) { SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt()); } if (match(BI, m_CombineOr(m_BuiltinInst(BuiltinValueKind::ICMP_UGE, m_SILValue(Other), m_Zero()), m_BuiltinInst(BuiltinValueKind::ICMP_ULE, m_Zero(), m_SILValue(Other)))) || match(BI, m_CombineOr(m_BuiltinInst(BuiltinValueKind::ICMP_SGE, MatchNonNegative, m_Zero()), m_BuiltinInst(BuiltinValueKind::ICMP_SLE, m_Zero(), MatchNonNegative)))) { SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt(1, 1)); } // Comparisons with Int.Max. IntegerLiteralInst *IntMax; // Check signed comparisons. if (match(BI, m_CombineOr( // Int.max < x m_BuiltinInst(BuiltinValueKind::ICMP_SLT, m_IntegerLiteralInst(IntMax), m_SILValue(Other)), // x > Int.max m_BuiltinInst(BuiltinValueKind::ICMP_SGT, m_SILValue(Other), m_IntegerLiteralInst(IntMax)))) && IntMax->getValue().isMaxSignedValue()) { // Any signed number should be <= then IntMax. SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt()); } if (match(BI, m_CombineOr( m_BuiltinInst(BuiltinValueKind::ICMP_SGE, m_IntegerLiteralInst(IntMax), m_SILValue(Other)), m_BuiltinInst(BuiltinValueKind::ICMP_SLE, m_SILValue(Other), m_IntegerLiteralInst(IntMax)))) && IntMax->getValue().isMaxSignedValue()) { // Any signed number should be <= then IntMax. SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt(1, 1)); } // For any x of the same size as Int.max and n>=1 , (x>>n) is always <= Int.max, // that is (x>>n) <= Int.max and Int.max >= (x>>n) are true. if (match(BI, m_CombineOr( // Int.max >= x m_BuiltinInst(BuiltinValueKind::ICMP_UGE, m_IntegerLiteralInst(IntMax), m_SILValue(Other)), // x <= Int.max m_BuiltinInst(BuiltinValueKind::ICMP_ULE, m_SILValue(Other), m_IntegerLiteralInst(IntMax)), // Int.max >= x m_BuiltinInst(BuiltinValueKind::ICMP_SGE, m_IntegerLiteralInst(IntMax), m_SILValue(Other)), // x <= Int.max m_BuiltinInst(BuiltinValueKind::ICMP_SLE, m_SILValue(Other), m_IntegerLiteralInst(IntMax)))) && IntMax->getValue().isMaxSignedValue()) { // Check if other is a result of a logical shift right by a strictly // positive number of bits. IntegerLiteralInst *ShiftCount; if (match(Other, m_BuiltinInst(BuiltinValueKind::LShr, m_ValueBase(), m_IntegerLiteralInst(ShiftCount))) && ShiftCount->getValue().isStrictlyPositive()) { SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt(1, 1)); } } // At the same time (x>>n) > Int.max and Int.max < (x>>n) is false. if (match(BI, m_CombineOr( // Int.max < x m_BuiltinInst(BuiltinValueKind::ICMP_ULT, m_IntegerLiteralInst(IntMax), m_SILValue(Other)), // x > Int.max m_BuiltinInst(BuiltinValueKind::ICMP_UGT, m_SILValue(Other), m_IntegerLiteralInst(IntMax)), // Int.max < x m_BuiltinInst(BuiltinValueKind::ICMP_SLT, m_IntegerLiteralInst(IntMax), m_SILValue(Other)), // x > Int.max m_BuiltinInst(BuiltinValueKind::ICMP_SGT, m_SILValue(Other), m_IntegerLiteralInst(IntMax)))) && IntMax->getValue().isMaxSignedValue()) { // Check if other is a result of a logical shift right by a strictly // positive number of bits. IntegerLiteralInst *ShiftCount; if (match(Other, m_BuiltinInst(BuiltinValueKind::LShr, m_ValueBase(), m_IntegerLiteralInst(ShiftCount))) && ShiftCount->getValue().isStrictlyPositive()) { SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), APInt()); } } return nullptr; } static SILValue constantFoldCompare(BuiltinInst *BI, BuiltinValueKind ID) { // Try folding integer comparison if (auto result = constantFoldCompareInt(BI, ID)) return result; // Try folding floating point comparison if (auto result = constantFoldCompareFloat(BI, ID)) return result; // Else, return nullptr return nullptr; } static SILValue constantFoldAndCheckDivision(BuiltinInst *BI, BuiltinValueKind ID, std::optional &ResultsInError) { assert(ID == BuiltinValueKind::SDiv || ID == BuiltinValueKind::SRem || ID == BuiltinValueKind::UDiv || ID == BuiltinValueKind::URem); OperandValueArrayRef Args = BI->getArguments(); SILModule &M = BI->getModule(); // Get the denominator. auto *Denom = dyn_cast(Args[1]); if (!Denom) return nullptr; APInt DenomVal = Denom->getValue(); // If the denominator is zero... if (DenomVal == 0) { // And if we are not asked to report errors, just return nullptr. if (!ResultsInError.has_value()) return nullptr; // Otherwise emit a diagnosis error and set ResultsInError to true. diagnose(M.getASTContext(), BI->getLoc().getSourceLoc(), diag::division_by_zero); ResultsInError = std::optional(true); return nullptr; } // Get the numerator. auto *Num = dyn_cast(Args[0]); if (!Num) return nullptr; APInt NumVal = Num->getValue(); bool Overflowed; APInt ResVal = constantFoldDiv(NumVal, DenomVal, Overflowed, ID); // If we overflowed... if (Overflowed) { // And we are not asked to produce diagnostics, just return nullptr... if (!ResultsInError.has_value()) return nullptr; bool IsRem = ID == BuiltinValueKind::SRem || ID == BuiltinValueKind::URem; // Otherwise emit the diagnostic, set ResultsInError to be true, and return // nullptr. diagnose(M.getASTContext(), BI->getLoc().getSourceLoc(), diag::division_overflow, llvm::toString(NumVal, /*Radix*/ 10, /*Signed*/ true), IsRem ? "%" : "/", llvm::toString(DenomVal, /*Radix*/ 10, /*Signed*/ true)); ResultsInError = std::optional(true); return nullptr; } // Add the literal instruction to represent the result of the division. SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), ResVal); } static SILValue specializePolymorphicBuiltin(BuiltinInst *bi, BuiltinValueKind id) { // If we are not a polymorphic builtin, return an empty SILValue() // so we keep on scanning. if (!isPolymorphicBuiltin(id)) return SILValue(); // Otherwise, try to perform the mapping. if (auto newBuiltin = getStaticOverloadForSpecializedPolymorphicBuiltin(bi)) return newBuiltin; return SILValue(); } /// Fold binary operations. /// /// The list of operations we constant fold might not be complete. Start with /// folding the operations used by the standard library. static SILValue constantFoldBinary(BuiltinInst *BI, BuiltinValueKind ID, std::optional &ResultsInError) { switch (ID) { default: return nullptr; // Not supported yet (not easily computable for APInt). case BuiltinValueKind::ExactSDiv: case BuiltinValueKind::ExactUDiv: return nullptr; // Not supported now. case BuiltinValueKind::FRem: return nullptr; // Fold constant division operations and report div by zero. case BuiltinValueKind::SDiv: case BuiltinValueKind::SRem: case BuiltinValueKind::UDiv: case BuiltinValueKind::URem: { return constantFoldAndCheckDivision(BI, ID, ResultsInError); } // Are there valid uses for these in stdlib? case BuiltinValueKind::Add: case BuiltinValueKind::Mul: case BuiltinValueKind::Sub: { OperandValueArrayRef Args = BI->getArguments(); auto *LHS = dyn_cast(Args[0]); auto *RHS = dyn_cast(Args[1]); if (!RHS || !LHS) return nullptr; APInt LHSI = LHS->getValue(); APInt RHSI = RHS->getValue(); switch (ID) { default: llvm_unreachable("Not all cases are covered!"); case BuiltinValueKind::Add: LHSI += RHSI; break; case BuiltinValueKind::Mul: LHSI *= RHSI; break; case BuiltinValueKind::Sub: LHSI -= RHSI; break; } SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), LHSI); } case BuiltinValueKind::And: case BuiltinValueKind::AShr: case BuiltinValueKind::LShr: case BuiltinValueKind::Or: case BuiltinValueKind::Shl: case BuiltinValueKind::Xor: { OperandValueArrayRef Args = BI->getArguments(); auto *LHS = dyn_cast(Args[0]); auto *RHS = dyn_cast(Args[1]); if (!RHS || !LHS) return nullptr; APInt LHSI = LHS->getValue(); APInt RHSI = RHS->getValue(); bool IsShift = ID == BuiltinValueKind::AShr || ID == BuiltinValueKind::LShr || ID == BuiltinValueKind::Shl; // Reject shifting all significant bits if (IsShift && RHSI.getZExtValue() >= LHSI.getBitWidth()) { diagnose(BI->getModule().getASTContext(), RHS->getLoc().getSourceLoc(), diag::shifting_all_significant_bits); ResultsInError = std::optional(true); return nullptr; } APInt ResI = constantFoldBitOperation(LHSI, RHSI, ID); // Add the literal instruction to represent the result. SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), ResI); } case BuiltinValueKind::FAdd: case BuiltinValueKind::FDiv: case BuiltinValueKind::FMul: case BuiltinValueKind::FSub: { OperandValueArrayRef Args = BI->getArguments(); auto *LHS = dyn_cast(Args[0]); auto *RHS = dyn_cast(Args[1]); if (!RHS || !LHS) return nullptr; APFloat LHSF = LHS->getValue(); APFloat RHSF = RHS->getValue(); switch (ID) { default: llvm_unreachable("Not all cases are covered!"); case BuiltinValueKind::FAdd: LHSF.add(RHSF, APFloat::rmNearestTiesToEven); break; case BuiltinValueKind::FDiv: LHSF.divide(RHSF, APFloat::rmNearestTiesToEven); break; case BuiltinValueKind::FMul: LHSF.multiply(RHSF, APFloat::rmNearestTiesToEven); break; case BuiltinValueKind::FSub: LHSF.subtract(RHSF, APFloat::rmNearestTiesToEven); break; } // Add the literal instruction to represent the result. SILBuilderWithScope B(BI); return B.createFloatLiteral(BI->getLoc(), BI->getType(), LHSF); } } } static SILValue constantFoldAndCheckIntegerConversions(BuiltinInst *BI, const BuiltinInfo &Builtin, std::optional &ResultsInError) { assert(Builtin.ID == BuiltinValueKind::SToSCheckedTrunc || Builtin.ID == BuiltinValueKind::UToUCheckedTrunc || Builtin.ID == BuiltinValueKind::SToUCheckedTrunc || Builtin.ID == BuiltinValueKind::UToSCheckedTrunc); // Check if we are converting a constant integer. OperandValueArrayRef Args = BI->getArguments(); auto *V = dyn_cast(Args[0]); if (!V) return nullptr; APInt SrcVal = V->getValue(); auto SrcBitWidth = SrcVal.getBitWidth(); bool DstTySigned = (Builtin.ID == BuiltinValueKind::SToSCheckedTrunc || Builtin.ID == BuiltinValueKind::UToSCheckedTrunc); bool SrcTySigned = (Builtin.ID == BuiltinValueKind::SToSCheckedTrunc || Builtin.ID == BuiltinValueKind::SToUCheckedTrunc); // Get source type and bit width. auto SrcTy = Builtin.Types[0]->castTo(); assert((SrcTySigned || !isa(SrcTy)) && "only the signed intrinsics can be used with integer literals"); // Compute the destination (for SrcBitWidth < DestBitWidth) and enough info // to check for overflow. APInt Result; bool OverflowError; Type DstTy; assert(Builtin.Types.size() == 2); DstTy = Builtin.Types[1]; uint32_t DstBitWidth = DstTy->castTo()->getGreatestWidth(); assert((DstBitWidth < SrcBitWidth || !SrcTy->getWidth().isFixedWidth()) && "preconditions on builtin trunc operations should prevent" "fixed-width truncations that actually extend"); // The only way a true extension can overflow is if the value is // negative and the result is unsigned. if (DstBitWidth > SrcBitWidth) { OverflowError = (SrcTySigned && !DstTySigned && SrcVal.isNegative()); Result = (SrcTySigned ? SrcVal.sext(DstBitWidth) : SrcVal.zext(DstBitWidth)); // A same-width change can overflow if the top bit disagrees. } else if (DstBitWidth == SrcBitWidth) { OverflowError = (SrcTySigned != DstTySigned && SrcVal.isNegative()); Result = SrcVal; // A truncation can overflow if the value changes. } else { Result = SrcVal.trunc(DstBitWidth); APInt Ext = (DstTySigned ? Result.sext(SrcBitWidth) : Result.zext(SrcBitWidth)); OverflowError = (SrcVal != Ext); } // Check for overflow. if (OverflowError) { // If we are not asked to emit overflow diagnostics, just return nullptr on // overflow. if (!ResultsInError.has_value()) return nullptr; SILLocation Loc = BI->getLoc(); SILModule &M = BI->getModule(); const ApplyExpr *CE = Loc.getAsASTNode(); Type UserSrcTy; Type UserDstTy; // Primitive heuristics to get the user-written type. // Eventually we might be able to use SILLocation (when it contains info // about inlined call chains). if (CE) { if (auto *unaryArg = CE->getArgs()->getUnaryExpr()) { UserSrcTy = unaryArg->getType(); UserDstTy = CE->getType(); } } else if (auto *ILE = Loc.getAsASTNode()) { UserDstTy = ILE->getType(); } // Assume that we're converting from a literal if the source type is // IntegerLiteral. Is there a better way to identify this if we start // using Builtin.IntegerLiteral in an exposed type? bool Literal = isa(SrcTy); // FIXME: This will prevent hard error in cases the error is coming // from ObjC interoperability code. Currently, we treat NSUInteger as // Int. if (Loc.getSourceLoc().isInvalid()) { // Otherwise emit the appropriate diagnostic and set ResultsInError. if (Literal) diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::integer_literal_overflow_warn, UserDstTy.isNull() ? DstTy : UserDstTy); else diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::integer_conversion_overflow_warn, UserSrcTy.isNull() ? SrcTy : UserSrcTy, UserDstTy.isNull() ? DstTy : UserDstTy); ResultsInError = std::optional(true); return nullptr; } // Otherwise report the overflow error. if (Literal) { SmallString<10> SrcAsString; SrcVal.toString(SrcAsString, /*radix*/10, SrcTySigned); // Try to print user-visible types if they are available. if (!UserDstTy.isNull()) { auto diagID = diag::integer_literal_overflow; // If this is a negative literal in an unsigned type, use a specific // diagnostic. if (!DstTySigned && SrcVal.isNegative()) diagID = diag::negative_integer_literal_overflow_unsigned; diagnose(M.getASTContext(), Loc.getSourceLoc(), diagID, UserDstTy, SrcAsString); // Otherwise, print the Builtin Types. } else { diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::integer_literal_overflow_builtin_types, DstTySigned, DstTy, SrcAsString); } } else { // Try to print user-visible types if they are available. if (!UserSrcTy.isNull()) { diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::integer_conversion_overflow, UserSrcTy, UserDstTy); // Otherwise, print the Builtin Types. } else { // Since builtin types are sign-agnostic, print the signedness // separately. diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::integer_conversion_overflow_builtin_types, SrcTySigned, SrcTy, DstTySigned, DstTy); } } ResultsInError = std::optional(true); return nullptr; } // The call to the builtin should be replaced with the constant value. return constructResultWithOverflowTuple(BI, Result, false); } /// A utility function that extracts the literal text corresponding /// to a given FloatLiteralInst the way it appears in the AST. /// This function can be used on FloatLiteralInsts generated by the /// constant folding phase. /// If the extraction is successful, the function returns true and /// 'fpStr' contains the literal the way it appears in the AST. /// If the extraction is unsuccessful, e.g. because there is no AST /// for the FloatLiteralInst, the function returns false. template static bool tryExtractLiteralText(FloatLiteralInst *flitInst, SmallString &fpStr) { auto *flitExpr = flitInst->getLoc().getAsASTNode(); if (!flitExpr) return false; if (flitExpr->isNegative()) fpStr += '-'; fpStr += flitExpr->getDigitsText(); return true; } static SILValue foldFPToIntConversion(BuiltinInst *BI, const BuiltinInfo &Builtin, std::optional &ResultsInError) { assert(Builtin.ID == BuiltinValueKind::FPToSI || Builtin.ID == BuiltinValueKind::FPToUI); OperandValueArrayRef Args = BI->getArguments(); bool conversionToUnsigned = (Builtin.ID == BuiltinValueKind::FPToUI); auto *flitInst = dyn_cast(Args[0]); if (!flitInst) return nullptr; APFloat fpVal = flitInst->getValue(); auto *destTy = Builtin.Types[1]->castTo(); // Check non-negativeness of 'fpVal' for conversion to unsigned int. if (conversionToUnsigned && fpVal.isNegative() && !fpVal.isZero()) { // Stop folding and emit diagnostics if enabled. if (ResultsInError.has_value()) { SILModule &M = BI->getModule(); const ApplyExpr *CE = BI->getLoc().getAsASTNode(); SmallString<10> fpStr; if (!tryExtractLiteralText(flitInst, fpStr)) flitInst->getValue().toString(fpStr); diagnose(M.getASTContext(), BI->getLoc().getSourceLoc(), diag::negative_fp_literal_overflow_unsigned, fpStr, CE ? CE->getType() : destTy, CE ? false : conversionToUnsigned); ResultsInError = std::optional(true); } return nullptr; } llvm::APSInt resInt(destTy->getFixedWidth(), conversionToUnsigned); bool isExact = false; APFloat::opStatus status = fpVal.convertToInteger(resInt, APFloat::rmTowardZero, &isExact); if (status & APFloat::opStatus::opInvalidOp) { // Stop folding and emit diagnostics if enabled. if (ResultsInError.has_value()) { SILModule &M = BI->getModule(); const ApplyExpr *CE = BI->getLoc().getAsASTNode(); SmallString<10> fpStr; if (!tryExtractLiteralText(flitInst, fpStr)) flitInst->getValue().toString(fpStr); diagnose(M.getASTContext(), BI->getLoc().getSourceLoc(), diag::float_to_int_overflow, fpStr, CE ? CE->getType() : destTy, CE ? CE->isImplicit() : false); ResultsInError = std::optional(true); } return nullptr; } if (status != APFloat::opStatus::opOK && status != APFloat::opStatus::opInexact) { return nullptr; } // The call to the builtin should be replaced with the constant value. SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), resInt); } /// Captures the layout of IEEE754 floating point values. struct IEEESemantics { uint8_t bitWidth; uint8_t exponentBitWidth; uint8_t significandBitWidth; // Ignores the integer part. bool explicitIntegerPart; int minExponent; public: IEEESemantics(uint8_t bits, uint8_t expBits, uint8_t sigBits, bool explicitIntPart) { bitWidth = bits; exponentBitWidth = expBits; significandBitWidth = sigBits; explicitIntegerPart = explicitIntPart; minExponent = -(1 << (exponentBitWidth - 1)) + 2; } }; IEEESemantics getFPSemantics(BuiltinFloatType *fpType) { switch (fpType->getFPKind()) { case BuiltinFloatType::IEEE16: return IEEESemantics(16, 5, 10, false); case BuiltinFloatType::IEEE32: return IEEESemantics(32, 8, 23, false); case BuiltinFloatType::IEEE64: return IEEESemantics(64, 11, 52, false); case BuiltinFloatType::IEEE80: return IEEESemantics(80, 15, 63, true); case BuiltinFloatType::IEEE128: return IEEESemantics(128, 15, 112, false); case BuiltinFloatType::PPC128: llvm_unreachable("ppc128 is not supported"); } llvm_unreachable("invalid floating point kind"); } /// This function, given the exponent and significand of a binary fraction /// equalling 1.srcSignificand x 2^srcExponent, /// determines whether converting the value to a given destination semantics /// results in an underflow and whether the significand precision is reduced /// because of the underflow. bool isLossyUnderflow(int srcExponent, uint64_t srcSignificand, IEEESemantics srcSem, IEEESemantics destSem) { if (srcExponent >= destSem.minExponent) return false; // Is the value smaller than the smallest non-zero value of destSem? if (srcExponent < destSem.minExponent - destSem.significandBitWidth) return true; // Truncate the significand to the significand width of destSem. uint8_t bitWidthDecrease = srcSem.significandBitWidth - destSem.significandBitWidth; uint64_t truncSignificand = bitWidthDecrease > 0 ? (srcSignificand >> bitWidthDecrease) : srcSignificand; // Compute the significand bits lost due to subnormal form. Note that the // integer part: 1 will use up a significand bit in subnormal form. unsigned additionalLoss = destSem.minExponent - srcExponent + 1; // Lost bits cannot exceed Double.minExponent - Double.significandWidth = 53. // This can happen when truncating from Float80 to Double. assert(additionalLoss <= 53); // Check whether a set LSB is lost due to subnormal representation. uint64_t lostLSBBitMask = ((uint64_t)1 << additionalLoss) - 1; return (truncSignificand & lostLSBBitMask); } /// This function, given an IEEE floating-point value (srcVal), determines /// whether the conversion to a given destination semantics results /// in an underflow and whether the significand precision is reduced /// because of the underflow. bool isLossyUnderflow(APFloat srcVal, BuiltinFloatType *srcType, BuiltinFloatType *destType) { if (srcVal.isNaN() || srcVal.isZero() || srcVal.isInfinity()) return false; IEEESemantics srcSem = getFPSemantics(srcType); IEEESemantics destSem = getFPSemantics(destType); if (srcSem.bitWidth <= destSem.bitWidth) return false; if (srcVal.isDenormal()) { // A denormal value of a larger IEEE FP type will definitely // reduce to zero when truncated to smaller IEEE FP type. return true; } APInt bitPattern = srcVal.bitcastToAPInt(); uint64_t significand = bitPattern.getLoBits(srcSem.significandBitWidth).getZExtValue(); return isLossyUnderflow(ilogb(srcVal), significand, srcSem, destSem); } /// This function determines whether the float literal in the given /// SIL instruction is specified using hex-float notation in the Swift source. bool isHexLiteralInSource(FloatLiteralInst *flitInst) { auto *flitExpr = flitInst->getLoc().getAsASTNode(); return flitExpr && flitExpr->getDigitsText().starts_with("0x"); } bool maybeExplicitFPCons(BuiltinInst *BI, const BuiltinInfo &Builtin) { assert(Builtin.ID == BuiltinValueKind::FPTrunc || Builtin.ID == BuiltinValueKind::IntToFPWithOverflow); if (auto *literal = BI->getLoc().getAsASTNode()) { return literal->isExplicitConversion(); } auto *callExpr = BI->getLoc().getAsASTNode(); if (!callExpr || !dyn_cast(callExpr->getFn())) return true; // not enough information here, so err on the safer side. if (!callExpr->isImplicit()) return true; // Here, the 'callExpr' is an implicit FP construction. However, if it is // constructing a Double it could be a part of an explicit construction of // another FP type, which uses an implicit conversion to Double as an // intermediate step. So we conservatively assume that an implicit // construction of Double could be a part of an explicit conversion // and suppress the warning. return callExpr->getType()->isDouble(); } static SILValue foldFPTrunc(BuiltinInst *BI, const BuiltinInfo &Builtin, std::optional &ResultsInError) { assert(Builtin.ID == BuiltinValueKind::FPTrunc); auto *flitInst = dyn_cast(BI->getArguments()[0]); if (!flitInst) return nullptr; // We can fold only compile-time constant arguments. SILLocation Loc = BI->getLoc(); auto *srcType = Builtin.Types[0]->castTo(); auto *destType = Builtin.Types[1]->castTo(); bool losesInfo; APFloat truncVal = flitInst->getValue(); APFloat::opStatus opStatus = truncVal.convert(destType->getAPFloatSemantics(), APFloat::rmNearestTiesToEven, &losesInfo); // Emit a warning if one of the following conditions hold: (a) the source // value overflows the destination type, or (b) the source value is tiny and // the tininess results in additional loss of precision when converted to the // destination type beyond what would result in the normal scenario, or // (c) the source value is a hex-float literal that cannot be precisely // represented in the destination type. // Suppress all warnings if the conversion is made through an explicit // constructor invocation. if (ResultsInError.has_value() && !maybeExplicitFPCons(BI, Builtin)) { bool overflow = opStatus & APFloat::opStatus::opOverflow; bool tinynInexact = isLossyUnderflow(flitInst->getValue(), srcType, destType); bool hexnInexact = (opStatus != APFloat::opStatus::opOK) && isHexLiteralInSource(flitInst); if (overflow || tinynInexact || hexnInexact) { SILModule &M = BI->getModule(); const ApplyExpr *CE = Loc.getAsASTNode(); SmallString<10> fplitStr; tryExtractLiteralText(flitInst, fplitStr); auto userType = CE ? CE->getType() : destType; if (auto *FLE = Loc.getAsASTNode()) { userType = FLE->getType(); } auto diagId = overflow ? diag::warning_float_trunc_overflow : (hexnInexact ? diag::warning_float_trunc_hex_inexact : diag::warning_float_trunc_underflow); diagnose(M.getASTContext(), Loc.getSourceLoc(), diagId, fplitStr, userType, truncVal.isNegative()); ResultsInError = std::optional(true); } } // Abort folding if we have subnormality, NaN or opInvalid status. if ((opStatus & APFloat::opStatus::opInvalidOp) || (opStatus & APFloat::opStatus::opDivByZero) || (opStatus & APFloat::opStatus::opUnderflow) || truncVal.isDenormal()) { return nullptr; } // Allow folding if there is no loss, overflow or normal imprecision // (i.e., opOverflow, opOk, or opInexact). SILBuilderWithScope B(BI); return B.createFloatLiteral(Loc, BI->getType(), truncVal); } static SILValue constantFoldIsConcrete(BuiltinInst *BI) { if (BI->getOperand(0)->getType().hasArchetype()) { return SILValue(); } SILBuilderWithScope builder(BI); auto *inst = builder.createIntegerLiteral( BI->getLoc(), SILType::getBuiltinIntegerType(1, builder.getASTContext()), true); BI->replaceAllUsesWith(inst); return inst; } SILValue swift::constantFoldBuiltin(BuiltinInst *BI, std::optional &ResultsInError) { const IntrinsicInfo &Intrinsic = BI->getIntrinsicInfo(); SILModule &M = BI->getModule(); // If it's an llvm intrinsic, fold the intrinsic. if (Intrinsic.ID != llvm::Intrinsic::not_intrinsic) return constantFoldIntrinsic(BI, Intrinsic.ID, ResultsInError); // Otherwise, it should be one of the builtin functions. OperandValueArrayRef Args = BI->getArguments(); const BuiltinInfo &Builtin = BI->getBuiltinInfo(); switch (Builtin.ID) { default: break; // Check and fold 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 constantFoldBinaryWithOverflow(BI, Builtin.ID, ResultsInError); #define BUILTIN(id, name, attrs) #define BUILTIN_BINARY_OPERATION(id, name, attrs) case BuiltinValueKind::id: #include "swift/AST/Builtins.def" return constantFoldBinary(BI, Builtin.ID, ResultsInError); // Fold comparison predicates. #define BUILTIN(id, name, Attrs) #define BUILTIN_BINARY_PREDICATE(id, name, attrs, overload) \ case BuiltinValueKind::id: #include "swift/AST/Builtins.def" return constantFoldCompare(BI, Builtin.ID); case BuiltinValueKind::Trunc: case BuiltinValueKind::ZExt: case BuiltinValueKind::SExt: case BuiltinValueKind::TruncOrBitCast: case BuiltinValueKind::ZExtOrBitCast: case BuiltinValueKind::SExtOrBitCast: { // We can fold if the value being cast is a constant. auto *V = dyn_cast(Args[0]); if (!V) return nullptr; APInt CastResV = constantFoldCast(V->getValue(), Builtin); // Add the literal instruction to represent the result of the cast. SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), CastResV); } // Process special builtins that are designed to check for overflows in // integer conversions. case BuiltinValueKind::SToSCheckedTrunc: case BuiltinValueKind::UToUCheckedTrunc: case BuiltinValueKind::SToUCheckedTrunc: case BuiltinValueKind::UToSCheckedTrunc: { return constantFoldAndCheckIntegerConversions(BI, Builtin, ResultsInError); } case BuiltinValueKind::SIToFP: { auto *intLiteral = dyn_cast(Args[0]); if (!intLiteral) return nullptr; APInt api = intLiteral->getValue(); auto *destTy = Builtin.Types[1]->castTo(); // The following code is taken from LLVM's constant folder. APFloat apf(destTy->getAPFloatSemantics(), APInt::getZero(destTy->getBitWidth())); apf.convertFromAPInt(api, Builtin.ID==BuiltinValueKind::SIToFP, APFloat::rmNearestTiesToEven); SILBuilderWithScope B(BI); return B.createFloatLiteral(BI->getLoc(), BI->getType(), apf); } case BuiltinValueKind::BitCast: { auto destTy = Builtin.Types[1]; // The following code is taken from LLVM's constant folder. if (auto *intLiteral = dyn_cast(Args[0])) { if (auto *floatDestTy = destTy->getAs()) { SILBuilderWithScope B(BI); return B.createFloatLiteral(BI->getLoc(), BI->getType(), APFloat(floatDestTy->getAPFloatSemantics(), intLiteral->getValue())); } } else if (auto *floatLiteral = dyn_cast(Args[0])) { if (destTy->is()) { SILBuilderWithScope B(BI); return B.createIntegerLiteral(BI->getLoc(), BI->getType(), floatLiteral->getValue().bitcastToAPInt()); } } return nullptr; } case BuiltinValueKind::IntToFPWithOverflow: { // Get the value. It should be a constant in most cases. // Note, this will not always be a constant, for example, when analyzing // _convertFromBuiltinIntegerLiteral function itself. auto *V = dyn_cast(Args[0]); if (!V) return nullptr; APInt SrcVal = V->getValue(); auto *DestTy = Builtin.Types[1]->castTo(); APFloat TruncVal(DestTy->getAPFloatSemantics()); APFloat::opStatus ConversionStatus = TruncVal.convertFromAPInt( SrcVal, /*IsSigned=*/true, APFloat::rmNearestTiesToEven); SILLocation Loc = BI->getLoc(); const Expr *CE = Loc.getAsASTNode(); if (!CE) CE = Loc.getAsASTNode(); bool overflow = ConversionStatus & APFloat::opOverflow; bool inexact = ConversionStatus & APFloat::opInexact; if (overflow || inexact) { // Check if diagnostics is enabled. If so, make sure to suppress // warnings for conversions through explicit initializers, // but do not suppress errors. if (ResultsInError.has_value() && (overflow || !maybeExplicitFPCons(BI, Builtin))) { SmallString<10> SrcAsString; SrcVal.toString(SrcAsString, /*radix*/ 10, true /*isSigned*/); if (overflow) { diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::integer_literal_overflow, CE ? CE->getType() : DestTy, SrcAsString); } else { SmallString<10> destStr; unsigned srcBitWidth = SrcVal.getBitWidth(); // Display the 'TruncVal' like an integer in order to make the // imprecision due to floating-point representation obvious. TruncVal.toString(destStr, srcBitWidth, srcBitWidth); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::warning_int_to_fp_inexact, CE ? CE->getType() : DestTy, SrcAsString, destStr); } ResultsInError = std::optional(true); } // If there is an overflow, just return nullptr as this is undefined // behavior. Otherwise, continue folding as in the normal workflow. if (overflow) return nullptr; } // The call to the builtin should be replaced with the constant value. SILBuilderWithScope B(BI); return B.createFloatLiteral(Loc, BI->getType(), TruncVal); } case BuiltinValueKind::FPTrunc: { return foldFPTrunc(BI, Builtin, ResultsInError); } // Conversions from floating point to integer, case BuiltinValueKind::FPToSI: case BuiltinValueKind::FPToUI: { return foldFPToIntConversion(BI, Builtin, ResultsInError); } case BuiltinValueKind::IntToPtr: { if (auto *op = dyn_cast(BI->getOperand(0))) { if (auto kind = op->getBuiltinKind()) { // If we have a single int_to_ptr user and all of the types line up, we // can simplify this instruction. if (*kind == BuiltinValueKind::PtrToInt && op->getOperand(0)->getType() == BI->getResult(0)->getType()) { return op->getOperand(0); } } } break; } case BuiltinValueKind::PtrToInt: { if (auto *op = dyn_cast(BI->getOperand(0))) { if (auto kind = op->getBuiltinKind()) { // If we have a single int_to_ptr user and all of the types line up, we // can simplify this instruction. if (*kind == BuiltinValueKind::IntToPtr && op->getOperand(0)->getType() == BI->getResult(0)->getType()) { return op->getOperand(0); } } } break; } case BuiltinValueKind::AssumeNonNegative: { auto *V = dyn_cast(Args[0]); if (!V) return nullptr; APInt VInt = V->getValue(); if (VInt.isNegative() && ResultsInError.has_value()) { diagnose(M.getASTContext(), BI->getLoc().getSourceLoc(), diag::wrong_non_negative_assumption, llvm::toString(VInt, /*Radix*/ 10, /*Signed*/ true)); ResultsInError = std::optional(true); } return V; } } return nullptr; } /// On success this places a new value for each result of Op->getUser() into /// Results. Results is guaranteed on success to have the same number of entries /// as results of User. If we could only simplify /some/ of an instruction's /// results, we still return true, but signal that we couldn't simplify by /// placing SILValue() in that position instead. static bool constantFoldInstruction(Operand *Op, std::optional &ResultsInError, SmallVectorImpl &Results) { auto *User = Op->getUser(); // Constant fold builtin invocations. if (auto *BI = dyn_cast(User)) { Results.push_back(constantFoldBuiltin(BI, ResultsInError)); return true; } // Constant fold extraction of a constant element. if (auto *TEI = dyn_cast(User)) { if (auto *TheTuple = dyn_cast(TEI->getOperand())) { Results.push_back(TheTuple->getElement(TEI->getFieldIndex())); return true; } } // Constant fold extraction of a constant struct element. if (auto *SEI = dyn_cast(User)) { if (auto *Struct = dyn_cast(SEI->getOperand())) { Results.push_back(Struct->getOperandForField(SEI->getField())->get()); return true; } } // Constant fold struct destructuring of a trivial value or a guaranteed // non-trivial value. // // We can not do this for non-trivial owned values without knowing that we // will eliminate the underlying struct since we would be introducing a // "use-after-free" from an ownership model perspective. if (auto *DSI = dyn_cast(User)) { if (auto *Struct = dyn_cast(DSI->getOperand())) { llvm::transform( Struct->getAllOperands(), std::back_inserter(Results), [&](Operand &op) -> SILValue { SILValue operandValue = op.get(); auto ownershipKind = operandValue->getOwnershipKind(); // First check if we are not compatible with guaranteed. This means // we would be Owned or Unowned. If so, return SILValue(). if (!ownershipKind.isCompatibleWith(OwnershipKind::Guaranteed)) return SILValue(); // Otherwise check if our operand is non-trivial and None. In cases // like that, the non-trivial type could be replacing an owned value // where we lost that our underlying value is None due to // intermediate aggregate literal operations. In that case, we /do // not/ want to eliminate the destructure. if (ownershipKind == OwnershipKind::None && !operandValue->getType().isTrivial(*Struct->getFunction())) return SILValue(); return operandValue; }); return true; } } // Constant fold tuple destructuring of a trivial value or a guaranteed // non-trivial value. // // We can not do this for non-trivial owned values without knowing that we // will eliminate the underlying tuple since we would be introducing a // "use-after-free" from the ownership model perspective. if (auto *DTI = dyn_cast(User)) { if (auto *Tuple = dyn_cast(DTI->getOperand())) { llvm::transform( Tuple->getAllOperands(), std::back_inserter(Results), [&](Operand &op) -> SILValue { SILValue operandValue = op.get(); auto ownershipKind = operandValue->getOwnershipKind(); // First check if we are not compatible with guaranteed. This means // we would be Owned or Unowned. If so, return SILValue(). if (!ownershipKind.isCompatibleWith(OwnershipKind::Guaranteed)) return SILValue(); // Otherwise check if our operand is non-trivial and None. In cases // like that, the non-trivial type could be replacing an owned value // where we lost that our underlying value is None due to // intermediate aggregate literal operations. In that case, we /do // not/ want to eliminate the destructure. if (ownershipKind == OwnershipKind::None && !operandValue->getType().isTrivial(*Tuple->getFunction())) return SILValue(); return operandValue; }); return true; } } // Constant fold indexing insts of a 0 integer literal. if (auto *II = dyn_cast(User)) { if (auto *IntLiteral = dyn_cast(II->getIndex())) { if (!IntLiteral->getValue()) { Results.push_back(II->getBase()); return true; } } } return false; } static bool isApplyOfBuiltin(SILInstruction &I, BuiltinValueKind kind) { if (auto *BI = dyn_cast(&I)) if (BI->getBuiltinInfo().ID == kind) return true; return false; } static bool isApplyOfKnownAvailability(SILInstruction &I) { // Inlinable functions can be deserialized in other modules which can be // compiled with a different deployment target. if (I.getFunction()->getResilienceExpansion() != ResilienceExpansion::Maximal) return false; auto apply = FullApplySite::isa(&I); if (!apply) return false; auto callee = apply.getReferencedFunctionOrNull(); if (!callee) return false; if (!callee->hasSemanticsAttr("availability.osversion")) return false; auto &context = I.getFunction()->getASTContext(); auto deploymentAvailability = AvailabilityRange::forDeploymentTarget(context); if (apply.getNumArguments() != 3) return false; auto arg0 = dyn_cast(apply.getArgument(0)); if (!arg0) return false; auto arg1 = dyn_cast(apply.getArgument(1)); if (!arg1) return false; auto arg2 = dyn_cast(apply.getArgument(2)); if (!arg2) return false; auto version = VersionRange::allGTE(llvm::VersionTuple( arg0->getValue().getLimitedValue(), arg1->getValue().getLimitedValue(), arg2->getValue().getLimitedValue())); auto callAvailability = AvailabilityRange(version); return deploymentAvailability.isContainedIn(callAvailability); } static bool isApplyOfStringConcat(SILInstruction &I) { if (auto *AI = dyn_cast(&I)) if (auto *Fn = AI->getReferencedFunctionOrNull()) if (Fn->hasSemanticsAttr(semantics::STRING_CONCAT)) return true; return false; } static bool isFoldable(SILInstruction *I) { return isa(I) || isa(I) || isa(I); } /// Given a buitin instruction calling globalStringTablePointer, check whether /// the string passed to the builtin is constructed from a literal and if so, /// replace the uses of the builtin instruction with the string_literal inst. /// Otherwise, emit diagnostics if the function containing the builtin is not a /// transparent function. Transparent functions will be handled in their /// callers. static bool constantFoldGlobalStringTablePointerBuiltin(BuiltinInst *bi, bool enableDiagnostics) { // Look through string initializer to extract the string_literal instruction. // // We can look through ownership instructions to get to the string value that // is passed to this builtin. SILValue builtinOperand = lookThroughOwnershipInsts(bi->getOperand(0)); SILFunction *caller = bi->getFunction(); FullApplySite stringInitSite = FullApplySite::isa(builtinOperand); if (!stringInitSite || !stringInitSite.getReferencedFunctionOrNull() || !stringInitSite.getReferencedFunctionOrNull()->hasSemanticsAttr( semantics::STRING_MAKE_UTF8)) { // Emit diagnostics only on non-transparent functions. if (enableDiagnostics && !caller->isTransparent()) { diagnose(caller->getASTContext(), bi->getLoc().getSourceLoc(), diag::global_string_pointer_on_non_constant); } return false; } // Replace the builtin by the first argument of the "string.makeUTF8" // initializer which must be a string_literal instruction. SILValue stringLiteral = stringInitSite.getArgument(0); assert(isa(stringLiteral)); bi->replaceAllUsesWith(stringLiteral); return true; } /// Initialize the worklist to all of the constant instructions. void ConstantFolder::initializeWorklist(SILFunction &f) { for (auto &block : f) { for (auto ii = block.begin(), ie = block.end(); ii != ie; ) { auto *inst = &*ii; ++ii; // TODO: Eliminate trivially dead instructions here. // If `I` is a floating-point literal instruction where the literal is // inf, it means the input has a literal that overflows even // MaxBuiltinFloatType. Diagnose this error, but allow this instruction // to be folded, if needed. if (auto *floatLit = dyn_cast(inst)) { APFloat fpVal = floatLit->getValue(); if (EnableDiagnostics && fpVal.isInfinity() && floatLit->getLoc().getAsASTNode()) { SmallString<10> litStr; tryExtractLiteralText(floatLit, litStr); diagnose(inst->getModule().getASTContext(), inst->getLoc().getSourceLoc(), diag::warning_float_overflows_maxbuiltin, litStr, fpVal.isNegative()); } } if (isFoldable(inst) && inst->hasUsesOfAnyResult()) { WorkList.insert(inst); continue; } // - Should we replace calls to assert_configuration by the assert // configuration and fold calls to any cond_unreachable. if (AssertConfiguration != SILOptions::DisableReplacement && (isApplyOfBuiltin(*inst, BuiltinValueKind::AssertConf) || isApplyOfBuiltin(*inst, BuiltinValueKind::CondUnreachable))) { WorkList.insert(inst); continue; } if (isApplyOfBuiltin(*inst, BuiltinValueKind::GlobalStringTablePointer) || isApplyOfBuiltin(*inst, BuiltinValueKind::IsConcrete)) { WorkList.insert(inst); continue; } // Builtin.ifdef only replaced when not building the stdlib. if (isApplyOfBuiltin(*inst, BuiltinValueKind::Ifdef)) { if (!inst->getModule().getASTContext().SILOpts.ParseStdlib) { WorkList.insert(inst); continue; } } if (isApplyOfKnownAvailability(*inst)) { WorkList.insert(inst); continue; } if (isa(inst) || isa(inst) || isa(inst) || isa(inst)) { WorkList.insert(inst); continue; } if (isApplyOfStringConcat(*inst)) { WorkList.insert(inst); continue; } if (auto *bi = dyn_cast(inst)) { if (auto kind = bi->getBuiltinKind()) { if (isPolymorphicBuiltin(kind.value())) { WorkList.insert(bi); continue; } } } // If we have nominal type literals like struct, tuple, enum visit them // like we do in the worklist to see if we can fold any projection // manipulation operations. if (isa(inst) || isa(inst)) { // TODO: Enum. WorkList.insert(inst); continue; } // ... } } } /// Returns true if \p i is an instruction that has a stateless inverse. We want /// to visit such instructions to eliminate such round-trip unnecessary /// operations. /// /// As an example, consider casts, inttoptr, ptrtoint and friends. static bool isReadNoneAndInvertible(SILInstruction *i) { if (auto *bi = dyn_cast(i)) { // Look for ptrtoint and inttoptr for now. if (auto kind = bi->getBuiltinKind()) { switch (*kind) { default: return false; case BuiltinValueKind::PtrToInt: case BuiltinValueKind::IntToPtr: return true; } } } // Be conservative and return false if we do not have any information. return false; } SILAnalysis::InvalidationKind ConstantFolder::processWorkList() { LLVM_DEBUG(llvm::dbgs() << "*** ConstPropagation processing: \n"); // This is the list of traits that this transformation might preserve. bool InvalidateBranches = false; bool InvalidateCalls = false; bool InvalidateInstructions = false; // The list of instructions whose evaluation resulted in error or warning. // This is used to avoid duplicate error reporting in case we reach the same // instruction from different entry points in the WorkList. llvm::DenseSet ErrorSet; CastOptimizer CastOpt(FuncBuilder, nullptr /*SILBuilderContext*/, /* replaceValueUsesAction */ [&](SILValue oldValue, SILValue newValue) { InvalidateInstructions = true; oldValue->replaceAllUsesWith(newValue); }, /* ReplaceInstUsesAction */ [&](SingleValueInstruction *I, ValueBase *V) { InvalidateInstructions = true; I->replaceAllUsesWith(V); }, /* EraseAction */ [&](SILInstruction *I) { auto *TI = dyn_cast(I); if (TI) { // Invalidate analysis information related to // branches. Replacing // unconditional_check_branch type instructions // by a trap will also invalidate branches/the // CFG. InvalidateBranches = true; } InvalidateInstructions = true; WorkList.remove(I); I->eraseFromParent(); }); auto callbacks = InstModCallbacks().onDelete([&](SILInstruction *instToDelete) { WorkList.remove(instToDelete); InvalidateInstructions = true; instToDelete->eraseFromParent(); }); InstructionDeleter deleter(std::move(callbacks)); // An out parameter array that we use to return new simplified results from // constantFoldInstruction. SmallVector ConstantFoldedResults; while (!WorkList.empty()) { SILInstruction *I = WorkList.pop_back_val(); assert(I->getParent() && "SILInstruction must have parent."); LLVM_DEBUG(llvm::dbgs() << "Visiting: " << *I); // Replace assert_configuration instructions by their constant value. We // want them to be replace even if we can't fully propagate the constant. if (AssertConfiguration != SILOptions::DisableReplacement) if (auto *BI = dyn_cast(I)) { if (isApplyOfBuiltin(*BI, BuiltinValueKind::AssertConf)) { // Instantiate the constant. SILBuilderWithScope B(BI); auto AssertConfInt = B.createIntegerLiteral( BI->getLoc(), BI->getType(), AssertConfiguration); BI->replaceAllUsesWith(AssertConfInt); // Schedule users for constant folding. WorkList.insert(AssertConfInt); // Delete the call. eliminateDeadInstruction(BI, deleter.getCallbacks()); continue; } // Kill calls to conditionallyUnreachable if we've folded assert // configuration calls. if (isApplyOfBuiltin(*BI, BuiltinValueKind::CondUnreachable)) { assert(BI->use_empty() && "use of conditionallyUnreachable?!"); recursivelyDeleteTriviallyDeadInstructions(BI, /*force*/ true, deleter.getCallbacks()); InvalidateInstructions = true; continue; } } // Replace a known availability.version semantic call. if (isApplyOfKnownAvailability(*I)) { if (auto apply = dyn_cast(I)) { SILBuilderWithScope B(I); auto tru = B.createIntegerLiteral(apply->getLoc(), apply->getType(), 1); apply->replaceAllUsesWith(tru); eliminateDeadInstruction(I, deleter.getCallbacks()); WorkList.insert(tru); InvalidateInstructions = true; } continue; } // If we have a cast instruction, try to optimize it. if (isa(I) || isa(I) || isa(I) || isa(I)) { // Try to perform cast optimizations. Invalidation is handled by a // callback inside the cast optimizer. SILInstruction *Result = nullptr; switch(I->getKind()) { default: llvm_unreachable("Unexpected instruction for cast optimizations"); case SILInstructionKind::CheckedCastBranchInst: Result = CastOpt.simplifyCheckedCastBranchInst(cast(I)); break; case SILInstructionKind::CheckedCastAddrBranchInst: Result = CastOpt.simplifyCheckedCastAddrBranchInst(cast(I)); break; case SILInstructionKind::UnconditionalCheckedCastInst: { auto Value = CastOpt.optimizeUnconditionalCheckedCastInst(cast(I)); if (Value) Result = Value->getDefiningInstruction(); break; } case SILInstructionKind::UnconditionalCheckedCastAddrInst: Result = CastOpt.optimizeUnconditionalCheckedCastAddrInst(cast(I)); break; } if (Result) { if (isa(Result) || isa(Result) || isa(Result) || isa(Result)) WorkList.insert(Result); } continue; } // Constant fold uses of globalStringTablePointer builtin. if (isApplyOfBuiltin(*I, BuiltinValueKind::GlobalStringTablePointer)) { if (constantFoldGlobalStringTablePointerBuiltin(cast(I), EnableDiagnostics)) { // Here, the builtin instruction got folded, so clean it up. eliminateDeadInstruction(I, deleter.getCallbacks()); } continue; } // See if we have a CondFailMessage that we can canonicalize. if (isApplyOfBuiltin(*I, BuiltinValueKind::CondFailMessage)) { // See if our operand is a string literal inst. In such a case, fold into // cond_fail instruction. if (auto *sli = dyn_cast(I->getOperand(1))) { if (sli->getEncoding() == StringLiteralInst::Encoding::UTF8) { SILBuilderWithScope builder(I); auto *cfi = builder.createCondFail(I->getLoc(), I->getOperand(0), sli->getValue()); WorkList.insert(cfi); recursivelyDeleteTriviallyDeadInstructions(I, /*force*/ true, deleter.getCallbacks()); } } continue; } if (isApplyOfBuiltin(*I, BuiltinValueKind::IsConcrete)) { if (constantFoldIsConcrete(cast(I))) { // Here, the builtin instruction got folded, so clean it up. recursivelyDeleteTriviallyDeadInstructions(I, /*force*/ true, deleter.getCallbacks()); } continue; } // Builtin.ifdef_... is expected to stay unresolved when building the stdlib // and must be only used in @_alwaysEmitIntoClient exported functions, which // means we never generate IR for it (when building stdlib). Client code is // then always constant-folding this builtin based on the compilation flags // of the client module. if (isApplyOfBuiltin(*I, BuiltinValueKind::Ifdef)) { if (!I->getModule().getASTContext().SILOpts.ParseStdlib) { auto *BI = cast(I); StringRef flagName = BI->getName().str().drop_front(strlen("ifdef_")); const LangOptions &langOpts = I->getModule().getASTContext().LangOpts; bool val = langOpts.isCustomConditionalCompilationFlagSet(flagName); SILBuilderWithScope builder(BI); auto *inst = builder.createIntegerLiteral( BI->getLoc(), SILType::getBuiltinIntegerType(1, builder.getASTContext()), val); BI->replaceAllUsesWith(inst); eliminateDeadInstruction(I, deleter.getCallbacks()); continue; } } if (auto *bi = dyn_cast(I)) { if (auto kind = bi->getBuiltinKind()) { if (SILValue v = specializePolymorphicBuiltin(bi, kind.value())) { // If bi had a result, RAUW. if (bi->getResult(0)->getType() != bi->getModule().Types.getEmptyTupleType()) bi->replaceAllUsesWith(v); // Then delete no matter what. bi->eraseFromParent(); InvalidateInstructions = true; continue; } } } // Go through all users of the constant and try to fold them. for (auto Result : I->getResults()) { for (auto *Use : Result->getUses()) { SILInstruction *User = Use->getUser(); LLVM_DEBUG(llvm::dbgs() << " User: " << *User); // It is possible that we had processed this user already. Do not try to // fold it again if we had previously produced an error while folding // it. It is not always possible to fold an instruction in case of // error. if (ErrorSet.count(User)) continue; // Some constant users may indirectly cause folding of their users. if (isa(User) || isa(User)) { WorkList.insert(User); continue; } // Always consider cond_fail instructions as potential for DCE. If the // expression feeding them is false, they are dead. We can't handle // this as part of the constant folding logic, because there is no value // they can produce (other than empty tuple, which is wasteful). if (isa(User)) deleter.trackIfDead(User); // See if we have an instruction that is read none and has a stateless // inverse. If we do, add it to the worklist so we can check its users // for the inverse operation and see if we can perform constant folding // on the inverse operation. This can eliminate annoying "round trip"s. // // NOTE: We are assuming on purpose that our inverse will be read none, // since otherwise we wouldn't be able to constant fold it this way. if (isReadNoneAndInvertible(User)) { WorkList.insert(User); } // See if we have a CondFailMessage of a string_Literal. If we do, add // it to the worklist, so we can clean it up. if (isApplyOfBuiltin(*User, BuiltinValueKind::CondFailMessage)) { if (auto *sli = dyn_cast(I)) { if (sli->getEncoding() == StringLiteralInst::Encoding::UTF8) { WorkList.insert(User); } } } // If the user is a bitcast, we may be able to constant // fold its users. if (isApplyOfBuiltin(*User, BuiltinValueKind::BitCast)) { WorkList.insert(User); } // Initialize ResultsInError as a None optional. // // We are essentially using this optional to represent 3 states: true, // false, and n/a. std::optional ResultsInError; // If we are asked to emit diagnostics, override ResultsInError with a // Some optional initialized to false. if (EnableDiagnostics) ResultsInError = false; // Try to fold the user. If ResultsInError is None, we do not emit any // diagnostics. If ResultsInError is some, we use it as our return // value. ConstantFoldedResults.clear(); bool Success = constantFoldInstruction(Use, ResultsInError, ConstantFoldedResults); // If we did not pass in a None and the optional is set to true, add the // user to our error set. if (ResultsInError.has_value() && ResultsInError.value()) ErrorSet.insert(User); // We failed to constant propagate... continue... if (!Success || llvm::none_of(ConstantFoldedResults, [](SILValue v) { return bool(v); })) continue; // Now iterate over our new results. for (auto pair : llvm::enumerate(ConstantFoldedResults)) { SILValue C = pair.value(); unsigned Index = pair.index(); // Skip any values that we couldn't simplify. if (!C) continue; // Handle a corner case: if this instruction is an unreachable CFG // loop there is no defined dominance order and we can end up with // loops in the use-def chain. Just bail in this case. if (C->getDefiningInstruction() == User) continue; // Ok, we have succeeded. ++NumInstFolded; // We were able to fold, so all users should use the new folded // value. If we don't have any such users, continue. // // NOTE: The reason why we check if our result has uses is that if // User is a MultipleValueInstruction an infinite loop can result if // User has a result different than the one at Index that we can not // constant fold and if C's defining instruction is an aggregate that // defines an operand of I. // // As an elucidating example, consider the following SIL: // // %w = integer_literal $Builtin.Word, 1 // %0 = struct $Int (%w : $Builtin.Word) (*) // %1 = apply %f() : $@convention(thin) () -> @owned Klass // %2 = tuple (%0 : $Int, %1 : $Klass) // (%3, %4) = destructure_tuple %2 : $(Int, Klass) // store %4 to [init] %mem2: %*Klass // // Without this check, we would infinite loop by processing our // worklist as follows: // // 1. We visit %w and add %0 to the worklist unconditionally since it // is a StructInst. // // 2. We visit %0 and then since %2 is a tuple, we add %2 to the // worklist unconditionally. // // 3. We visit %2 and see that it has a destructure_tuple user. We see // that we can simplify %3 -> %0, but cannot simplify %4. This // means that if we just assume success if we can RAUW %3 without // checking if we will actually replace anything, we will add %0's // defining instruction (*) to the worklist. Q.E.D. // // In contrast, if we realize that RAUWing %3 does nothing and skip // it, we exit the worklist as expected. SILValue r = User->getResult(Index); if (r->use_empty()) { deleter.trackIfDead(User); continue; } // Otherwise, do the RAUW. User->getResult(Index)->replaceAllUsesWith(C); // Record the user if it is dead to perform the necessary cleanups // later. deleter.trackIfDead(User); // The new constant could be further folded now, add it to the // worklist. if (auto *Inst = C->getDefiningInstruction()) WorkList.insert(Inst); } } } // Eagerly DCE. We do this after visiting all users to ensure we don't // invalidate the uses iterator. deleter.cleanupDeadInstructions(); } // TODO: refactor this code outside of the method. Passes should not merge // invalidation kinds themselves. using InvalidationKind = SILAnalysis::InvalidationKind; unsigned Inv = InvalidationKind::Nothing; if (InvalidateInstructions) Inv |= (unsigned) InvalidationKind::Instructions; if (InvalidateCalls) Inv |= (unsigned) InvalidationKind::Calls; if (InvalidateBranches) Inv |= (unsigned) InvalidationKind::Branches; return InvalidationKind(Inv); } void ConstantFolder::dumpWorklist() const { #ifndef NDEBUG llvm::dbgs() << "*** Dumping Constant Folder Worklist ***\n"; for (auto *i : WorkList) { llvm::dbgs() << *i; } llvm::dbgs() << "\n"; #endif }