Add a -ftrapv-handler= option which allows a handler to invoke instead of simply aborting when a signed operation overflows. This mirrors the (GCC-incompatible) behaviour from clang 1.0 and 1.1 when -ftrapv was specified, but allows the handler to be defined for each compilation unit.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@114192 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h
index 5bd0d04..fec984f 100644
--- a/include/clang/Basic/LangOptions.h
+++ b/include/clang/Basic/LangOptions.h
@@ -134,6 +134,9 @@
SOB_Defined, // -fwrapv
SOB_Trapping // -ftrapv
};
+ /// The name of the handler function to be called when -ftrapv is specified.
+ /// If none is specified, abort (GCC-compatible behaviour).
+ std::string OverflowHandler;
LangOptions() {
Trigraphs = BCPLComment = Bool = DollarIdents = AsmPreprocessor = 0;
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index 99f748e..512c2ca 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -455,6 +455,9 @@
HelpText<"enable objective-c's enhanced nonfragile abi">;
def ftrapv : Flag<"-ftrapv">,
HelpText<"Trap on integer overflow">;
+def ftrapv_handler : Separate<"-ftrapv-handler">,
+ MetaVarName<"<function name>">,
+ HelpText<"Specify the function to be called on overflow.">;
def fwrapv : Flag<"-fwrapv">,
HelpText<"Treat signed integer overflow as two's complement">;
def pic_level : Separate<"-pic-level">,
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 040b2b2..c00e93f 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -390,6 +390,7 @@
def fthreadsafe_statics : Flag<"-fthreadsafe-statics">, Group<f_Group>;
def ftime_report : Flag<"-ftime-report">, Group<f_Group>;
def ftrapv : Flag<"-ftrapv">, Group<f_Group>;
+def ftrapv_handler_EQ : Joined<"-ftrapv-handler=">, Group<f_Group>;
def funit_at_a_time : Flag<"-funit-at-a-time">, Group<f_Group>;
def funroll_loops : Flag<"-funroll-loops">, Group<f_Group>;
def funsigned_bitfields : Flag<"-funsigned-bitfields">, Group<f_Group>;
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index 68e0ff9..bacb564 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -1627,18 +1627,56 @@
Value *overflow = Builder.CreateExtractValue(resultAndOverflow, 1);
// Branch in case of overflow.
+ llvm::BasicBlock *initialBB = Builder.GetInsertBlock();
llvm::BasicBlock *overflowBB = CGF.createBasicBlock("overflow", CGF.CurFn);
llvm::BasicBlock *continueBB = CGF.createBasicBlock("nooverflow", CGF.CurFn);
Builder.CreateCondBr(overflow, overflowBB, continueBB);
// Handle overflow with llvm.trap.
- // TODO: it would be better to generate one of these blocks per function.
- EmitOverflowBB(overflowBB);
-
- // Continue on.
+ const std::string *handlerName =
+ &CGF.getContext().getLangOptions().OverflowHandler;
+ if (handlerName->empty()) {
+ EmitOverflowBB(overflowBB);
+ Builder.SetInsertPoint(continueBB);
+ return result;
+ }
+
+ // If an overflow handler is set, then we want to call it and then use its
+ // result, if it returns.
+ Builder.SetInsertPoint(overflowBB);
+
+ // Get the overflow handler.
+ const llvm::Type *Int8Ty = llvm::Type::getInt8Ty(VMContext);
+ std::vector<const llvm::Type*> argTypes;
+ argTypes.push_back(CGF.Int64Ty); argTypes.push_back(CGF.Int64Ty);
+ argTypes.push_back(Int8Ty); argTypes.push_back(Int8Ty);
+ llvm::FunctionType *handlerTy =
+ llvm::FunctionType::get(CGF.Int64Ty, argTypes, true);
+ llvm::Value *handler = CGF.CGM.CreateRuntimeFunction(handlerTy, *handlerName);
+
+ // Sign extend the args to 64-bit, so that we can use the same handler for
+ // all types of overflow.
+ llvm::Value *lhs = Builder.CreateSExt(Ops.LHS, CGF.Int64Ty);
+ llvm::Value *rhs = Builder.CreateSExt(Ops.RHS, CGF.Int64Ty);
+
+ // Call the handler with the two arguments, the operation, and the size of
+ // the result.
+ llvm::Value *handlerResult = Builder.CreateCall4(handler, lhs, rhs,
+ Builder.getInt8(OpID),
+ Builder.getInt8(cast<llvm::IntegerType>(opTy)->getBitWidth()));
+
+ // Truncate the result back to the desired size.
+ handlerResult = Builder.CreateTrunc(handlerResult, opTy);
+ Builder.CreateBr(continueBB);
+
Builder.SetInsertPoint(continueBB);
- return result;
+ llvm::PHINode *phi = Builder.CreatePHI(opTy);
+ phi->reserveOperandSpace(2);
+ phi->addIncoming(result, initialBB);
+ phi->addIncoming(handlerResult, overflowBB);
+
+ return phi;
}
Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &Ops) {
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 1e1af35..028db8c 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1178,6 +1178,12 @@
Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits);
Args.AddLastArg(CmdArgs, options::OPT_ftime_report);
Args.AddLastArg(CmdArgs, options::OPT_ftrapv);
+
+ if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
+ CmdArgs.push_back("-ftrapv-handler");
+ CmdArgs.push_back(A->getValue(Args));
+ }
+
Args.AddLastArg(CmdArgs, options::OPT_fwrapv);
Args.AddLastArg(CmdArgs, options::OPT_fwritable_strings);
Args.AddLastArg(CmdArgs, options::OPT_funroll_loops);
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 64ac3ba..d793fc0 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -581,7 +581,12 @@
switch (Opts.getSignedOverflowBehavior()) {
case LangOptions::SOB_Undefined: break;
case LangOptions::SOB_Defined: Res.push_back("-fwrapv"); break;
- case LangOptions::SOB_Trapping: Res.push_back("-ftrapv"); break;
+ case LangOptions::SOB_Trapping:
+ Res.push_back("-ftrapv"); break;
+ if (!Opts.OverflowHandler.empty()) {
+ Res.push_back("-ftrapv-handler");
+ Res.push_back(Opts.OverflowHandler);
+ }
}
if (Opts.HeinousExtensions)
Res.push_back("-fheinous-gnu-extensions");
@@ -1312,8 +1317,12 @@
if (Args.hasArg(OPT_fvisibility_inlines_hidden))
Opts.InlineVisibilityHidden = 1;
- if (Args.hasArg(OPT_ftrapv))
+ if (Args.hasArg(OPT_ftrapv)) {
Opts.setSignedOverflowBehavior(LangOptions::SOB_Trapping);
+ // Set the handler, if one is specified.
+ Opts.OverflowHandler =
+ Args.getLastArgValue(OPT_ftrapv_handler);
+ }
else if (Args.hasArg(OPT_fwrapv))
Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined);
diff --git a/test/CodeGen/integer-overflow.c b/test/CodeGen/integer-overflow.c
index 9bed741..103cc84 100644
--- a/test/CodeGen/integer-overflow.c
+++ b/test/CodeGen/integer-overflow.c
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
// RUN: %clang_cc1 %s -emit-llvm -o - -fwrapv | FileCheck %s --check-prefix=WRAPV
// RUN: %clang_cc1 %s -emit-llvm -o - -ftrapv | FileCheck %s --check-prefix=TRAPV
+// RUN: %clang_cc1 %s -emit-llvm -o - -ftrapv -ftrapv-handler foo | FileCheck %s --check-prefix=TRAPV_HANDLER
// Tests for signed integer overflow stuff.
@@ -14,21 +15,25 @@
// DEFAULT: add nsw i32
// WRAPV: add i32
// TRAPV: llvm.sadd.with.overflow.i32
+ // TRAPV_HANDLER: foo(
f11G = a + b;
// DEFAULT: sub nsw i32
// WRAPV: sub i32
// TRAPV: llvm.ssub.with.overflow.i32
+ // TRAPV_HANDLER: foo(
f11G = a - b;
// DEFAULT: mul nsw i32
// WRAPV: mul i32
// TRAPV: llvm.smul.with.overflow.i32
+ // TRAPV_HANDLER: foo(
f11G = a * b;
// DEFAULT: sub nsw i32 0,
// WRAPV: sub i32 0,
// TRAPV: llvm.ssub.with.overflow.i32(i32 0
+ // TRAPV_HANDLER: foo(
f11G = -a;
// PR7426 - Overflow checking for increments.
@@ -36,10 +41,12 @@
// DEFAULT: add nsw i32 {{.*}}, 1
// WRAPV: add i32 {{.*}}, 1
// TRAPV: llvm.sadd.with.overflow.i32({{.*}}, i32 1)
+ // TRAPV_HANDLER: foo(
++a;
// DEFAULT: add nsw i32 {{.*}}, -1
// WRAPV: add i32 {{.*}}, -1
// TRAPV: llvm.sadd.with.overflow.i32({{.*}}, i32 -1)
+ // TRAPV_HANDLER: foo(
--a;
}