Files
swift-mirror/lib/SILPasses/ConstantPropagation.cpp
Anna Zaks 740fc0a9d9 Revert "Revert "Reimplement integer arithmetic overflow checking to use special, error on overflow builtins""
(This only fails under -DSWIFT_OPTIMIZED=NO; most likely due to an llvm bug.)

We've decided that it's best to specialize each arithmetic builtin that could overflow, instead of calling a separate generic "staticReport" builtin and passing it enough info to produce the message. The main advantage of this approach is that it would be possible for the compiler to customize the message and better link it to the builtin that overflows. For example, the constants that participated in the computation could be printed. In addition, less code will be generated and the compiler could, in the future, automatically emit the overflow diagnostics/trap at runtime.

This patch introduces new versions of op_with_overflow swift builtins. Which are lowered to llvm.op_with_overflow builtins in IRGen after the static diagnostics. If the last argument to the builtins evaluates to true, the overflow is unintentional. CCP uses the builtins to diagnose the overflow detectable at compile time. FixedPoint is changed to rely on these in implementation of primitive arithmetic operations.

Swift SVN r9328
2013-10-14 21:42:33 +00:00

409 lines
14 KiB
C++

//===--- ConstantPropagation.cpp - Promote alloc_box to alloc_stack ------===//
//
// 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/AST/Builtins.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/Diagnostics.h"
#include "swift/AST/Type.h"
#include "swift/Subsystems.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 Fold arithmetic intrinsics with overflow.
static SILInstruction *constantFoldBinaryWithOverflow(ApplyInst *AI,
llvm::Intrinsic::ID ID,
SILModule &M,
bool ReportOverflow) {
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;
std::string 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;
}
// Get the SIL subtypes of the returned tuple type.
SILType FuncResType = AI->getFunctionTypeInfo(M)->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);
SILValue Result[] = {
B.createIntegerLiteral(AI->getLoc(), ResTy1, Res),
B.createIntegerLiteral(AI->getLoc(), ResTy2, Overflow)
};
// If we can statically determine that the operation overflows,
// warn about it.
if (Overflow && ReportOverflow) {
diagnose(M.getASTContext(),
AI->getLoc().getSourceLoc(),
diag::arithmetic_operation_overflow,
LHSInt.toString(/*Radix*/ 10, Signed),
Operator,
RHSInt.toString(/*Radix*/ 10, Signed));
}
return B.createTuple(AI->getLoc(), FuncResType, Result);
}
static SILInstruction *constantFoldOverflowBuiltin(ApplyInst *AI,
BuiltinValueKind ID,
SILModule &M) {
OperandValueArrayRef Args = AI->getArguments();
IntegerLiteralInst *ShouldReportFlag = dyn_cast<IntegerLiteralInst>(Args[2]);
return constantFoldBinaryWithOverflow(AI,
getLLVMIntrinsicIDForBuiltinWithOverflow(ID), M,
ShouldReportFlag && (ShouldReportFlag->getValue() == 1));
}
static SILInstruction *constantFoldIntrinsic(ApplyInst *AI,
llvm::Intrinsic::ID ID,
SILModule &M) {
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, M, /*ReportOverflow*/false);
}
}
return nullptr;
}
static SILInstruction *constantFoldBuiltin(ApplyInst *AI,
BuiltinFunctionRefInst *FR,
SILModule &M) {
const IntrinsicInfo &Intrinsic = FR->getIntrinsicInfo();
// If it's an llvm intrinsic, fold the intrinsic.
if (Intrinsic.ID != llvm::Intrinsic::not_intrinsic) {
return constantFoldIntrinsic(AI, Intrinsic.ID, M);
}
// 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;
#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, M);
case BuiltinValueKind::Trunc:
case BuiltinValueKind::ZExt:
case BuiltinValueKind::SExt: {
// 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.
APInt CastResV;
Type DestTy = Builtin.Types.size() == 2 ? Builtin.Types[1] : Type();
uint32_t DestBitWidth =
DestTy->castTo<BuiltinIntegerType>()->getBitWidth();
switch (Builtin.ID) {
default : llvm_unreachable("Invalid case.");
case BuiltinValueKind::Trunc:
CastResV = V->getValue().trunc(DestBitWidth);
break;
case BuiltinValueKind::ZExt:
CastResV = V->getValue().zext(DestBitWidth);
break;
case BuiltinValueKind::SExt:
CastResV = V->getValue().sext(DestBitWidth);
break;
}
// Add the literal instruction to represnet the result of the cast.
SILBuilder B(AI);
return B.createIntegerLiteral(AI->getLoc(),
SILType::getPrimitiveType(CanType(DestTy),
SILValueCategory::Object),
CastResV);
}
// Deal with special builtins that are designed to check overflows on
// integer literals.
case BuiltinValueKind::STruncWithOverflow:
case BuiltinValueKind::UTruncWithOverflow: {
// 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::STruncWithOverflow);
// 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:
// 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) {
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_literal_overflow,
CE ? CE->getType() : DestTy);
}
// The call to the builtin should be replaced with the constant value.
SILBuilder B(AI);
return B.createIntegerLiteral(Loc,
SILType::getPrimitiveType(CanType(DestTy),
SILValueCategory::Object),
TruncVal);
}
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);
}
// The call to the builtin should be replaced with the constant value.
SILBuilder B(AI);
return B.createFloatLiteral(Loc,
SILType::getPrimitiveType(CanType(DestTy),
SILValueCategory::Object),
TruncVal);
}
}
return nullptr;
}
static SILInstruction *constantFoldInstruction(SILInstruction &I,
SILModule &M) {
// Constant fold function calls.
if (ApplyInst *AI = dyn_cast<ApplyInst>(&I)) {
// Constant fold calls to builtins.
if (BuiltinFunctionRefInst *FR =
dyn_cast<BuiltinFunctionRefInst>(AI->getCallee().getDef())) {
return constantFoldBuiltin(AI, FR, M);
}
return nullptr;
}
// Constant fold extraction of a constant element.
if (TupleExtractInst *TEI = dyn_cast<TupleExtractInst>(&I)) {
if (TupleInst *TheTuple = dyn_cast<TupleInst>(TEI->getOperand().getDef())) {
unsigned FieldNo = TEI->getFieldNo();
ValueBase *Elem = TheTuple->getElements()[FieldNo].getDef();
if (SILInstruction *SILInstElem = dyn_cast<SILInstruction>(Elem)) {
return SILInstElem;
}
}
}
// Constant fold extraction of a constant element.
if (StructExtractInst *SEI = dyn_cast<StructExtractInst>(&I)) {
if (StructInst *Struct = dyn_cast<StructInst>(SEI->getOperand().getDef())) {
// Find the Field number corresponding to the FieldDecl.
unsigned FieldNo = 0;
StructDecl *SD =
cast<StructDecl>(Struct->getType().getSwiftType()->getAnyNominal());
for (auto MD : SD->getStoredProperties()) {
if (MD == SEI->getField()) {
ValueBase *E = Struct->getElements()[FieldNo].getDef();
// If the element the struct_extract is extracting is const, fold it.
if (SILInstruction *SILInstElem = dyn_cast<SILInstruction>(E)) {
return SILInstElem;
}
break;
}
++FieldNo;
}
}
}
return nullptr;
}
static bool CCPFunctionBody(SILFunction &F, SILModule &M) {
DEBUG(llvm::errs() << "*** ConstPropagation processing: " << F.getName()
<< "\n");
// Initialize the worklist to all of the instructions ready to process...
llvm::SetVector<SILInstruction*> WorkList;
for (auto &BB : F) {
for(auto &I : BB) {
WorkList.insert(&I);
}
}
// Try to fold instructions in the list one by one.
bool Folded = false;
while (!WorkList.empty()) {
SILInstruction *I = *WorkList.begin();
WorkList.remove(I);
if (!I->use_empty())
// Try to fold the instruction.
if (SILInstruction *C = constantFoldInstruction(*I, M)) {
// The users could be constant propagatable now.
for (auto UseI = I->use_begin(),
UseE = I->use_end(); UseI != UseE; ++UseI) {
SILInstruction *User = cast<SILInstruction>(UseI.getUser());
WorkList.insert(User);
// Some constant users may indirectly cause folding of their users.
if (isa<StructInst>(User) || isa<TupleInst>(User)) {
for (auto UseUseI = User->use_begin(),
UseUseE = User->use_end(); UseUseI != UseUseE; ++UseUseI) {
WorkList.insert(cast<SILInstruction>(UseUseI.getUser()));
}
}
}
// We were able to fold, so all users should use the new folded value.
assert(I->getTypes().size() == 1 &&
"Currently, we only support single result instructions.");
SILValue(I).replaceAllUsesWith(C);
// Remove the unused instruction.
WorkList.remove(I);
// Eagerly DCE.
recursivelyDeleteTriviallyDeadInstructions(I);
Folded = true;
++NumInstFolded;
}
}
return false;
}
//===----------------------------------------------------------------------===//
// Top Level Driver
//===----------------------------------------------------------------------===//
void swift::performSILConstantPropagation(SILModule *M) {
for (auto &Fn : *M) {
CCPFunctionBody(Fn, *M);
}
}