Files
swift-mirror/lib/SILPasses/ConstantPropagation.cpp
Anna Zaks b687c3ce9c Add runtime integer truncation checking to the conversion constructors
Add new builtins(by generalizing, renaming, and extending the builtins used for compile time integer literal checking). These new builtins truncate integers and check for overflow/truncation errors at runtime. Use these for FixedPoint conversion constructors.

Fix a routine in stdlib's String implementation and a test that relied on bitwise behavior of the constructors (and triggered overflows).

TODO:
- Teach CCP about these to get static checking.
- Add special builtins for same size signed <-> unsigned conversions.

Swift SVN r10432
2013-11-13 21:54:34 +00:00

583 lines
21 KiB
C++

//===--- ConstantPropagation.cpp - Constant fold and diagnose overflows ---===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "constant-propagation"
#include "swift/Subsystems.h"
#include "swift/AST/Diagnostics.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SILPasses/Utils/Local.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Debug.h"
using namespace swift;
STATISTIC(NumInstFolded, "Number of constant folded instructions");
template<typename...T, typename...U>
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
U &&...args) {
Context.Diags.diagnose(loc,
diag, std::forward<U>(args)...);
}
/// \brief Construct (int, overflow) result tuple.
static SILInstruction *constructResultWithOverflowTuple(ApplyInst *AI,
APInt Res,
bool Overflow) {
// Get the SIL subtypes of the returned tuple type.
SILType FuncResType = AI->getFunctionTypeInfo()->getResult().getSILType();
TupleType *T = FuncResType.castTo<TupleType>();
assert(T->getNumElements() == 2);
SILType ResTy1 =
SILType::getPrimitiveType(CanType(T->getElementType(0)),
SILValueCategory::Object);
SILType ResTy2 =
SILType::getPrimitiveType(CanType(T->getElementType(1)),
SILValueCategory::Object);
// Construct the folded instruction - a tuple of two literals, the
// result and overflow.
SILBuilder B(AI);
SILLocation Loc = AI->getLoc();
SILValue Result[] = {
B.createIntegerLiteral(Loc, ResTy1, Res),
B.createIntegerLiteral(Loc, ResTy2, Overflow)
};
return B.createTuple(Loc, FuncResType, Result);
}
/// \brief Fold arithmetic intrinsics with overflow.
static SILInstruction *constantFoldBinaryWithOverflow(ApplyInst *AI,
llvm::Intrinsic::ID ID,
bool ReportOverflow,
bool &ResultsInError) {
OperandValueArrayRef Args = AI->getArguments();
assert(Args.size() >= 2);
// Check if both arguments are literals.
IntegerLiteralInst *Op1 = dyn_cast<IntegerLiteralInst>(Args[0]);
IntegerLiteralInst *Op2 = dyn_cast<IntegerLiteralInst>(Args[1]);
// We cannot fold a builtin if one of the arguments is not a constant.
if (!Op1 || !Op2)
return nullptr;
// Calculate the result.
APInt LHSInt = Op1->getValue();
APInt RHSInt = Op2->getValue();
APInt Res;
bool Overflow;
bool Signed = false;
StringRef Operator = "+";
switch (ID) {
default: llvm_unreachable("Invalid case");
case llvm::Intrinsic::sadd_with_overflow:
Res = LHSInt.sadd_ov(RHSInt, Overflow);
Signed = true;
break;
case llvm::Intrinsic::uadd_with_overflow:
Res = LHSInt.uadd_ov(RHSInt, Overflow);
break;
case llvm::Intrinsic::ssub_with_overflow:
Res = LHSInt.ssub_ov(RHSInt, Overflow);
Operator = "-";
Signed = true;
break;
case llvm::Intrinsic::usub_with_overflow:
Res = LHSInt.usub_ov(RHSInt, Overflow);
Operator = "-";
break;
case llvm::Intrinsic::smul_with_overflow:
Res = LHSInt.smul_ov(RHSInt, Overflow);
Operator = "*";
Signed = true;
break;
case llvm::Intrinsic::umul_with_overflow:
Res = LHSInt.umul_ov(RHSInt, Overflow);
Operator = "*";
break;
}
// If we can statically determine that the operation overflows,
// warn about it.
if (Overflow && ReportOverflow) {
// 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 = AI->getLoc();
const ApplyExpr *CE = Loc.getAsASTNode<ApplyExpr>();
if (CE) {
const TupleExpr *Args = dyn_cast_or_null<TupleExpr>(CE->getArg());
if (Args && Args->getNumElements() == 2) {
CanType LHSTy = Args->getElement(0)->getType()->getCanonicalType();
CanType RHSTy = Args->getElement(0)->getType()->getCanonicalType();
if (LHSTy == RHSTy)
OpType = Args->getElement(1)->getType();
}
}
if (!OpType.isNull()) {
diagnose(AI->getModule().getASTContext(),
Loc.getSourceLoc(),
diag::arithmetic_operation_overflow,
LHSInt.toString(/*Radix*/ 10, Signed),
Operator,
RHSInt.toString(/*Radix*/ 10, Signed),
OpType);
ResultsInError = true;
} else {
// If we cannot get the type info in an expected way, describe the type.
diagnose(AI->getModule().getASTContext(),
Loc.getSourceLoc(),
diag::arithmetic_operation_overflow_generic_type,
LHSInt.toString(/*Radix*/ 10, Signed),
Operator,
RHSInt.toString(/*Radix*/ 10, Signed),
Signed,
LHSInt.getBitWidth());
ResultsInError = true;
}
}
return constructResultWithOverflowTuple(AI, Res, Overflow);
}
static SILInstruction *constantFoldOverflowBuiltin(ApplyInst *AI,
BuiltinValueKind ID,
bool &ResultsInError) {
OperandValueArrayRef Args = AI->getArguments();
IntegerLiteralInst *ShouldReportFlag = dyn_cast<IntegerLiteralInst>(Args[2]);
return constantFoldBinaryWithOverflow(AI,
getLLVMIntrinsicIDForBuiltinWithOverflow(ID),
ShouldReportFlag && (ShouldReportFlag->getValue() == 1),
ResultsInError);
}
static SILInstruction *constantFoldIntrinsic(ApplyInst *AI,
llvm::Intrinsic::ID ID,
bool &ResultsInError) {
switch (ID) {
default: break;
case llvm::Intrinsic::sadd_with_overflow:
case llvm::Intrinsic::uadd_with_overflow:
case llvm::Intrinsic::ssub_with_overflow:
case llvm::Intrinsic::usub_with_overflow:
case llvm::Intrinsic::smul_with_overflow:
case llvm::Intrinsic::umul_with_overflow:
return constantFoldBinaryWithOverflow(AI, ID,
/* ReportOverflow */ false,
ResultsInError);
}
return nullptr;
}
static SILInstruction *constantFoldCompareBuiltin(ApplyInst *AI,
BuiltinValueKind ID) {
OperandValueArrayRef Args = AI->getArguments();
// Fold for integer constant arguments.
IntegerLiteralInst *LHS = dyn_cast<IntegerLiteralInst>(Args[0]);
IntegerLiteralInst *RHS = dyn_cast<IntegerLiteralInst>(Args[1]);
if (LHS && RHS) {
APInt V1 = LHS->getValue();
APInt V2 = RHS->getValue();
APInt Res;
switch (ID) {
default: llvm_unreachable("Invalid integer compare kind");
case BuiltinValueKind::ICMP_EQ: Res = V1 == V2; break;
case BuiltinValueKind::ICMP_NE: Res = V1 != V2; break;
case BuiltinValueKind::ICMP_SLT: Res = V1.slt(V2); break;
case BuiltinValueKind::ICMP_SGT: Res = V1.sgt(V2); break;
case BuiltinValueKind::ICMP_SLE: Res = V1.sle(V2); break;
case BuiltinValueKind::ICMP_SGE: Res = V1.sge(V2); break;
case BuiltinValueKind::ICMP_ULT: Res = V1.ult(V2); break;
case BuiltinValueKind::ICMP_UGT: Res = V1.ugt(V2); break;
case BuiltinValueKind::ICMP_ULE: Res = V1.ule(V2); break;
case BuiltinValueKind::ICMP_UGE: Res = V1.uge(V2); break;
}
SILBuilder B(AI);
SILLocation Loc = AI->getLoc();
SILType ResType = AI->getFunctionTypeInfo()->getResult().getSILType();
return B.createIntegerLiteral(Loc, ResType, Res);
}
return nullptr;
}
static SILInstruction *constantFoldBuiltin(ApplyInst *AI,
BuiltinFunctionRefInst *FR,
bool &ResultsInError) {
const IntrinsicInfo &Intrinsic = FR->getIntrinsicInfo();
SILModule &M = AI->getModule();
// If it's an llvm intrinsic, fold the intrinsic.
if (Intrinsic.ID != llvm::Intrinsic::not_intrinsic)
return constantFoldIntrinsic(AI, Intrinsic.ID, ResultsInError);
// Otherwise, it should be one of the builin functions.
OperandValueArrayRef Args = AI->getArguments();
const BuiltinInfo &Builtin = M.getBuiltinInfo(FR->getReferencedFunction());
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 constantFoldOverflowBuiltin(AI, 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 constantFoldCompareBuiltin(AI, 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.
IntegerLiteralInst *V = dyn_cast<IntegerLiteralInst>(Args[0]);
if (!V)
return nullptr;
// Get the cast result.
Type SrcTy = Builtin.Types[0];
Type DestTy = Builtin.Types.size() == 2 ? Builtin.Types[1] : Type();
uint32_t SrcBitWidth =
SrcTy->castTo<BuiltinIntegerType>()->getBitWidth();
uint32_t DestBitWidth =
DestTy->castTo<BuiltinIntegerType>()->getBitWidth();
APInt CastResV;
if (SrcBitWidth == DestBitWidth) {
CastResV = V->getValue();
} else switch (Builtin.ID) {
default : llvm_unreachable("Invalid case.");
case BuiltinValueKind::Trunc:
case BuiltinValueKind::TruncOrBitCast:
CastResV = V->getValue().trunc(DestBitWidth);
break;
case BuiltinValueKind::ZExt:
case BuiltinValueKind::ZExtOrBitCast:
CastResV = V->getValue().zext(DestBitWidth);
break;
case BuiltinValueKind::SExt:
case BuiltinValueKind::SExtOrBitCast:
CastResV = V->getValue().sext(DestBitWidth);
break;
}
// Add the literal instruction to represent the result of the cast.
SILBuilder B(AI);
return B.createIntegerLiteral(AI->getLoc(), AI->getType(), CastResV);
}
// Fold constant division operations and report div by zero.
case BuiltinValueKind::SDiv:
case BuiltinValueKind::ExactSDiv:
case BuiltinValueKind::SRem:
case BuiltinValueKind::UDiv:
case BuiltinValueKind::ExactUDiv:
case BuiltinValueKind::URem: {
// Get the denominator.
IntegerLiteralInst *Denom = dyn_cast<IntegerLiteralInst>(Args[1]);
if (!Denom)
return nullptr;
APInt DenomVal = Denom->getValue();
// Reoprt an error if the denominator is zero.
if (DenomVal == 0) {
diagnose(M.getASTContext(),
AI->getLoc().getSourceLoc(),
diag::division_by_zero);
ResultsInError = true;
return nullptr;
}
// Get the numerator.
IntegerLiteralInst *Num = dyn_cast<IntegerLiteralInst>(Args[0]);
if (!Num)
return nullptr;
APInt NumVal = Num->getValue();
APInt ResVal;
bool Overflowed = false;
switch (Builtin.ID) {
// We do not cover all the cases below - only the ones that are easily
// computable for APInt.
default : return nullptr;
case BuiltinValueKind::SDiv:
ResVal = NumVal.sdiv_ov(DenomVal, Overflowed);
break;
case BuiltinValueKind::SRem:
ResVal = NumVal.srem(DenomVal);
break;
case BuiltinValueKind::UDiv:
ResVal = NumVal.udiv(DenomVal);
break;
case BuiltinValueKind::URem:
ResVal = NumVal.urem(DenomVal);
break;
}
if (Overflowed) {
diagnose(M.getASTContext(),
AI->getLoc().getSourceLoc(),
diag::division_overflow,
NumVal.toString(/*Radix*/ 10, /*Signed*/true),
"/",
DenomVal.toString(/*Radix*/ 10, /*Signed*/true));
ResultsInError = true;
return nullptr;
}
// Add the literal instruction to represnet the result of the division.
SILBuilder B(AI);
return B.createIntegerLiteral(AI->getLoc(), AI->getType(), ResVal);
}
// Deal with special builtins that are designed to check overflows on
// integer literals.
case BuiltinValueKind::SToSCheckedTrunc:
case BuiltinValueKind::SToUCheckedTrunc: {
// 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.
IntegerLiteralInst *V = dyn_cast<IntegerLiteralInst>(Args[0]);
if (!V)
return nullptr;
APInt SrcVal = V->getValue();
// Get the signedness of the destination.
bool Signed = (Builtin.ID == BuiltinValueKind::SToSCheckedTrunc);
// Get the source and destination bit width.
assert(Builtin.Types.size() == 2);
uint32_t SrcBitWidth =
Builtin.Types[0]->castTo<BuiltinIntegerType>()->getBitWidth();
Type DestTy = Builtin.Types[1];
uint32_t DestBitWidth =
DestTy->castTo<BuiltinIntegerType>()->getBitWidth();
// Compute the destination (for SrcBitWidth < DestBitWidth):
// truncVal = trunc_IntFrom_IntTo(val)
// strunc_IntFrom_IntTo(val) =
// sext_IntFrom(truncVal) == val ? truncVal : overflow_error
// utrunc_IntFrom_IntTo(val) =
// zext_IntFrom(truncVal) == val ? truncVal : overflow_error
APInt TruncVal = SrcVal.trunc(DestBitWidth);
APInt T = Signed ? TruncVal.sext(SrcBitWidth) : TruncVal.zext(SrcBitWidth);
SILLocation Loc = AI->getLoc();
const ApplyExpr *CE = Loc.getAsASTNode<ApplyExpr>();
// Check for overflow.
if (SrcVal != T) {
// FIXME: This will prevent hard error in cases the error is comming
// from ObjC interoperability code. Currently, we treat NSUInteger as
// Int.
if (Loc.getSourceLoc().isInvalid()) {
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_literal_overflow_warn,
CE ? CE->getType() : DestTy);
ResultsInError = true;
return nullptr;
}
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_literal_overflow,
CE ? CE->getType() : DestTy);
ResultsInError = true;
return nullptr;
}
// The call to the builtin should be replaced with the constant value.
return constructResultWithOverflowTuple(AI, TruncVal, false);
}
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.
IntegerLiteralInst *V = dyn_cast<IntegerLiteralInst>(Args[0]);
if (!V)
return nullptr;
APInt SrcVal = V->getValue();
Type DestTy = Builtin.Types[1];
APFloat TruncVal(
DestTy->castTo<BuiltinFloatType>()->getAPFloatSemantics());
APFloat::opStatus ConversionStatus = TruncVal.convertFromAPInt(
SrcVal, /*isSigned=*/true, APFloat::rmNearestTiesToEven);
SILLocation Loc = AI->getLoc();
const ApplyExpr *CE = Loc.getAsASTNode<ApplyExpr>();
// Check for overflow.
if (ConversionStatus & APFloat::opOverflow) {
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_literal_overflow,
CE ? CE->getType() : DestTy);
ResultsInError = true;
return nullptr;
}
// The call to the builtin should be replaced with the constant value.
SILBuilder B(AI);
return B.createFloatLiteral(Loc, AI->getType(), TruncVal);
}
}
return nullptr;
}
static SILValue constantFoldInstruction(SILInstruction &I,
bool &ResultsInError) {
// Constant fold function calls.
if (ApplyInst *AI = dyn_cast<ApplyInst>(&I)) {
// Constant fold calls to builtins.
if (auto *FR = dyn_cast<BuiltinFunctionRefInst>(AI->getCallee())) {
return constantFoldBuiltin(AI, FR, ResultsInError);
}
return SILValue();
}
// Constant fold extraction of a constant element.
if (TupleExtractInst *TEI = dyn_cast<TupleExtractInst>(&I)) {
if (TupleInst *TheTuple = dyn_cast<TupleInst>(TEI->getOperand()))
return TheTuple->getElements()[TEI->getFieldNo()];
}
// Constant fold extraction of a constant struct element.
if (StructExtractInst *SEI = dyn_cast<StructExtractInst>(&I)) {
if (StructInst *Struct = dyn_cast<StructInst>(SEI->getOperand()))
return Struct->getOperandForField(SEI->getField())->get();
}
return SILValue();
}
static bool isFoldable(SILInstruction *I) {
return isa<IntegerLiteralInst>(I);
}
static bool CCPFunctionBody(SILFunction &F) {
DEBUG(llvm::errs() << "*** ConstPropagation processing: " << F.getName()
<< "\n");
// The list of instructions whose evaluation resulted in errror 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<SILInstruction*> ErrorSet;
// The worklist of the constants that could be folded into their users.
llvm::SetVector<ValueBase*> WorkList;
// Initialize the worklist to all of the constant instructions.
for (auto &BB : F) {
for (auto &I : BB) {
if (isFoldable(&I) && !I.use_empty())
WorkList.insert(&I);
}
}
llvm::DenseSet<SILInstruction*> FoldedUsers;
while (!WorkList.empty()) {
ValueBase *I = *WorkList.begin();
WorkList.remove(I);
// Go through all users of the constant and try to fold them.
FoldedUsers.clear();
for (auto Use : I->getUses()) {
SILInstruction *User = Use->getUser();
// 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<StructInst>(User) || isa<TupleInst>(User)) {
WorkList.insert(User);
continue;
}
// Try to fold the user.
bool ResultsInError = false;
SILValue C = constantFoldInstruction(*User, ResultsInError);
if (ResultsInError)
ErrorSet.insert(User);
if (!C) continue;
FoldedUsers.insert(User);
++NumInstFolded;
// If the constant produced a tuple, be smarter than RAUW: explicitly nuke
// any tuple_extract instructions using the apply. This is a common case
// for functions returning multiple values.
if (auto *TI = dyn_cast<TupleInst>(C)) {
for (auto UI = User->use_begin(), E = User->use_end(); UI != E;) {
Operand *O = *UI++;
// If the user is a tuple_extract, just substitute the right value in.
if (auto *TEI = dyn_cast<TupleExtractInst>(O->getUser())) {
SILValue NewVal = TI->getOperand(TEI->getFieldNo());
SILValue(TEI, 0).replaceAllUsesWith(NewVal);
TEI->dropAllReferences();
FoldedUsers.insert(TEI);
WorkList.insert(NewVal.getDef());
}
}
if (User->use_empty())
FoldedUsers.insert(TI);
}
// We were able to fold, so all users should use the new folded value.
assert(User->getTypes().size() == 1 &&
"Currently, we only support single result instructions");
SILValue(User).replaceAllUsesWith(C);
// The new constant could be further folded now, add it to the worklist.
WorkList.insert(C.getDef());
}
// Eagerly DCE. We do this after visiting all users to ensure we don't
// invalidate the uses iterator.
for (auto U : FoldedUsers)
recursivelyDeleteTriviallyDeadInstructions(U);
}
return false;
}
//===----------------------------------------------------------------------===//
// Top Level Driver
//===----------------------------------------------------------------------===//
void swift::performSILConstantPropagation(SILModule *M) {
for (auto &Fn : *M)
CCPFunctionBody(Fn);
}