diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -254,6 +254,7 @@ CODEGENOPT(SanitizeCoveragePCTable, 1, 0) ///< Create a PC Table. CODEGENOPT(SanitizeCoverageNoPrune, 1, 0) ///< Disable coverage pruning. CODEGENOPT(SanitizeCoverageStackDepth, 1, 0) ///< Enable max stack depth tracing +CODEGENOPT(SanitizeMergeTraps, 1, 0) ///< Whether to share trap instructions. CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers. CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled. CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1608,6 +1608,13 @@ PosFlag, NegFlag, BothFlags<[], " sanitizer statistics gathering.">>, Group; +def fsanitize_merge_traps : Flag<["-"], "fsanitize-merge-traps">, + Group, + HelpText<"Merge sanitizer check trap instructions">; +def fno_sanitize_merge_traps : Flag<["-"], "fno-sanitize-merge-traps">, + Group, + Flags<[CoreOption, NoXarchOption]>, + HelpText<"Do not merge sanitizer check trap instructions">; def fsanitize_thread_memory_access : Flag<["-"], "fsanitize-thread-memory-access">, Group, HelpText<"Enable memory access instrumentation in ThreadSanitizer (default)">; diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -57,6 +57,7 @@ // True if cross-dso CFI support if provided by the system (i.e. Android). bool ImplicitCfiRuntime = false; bool NeedsMemProfRt = false; + Optional MergeTraps; public: /// Parses the sanitizer arguments from an argument list. diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -3474,7 +3474,7 @@ TrapBBs.resize(CheckHandlerID + 1); llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; - if (!CGM.getCodeGenOpts().OptimizationLevel || !TrapBB) { + if (!CGM.getCodeGenOpts().SanitizeMergeTraps || !TrapBB) { TrapBB = createBasicBlock("trap"); Builder.CreateCondBr(Checked, Cont, TrapBB); EmitBlock(TrapBB); @@ -3490,6 +3490,12 @@ } TrapCall->setDoesNotReturn(); TrapCall->setDoesNotThrow(); + if (!CGM.getCodeGenOpts().SanitizeMergeTraps) { + // Prevent the optimizer from merging traps if the user asked us not + // to. + TrapCall->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoMerge); + } Builder.CreateUnreachable(); } else { auto Call = TrapBB->begin(); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -655,6 +655,14 @@ Stats = Args.hasFlag(options::OPT_fsanitize_stats, options::OPT_fno_sanitize_stats, false); + // If the user didn't pass an explicit sanitize-merge-traps value, don't add + // it to the cc1 line. + if (Args.hasArg(options::OPT_fsanitize_merge_traps, + options::OPT_fno_sanitize_merge_traps)) { + MergeTraps = Args.hasFlag(options::OPT_fsanitize_merge_traps, + options::OPT_fno_sanitize_merge_traps); + } + if (MinimalRuntime) { SanitizerMask IncompatibleMask = Kinds & ~setGroupBits(CompatibleWithMinimalRuntime); @@ -1066,6 +1074,13 @@ if (Stats) CmdArgs.push_back("-fsanitize-stats"); + if (MergeTraps) { + if (*MergeTraps) + CmdArgs.push_back("-fsanitize-merge-traps"); + else + CmdArgs.push_back("-fno-sanitize-merge-traps"); + } + if (MinimalRuntime) CmdArgs.push_back("-fsanitize-minimal-runtime"); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1935,6 +1935,13 @@ Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags, Opts.SanitizeTrap); + // Merging sanitizer traps saves code size, but makes debugging harder. By + // default, merge them when optimizations are enabled. Let the user override + // the setting either way. + Opts.SanitizeMergeTraps = Args.hasFlag(options::OPT_fsanitize_merge_traps, + options::OPT_fno_sanitize_merge_traps, + OptimizationLevel > 0); + Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true); if (Args.hasArg(options::OPT_ffinite_loops)) diff --git a/clang/test/CodeGen/catch-undef-behavior.c b/clang/test/CodeGen/catch-undef-behavior.c --- a/clang/test/CodeGen/catch-undef-behavior.c +++ b/clang/test/CodeGen/catch-undef-behavior.c @@ -387,4 +387,4 @@ // CHECK-UBSAN: ![[WEIGHT_MD]] = !{!"branch_weights", i32 1048575, i32 1} -// CHECK-TRAP: attributes [[NR_NUW]] = { noreturn nounwind } +// CHECK-TRAP: attributes [[NR_NUW]] = { nomerge noreturn nounwind } diff --git a/clang/test/CodeGen/cfi-nomerge.c b/clang/test/CodeGen/cfi-nomerge.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/cfi-nomerge.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s -fsanitize-merge-traps | FileCheck --check-prefix=MERGE %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s -fno-sanitize-merge-traps | FileCheck --check-prefix=NOMERGE %s + +int twoChecks(void (*f)(), void (*g)()) { + f(); + g(); + return 42; +} + +// NOMERGE-LABEL: define dso_local i32 @twoChecks( +// NOMERGE: call void @llvm.ubsantrap(i8 2) #[[ATTR:[0-9]+]] +// NOMERGE-NEXT: unreachable +// NOMERGE: call void @llvm.ubsantrap(i8 2) #[[ATTR]] +// NOMERGE-NEXT: unreachable + +// NOMERGE: #[[ATTR]] = { nomerge noreturn nounwind } + +// MERGE-LABEL: define dso_local i32 @twoChecks( +// MERGE: call void @llvm.ubsantrap(i8 2) +// MERGE-NEXT: unreachable +// MERGE-NOT: llvm.ubsantrap +// MERGE: ret i32 42 diff --git a/clang/test/CodeGen/trapv-nomerge.c b/clang/test/CodeGen/trapv-nomerge.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/trapv-nomerge.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -O2 -triple x86_64-apple-darwin10 -ftrapv -fno-sanitize-merge-traps %s -emit-llvm -o - | FileCheck %s --check-prefix=NOMERGE +// RUN: %clang_cc1 -O2 -triple x86_64-apple-darwin10 -ftrapv -fsanitize-merge-traps %s -emit-llvm -o - | FileCheck %s --check-prefix=MERGE + +unsigned int ui, uj, uk; +int i, j, k; + +int twoChecks() { + ++i; + ++j; + return 42; +} + +// NOMERGE-LABEL: define i32 @twoChecks() +// NOMERGE: call void @llvm.ubsantrap(i8 0) #[[ATTR:[0-9]+]] +// NOMERGE-NEXT: unreachable +// NOMERGE: call void @llvm.ubsantrap(i8 0) #[[ATTR]] +// NOMERGE-NEXT: unreachable + +// NOMERGE: #[[ATTR]] = { nomerge noreturn nounwind } + +// MERGE-LABEL: define i32 @twoChecks() +// MERGE: call void @llvm.ubsantrap(i8 0) +// MERGE-NEXT: unreachable +// MERGE-NOT: llvm.ubsantrap +// MERGE: ret i32 42 diff --git a/clang/test/Driver/sanitize-merge-traps.c b/clang/test/Driver/sanitize-merge-traps.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/sanitize-merge-traps.c @@ -0,0 +1,8 @@ +// RUN: %clang -c %s -fsanitize=cfi -flto -O2 -fno-sanitize-merge-traps -### 2>&1 | FileCheck %s --check-prefix=NOMERGE +// RUN: %clang -c %s -fsanitize=cfi -flto -O0 -fsanitize-merge-traps -### 2>&1 | FileCheck %s --check-prefix=MERGE +// RUN: %clang -c %s -fsanitize=cfi -flto -O2 -### 2>&1 | FileCheck %s --check-prefix=NONE +// RUN: %clang -c %s -fsanitize=cfi -flto -O0 -### 2>&1 | FileCheck %s --check-prefix=NONE + +// NOMERGE: "-fno-sanitize-merge-traps" +// MERGE: "-fsanitize-merge-traps" +// NONE-NOT: "-f{{(no-)?}}sanitize-merge-traps"