-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Remove -bounds-checking-unique-traps (replace with -fno-sanitize-merge=local-bounds) #120682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…e=local-bounds) -fno-sanitize-merge (introduced in llvm#120511) combines the functionality of -ubsan-unique-traps and -bounds-checking-unique-traps, while allowing fine-grained control of which UBSan checks to prevent merging. llvm#120613 removed -ubsan-unique-traps. This patch removes -bound-checking-unique-traps, which can be controlled via -fno-sanitize-merge=local-bounds. Note: this patch subtly changes -fsanitize-merge (the default) to also include -fsanitize-merge=local-bounds. This is different from the previous behavior, where -fsanitize-merge (or the old -ubsan-unique-traps) did not affect local-bounds (requiring the separate -bounds-checking-unique-traps). However, we argue that the new behavior is more intuitive. Removing -bounds-checking-unique-traps and merging its functionality into -fsanitize-merge breaks backwards compatibility; we hope that this is acceptable since '-mllvm -bounds-checking-unique-traps' was an experimental flag.
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: Thurston Dang (thurstond) Changes-fno-sanitize-merge (introduced in #120511) combines the functionality of -ubsan-unique-traps and -bounds-checking-unique-traps, while allowing fine-grained control of which UBSan checks to prevent merging. #120613 removed -ubsan-unique-traps. This patch removes -bound-checking-unique-traps, which can be controlled via -fno-sanitize-merge=local-bounds. Most of this patch is simply plumbing through the compiler flags into the bounds checking pass. Note: this patch subtly changes -fsanitize-merge (the default) to also include -fsanitize-merge=local-bounds. This is different from the previous behavior, where -fsanitize-merge (or the old -ubsan-unique-traps) did not affect local-bounds (requiring the separate -bounds-checking-unique-traps). However, we argue that the new behavior is more intuitive. Removing -bounds-checking-unique-traps and merging its functionality into -fsanitize-merge breaks backwards compatibility; we hope that this is acceptable since '-mllvm -bounds-checking-unique-traps' was an experimental flag. Patch is 21.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/120682.diff 9 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b8d92a6c881c68..0c6c894e17416a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -445,9 +445,10 @@ New Compiler Flags
- The ``-Warray-compare-cxx26`` warning has been added to warn about array comparison
starting from C++26, this warning is enabled as an error by default.
-- '-fsanitize-merge' (default) and '-fno-sanitize-merge' have been added for
- fine-grained control of which UBSan checks are allowed to be merged by the
- backend (for example, -fno-sanitize-merge=bool,enum).
+- ``-fsanitize-merge`` (default) and ``-fno-sanitize-merge`` have been added for
+ fine-grained, unified control of which UBSan checks can potentially be merged
+ by the compiler (for example,
+ ``-fno-sanitize-merge=bool,enum,array-bounds,local-bounds``).
Deprecated Compiler Flags
-------------------------
@@ -488,8 +489,11 @@ Removed Compiler Flags
derivatives) is now removed, since it's no longer possible to suppress the
diagnostic (see above). Users can expect an `unknown warning` diagnostic if
it's still in use.
-- The experimental flag '-ubsan-unique-traps' has been removed. It is
- superseded by '-fno-sanitize-merge'.
+- The experimental flags '-ubsan-unique-traps' and
+ '-bounds-checking-unique-traps' have been removed. The combination of the
+ two flags is equivalent to '-fno-sanitize-merge' with no parameters.
+ '-bounds-checking-unique-traps' can be selectively controlled via
+ '-f(no-)sanitize-merge=local-bounds'.
Attribute Changes in Clang
--------------------------
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index e6c9d77d29f6f1..bfb73aa51b9b5b 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1030,6 +1030,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
PB.registerScalarOptimizerLateEPCallback(
[this](FunctionPassManager &FPM, OptimizationLevel Level) {
BoundsCheckingPass::ReportingMode Mode;
+ bool Merge = CodeGenOpts.SanitizeMergeHandlers.has(SanitizerKind::LocalBounds);
+
if (CodeGenOpts.SanitizeTrap.has(SanitizerKind::LocalBounds)) {
Mode = BoundsCheckingPass::ReportingMode::Trap;
} else if (CodeGenOpts.SanitizeMinimalRuntime) {
@@ -1041,7 +1043,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
? BoundsCheckingPass::ReportingMode::FullRuntime
: BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
}
- FPM.addPass(BoundsCheckingPass(Mode));
+ BoundsCheckingPass::BoundsCheckingOptions Options(Mode, Merge);
+ FPM.addPass(BoundsCheckingPass(Options));
});
// Don't add sanitizers if we are here from ThinLTO PostLink. That already
diff --git a/clang/test/CodeGen/bounds-checking.c b/clang/test/CodeGen/bounds-checking.c
index f9319ca61670c3..5e6b317a99969e 100644
--- a/clang/test/CodeGen/bounds-checking.c
+++ b/clang/test/CodeGen/bounds-checking.c
@@ -1,12 +1,14 @@
-// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s
-// RUN: %clang_cc1 -fsanitize=array-bounds -O -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s
+// N.B. The clang driver defaults to -fsanitize-merge but clang_cc1 effectively
+// defaults to -fno-sanitize-merge.
// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fsanitize=array-bounds -O -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s
//
-// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -O3 -mllvm -bounds-checking-unique-traps -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTLOCAL
-// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s --check-prefixes=NOOPTLOCAL
+// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s
+//
+// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTLOCAL
+// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -fno-sanitize-merge -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTLOCAL
+// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -fsanitize-merge=local-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s --check-prefixes=NOOPTLOCAL
//
-// N.B. The clang driver defaults to -fsanitize-merge but clang_cc1 effectively
-// defaults to -fno-sanitize-merge.
// RUN: %clang_cc1 -fsanitize=array-bounds -fsanitize-trap=array-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTARRAY
// RUN: %clang_cc1 -fsanitize=array-bounds -fsanitize-trap=array-bounds -fno-sanitize-merge -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTARRAY
// RUN: %clang_cc1 -fsanitize=array-bounds -fsanitize-trap=array-bounds -fsanitize-merge=array-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s --check-prefixes=NOOPTARRAY
diff --git a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
index 1876e5b72e8c99..eca93d89838134 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
@@ -17,6 +17,7 @@ class Function;
/// A pass to instrument code and perform run-time bounds checking on loads,
/// stores, and other memory intrinsics.
class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {
+
public:
enum class ReportingMode {
Trap,
@@ -26,15 +27,21 @@ class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {
FullRuntimeAbort,
};
-private:
- ReportingMode Mode = ReportingMode::Trap;
+ struct BoundsCheckingOptions {
+ BoundsCheckingOptions(ReportingMode Mode, bool Merge);
-public:
- BoundsCheckingPass(ReportingMode Mode) : Mode(Mode) {}
+ ReportingMode Mode;
+ bool Merge;
+ };
+
+ BoundsCheckingPass(BoundsCheckingOptions Options) : Options(Options) {}
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
static bool isRequired() { return true; }
void printPipeline(raw_ostream &OS,
function_ref<StringRef(StringRef)> MapClassName2PassName);
+
+ private:
+ BoundsCheckingOptions Options;
};
} // end namespace llvm
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index d70ac48f251180..87756acf724261 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1281,23 +1281,34 @@ parseRegAllocFastPassOptions(PassBuilder &PB, StringRef Params) {
return Opts;
}
-Expected<BoundsCheckingPass::ReportingMode>
+Expected<BoundsCheckingPass::BoundsCheckingOptions>
parseBoundsCheckingOptions(StringRef Params) {
- BoundsCheckingPass::ReportingMode Mode =
- BoundsCheckingPass::ReportingMode::Trap;
+ BoundsCheckingPass::BoundsCheckingOptions Options (BoundsCheckingPass::ReportingMode::Trap, true);
while (!Params.empty()) {
StringRef ParamName;
std::tie(ParamName, Params) = Params.split(';');
if (ParamName == "trap") {
- Mode = BoundsCheckingPass::ReportingMode::Trap;
+ Options.Mode = BoundsCheckingPass::ReportingMode::Trap;
} else if (ParamName == "rt") {
- Mode = BoundsCheckingPass::ReportingMode::FullRuntime;
+ Options.Mode = BoundsCheckingPass::ReportingMode::FullRuntime;
} else if (ParamName == "rt-abort") {
- Mode = BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
+ Options.Mode = BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
} else if (ParamName == "min-rt") {
- Mode = BoundsCheckingPass::ReportingMode::MinRuntime;
+ Options.Mode = BoundsCheckingPass::ReportingMode::MinRuntime;
} else if (ParamName == "min-rt-abort") {
- Mode = BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
+ Options.Mode = BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
+ } else if (ParamName.consume_front("merge=")) {
+ if (ParamName == "true")
+ Options.Merge = true;
+ else if (ParamName == "false")
+ Options.Merge = false;
+ else {
+ return make_error<StringError>(
+ formatv("invalid BoundsChecking pass merge parameter: '{0}' ",
+ ParamName)
+ .str(),
+ inconvertibleErrorCode());
+ }
} else {
return make_error<StringError>(
formatv("invalid BoundsChecking pass parameter '{0}' ", ParamName)
@@ -1305,7 +1316,7 @@ parseBoundsCheckingOptions(StringRef Params) {
inconvertibleErrorCode());
}
}
- return Mode;
+ return Options;
}
} // namespace
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index ba3adcb0e317c0..9f0b09278edcca 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -624,8 +624,8 @@ FUNCTION_PASS_WITH_PARAMS(
parseWinEHPrepareOptions, "demote-catchswitch-only")
FUNCTION_PASS_WITH_PARAMS(
"bounds-checking", "BoundsCheckingPass",
- [](BoundsCheckingPass::ReportingMode Mode) {
- return BoundsCheckingPass(Mode);
+ [](BoundsCheckingPass::BoundsCheckingOptions Options) {
+ return BoundsCheckingPass(Options);
},
parseBoundsCheckingOptions, "trap")
#undef FUNCTION_PASS_WITH_PARAMS
diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
index f639d0628d6053..5ce3e0de59c559 100644
--- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
+++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
@@ -37,15 +37,15 @@ using namespace llvm;
static cl::opt<bool> SingleTrapBB("bounds-checking-single-trap",
cl::desc("Use one trap block per function"));
-static cl::opt<bool> DebugTrapBB("bounds-checking-unique-traps",
- cl::desc("Always use one trap per check"));
-
STATISTIC(ChecksAdded, "Bounds checks added");
STATISTIC(ChecksSkipped, "Bounds checks skipped");
STATISTIC(ChecksUnable, "Bounds checks unable to add");
using BuilderTy = IRBuilder<TargetFolder>;
+BoundsCheckingPass::BoundsCheckingOptions::BoundsCheckingOptions(ReportingMode Mode, bool Merge)
+ : Mode(Mode), Merge(Merge) {}
+
/// Gets the conditions under which memory accessing instructions will overflow.
///
/// \p Ptr is the pointer that will be read/written, and \p InstVal is either
@@ -105,7 +105,7 @@ static Value *getBoundsCheckCond(Value *Ptr, Value *InstVal,
return Or;
}
-static CallInst *InsertTrap(BuilderTy &IRB) {
+static CallInst *InsertTrap(BuilderTy &IRB, bool DebugTrapBB) {
if (!DebugTrapBB)
return IRB.CreateIntrinsic(Intrinsic::trap, {}, {});
// FIXME: Ideally we would use the SanitizerHandler::OutOfBounds constant.
@@ -169,9 +169,10 @@ struct ReportingOpts {
bool MayReturn = false;
bool UseTrap = false;
bool MinRuntime = false;
+ bool MayMerge = true;
StringRef Name;
- ReportingOpts(BoundsCheckingPass::ReportingMode Mode) {
+ ReportingOpts(BoundsCheckingPass::ReportingMode Mode, bool Merge) {
switch (Mode) {
case BoundsCheckingPass::ReportingMode::Trap:
UseTrap = true;
@@ -193,6 +194,8 @@ struct ReportingOpts {
Name = "__ubsan_handle_local_out_of_bounds_abort";
break;
}
+
+ MayMerge = Merge;
}
};
@@ -253,13 +256,12 @@ static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
BasicBlock *TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
IRB.SetInsertPoint(TrapBB);
+ bool DebugTrapBB = !Opts.MayMerge;
CallInst *TrapCall = Opts.UseTrap
- ? InsertTrap(IRB)
+ ? InsertTrap(IRB, DebugTrapBB)
: InsertCall(IRB, Opts.MayReturn, Opts.Name);
- if (DebugTrapBB) {
- // FIXME: Pass option form clang.
+ if (DebugTrapBB)
TrapCall->addFnAttr(llvm::Attribute::NoMerge);
- }
TrapCall->setDoesNotThrow();
TrapCall->setDebugLoc(DebugLoc);
@@ -289,7 +291,7 @@ PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &
auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
- if (!addBoundsChecking(F, TLI, SE, ReportingOpts(Mode)))
+ if (!addBoundsChecking(F, TLI, SE, ReportingOpts(Options.Mode, Options.Merge)))
return PreservedAnalyses::all();
return PreservedAnalyses::none();
@@ -299,7 +301,7 @@ void BoundsCheckingPass::printPipeline(
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
static_cast<PassInfoMixin<BoundsCheckingPass> *>(this)->printPipeline(
OS, MapClassName2PassName);
- switch (Mode) {
+ switch (Options.Mode) {
case ReportingMode::Trap:
OS << "<trap>";
break;
diff --git a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
index 357f92aca85c08..8726606665d7ca 100644
--- a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
+++ b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
@@ -5,6 +5,21 @@
; RUN: opt < %s -passes='bounds-checking<rt-abort>' -S | FileCheck %s --check-prefixes=RTABORT
; RUN: opt < %s -passes='bounds-checking<min-rt>' -S | FileCheck %s --check-prefixes=MINRT
; RUN: opt < %s -passes='bounds-checking<min-rt-abort>' -S | FileCheck %s --check-prefixes=MINRTABORT
+;
+; merge defaults to true
+; RUN: opt < %s -passes='bounds-checking<merge=true>' -S | FileCheck %s --check-prefixes=TR
+; RUN: opt < %s -passes='bounds-checking<trap;merge=true>' -S | FileCheck %s --check-prefixes=TR
+; RUN: opt < %s -passes='bounds-checking<rt;merge=true>' -S | FileCheck %s --check-prefixes=RT
+; RUN: opt < %s -passes='bounds-checking<rt-abort;merge=true>' -S | FileCheck %s --check-prefixes=RTABORT
+; RUN: opt < %s -passes='bounds-checking<min-rt;merge=true>' -S | FileCheck %s --check-prefixes=MINRT
+; RUN: opt < %s -passes='bounds-checking<min-rt-abort;merge=true>' -S | FileCheck %s --check-prefixes=MINRTABORT
+;
+; RUN: opt < %s -passes='bounds-checking<merge=false>' -S | FileCheck %s --check-prefixes=TR-NOMERGE
+; RUN: opt < %s -passes='bounds-checking<trap;merge=false>' -S | FileCheck %s --check-prefixes=TR-NOMERGE
+; RUN: opt < %s -passes='bounds-checking<rt;merge=false>' -S | FileCheck %s --check-prefixes=RT-NOMERGE
+; RUN: opt < %s -passes='bounds-checking<rt-abort;merge=false>' -S | FileCheck %s --check-prefixes=RTABORT-NOMERGE
+; RUN: opt < %s -passes='bounds-checking<min-rt;merge=false>' -S | FileCheck %s --check-prefixes=MINRT-NOMERGE
+; RUN: opt < %s -passes='bounds-checking<min-rt-abort;merge=false>' -S | FileCheck %s --check-prefixes=MINRTABORT-NOMERGE
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
@@ -88,8 +103,100 @@ define void @f1(i64 %x) nounwind {
; MINRTABORT: [[TRAP]]:
; MINRTABORT-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal_abort() #[[ATTR1:[0-9]+]]
; MINRTABORT-NEXT: unreachable
+;
+; TR-NOMERGE-LABEL: define void @f1(
+; TR-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; TR-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; TR-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; TR-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; TR-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; TR-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; TR-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; TR-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; TR-NOMERGE: [[BB7]]:
+; TR-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; TR-NOMERGE-NEXT: ret void
+; TR-NOMERGE: [[TRAP]]:
+; TR-NOMERGE-NEXT: call void @llvm.ubsantrap(i8 3) #[[ATTR2:[0-9]+]]
+; TR-NOMERGE-NEXT: unreachable
+;
+; RT-NOMERGE-LABEL: define void @f1(
+; RT-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; RT-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; RT-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; RT-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; RT-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; RT-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; RT-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; RT-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; RT-NOMERGE: [[BB7]]:
+; RT-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; RT-NOMERGE-NEXT: ret void
+; RT-NOMERGE: [[TRAP]]:
+; RT-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds() #[[ATTR1:[0-9]+]]
+; RT-NOMERGE-NEXT: br label %[[BB7]]
+;
+; RTABORT-NOMERGE-LABEL: define void @f1(
+; RTABORT-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; RTABORT-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; RTABORT-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; RTABORT-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; RTABORT-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; RTABORT-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; RTABORT-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; RTABORT-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; RTABORT-NOMERGE: [[BB7]]:
+; RTABORT-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; RTABORT-NOMERGE-NEXT: ret void
+; RTABORT-NOMERGE: [[TRAP]]:
+; RTABORT-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_abort() #[[ATTR2:[0-9]+]]
+; RTABORT-NOMERGE-NEXT: unreachable
+;
+; MINRT-NOMERGE-LABEL: define void @f1(
+; MINRT-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; MINRT-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; MINRT-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; MINRT-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; MINRT-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; MINRT-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; MINRT-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; MINRT-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; MINRT-NOMERGE: [[BB7]]:
+; MINRT-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; MINRT-NOMERGE-NEXT: ret void
+; MINRT-NOMERGE: [[TRAP]]:
+; MINRT-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal() #[[ATTR1:[0-9]+]]
+; MINRT-NOMERGE-NEXT: br label %[[BB7]]
+;
+; MINRTABORT-NOMERGE-LABEL: define void @f1(
+; MINRTABORT-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; MINRTABORT-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; MINRTABORT-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; MINRTABORT-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; MINRTABORT-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; MINRTABORT-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; MINRTABORT-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; MINRTABORT-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; MINRTABORT-NOMERGE: [[BB7]]:
+; MINRTABORT-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; MINRTABORT-NOMERGE-NEXT: ret void
+; MINRTABORT-NOMERGE: [[TRAP]]:
+; MINRTABORT-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal_abort() #[[ATTR2:[0-9]+]]
+; MINRTA...
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
#120613 removed -ubsan-unique-traps and replaced it with -fno-sanitize-merge (introduced in #120511), which allows fine-grained control of which UBSan checks to prevent merging. This analogous patch removes -bound-checking-unique-traps, and allows it to be controlled via -fno-sanitize-merge=local-bounds.
Most of this patch is simply plumbing through the compiler flags into the bounds checking pass.
Note: this patch subtly changes -fsanitize-merge (the default) to also include -fsanitize-merge=local-bounds. This is different from the previous behavior, where -fsanitize-merge (or the old -ubsan-unique-traps) did not affect local-bounds (requiring the separate -bounds-checking-unique-traps). However, we argue that the new behavior is more intuitive.
Removing -bounds-checking-unique-traps and merging its functionality into -fsanitize-merge breaks backwards compatibility; we hope that this is acceptable since '-mllvm -bounds-checking-unique-traps' was an experimental flag.