https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/174894
>From fe13c35178eca2a207912a9c20c7c69c3c6163af Mon Sep 17 00:00:00 2001 From: Paul Kirth <[email protected]> Date: Wed, 7 Jan 2026 14:30:55 -0800 Subject: [PATCH 1/4] [clang][driver] Expose a frontend option for trap-unreachable We have several issues that list surprising behavior around UB. In many cases, this causes undesirable control flow, such as execution falling through to the next function (or whatever is in memory) instead of remaining within the bounds of the procedure. #174844, #48943, #146791, and #137741 all discuss a host of related issues. In #174844, it was mentioned that we have backend support for this for Rust, and at least one big class of these issues could be addressed by exposing the option to clang. This patch adds a new driver option that does just that. For now, we're leaving this option off by default, though we expect only small differences in code size or performance as a result if it were to be enabled. There will be an RFC in the future when we have more confidence this should be the default configuration. Fixes #174844 --- clang/include/clang/Basic/CodeGenOptions.def | 3 +++ clang/include/clang/Options/Options.td | 7 +++++++ clang/lib/CodeGen/BackendUtil.cpp | 1 + clang/lib/Driver/ToolChains/Clang.cpp | 3 +++ clang/test/CodeGen/X86/unreachable-trap.c | 18 ++++++++++++++++++ clang/test/Driver/clang_f_opts.c | 5 +++++ 6 files changed, 37 insertions(+) create mode 100644 clang/test/CodeGen/X86/unreachable-trap.c diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index baf8b093c10e6..3bf91c56c4cf1 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -430,6 +430,9 @@ CODEGENOPT(DirectAccessExternalData, 1, 0, Benign) /// paths that reach the end of a function without executing a required return. CODEGENOPT(StrictReturn, 1, 1, Benign) +/// Whether we should use make unreachable trap or not. +CODEGENOPT(TrapUnreachable, 1, 0, Benign) + /// Whether emit pseudo probes for sample pgo profile collection. CODEGENOPT(PseudoProbeForProfiling, 1, 0, Benign) diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 2f57a5b13b917..7817c2c5ab67d 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4693,6 +4693,13 @@ defm strict_return : BoolFOption<"strict-return", " of a non-void function as unreachable">, PosFlag<SetTrue>>; +defm trap_unreachable + : BoolFOption<"trap-unreachable", CodeGenOpts<"TrapUnreachable">, + DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], + "Treat unreachable instructions as traps">, + NegFlag<SetFalse>>; + let Flags = [TargetSpecific] in { defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; defm ptrauth_calls : OptInCC1FFlag<"ptrauth-calls", "Enable signing and authentication of all indirect calls">; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index d411ef1bf8763..acb262f584328 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -468,6 +468,7 @@ static bool initTargetOptions(const CompilerInstance &CI, Options.XCOFFReadOnlyPointers = CodeGenOpts.XCOFFReadOnlyPointers; Options.VecLib = convertDriverVectorLibraryToVectorLibrary(CodeGenOpts.getVecLib()); + Options.TrapUnreachable = CodeGenOpts.TrapUnreachable; switch (CodeGenOpts.getSwiftAsyncFramePointer()) { case CodeGenOptions::SwiftAsyncFramePointerKind::Auto: diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 4ca98600d6e93..e76319a3ac563 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5848,6 +5848,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_fsplit_stack, options::OPT_fno_split_stack); + Args.addOptInFlag(CmdArgs, options::OPT_ftrap_unreachable, + options::OPT_ftrap_unreachable); + // -fprotect-parens=0 is default. if (Args.hasFlag(options::OPT_fprotect_parens, options::OPT_fno_protect_parens, false)) diff --git a/clang/test/CodeGen/X86/unreachable-trap.c b/clang/test/CodeGen/X86/unreachable-trap.c new file mode 100644 index 0000000000000..bea34e80dd873 --- /dev/null +++ b/clang/test/CodeGen/X86/unreachable-trap.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 %s -O1 -triple=x86_64-unknown-linux-gnu -ftrap-unreachable -S -o - 2>&1 | FileCheck %s --check-prefix=TRAP +// RUN: %clang_cc1 %s -O1 -triple=x86_64-unknown-linux-gnu -S -o - 2>&1 | FileCheck %s --check-prefix=NOTRAP + +// TRAP: ud2 +// NOTRAP-NOT: ud2 + +[[noreturn]] +void exit(int); + +#define NULL 0 + +static void test(void) { + int *ptr = NULL; + *ptr = 0; + exit(0); +} + +void foo() { test(); } diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c index 5871f1580d6b7..aedbef393abcc 100644 --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -651,3 +651,8 @@ // RUN: %clang -### --target=x86_64-pc-windows-msvc -fno-strict-aliasing %s 2>&1 | FileCheck -check-prefix=CHECK-NO-STRICT-ALIASING %s // CHECK-STRICT-ALIASING-NOT: -relaxed-aliasing // CHECK-NO-STRICT-ALIASING: -relaxed-aliasing + +// RUN: %clang -### -ftrap-unreachable %s 2>&1 | FileCheck %s -check-prefix=UNREACHABLE-TRAP +// RUN: %clang -### -fno-trap-unreachable %s 2>&1 | FileCheck %s -check-prefix=NO-UNREACHABLE-TRAP +// UNREACHABLE-TRAP: "-ftrap-unreachable" +// NO-UNREACHABLE-TRAP-NOT: "-ftrap-unreachable" >From 8289e84bc3c7e572f625899246949a82d63707b9 Mon Sep 17 00:00:00 2001 From: Paul Kirth <[email protected]> Date: Mon, 12 Jan 2026 11:53:59 -0800 Subject: [PATCH 2/4] Also support NoTrapAfterNoreturn --- clang/include/clang/Basic/CodeGenOptions.def | 6 ++--- clang/include/clang/Basic/CodeGenOptions.h | 6 +++++ clang/include/clang/Options/Options.td | 13 ++++++----- clang/lib/CodeGen/BackendUtil.cpp | 12 +++++++++- clang/lib/Driver/ToolChains/Clang.cpp | 5 ++--- clang/test/CodeGen/X86/unreachable-trap.c | 23 +++++++++++++++----- clang/test/Driver/clang_f_opts.c | 12 ++++++---- 7 files changed, 55 insertions(+), 22 deletions(-) diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 3bf91c56c4cf1..90303062779b8 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -413,6 +413,9 @@ VALUE_CODEGENOPT(TLSSize, 8, 0, Benign) /// The types of variables that we will extend the live ranges of. ENUM_CODEGENOPT(ExtendVariableLiveness, ExtendVariableLivenessKind, 2, ExtendVariableLivenessKind::None, Benign) +/// Whether we should use make unreachable trap or not. +ENUM_CODEGENOPT(TrapUnreachable, TrapUnreachableKind, 2, TrapUnreachableKind::None, Benign) + /// The default stack protector guard offset to use. VALUE_CODEGENOPT(StackProtectorGuardOffset, 32, INT_MAX, Benign) @@ -430,9 +433,6 @@ CODEGENOPT(DirectAccessExternalData, 1, 0, Benign) /// paths that reach the end of a function without executing a required return. CODEGENOPT(StrictReturn, 1, 1, Benign) -/// Whether we should use make unreachable trap or not. -CODEGENOPT(TrapUnreachable, 1, 0, Benign) - /// Whether emit pseudo probes for sample pgo profile collection. CODEGENOPT(PseudoProbeForProfiling, 1, 0, Benign) diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index c60ca507ff917..6265272f9507f 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -126,6 +126,12 @@ class CodeGenOptions : public CodeGenOptionsBase { All, }; + enum class TrapUnreachableKind { + None, + ExceptNoreturn, + All, + }; + enum InlineAsmDialectKind { IAD_ATT, IAD_Intel, diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 7817c2c5ab67d..10a152f4a3c0b 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4693,12 +4693,13 @@ defm strict_return : BoolFOption<"strict-return", " of a non-void function as unreachable">, PosFlag<SetTrue>>; -defm trap_unreachable - : BoolFOption<"trap-unreachable", CodeGenOpts<"TrapUnreachable">, - DefaultFalse, - PosFlag<SetTrue, [], [ClangOption, CC1Option], - "Treat unreachable instructions as traps">, - NegFlag<SetFalse>>; +def ftrap_unreachable : Joined<["-"], "ftrap-unreachable=">, + Group<f_Group>, Visibility<[ClangOption, CC1Option]>, + HelpText<"Treat unreachable instruction as traps.">, + Values<"all,except-noreturn,none">, + NormalizedValues<["All", "ExceptNoreturn", "None"]>, + NormalizedValuesScope<"CodeGenOptions::TrapUnreachableKind">, + MarshallingInfoEnum<CodeGenOpts<"TrapUnreachable">, "None">; let Flags = [TargetSpecific] in { defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index acb262f584328..cacce9f30b48d 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -468,7 +468,17 @@ static bool initTargetOptions(const CompilerInstance &CI, Options.XCOFFReadOnlyPointers = CodeGenOpts.XCOFFReadOnlyPointers; Options.VecLib = convertDriverVectorLibraryToVectorLibrary(CodeGenOpts.getVecLib()); - Options.TrapUnreachable = CodeGenOpts.TrapUnreachable; + + switch(CodeGenOpts.getTrapUnreachable()){ + case clang::CodeGenOptions::TrapUnreachableKind::ExceptNoreturn: + Options.NoTrapAfterNoreturn = true; + LLVM_FALLTHROUGH; + case clang::CodeGenOptions::TrapUnreachableKind::All: + Options.TrapUnreachable = true; + break; + case clang::CodeGenOptions::TrapUnreachableKind::None: + break; + }; switch (CodeGenOpts.getSwiftAsyncFramePointer()) { case CodeGenOptions::SwiftAsyncFramePointerKind::Auto: diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index e76319a3ac563..4c3d04a0bbd7f 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5848,9 +5848,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_fsplit_stack, options::OPT_fno_split_stack); - Args.addOptInFlag(CmdArgs, options::OPT_ftrap_unreachable, - options::OPT_ftrap_unreachable); - // -fprotect-parens=0 is default. if (Args.hasFlag(options::OPT_fprotect_parens, options::OPT_fno_protect_parens, false)) @@ -5865,6 +5862,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_fatomic_ignore_denormal_mode, options::OPT_fno_atomic_ignore_denormal_mode); + Args.addLastArg(CmdArgs, options::OPT_ftrap_unreachable); + if (Arg *A = Args.getLastArg(options::OPT_fextend_args_EQ)) { const llvm::Triple::ArchType Arch = TC.getArch(); if (Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64) { diff --git a/clang/test/CodeGen/X86/unreachable-trap.c b/clang/test/CodeGen/X86/unreachable-trap.c index bea34e80dd873..e7f2adc3f9f9b 100644 --- a/clang/test/CodeGen/X86/unreachable-trap.c +++ b/clang/test/CodeGen/X86/unreachable-trap.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 %s -O1 -triple=x86_64-unknown-linux-gnu -ftrap-unreachable -S -o - 2>&1 | FileCheck %s --check-prefix=TRAP -// RUN: %clang_cc1 %s -O1 -triple=x86_64-unknown-linux-gnu -S -o - 2>&1 | FileCheck %s --check-prefix=NOTRAP +// RUN: %clang_cc1 %s -O1 -triple=x86_64-unknown-linux-gnu -ftrap-unreachable=all -S -o - 2>&1 | FileCheck %s --check-prefixes=TRAP,COMMON +// RUN: %clang_cc1 %s -O1 -triple=x86_64-unknown-linux-gnu -ftrap-unreachable=except-noreturn -S -o - 2>&1 | FileCheck %s --check-prefixes=NORETURN,COMMON +// RUN: %clang_cc1 %s -O1 -triple=x86_64-unknown-linux-gnu -ftrap-unreachable=none -S -o - 2>&1 | FileCheck %s --check-prefixes=NOTRAP,COMMON -// TRAP: ud2 // NOTRAP-NOT: ud2 [[noreturn]] @@ -9,10 +9,23 @@ void exit(int); #define NULL 0 -static void test(void) { +[[gnu::noinline]] +[[noreturn]] +void a() { +// COMMON-LABEL: a: +// TRAP: ud2 +// NORETURN: ud2 int *ptr = NULL; *ptr = 0; exit(0); } -void foo() { test(); } +[[gnu::noinline]] +[[noreturn]] + void b() { +// COMMON-LABEL: b: +// COMMON: call{{.*}} exit +// TRAP: ud2 +// NORETURN-NOT: ud2 + exit(0); +} diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c index aedbef393abcc..68db27a2f63d2 100644 --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -652,7 +652,11 @@ // CHECK-STRICT-ALIASING-NOT: -relaxed-aliasing // CHECK-NO-STRICT-ALIASING: -relaxed-aliasing -// RUN: %clang -### -ftrap-unreachable %s 2>&1 | FileCheck %s -check-prefix=UNREACHABLE-TRAP -// RUN: %clang -### -fno-trap-unreachable %s 2>&1 | FileCheck %s -check-prefix=NO-UNREACHABLE-TRAP -// UNREACHABLE-TRAP: "-ftrap-unreachable" -// NO-UNREACHABLE-TRAP-NOT: "-ftrap-unreachable" +// RUN: %clang -### -ftrap-unreachable=all %s 2>&1 | FileCheck %s -check-prefix=UNREACHABLE-TRAP-ALL +// RUN: %clang -### -ftrap-unreachable=none %s 2>&1 | FileCheck %s -check-prefix=UNREACHABLE-TRAP-NONE +// RUN: %clang -### -ftrap-unreachable=except-noreturn %s 2>&1 | FileCheck %s -check-prefix=UNREACHABLE-TRAP-EXCEPT-NORETURN +// RUN: %clang -### %s 2>&1 | FileCheck %s -check-prefix=UNREACHABLE-TRAP-EMPTY +// UNREACHABLE-TRAP-ALL: "-ftrap-unreachable=all" +// UNREACHABLE-TRAP-EXCEPT-NORETURN: "-ftrap-unreachable=except-noreturn" +// UNREACHABLE-TRAP-NONE: "-ftrap-unreachable=none" +// UNREACHABLE-TRAP-EMPTY-NOT: -ftrap-unreachable >From 713baac7ae3e99f9ddf4ba69943f50e2b2cfd346 Mon Sep 17 00:00:00 2001 From: Paul Kirth <[email protected]> Date: Mon, 12 Jan 2026 13:08:51 -0800 Subject: [PATCH 3/4] Fix formatting --- clang/include/clang/Options/Options.td | 16 +++++++++------- clang/lib/CodeGen/BackendUtil.cpp | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 10a152f4a3c0b..9e74895879fa6 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4693,13 +4693,15 @@ defm strict_return : BoolFOption<"strict-return", " of a non-void function as unreachable">, PosFlag<SetTrue>>; -def ftrap_unreachable : Joined<["-"], "ftrap-unreachable=">, - Group<f_Group>, Visibility<[ClangOption, CC1Option]>, - HelpText<"Treat unreachable instruction as traps.">, - Values<"all,except-noreturn,none">, - NormalizedValues<["All", "ExceptNoreturn", "None"]>, - NormalizedValuesScope<"CodeGenOptions::TrapUnreachableKind">, - MarshallingInfoEnum<CodeGenOpts<"TrapUnreachable">, "None">; +def ftrap_unreachable + : Joined<["-"], "ftrap-unreachable=">, + Group<f_Group>, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Treat unreachable instruction as traps.">, + Values<"all,except-noreturn,none">, + NormalizedValues<["All", "ExceptNoreturn", "None"]>, + NormalizedValuesScope<"CodeGenOptions::TrapUnreachableKind">, + MarshallingInfoEnum<CodeGenOpts<"TrapUnreachable">, "None">; let Flags = [TargetSpecific] in { defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index cacce9f30b48d..3f5f68e444edd 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -469,7 +469,7 @@ static bool initTargetOptions(const CompilerInstance &CI, Options.VecLib = convertDriverVectorLibraryToVectorLibrary(CodeGenOpts.getVecLib()); - switch(CodeGenOpts.getTrapUnreachable()){ + switch (CodeGenOpts.getTrapUnreachable()) { case clang::CodeGenOptions::TrapUnreachableKind::ExceptNoreturn: Options.NoTrapAfterNoreturn = true; LLVM_FALLTHROUGH; >From b864741c6bae28008bfbfd0516d90572fdb111ad Mon Sep 17 00:00:00 2001 From: Paul Kirth <[email protected]> Date: Tue, 13 Jan 2026 13:17:51 -0800 Subject: [PATCH 4/4] Fix release notes and help text --- clang/docs/ReleaseNotes.rst | 2 ++ clang/include/clang/Options/Options.td | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 85d2e562d1ecd..b092524a66f7d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -90,6 +90,8 @@ Non-comprehensive list of changes in this release New Compiler Flags ------------------ +- New option ``-ftrap-unreachable`` added to enable the existing backend option: TrapUnreachable. + This behavior is off by default (e.g. no change in the compiler's behavior) for now. Deprecated Compiler Flags ------------------------- diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 9e74895879fa6..3cf5fc589979d 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4697,7 +4697,7 @@ def ftrap_unreachable : Joined<["-"], "ftrap-unreachable=">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>, - HelpText<"Treat unreachable instruction as traps.">, + HelpText<"Replace ``llvm.unreachable`` instructions with traps, when it is supported and profitable.">, Values<"all,except-noreturn,none">, NormalizedValues<["All", "ExceptNoreturn", "None"]>, NormalizedValuesScope<"CodeGenOptions::TrapUnreachableKind">, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
