[Lldb-commits] [clang] [lldb] [UBSan][BoundsSafety] Implement support for more expressive "trap reasons" (PR #154618)
Dan Liew via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 27 12:15:55 PDT 2025
https://github.com/delcypher updated https://github.com/llvm/llvm-project/pull/154618
>From 5044fa0326beebd1465b44f3c90c765389e8bdd2 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 19 Aug 2025 15:08:44 -0700
Subject: [PATCH 01/26] [UBSan][BoundsSafety] Implement support for more
expressive "trap reasons"
In 29992cfd628ed5b968ccb73b17ed0521382ba317 (#145967) support was added
for "trap reasons" on traps emitted in UBSan in trapping mode (e.g.
`-fsanitize-trap=undefined`). This improved the debugging experience by
attaching the reason for trapping as a string on the debug info on trap
instructions. Consumers such as LLDB can display this trap reason string
when the trap is reached.
A limitation of that patch is that the trap reason string is hard-coded
for each `SanitizerKind` even though the compiler actually has much more
information about the trap available at compile time that could be shown
to the user.
This patch is an incremental step in fixing that. It consists of two
main steps.
**1. Introduce infrastructure for building trap reason strings **
To make it convenient to construct trap reason strings this patch
re-uses Clang's powerful diagnostic infrastructure to provide a
convenient API for constructing trap reason strings. This is achieved
by:
* Introducing a new `Trap` diagnostic kind to represent trap
diagnostics in TableGen files.
* Adding a new `CodeGen` diagnostic component. While this part probably
isn't technically necessary it seemed like I should follow the
existing convention used by the diagnostic system.
* Adding `DiagnosticCodeGenKinds.td` to describe the different trap
reasons.
* Add the `RuntimeTrapDiagnosticBuilder` class to provide an interface
for constructing trap reason strings and the trap category. Note this
API while similar to `DiagnosticBuilder` has different semantics which
are described in the code comments. In particular the behavior when
the destructor is called is very different.
* Adding `CodeGenModule::RuntimeDiag()` as a convenient constructor for
the `RuntimeTrapDiagnosticBuilder`.
This use of the diagnostic system is a little unusual in that the
emitted trap diagnostics aren't actually consumed by normal diagnostic
consumers (e.g. the console). Instead the `RuntimeTrapDiagnosticBuilder`
is just used to format a string, so in effect the builder is somewhat
analagous to "printf". However, re-using the diagnostics system in this
way brings a several benefits:
* The powerful diagnostic templating languge (e.g. `%select`) can be
used.
* Formatting Clang data types (e.g. `Type`, `Expr`, etc.) just
work out-of-the-box.
* Describing trap reasons in tablegen files opens the door for
translation to different languages in the future.
* The `RuntimeTrapDiagnosticBuilder` API is very similar to
`DiagnosticBuilder` which makes it easy to use by anyone already
familiar with Clang's diagnostic system.
While UBSan is the first consumer of this new infrastructure the intent
is to use this to overhaul how trap reasons are implemented in the
`-fbounds-safety` implementation (currently exists downstream).
**2. Apply the new infrastructure to UBSan checks for arithmetic overflow**
To demonstrate using `RuntimeTrapDiagnosticBuilder` this patch applies
it to UBSan traps for arithmetic overflow. The intention is that we
would iteratively switch to using the `RuntimeTrapDiagnosticBuilder` for
all UBSan traps where it makes sense in future patches.
Previously for code like
```
int test(int a, int b) { return a + b; }
```
The trap reason string looked like
```
Undefined Behavior Sanitizer: Integer addition overflowed
```
now the trap message looks like:
```
Undefined Behavior Sanitizer: signed integer addition overflow in 'a + b'
```
This string is much more specific because
* It explains if signed or unsigned overflow occurred
* It actually shows the expression that overflowed
This seems a lot more helpful.
One possible downside of this approach is it may blow up Debug info size
because now there can be many more distinct trap reason strings. If this
is a concern we may want to add a flag to make it possible to continue
to use the original hard-coded trap messages to avoid increasing the
size of Debug info.
rdar://158612755
---
.../clang/Basic/AllDiagnosticKinds.inc | 1 +
clang/include/clang/Basic/AllDiagnostics.h | 1 +
clang/include/clang/Basic/CMakeLists.txt | 1 +
clang/include/clang/Basic/Diagnostic.h | 67 +++++++++++++++++++
clang/include/clang/Basic/Diagnostic.td | 5 +-
clang/include/clang/Basic/DiagnosticIDs.h | 11 ++-
clang/include/clang/Basic/DiagnosticTrap.h | 15 +++++
.../clang/Basic/DiagnosticTrapKinds.td | 26 +++++++
clang/lib/Basic/Diagnostic.cpp | 31 +++++++++
clang/lib/Basic/DiagnosticIDs.cpp | 4 ++
clang/lib/CodeGen/CGExpr.cpp | 18 +++--
clang/lib/CodeGen/CGExprScalar.cpp | 35 ++++++++--
clang/lib/CodeGen/CodeGenFunction.h | 6 +-
clang/lib/CodeGen/CodeGenModule.h | 7 ++
.../Generic/ubsan-trap-reason-add-overflow.c | 18 ++++-
.../Generic/ubsan-trap-reason-flag.c | 2 +-
.../Generic/ubsan-trap-reason-mul-overflow.c | 17 ++++-
.../Generic/ubsan-trap-reason-sub-overflow.c | 17 ++++-
.../Shell/Recognizer/ubsan_add_overflow.test | 4 +-
19 files changed, 259 insertions(+), 27 deletions(-)
create mode 100644 clang/include/clang/Basic/DiagnosticTrap.h
create mode 100644 clang/include/clang/Basic/DiagnosticTrapKinds.td
diff --git a/clang/include/clang/Basic/AllDiagnosticKinds.inc b/clang/include/clang/Basic/AllDiagnosticKinds.inc
index a946b4a640ac6..2d08bb0525970 100644
--- a/clang/include/clang/Basic/AllDiagnosticKinds.inc
+++ b/clang/include/clang/Basic/AllDiagnosticKinds.inc
@@ -30,4 +30,5 @@
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
#include "clang/Basic/DiagnosticRefactoringKinds.inc"
#include "clang/Basic/DiagnosticInstallAPIKinds.inc"
+#include "clang/Basic/DiagnosticTrapKinds.inc"
// clang-format on
diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h
index 3b782732c1507..78e5428ddbfff 100644
--- a/clang/include/clang/Basic/AllDiagnostics.h
+++ b/clang/include/clang/Basic/AllDiagnostics.h
@@ -26,6 +26,7 @@
#include "clang/Basic/DiagnosticRefactoring.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/DiagnosticSerialization.h"
+#include "clang/Basic/DiagnosticTrap.h"
namespace clang {
template <size_t SizeOfStr, typename FieldType> class StringSizerHelper {
diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt
index 0cf661a57dfa8..81736006a21a0 100644
--- a/clang/include/clang/Basic/CMakeLists.txt
+++ b/clang/include/clang/Basic/CMakeLists.txt
@@ -33,6 +33,7 @@ clang_diag_gen(Parse)
clang_diag_gen(Refactoring)
clang_diag_gen(Sema)
clang_diag_gen(Serialization)
+clang_diag_gen(Trap)
clang_tablegen(DiagnosticGroups.inc -gen-clang-diag-groups
SOURCE Diagnostic.td
TARGET ClangDiagnosticGroups)
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index cee5bed665d0a..92e8e9101e9db 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -1233,6 +1233,7 @@ class DiagnosticBuilder : public StreamingDiagnostic {
friend class DiagnosticsEngine;
friend class PartialDiagnostic;
friend class Diagnostic;
+ friend class RuntimeTrapDiagnosticBuilder;
mutable DiagnosticsEngine *DiagObj = nullptr;
@@ -1534,6 +1535,72 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
return Report(SourceLocation(), DiagID);
}
+//===----------------------------------------------------------------------===//
+// RuntimeTrapDiagnosticBuilder
+//===----------------------------------------------------------------------===//
+/// Class to make it convenient to construct "trap reasons" to attach to trap
+/// instructions.
+///
+/// Although this class inherits from `DiagnosticBuilder` it has very different
+/// semantics.
+///
+/// * This class should only be used with trap diagnostics (declared in
+/// `DiagnosticTrapKinds.td`).
+/// * The `RuntimeTrapDiagnosticBuilder` does not emit diagnostics to the normal
+/// diagnostics consumers on destruction like normal Diagnostic builders.
+/// Instead it does nothing on destruction.
+/// * Users of this class that want to retrieve the "trap reason" should call
+/// call the `getMessage()` and `getCategory()` and use those results before
+/// the builder is destroyed.
+/// * Unlike the `DiagnosticBuilder` the `RuntimeDiagnosticBuilder` should never
+/// be created as a temporary (i.e. rvalue) and instead should be stored. This
+/// is because the class is only useful if `getMessage()` and `getCategory()`
+/// can be called.
+///
+/// Given that this class inherits from `DiagnosticBuilder` it inherits all of
+/// its abilities to format diagnostic messages and consume various types in
+/// class (e.g. Type, Exprs, etc.). This makes it particularly suited to
+/// printing types and expressions from the AST while codegen-ing runtime
+/// checks.
+///
+///
+/// Example use via the `CodeGenModule::RuntimeDiag` helper.
+///
+/// \code
+/// {
+/// auto* RTDB = CGM.RuntimeDiag(diag::trap_diagnostic);
+/// *RTDB << 0 << SomeExpr << SomeType;
+/// consume(RTDB->getCategory(), RTDB->getMessage());
+/// }
+/// \endcode
+///
+///
+class RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
+public:
+ RuntimeTrapDiagnosticBuilder(DiagnosticsEngine *DiagObj, unsigned DiagID);
+ ~RuntimeTrapDiagnosticBuilder();
+
+ // Prevent accidentally copying or assigning
+ RuntimeTrapDiagnosticBuilder &
+ operator=(const RuntimeTrapDiagnosticBuilder &) = delete;
+ RuntimeTrapDiagnosticBuilder &
+ operator=(const RuntimeTrapDiagnosticBuilder &&) = delete;
+ RuntimeTrapDiagnosticBuilder(const RuntimeTrapDiagnosticBuilder &) = delete;
+ RuntimeTrapDiagnosticBuilder(const RuntimeTrapDiagnosticBuilder &&) = delete;
+
+ /// \return Format the trap message and return it. Note the lifetime of
+ /// the underlying storage pointed to by the returned StringRef is the same
+ /// as the lifetime of this class. This means it is likely unsafe to store
+ /// the returned StringRef.
+ StringRef getMessage();
+ /// \return Return the trap category. These are the `CategoryName` property
+ /// of `trap` diagnostics declared in `DiagnosticTrapKinds.td`.
+ StringRef getCategory();
+
+private:
+ llvm::SmallVector<char, 64> MessageStorage;
+};
+
//===----------------------------------------------------------------------===//
// Diagnostic
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td
index 65b19f3feea4f..d190c70a401e3 100644
--- a/clang/include/clang/Basic/Diagnostic.td
+++ b/clang/include/clang/Basic/Diagnostic.td
@@ -30,6 +30,7 @@ def CLASS_REMARK : DiagClass;
def CLASS_WARNING : DiagClass;
def CLASS_EXTENSION : DiagClass;
def CLASS_ERROR : DiagClass;
+def CLASS_TRAP : DiagClass;
// Responses to a diagnostic in a SFINAE context.
class SFINAEResponse;
@@ -144,7 +145,8 @@ class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>;
class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>;
// Notes can provide supplementary information on errors, warnings, and remarks.
class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>;
-
+// Traps messages attached to traps in debug info
+class Trap<string str> : Diagnostic<str, CLASS_TRAP, SEV_Fatal/*ignored*/>;
class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; }
class DefaultWarn { Severity DefaultSeverity = SEV_Warning; }
@@ -235,3 +237,4 @@ include "DiagnosticParseKinds.td"
include "DiagnosticRefactoringKinds.td"
include "DiagnosticSemaKinds.td"
include "DiagnosticSerializationKinds.td"
+include "DiagnosticTrapKinds.td"
diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h
index 17fecd346f03e..a8c00fff0d9c5 100644
--- a/clang/include/clang/Basic/DiagnosticIDs.h
+++ b/clang/include/clang/Basic/DiagnosticIDs.h
@@ -47,6 +47,7 @@ enum {
DIAG_SIZE_ANALYSIS = 100,
DIAG_SIZE_REFACTORING = 1000,
DIAG_SIZE_INSTALLAPI = 100,
+ DIAG_SIZE_TRAP = 100,
};
// Start position for diagnostics.
// clang-format off
@@ -64,7 +65,8 @@ enum {
DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast<int>(DIAG_SIZE_SEMA),
DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast<int>(DIAG_SIZE_ANALYSIS),
DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast<int>(DIAG_SIZE_REFACTORING),
- DIAG_UPPER_LIMIT = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_SIZE_INSTALLAPI)
+ DIAG_START_TRAP = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_SIZE_INSTALLAPI),
+ DIAG_UPPER_LIMIT = DIAG_START_TRAP + static_cast<int>(DIAG_SIZE_TRAP)
};
// clang-format on
@@ -189,7 +191,8 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
CLASS_REMARK = 0x02,
CLASS_WARNING = 0x03,
CLASS_EXTENSION = 0x04,
- CLASS_ERROR = 0x05
+ CLASS_ERROR = 0x05,
+ CLASS_TRAP = 0x06
};
static bool IsCustomDiag(diag::kind Diag) {
@@ -363,6 +366,10 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
///
bool isExtensionDiag(unsigned DiagID, bool &EnabledByDefault) const;
+ bool isTrapDiag(unsigned DiagID) const {
+ return getDiagClass(DiagID) == CLASS_TRAP;
+ }
+
/// Given a group ID, returns the flag that toggles the group.
/// For example, for Group::DeprecatedDeclarations, returns
/// "deprecated-declarations".
diff --git a/clang/include/clang/Basic/DiagnosticTrap.h b/clang/include/clang/Basic/DiagnosticTrap.h
new file mode 100644
index 0000000000000..7c6a5dc5d40d7
--- /dev/null
+++ b/clang/include/clang/Basic/DiagnosticTrap.h
@@ -0,0 +1,15 @@
+//===--- DiagnosticTrap.h - Diagnostics for trap instructions ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_TRAP_H
+#define LLVM_CLANG_BASIC_TRAP_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticTrapInterface.inc"
+
+#endif
diff --git a/clang/include/clang/Basic/DiagnosticTrapKinds.td b/clang/include/clang/Basic/DiagnosticTrapKinds.td
new file mode 100644
index 0000000000000..ce5b16747071e
--- /dev/null
+++ b/clang/include/clang/Basic/DiagnosticTrapKinds.td
@@ -0,0 +1,26 @@
+//==--- DiagnosticTrapKinds.td - CodeGen Diagnostics -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Trap Diagnostics
+//
+// These are diagnostics that are emitted into Debug Info, rather than to the
+// traditional consumers like the terminal. Their primary purpose is to make
+// debugging traps (e.g. `-fsanitize-trap=undefined`) easier by attaching
+// a trap category and reason to the trap instruction that tools like a debugger
+// can show.
+//===----------------------------------------------------------------------===//
+let Component = "Trap" in {
+let CategoryName = "Undefined Behavior Sanitizer" in {
+
+def trap_ubsan_arith_overflow : Trap<
+ "%select{unsigned|signed}0 integer "
+ "%select{addition|subtraction|multiplication}1 overflow in %2">;
+
+}
+}
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index e33e843db6a44..75729d5c8fd9e 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -664,6 +664,8 @@ void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
void DiagnosticsEngine::Report(Level DiagLevel, const Diagnostic &Info) {
assert(DiagLevel != Ignored && "Cannot emit ignored diagnostics!");
+ assert(!getDiagnosticIDs()->isTrapDiag(Info.getID()) &&
+ "Trap diagnostics should not be consumed by the DiagnosticsEngine");
Client->HandleDiagnostic(DiagLevel, Info);
if (Client->IncludeInDiagnosticCounts()) {
if (DiagLevel == Warning)
@@ -793,6 +795,35 @@ DiagnosticBuilder::DiagnosticBuilder(const DiagnosticBuilder &D)
D.Clear();
}
+RuntimeTrapDiagnosticBuilder::RuntimeTrapDiagnosticBuilder(
+ DiagnosticsEngine *DiagObj, unsigned DiagID)
+ : DiagnosticBuilder(DiagObj, SourceLocation(), DiagID) {
+ assert(DiagObj->getDiagnosticIDs()->isTrapDiag(DiagID));
+}
+
+RuntimeTrapDiagnosticBuilder::~RuntimeTrapDiagnosticBuilder() {
+ // Make sure that when `DiagnosticBuilder::~DiagnosticBuilder()`
+ // calls `Emit()` that it does nothing.
+ Clear();
+}
+
+StringRef RuntimeTrapDiagnosticBuilder::getMessage() {
+ if (MessageStorage.size() == 0) {
+ // Render the Diagnostic
+ Diagnostic Info(DiagObj, *this);
+ Info.FormatDiagnostic(MessageStorage);
+ }
+ return StringRef(MessageStorage.data(), MessageStorage.size());
+}
+
+StringRef RuntimeTrapDiagnosticBuilder::getCategory() {
+ auto CategoryID =
+ DiagObj->getDiagnosticIDs()->getCategoryNumberForDiag(DiagID);
+ if (CategoryID == 0)
+ return "";
+ return DiagObj->getDiagnosticIDs()->getCategoryNameFromID(CategoryID);
+}
+
Diagnostic::Diagnostic(const DiagnosticsEngine *DO,
const DiagnosticBuilder &DiagBuilder)
: DiagObj(DO), DiagLoc(DiagBuilder.DiagLoc), DiagID(DiagBuilder.DiagID),
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index 73f24a82d4c75..a1d9d0f34d20d 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -69,6 +69,7 @@ enum DiagnosticClass {
CLASS_WARNING = DiagnosticIDs::CLASS_WARNING,
CLASS_EXTENSION = DiagnosticIDs::CLASS_EXTENSION,
CLASS_ERROR = DiagnosticIDs::CLASS_ERROR,
+ CLASS_TRAP = DiagnosticIDs::CLASS_TRAP,
};
struct StaticDiagInfoRec {
@@ -139,6 +140,7 @@ VALIDATE_DIAG_SIZE(SEMA)
VALIDATE_DIAG_SIZE(ANALYSIS)
VALIDATE_DIAG_SIZE(REFACTORING)
VALIDATE_DIAG_SIZE(INSTALLAPI)
+VALIDATE_DIAG_SIZE(TRAP)
#undef VALIDATE_DIAG_SIZE
#undef STRINGIFY_NAME
@@ -171,6 +173,7 @@ const StaticDiagInfoRec StaticDiagInfo[] = {
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
#include "clang/Basic/DiagnosticRefactoringKinds.inc"
#include "clang/Basic/DiagnosticInstallAPIKinds.inc"
+#include "clang/Basic/DiagnosticTrapKinds.inc"
// clang-format on
#undef DIAG
};
@@ -214,6 +217,7 @@ CATEGORY(SEMA, CROSSTU)
CATEGORY(ANALYSIS, SEMA)
CATEGORY(REFACTORING, ANALYSIS)
CATEGORY(INSTALLAPI, REFACTORING)
+CATEGORY(TRAP, INSTALLAPI)
#undef CATEGORY
// Avoid out of bounds reads.
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index eeaf68dfd0521..272a2290bf315 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -33,6 +33,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/STLExtras.h"
@@ -3782,7 +3783,8 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
void CodeGenFunction::EmitCheck(
ArrayRef<std::pair<llvm::Value *, SanitizerKind::SanitizerOrdinal>> Checked,
SanitizerHandler CheckHandler, ArrayRef<llvm::Constant *> StaticArgs,
- ArrayRef<llvm::Value *> DynamicArgs) {
+ ArrayRef<llvm::Value *> DynamicArgs,
+ std::unique_ptr<RuntimeTrapDiagnosticBuilder> RTDB) {
assert(IsSanitizerScope);
assert(Checked.size() > 0);
assert(CheckHandler >= 0 &&
@@ -3821,7 +3823,9 @@ void CodeGenFunction::EmitCheck(
}
if (TrapCond)
- EmitTrapCheck(TrapCond, CheckHandler, NoMerge);
+ EmitTrapCheck(TrapCond, CheckHandler, NoMerge,
+ RTDB ? RTDB->getMessage() : "",
+ RTDB ? RTDB->getCategory() : "");
if (!FatalCond && !RecoverableCond)
return;
@@ -4133,7 +4137,8 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
SanitizerHandler CheckHandlerID,
- bool NoMerge) {
+ bool NoMerge, StringRef Message,
+ StringRef Category) {
llvm::BasicBlock *Cont = createBasicBlock("cont");
// If we're optimizing, collapse all calls to trap down to just one per
@@ -4144,12 +4149,15 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];
llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
- llvm::StringRef TrapMessage = GetUBSanTrapForHandler(CheckHandlerID);
+ llvm::StringRef TrapMessage =
+ Message.size() > 0 ? Message : GetUBSanTrapForHandler(CheckHandlerID);
+ llvm::StringRef TrapCategory =
+ Category.size() > 0 ? Category : "Undefined Behavior Sanitizer";
if (getDebugInfo() && !TrapMessage.empty() &&
CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) {
TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
- TrapLocation, "Undefined Behavior Sanitizer", TrapMessage);
+ TrapLocation, TrapCategory, TrapMessage);
}
NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel ||
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 2338cbd74f002..a1743bcbcc34e 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1813,6 +1813,7 @@ void ScalarExprEmitter::EmitBinOpCheck(
SanitizerHandler Check;
SmallVector<llvm::Constant *, 4> StaticData;
SmallVector<llvm::Value *, 2> DynamicData;
+ std::unique_ptr<RuntimeTrapDiagnosticBuilder> RTDB = nullptr;
BinaryOperatorKind Opcode = Info.Opcode;
if (BinaryOperator::isCompoundAssignmentOp(Opcode))
@@ -1839,19 +1840,43 @@ void ScalarExprEmitter::EmitBinOpCheck(
StaticData.push_back(CGF.EmitCheckTypeDescriptor(Info.Ty));
} else {
// Arithmetic overflow (+, -, *).
+ unsigned ArithOverflowKind = 0;
switch (Opcode) {
- case BO_Add: Check = SanitizerHandler::AddOverflow; break;
- case BO_Sub: Check = SanitizerHandler::SubOverflow; break;
- case BO_Mul: Check = SanitizerHandler::MulOverflow; break;
- default: llvm_unreachable("unexpected opcode for bin op check");
+ case BO_Add: {
+ ArithOverflowKind = 0;
+ Check = SanitizerHandler::AddOverflow;
+ break;
+ }
+ case BO_Sub: {
+ Check = SanitizerHandler::SubOverflow;
+ ArithOverflowKind = 1;
+ break;
+ }
+ case BO_Mul: {
+ Check = SanitizerHandler::MulOverflow;
+ ArithOverflowKind = 2;
+ break;
+ }
+ default:
+ llvm_unreachable("unexpected opcode for bin op check");
}
StaticData.push_back(CGF.EmitCheckTypeDescriptor(Info.Ty));
+ if (CGF.CGM.getCodeGenOpts().SanitizeTrap.has(
+ SanitizerKind::UnsignedIntegerOverflow) ||
+ CGF.CGM.getCodeGenOpts().SanitizeTrap.has(
+ SanitizerKind::SignedIntegerOverflow)) {
+ // Only pay the cost for constructing the trap diagnostic if they are
+ // going to be used.
+ RTDB = CGF.CGM.RuntimeDiag(diag::trap_ubsan_arith_overflow);
+ *RTDB << Info.Ty->isSignedIntegerOrEnumerationType()
+ << ArithOverflowKind << Info.E;
+ }
}
DynamicData.push_back(Info.LHS);
DynamicData.push_back(Info.RHS);
}
- CGF.EmitCheck(Checks, Check, StaticData, DynamicData);
+ CGF.EmitCheck(Checks, Check, StaticData, DynamicData, std::move(RTDB));
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index a562a6a1ea6e1..340dbcf9e2212 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5278,7 +5278,8 @@ class CodeGenFunction : public CodeGenTypeCache {
EmitCheck(ArrayRef<std::pair<llvm::Value *, SanitizerKind::SanitizerOrdinal>>
Checked,
SanitizerHandler Check, ArrayRef<llvm::Constant *> StaticArgs,
- ArrayRef<llvm::Value *> DynamicArgs);
+ ArrayRef<llvm::Value *> DynamicArgs,
+ std::unique_ptr<RuntimeTrapDiagnosticBuilder> RTDB = nullptr);
/// Emit a slow path cross-DSO CFI check which calls __cfi_slowpath
/// if Cond if false.
@@ -5294,7 +5295,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Create a basic block that will call the trap intrinsic, and emit a
/// conditional branch to it, for the -ftrapv checks.
void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID,
- bool NoMerge = false);
+ bool NoMerge = false, StringRef Message = "",
+ StringRef Category = "");
/// Emit a call to trap or debugtrap and attach function attribute
/// "trap-func-name" if specified.
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 705d9a3cb9de3..535dc968f648f 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -23,6 +23,7 @@
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"
#include "clang/Basic/ABI.h"
+#include "clang/Basic/DiagnosticTrap.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/NoSanitizeList.h"
#include "clang/Basic/ProfileList.h"
@@ -1824,6 +1825,12 @@ class CodeGenModule : public CodeGenTypeCache {
return PAlign;
}
+ /// Helper function to construct a RuntimeTrapDiagnosticBuilder
+ [[nodiscard]] std::unique_ptr<RuntimeTrapDiagnosticBuilder>
+ RuntimeDiag(unsigned DiagID) {
+ return std::make_unique<RuntimeTrapDiagnosticBuilder>(&getDiags(), DiagID);
+ }
+
private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
index 225778d68833d..aefb92819d215 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
@@ -1,9 +1,21 @@
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
-// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -emit-llvm %s -o - | FileCheck %s
-int add_overflow(int a, int b) { return a + b; }
+int sadd_overflow(int a, int b) { return a + b; }
+
+unsigned add_overflow(unsigned c, unsigned d) { return c + d; }
+
+// CHECK-LABEL: @sadd_overflow
+// CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[SLOC:![0-9]+]]
// CHECK-LABEL: @add_overflow
// CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
+
+
+// CHECK: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
+// CHECK: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'"
+
// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed"
+// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer addition overflow in 'c + d'"
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c
index 5cc16d154bf68..f0e707e5804be 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c
@@ -15,7 +15,7 @@ int add_overflow(int a, int b) { return a + b; }
// ANNOTATE-LABEL: @add_overflow
// ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
// ANNOTATE: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// ANNOTATE: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed"
+// ANNOTATE: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'"
// NO-ANNOTATE-LABEL: @add_overflow
// NO-ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
index cf9a0b4e7439c..b1a79b8a6cb24 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
@@ -1,9 +1,20 @@
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
-// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -emit-llvm %s -o - | FileCheck %s
-int mul_overflow(int a, int b) { return a * b; }
+int smul_overflow(int a, int b) { return a * b; }
+
+unsigned mul_overflow(unsigned c, unsigned d) { return c * d; }
+
+// CHECK-LABEL: @smul_overflow
+// CHECK: call void @llvm.ubsantrap(i8 12) {{.*}}!dbg [[SLOC:![0-9]+]]
// CHECK-LABEL: @mul_overflow
// CHECK: call void @llvm.ubsantrap(i8 12) {{.*}}!dbg [[LOC:![0-9]+]]
+
+// CHECK: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
+// CHECK: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer multiplication overflow in 'a * b'"
+
// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer multiplication overflowed"
+// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer multiplication overflow in 'c * d'"
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
index 62aa7fc953dad..f38a5b47e6a00 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
@@ -1,9 +1,20 @@
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
-// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -emit-llvm %s -o - | FileCheck %s
-int sub_overflow(int a, int b) { return a - b; }
+int ssub_overflow(int a, int b) { return a - b; }
+
+unsigned sub_overflow(unsigned c, unsigned d) { return c - d; }
+
+// CHECK-LABEL: @ssub_overflow
+// CHECK: call void @llvm.ubsantrap(i8 21) {{.*}}!dbg [[SLOC:![0-9]+]]
// CHECK-LABEL: @sub_overflow
// CHECK: call void @llvm.ubsantrap(i8 21) {{.*}}!dbg [[LOC:![0-9]+]]
+
+// CHECK: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
+// CHECK: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer subtraction overflow in 'a - b'"
+
// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer subtraction overflowed"
+// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer subtraction overflow in 'c - d'"
diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test
index a5e95cf5a898f..872b5a7a4d585 100644
--- a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test
+++ b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test
@@ -6,11 +6,11 @@
# RUN: %lldb -b -s %s %t.out | FileCheck %s
run
-# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: Integer addition overflowed
+# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: signed integer addition overflow in '2147483647 + 1'
# CHECK-NEXT: frame #1: {{.*}}`main at ubsan_add_overflow.c
bt
-# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed{{.*}}
+# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in '2147483647 + 1'{{.*}}
# CHECK: frame #1: {{.*}}`main at ubsan_add_overflow.c
frame info
>From edafb6c43c32eed67255df828e6ed05ad955eebc Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Thu, 21 Aug 2025 15:49:24 -0700
Subject: [PATCH 02/26] Add `[[nodiscard]]` to `RuntimeTrapDiagnosticBuilder`
---
clang/include/clang/Basic/Diagnostic.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index 92e8e9101e9db..a8bfe0a7f0527 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -1575,7 +1575,7 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
/// \endcode
///
///
-class RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
+class [[nodiscard]] RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
public:
RuntimeTrapDiagnosticBuilder(DiagnosticsEngine *DiagObj, unsigned DiagID);
~RuntimeTrapDiagnosticBuilder();
>From 9c7cf18d2356c8eccb1eab1615eb70d3ca0cfd03 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Thu, 21 Aug 2025 16:07:02 -0700
Subject: [PATCH 03/26] Remove unnecessary incldue
---
clang/lib/CodeGen/CGExpr.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 272a2290bf315..d817504ac6ed8 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -33,7 +33,6 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CodeGenOptions.h"
-#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/STLExtras.h"
>From 9fcb7a7668e450c409c46db1f075db285cd21926 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Thu, 21 Aug 2025 16:11:03 -0700
Subject: [PATCH 04/26] Use modern header block for `DiagnosticTrap.h` and add
a little bit of documentation.
---
clang/include/clang/Basic/DiagnosticTrap.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticTrap.h b/clang/include/clang/Basic/DiagnosticTrap.h
index 7c6a5dc5d40d7..716be525e0527 100644
--- a/clang/include/clang/Basic/DiagnosticTrap.h
+++ b/clang/include/clang/Basic/DiagnosticTrap.h
@@ -1,10 +1,16 @@
-//===--- DiagnosticTrap.h - Diagnostics for trap instructions ---*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the necessary includes for using
+/// `RuntimeTrapDiagnosticBuilder`.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_TRAP_H
#define LLVM_CLANG_BASIC_TRAP_H
>From e2449b7555f2e0ca61fa7f3bb8c904c1ea56a4c0 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Thu, 21 Aug 2025 16:16:35 -0700
Subject: [PATCH 05/26] Fix some formatting
---
clang/include/clang/Basic/DiagnosticIDs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h
index a8c00fff0d9c5..06446cf580389 100644
--- a/clang/include/clang/Basic/DiagnosticIDs.h
+++ b/clang/include/clang/Basic/DiagnosticIDs.h
@@ -47,7 +47,7 @@ enum {
DIAG_SIZE_ANALYSIS = 100,
DIAG_SIZE_REFACTORING = 1000,
DIAG_SIZE_INSTALLAPI = 100,
- DIAG_SIZE_TRAP = 100,
+ DIAG_SIZE_TRAP = 100,
};
// Start position for diagnostics.
// clang-format off
>From 3aed65856c93c4af9f21398806e7eaeb7b512e04 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Thu, 21 Aug 2025 16:36:02 -0700
Subject: [PATCH 06/26] Use %enum_select
---
clang/include/clang/Basic/DiagnosticTrapKinds.td | 6 +++++-
clang/lib/CodeGen/CGExprScalar.cpp | 8 ++++----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticTrapKinds.td b/clang/include/clang/Basic/DiagnosticTrapKinds.td
index ce5b16747071e..edf44e19465e7 100644
--- a/clang/include/clang/Basic/DiagnosticTrapKinds.td
+++ b/clang/include/clang/Basic/DiagnosticTrapKinds.td
@@ -20,7 +20,11 @@ let CategoryName = "Undefined Behavior Sanitizer" in {
def trap_ubsan_arith_overflow : Trap<
"%select{unsigned|signed}0 integer "
- "%select{addition|subtraction|multiplication}1 overflow in %2">;
+ "%enum_select<UBSanArithKind>{"
+ "%Add{addition}|"
+ "%Sub{subtraction}|"
+ "%Mul{multiplication}"
+ "}1 overflow in %2">;
}
}
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index a1743bcbcc34e..324eaf0fc65cb 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1840,21 +1840,21 @@ void ScalarExprEmitter::EmitBinOpCheck(
StaticData.push_back(CGF.EmitCheckTypeDescriptor(Info.Ty));
} else {
// Arithmetic overflow (+, -, *).
- unsigned ArithOverflowKind = 0;
+ int ArithOverflowKind = 0;
switch (Opcode) {
case BO_Add: {
- ArithOverflowKind = 0;
+ ArithOverflowKind = diag::UBSanArithKind::Add;
Check = SanitizerHandler::AddOverflow;
break;
}
case BO_Sub: {
Check = SanitizerHandler::SubOverflow;
- ArithOverflowKind = 1;
+ ArithOverflowKind = diag::UBSanArithKind::Sub;
break;
}
case BO_Mul: {
Check = SanitizerHandler::MulOverflow;
- ArithOverflowKind = 2;
+ ArithOverflowKind = diag::UBSanArithKind::Mul;
break;
}
default:
>From eff527d3a315a9c5153dadd35ac8518d3460b4e8 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Thu, 21 Aug 2025 16:54:02 -0700
Subject: [PATCH 07/26] Use llvm::SmallString instead of llvm::SmallVector
---
clang/include/clang/Basic/Diagnostic.h | 3 ++-
clang/lib/Basic/Diagnostic.cpp | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index a8bfe0a7f0527..aaa7bb99492e2 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -24,6 +24,7 @@
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
@@ -1598,7 +1599,7 @@ class [[nodiscard]] RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
StringRef getCategory();
private:
- llvm::SmallVector<char, 64> MessageStorage;
+ llvm::SmallString<64> MessageStorage;
};
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index 75729d5c8fd9e..d8a410303d94a 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -813,7 +813,7 @@ StringRef RuntimeTrapDiagnosticBuilder::getMessage() {
Diagnostic Info(DiagObj, *this);
Info.FormatDiagnostic(MessageStorage);
}
- return StringRef(MessageStorage.data(), MessageStorage.size());
+ return MessageStorage;
}
StringRef RuntimeTrapDiagnosticBuilder::getCategory() {
>From 7e0cde94a8b403f691e9c164a3231d63a9d191fa Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Fri, 22 Aug 2025 14:20:50 -0700
Subject: [PATCH 08/26] Move `RuntimeTrapDiagnosticBuilder` to a safer API
where the storage and transport of the "Trap Reason" is moved out of
`RuntimeTrapDiagnosticBuilder` into a new helper class called `TrapReason`.
---
clang/include/clang/Basic/Diagnostic.h | 81 ++++++++++++++++++--------
clang/lib/Basic/Diagnostic.cpp | 19 +++---
clang/lib/CodeGen/CGExpr.cpp | 14 ++---
clang/lib/CodeGen/CGExprScalar.cpp | 9 +--
clang/lib/CodeGen/CodeGenFunction.h | 5 +-
clang/lib/CodeGen/CodeGenModule.h | 5 +-
6 files changed, 80 insertions(+), 53 deletions(-)
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index aaa7bb99492e2..3cb6b01ee8226 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -23,8 +23,8 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
-#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
@@ -1537,26 +1537,58 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
}
//===----------------------------------------------------------------------===//
-// RuntimeTrapDiagnosticBuilder
+// RuntimeTrapDiagnosticBuilder and helper classes
//===----------------------------------------------------------------------===//
-/// Class to make it convenient to construct "trap reasons" to attach to trap
-/// instructions.
+
+/// Helper class for \class RuntimeTrapDiagnosticBuilder. This class stores the
+/// "trap reason" built by \class RuntimeTrapDiagnosticBuilder. This consists of
+/// a trap message and trap category.
///
-/// Although this class inherits from `DiagnosticBuilder` it has very different
-/// semantics.
+/// It is intended that this object be allocated on the stack.
+class TrapReason {
+public:
+ TrapReason() {}
+ /// \return The trap message. Note the lifetime of the underlying storage for
+ /// the returned StringRef lives in this class which means the returned
+ /// StringRef should not be used after this class is destroyed.
+ StringRef getMessage() const { return Message; }
+
+ /// \return the trap category (e.g. "Undefined Behavior Sanitizer")
+ StringRef getCategory() const { return Category; }
+
+ bool isEmpty() const { return Message.size() == 0 && Category.size() == 0; }
+
+ /// \return a pointer to this object if it contains a non-empty trap reason,
+ /// otherwise return `nullptr`. This is a convenient helper for passing
+ /// TrapReason objects to function calls that have a `TrapReason*` parameter.
+ TrapReason *getPtr() {
+ if (isEmpty())
+ return nullptr;
+ return this;
+ }
+
+private:
+ llvm::SmallString<64> Message;
+ // The Category doesn't need its own storage because the StringRef points
+ // to a global constant string.
+ StringRef Category;
+
+ // Only this class can set the private fields.
+ friend class RuntimeTrapDiagnosticBuilder;
+};
+
+/// Class to make it convenient to initialize TrapReason objects which can be
+/// used to attach the "trap reason" to trap instructions.
+///
+/// Although this class inherits from \class DiagnosticBuilder it has slightly
+/// different semantics.
///
/// * This class should only be used with trap diagnostics (declared in
/// `DiagnosticTrapKinds.td`).
/// * The `RuntimeTrapDiagnosticBuilder` does not emit diagnostics to the normal
/// diagnostics consumers on destruction like normal Diagnostic builders.
-/// Instead it does nothing on destruction.
-/// * Users of this class that want to retrieve the "trap reason" should call
-/// call the `getMessage()` and `getCategory()` and use those results before
-/// the builder is destroyed.
-/// * Unlike the `DiagnosticBuilder` the `RuntimeDiagnosticBuilder` should never
-/// be created as a temporary (i.e. rvalue) and instead should be stored. This
-/// is because the class is only useful if `getMessage()` and `getCategory()`
-/// can be called.
+/// Instead on destruction it assigns to the TrapReason object passed in to
+/// the constructor.
///
/// Given that this class inherits from `DiagnosticBuilder` it inherits all of
/// its abilities to format diagnostic messages and consume various types in
@@ -1569,16 +1601,17 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
///
/// \code
/// {
-/// auto* RTDB = CGM.RuntimeDiag(diag::trap_diagnostic);
-/// *RTDB << 0 << SomeExpr << SomeType;
-/// consume(RTDB->getCategory(), RTDB->getMessage());
+/// TrapReason TR;
+/// CGM.RuntimeDiag(diag::trap_diagnostic, TR) << 0 << SomeExpr << SomeType;
+/// consume(TR.getPtr());
/// }
/// \endcode
///
///
-class [[nodiscard]] RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
+class RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
public:
- RuntimeTrapDiagnosticBuilder(DiagnosticsEngine *DiagObj, unsigned DiagID);
+ RuntimeTrapDiagnosticBuilder(DiagnosticsEngine *DiagObj, unsigned DiagID,
+ TrapReason &TR);
~RuntimeTrapDiagnosticBuilder();
// Prevent accidentally copying or assigning
@@ -1589,17 +1622,15 @@ class [[nodiscard]] RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
RuntimeTrapDiagnosticBuilder(const RuntimeTrapDiagnosticBuilder &) = delete;
RuntimeTrapDiagnosticBuilder(const RuntimeTrapDiagnosticBuilder &&) = delete;
- /// \return Format the trap message and return it. Note the lifetime of
- /// the underlying storage pointed to by the returned StringRef is the same
- /// as the lifetime of this class. This means it is likely unsafe to store
- /// the returned StringRef.
- StringRef getMessage();
+ /// \return Format the trap message into `Storage`.
+ void getMessage(SmallVectorImpl<char> &Storage);
+
/// \return Return the trap category. These are the `CategoryName` property
/// of `trap` diagnostics declared in `DiagnosticTrapKinds.td`.
StringRef getCategory();
private:
- llvm::SmallString<64> MessageStorage;
+ TrapReason &TR;
};
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index d8a410303d94a..d1249ac75b1ae 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -796,24 +796,25 @@ DiagnosticBuilder::DiagnosticBuilder(const DiagnosticBuilder &D)
}
RuntimeTrapDiagnosticBuilder::RuntimeTrapDiagnosticBuilder(
- DiagnosticsEngine *DiagObj, unsigned DiagID)
- : DiagnosticBuilder(DiagObj, SourceLocation(), DiagID) {
+ DiagnosticsEngine *DiagObj, unsigned DiagID, TrapReason &TR)
+ : DiagnosticBuilder(DiagObj, SourceLocation(), DiagID), TR(TR) {
assert(DiagObj->getDiagnosticIDs()->isTrapDiag(DiagID));
}
RuntimeTrapDiagnosticBuilder::~RuntimeTrapDiagnosticBuilder() {
+ // Store the trap message and category into the TrapReason object.
+ getMessage(TR.Message);
+ TR.Category = getCategory();
+
// Make sure that when `DiagnosticBuilder::~DiagnosticBuilder()`
// calls `Emit()` that it does nothing.
Clear();
}
-StringRef RuntimeTrapDiagnosticBuilder::getMessage() {
- if (MessageStorage.size() == 0) {
- // Render the Diagnostic
- Diagnostic Info(DiagObj, *this);
- Info.FormatDiagnostic(MessageStorage);
- }
- return MessageStorage;
+void RuntimeTrapDiagnosticBuilder::getMessage(SmallVectorImpl<char> &Storage) {
+ // Render the Diagnostic
+ Diagnostic Info(DiagObj, *this);
+ Info.FormatDiagnostic(Storage);
}
StringRef RuntimeTrapDiagnosticBuilder::getCategory() {
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index d817504ac6ed8..23ee285e4d242 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3782,8 +3782,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
void CodeGenFunction::EmitCheck(
ArrayRef<std::pair<llvm::Value *, SanitizerKind::SanitizerOrdinal>> Checked,
SanitizerHandler CheckHandler, ArrayRef<llvm::Constant *> StaticArgs,
- ArrayRef<llvm::Value *> DynamicArgs,
- std::unique_ptr<RuntimeTrapDiagnosticBuilder> RTDB) {
+ ArrayRef<llvm::Value *> DynamicArgs, const TrapReason *TR) {
assert(IsSanitizerScope);
assert(Checked.size() > 0);
assert(CheckHandler >= 0 &&
@@ -3822,9 +3821,7 @@ void CodeGenFunction::EmitCheck(
}
if (TrapCond)
- EmitTrapCheck(TrapCond, CheckHandler, NoMerge,
- RTDB ? RTDB->getMessage() : "",
- RTDB ? RTDB->getCategory() : "");
+ EmitTrapCheck(TrapCond, CheckHandler, NoMerge, TR);
if (!FatalCond && !RecoverableCond)
return;
@@ -4136,8 +4133,7 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
SanitizerHandler CheckHandlerID,
- bool NoMerge, StringRef Message,
- StringRef Category) {
+ bool NoMerge, const TrapReason *TR) {
llvm::BasicBlock *Cont = createBasicBlock("cont");
// If we're optimizing, collapse all calls to trap down to just one per
@@ -4149,9 +4145,9 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
llvm::StringRef TrapMessage =
- Message.size() > 0 ? Message : GetUBSanTrapForHandler(CheckHandlerID);
+ TR ? TR->getMessage() : GetUBSanTrapForHandler(CheckHandlerID);
llvm::StringRef TrapCategory =
- Category.size() > 0 ? Category : "Undefined Behavior Sanitizer";
+ TR ? TR->getCategory() : "Undefined Behavior Sanitizer";
if (getDebugInfo() && !TrapMessage.empty() &&
CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) {
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 324eaf0fc65cb..5a78a4a57d467 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1814,6 +1814,7 @@ void ScalarExprEmitter::EmitBinOpCheck(
SmallVector<llvm::Constant *, 4> StaticData;
SmallVector<llvm::Value *, 2> DynamicData;
std::unique_ptr<RuntimeTrapDiagnosticBuilder> RTDB = nullptr;
+ TrapReason TR;
BinaryOperatorKind Opcode = Info.Opcode;
if (BinaryOperator::isCompoundAssignmentOp(Opcode))
@@ -1867,16 +1868,16 @@ void ScalarExprEmitter::EmitBinOpCheck(
SanitizerKind::SignedIntegerOverflow)) {
// Only pay the cost for constructing the trap diagnostic if they are
// going to be used.
- RTDB = CGF.CGM.RuntimeDiag(diag::trap_ubsan_arith_overflow);
- *RTDB << Info.Ty->isSignedIntegerOrEnumerationType()
- << ArithOverflowKind << Info.E;
+ CGF.CGM.RuntimeDiag(diag::trap_ubsan_arith_overflow, TR)
+ << Info.Ty->isSignedIntegerOrEnumerationType() << ArithOverflowKind
+ << Info.E;
}
}
DynamicData.push_back(Info.LHS);
DynamicData.push_back(Info.RHS);
}
- CGF.EmitCheck(Checks, Check, StaticData, DynamicData, std::move(RTDB));
+ CGF.EmitCheck(Checks, Check, StaticData, DynamicData, TR.getPtr());
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 340dbcf9e2212..c02ac18ec0198 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5279,7 +5279,7 @@ class CodeGenFunction : public CodeGenTypeCache {
Checked,
SanitizerHandler Check, ArrayRef<llvm::Constant *> StaticArgs,
ArrayRef<llvm::Value *> DynamicArgs,
- std::unique_ptr<RuntimeTrapDiagnosticBuilder> RTDB = nullptr);
+ const TrapReason *TR = nullptr);
/// Emit a slow path cross-DSO CFI check which calls __cfi_slowpath
/// if Cond if false.
@@ -5295,8 +5295,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Create a basic block that will call the trap intrinsic, and emit a
/// conditional branch to it, for the -ftrapv checks.
void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID,
- bool NoMerge = false, StringRef Message = "",
- StringRef Category = "");
+ bool NoMerge = false, const TrapReason *TR = nullptr);
/// Emit a call to trap or debugtrap and attach function attribute
/// "trap-func-name" if specified.
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 535dc968f648f..a183c35fa0396 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1826,9 +1826,8 @@ class CodeGenModule : public CodeGenTypeCache {
}
/// Helper function to construct a RuntimeTrapDiagnosticBuilder
- [[nodiscard]] std::unique_ptr<RuntimeTrapDiagnosticBuilder>
- RuntimeDiag(unsigned DiagID) {
- return std::make_unique<RuntimeTrapDiagnosticBuilder>(&getDiags(), DiagID);
+ RuntimeTrapDiagnosticBuilder RuntimeDiag(unsigned DiagID, TrapReason &TR) {
+ return RuntimeTrapDiagnosticBuilder(&getDiags(), DiagID, TR);
}
private:
>From ac683d70f73047b82d90e4bd88e873d6a03ec525 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Fri, 22 Aug 2025 14:38:10 -0700
Subject: [PATCH 09/26] Fix `tools/clang/test/Misc/warning-flags.c` by teaching
`diagtool` to not treat trap diagnostics as a warning diagnostic.
---
clang/lib/Basic/DiagnosticIDs.cpp | 2 ++
clang/tools/diagtool/ListWarnings.cpp | 3 +++
2 files changed, 5 insertions(+)
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index a1d9d0f34d20d..9ae0c2c5a4b19 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -395,6 +395,8 @@ unsigned DiagnosticIDs::getCustomDiagID(CustomDiagDesc Diag) {
}
bool DiagnosticIDs::isWarningOrExtension(unsigned DiagID) const {
+ // FIXME: Returning `true` for notes, remarks, traps, and
+ // CLASS_INVALID classes looks wrong.
return DiagID < diag::DIAG_UPPER_LIMIT
? getDiagClass(DiagID) != CLASS_ERROR
: CustomDiagInfo->getDescription(DiagID).GetClass() != CLASS_ERROR;
diff --git a/clang/tools/diagtool/ListWarnings.cpp b/clang/tools/diagtool/ListWarnings.cpp
index 9f9647126dd8a..ce24f11bd1411 100644
--- a/clang/tools/diagtool/ListWarnings.cpp
+++ b/clang/tools/diagtool/ListWarnings.cpp
@@ -56,6 +56,9 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
if (DiagnosticIDs{}.isNote(diagID))
continue;
+ if (DiagnosticIDs{}.isTrapDiag(diagID))
+ continue;
+
if (!DiagnosticIDs{}.isWarningOrExtension(diagID))
continue;
>From 6aa829672eb495ee3843dc0cb5e31d91df101696 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 12:32:43 -0700
Subject: [PATCH 10/26] Update clang/include/clang/Basic/Diagnostic.td
Co-authored-by: Sirraide <aeternalmail at gmail.com>
---
clang/include/clang/Basic/Diagnostic.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td
index d190c70a401e3..b1d57d2b5ecc1 100644
--- a/clang/include/clang/Basic/Diagnostic.td
+++ b/clang/include/clang/Basic/Diagnostic.td
@@ -145,7 +145,7 @@ class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>;
class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>;
// Notes can provide supplementary information on errors, warnings, and remarks.
class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>;
-// Traps messages attached to traps in debug info
+// Traps messages attached to traps in debug info.
class Trap<string str> : Diagnostic<str, CLASS_TRAP, SEV_Fatal/*ignored*/>;
class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; }
>From 5d819f2e4df19aa6f358f1a525ad9d1549a968ec Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 12:35:41 -0700
Subject: [PATCH 11/26] Update clang/include/clang/Basic/DiagnosticTrap.h
Co-authored-by: Sirraide <aeternalmail at gmail.com>
---
clang/include/clang/Basic/DiagnosticTrap.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticTrap.h b/clang/include/clang/Basic/DiagnosticTrap.h
index 716be525e0527..b93f9ebc6f34c 100644
--- a/clang/include/clang/Basic/DiagnosticTrap.h
+++ b/clang/include/clang/Basic/DiagnosticTrap.h
@@ -12,8 +12,8 @@
///
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_BASIC_TRAP_H
-#define LLVM_CLANG_BASIC_TRAP_H
+#ifndef LLVM_CLANG_BASIC_DIAGNOSTICTRAP_H
+#define LLVM_CLANG_BASIC_DIAGNOSTICTRAP_H
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticTrapInterface.inc"
>From c54409f7cf295b0f22864f06d112876f45de3a8c Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 12:35:53 -0700
Subject: [PATCH 12/26] Update clang/lib/CodeGen/CGExprScalar.cpp
Co-authored-by: Sirraide <aeternalmail at gmail.com>
---
clang/lib/CodeGen/CGExprScalar.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 5a78a4a57d467..e4543eb137c33 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1813,7 +1813,6 @@ void ScalarExprEmitter::EmitBinOpCheck(
SanitizerHandler Check;
SmallVector<llvm::Constant *, 4> StaticData;
SmallVector<llvm::Value *, 2> DynamicData;
- std::unique_ptr<RuntimeTrapDiagnosticBuilder> RTDB = nullptr;
TrapReason TR;
BinaryOperatorKind Opcode = Info.Opcode;
>From ad5cd321746452861bdb6f34cabef4634034d954 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 16:04:22 -0700
Subject: [PATCH 13/26] Rename classes and move into CodeGen
---
clang/include/clang/Basic/Diagnostic.h | 97 +-------------------
clang/lib/Basic/Diagnostic.cpp | 30 -------
clang/lib/CodeGen/CGExprScalar.cpp | 5 +-
clang/lib/CodeGen/CMakeLists.txt | 1 +
clang/lib/CodeGen/CodeGenModule.h | 7 +-
clang/lib/CodeGen/TrapReasonBuilder.cpp | 50 +++++++++++
clang/lib/CodeGen/TrapReasonBuilder.h | 112 ++++++++++++++++++++++++
7 files changed, 174 insertions(+), 128 deletions(-)
create mode 100644 clang/lib/CodeGen/TrapReasonBuilder.cpp
create mode 100644 clang/lib/CodeGen/TrapReasonBuilder.h
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index 3cb6b01ee8226..422b5bc3c84c6 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -1234,7 +1234,6 @@ class DiagnosticBuilder : public StreamingDiagnostic {
friend class DiagnosticsEngine;
friend class PartialDiagnostic;
friend class Diagnostic;
- friend class RuntimeTrapDiagnosticBuilder;
mutable DiagnosticsEngine *DiagObj = nullptr;
@@ -1261,10 +1260,13 @@ class DiagnosticBuilder : public StreamingDiagnostic {
DiagnosticBuilder() = default;
+protected:
DiagnosticBuilder(DiagnosticsEngine *DiagObj, SourceLocation DiagLoc,
unsigned DiagID);
-protected:
+ DiagnosticsEngine *getDiagnosticsEngine() const { return DiagObj; }
+ unsigned getDiagID() const { return DiagID; }
+
/// Clear out the current diagnostic.
void Clear() const {
DiagObj = nullptr;
@@ -1540,98 +1542,7 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
// RuntimeTrapDiagnosticBuilder and helper classes
//===----------------------------------------------------------------------===//
-/// Helper class for \class RuntimeTrapDiagnosticBuilder. This class stores the
-/// "trap reason" built by \class RuntimeTrapDiagnosticBuilder. This consists of
-/// a trap message and trap category.
-///
-/// It is intended that this object be allocated on the stack.
-class TrapReason {
-public:
- TrapReason() {}
- /// \return The trap message. Note the lifetime of the underlying storage for
- /// the returned StringRef lives in this class which means the returned
- /// StringRef should not be used after this class is destroyed.
- StringRef getMessage() const { return Message; }
-
- /// \return the trap category (e.g. "Undefined Behavior Sanitizer")
- StringRef getCategory() const { return Category; }
-
- bool isEmpty() const { return Message.size() == 0 && Category.size() == 0; }
- /// \return a pointer to this object if it contains a non-empty trap reason,
- /// otherwise return `nullptr`. This is a convenient helper for passing
- /// TrapReason objects to function calls that have a `TrapReason*` parameter.
- TrapReason *getPtr() {
- if (isEmpty())
- return nullptr;
- return this;
- }
-
-private:
- llvm::SmallString<64> Message;
- // The Category doesn't need its own storage because the StringRef points
- // to a global constant string.
- StringRef Category;
-
- // Only this class can set the private fields.
- friend class RuntimeTrapDiagnosticBuilder;
-};
-
-/// Class to make it convenient to initialize TrapReason objects which can be
-/// used to attach the "trap reason" to trap instructions.
-///
-/// Although this class inherits from \class DiagnosticBuilder it has slightly
-/// different semantics.
-///
-/// * This class should only be used with trap diagnostics (declared in
-/// `DiagnosticTrapKinds.td`).
-/// * The `RuntimeTrapDiagnosticBuilder` does not emit diagnostics to the normal
-/// diagnostics consumers on destruction like normal Diagnostic builders.
-/// Instead on destruction it assigns to the TrapReason object passed in to
-/// the constructor.
-///
-/// Given that this class inherits from `DiagnosticBuilder` it inherits all of
-/// its abilities to format diagnostic messages and consume various types in
-/// class (e.g. Type, Exprs, etc.). This makes it particularly suited to
-/// printing types and expressions from the AST while codegen-ing runtime
-/// checks.
-///
-///
-/// Example use via the `CodeGenModule::RuntimeDiag` helper.
-///
-/// \code
-/// {
-/// TrapReason TR;
-/// CGM.RuntimeDiag(diag::trap_diagnostic, TR) << 0 << SomeExpr << SomeType;
-/// consume(TR.getPtr());
-/// }
-/// \endcode
-///
-///
-class RuntimeTrapDiagnosticBuilder : public DiagnosticBuilder {
-public:
- RuntimeTrapDiagnosticBuilder(DiagnosticsEngine *DiagObj, unsigned DiagID,
- TrapReason &TR);
- ~RuntimeTrapDiagnosticBuilder();
-
- // Prevent accidentally copying or assigning
- RuntimeTrapDiagnosticBuilder &
- operator=(const RuntimeTrapDiagnosticBuilder &) = delete;
- RuntimeTrapDiagnosticBuilder &
- operator=(const RuntimeTrapDiagnosticBuilder &&) = delete;
- RuntimeTrapDiagnosticBuilder(const RuntimeTrapDiagnosticBuilder &) = delete;
- RuntimeTrapDiagnosticBuilder(const RuntimeTrapDiagnosticBuilder &&) = delete;
-
- /// \return Format the trap message into `Storage`.
- void getMessage(SmallVectorImpl<char> &Storage);
-
- /// \return Return the trap category. These are the `CategoryName` property
- /// of `trap` diagnostics declared in `DiagnosticTrapKinds.td`.
- StringRef getCategory();
-
-private:
- TrapReason &TR;
-};
//===----------------------------------------------------------------------===//
// Diagnostic
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index d1249ac75b1ae..dc3778bbf339c 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -795,36 +795,6 @@ DiagnosticBuilder::DiagnosticBuilder(const DiagnosticBuilder &D)
D.Clear();
}
-RuntimeTrapDiagnosticBuilder::RuntimeTrapDiagnosticBuilder(
- DiagnosticsEngine *DiagObj, unsigned DiagID, TrapReason &TR)
- : DiagnosticBuilder(DiagObj, SourceLocation(), DiagID), TR(TR) {
- assert(DiagObj->getDiagnosticIDs()->isTrapDiag(DiagID));
-}
-
-RuntimeTrapDiagnosticBuilder::~RuntimeTrapDiagnosticBuilder() {
- // Store the trap message and category into the TrapReason object.
- getMessage(TR.Message);
- TR.Category = getCategory();
-
- // Make sure that when `DiagnosticBuilder::~DiagnosticBuilder()`
- // calls `Emit()` that it does nothing.
- Clear();
-}
-
-void RuntimeTrapDiagnosticBuilder::getMessage(SmallVectorImpl<char> &Storage) {
- // Render the Diagnostic
- Diagnostic Info(DiagObj, *this);
- Info.FormatDiagnostic(Storage);
-}
-
-StringRef RuntimeTrapDiagnosticBuilder::getCategory() {
- auto CategoryID =
- DiagObj->getDiagnosticIDs()->getCategoryNumberForDiag(DiagID);
- if (CategoryID == 0)
- return "";
- return DiagObj->getDiagnosticIDs()->getCategoryNameFromID(CategoryID);
-}
-
Diagnostic::Diagnostic(const DiagnosticsEngine *DO,
const DiagnosticBuilder &DiagBuilder)
: DiagObj(DO), DiagLoc(DiagBuilder.DiagLoc), DiagID(DiagBuilder.DiagID),
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index e4543eb137c33..a7a26f1b125a2 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -21,6 +21,7 @@
#include "CodeGenModule.h"
#include "ConstantEmitter.h"
#include "TargetInfo.h"
+#include "TrapReasonBuilder.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclObjC.h"
@@ -1867,7 +1868,7 @@ void ScalarExprEmitter::EmitBinOpCheck(
SanitizerKind::SignedIntegerOverflow)) {
// Only pay the cost for constructing the trap diagnostic if they are
// going to be used.
- CGF.CGM.RuntimeDiag(diag::trap_ubsan_arith_overflow, TR)
+ CGF.CGM.BuildTrapReason(diag::trap_ubsan_arith_overflow, TR)
<< Info.Ty->isSignedIntegerOrEnumerationType() << ArithOverflowKind
<< Info.E;
}
@@ -1876,7 +1877,7 @@ void ScalarExprEmitter::EmitBinOpCheck(
DynamicData.push_back(Info.RHS);
}
- CGF.EmitCheck(Checks, Check, StaticData, DynamicData, TR.getPtr());
+ CGF.EmitCheck(Checks, Check, StaticData, DynamicData, &TR);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 0f2a352886e7f..ad9ef91c781a8 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -154,6 +154,7 @@ add_clang_library(clangCodeGen
Targets/WebAssembly.cpp
Targets/X86.cpp
Targets/XCore.cpp
+ TrapReasonBuilder.cpp
VarBypassDetector.cpp
DEPENDS
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index a183c35fa0396..cbeb5634b9531 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -17,6 +17,7 @@
#include "CodeGenTypeCache.h"
#include "CodeGenTypes.h"
#include "SanitizerMetadata.h"
+#include "TrapReasonBuilder.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclOpenMP.h"
@@ -1825,9 +1826,9 @@ class CodeGenModule : public CodeGenTypeCache {
return PAlign;
}
- /// Helper function to construct a RuntimeTrapDiagnosticBuilder
- RuntimeTrapDiagnosticBuilder RuntimeDiag(unsigned DiagID, TrapReason &TR) {
- return RuntimeTrapDiagnosticBuilder(&getDiags(), DiagID, TR);
+ /// Helper function to construct a TrapReasonBuilder
+ TrapReasonBuilder BuildTrapReason(unsigned DiagID, TrapReason &TR) {
+ return TrapReasonBuilder(&getDiags(), DiagID, TR);
}
private:
diff --git a/clang/lib/CodeGen/TrapReasonBuilder.cpp b/clang/lib/CodeGen/TrapReasonBuilder.cpp
new file mode 100644
index 0000000000000..5881229bf747d
--- /dev/null
+++ b/clang/lib/CodeGen/TrapReasonBuilder.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements TrapReasonBuilder and related classes.
+///
+//===----------------------------------------------------------------------===//
+#include "TrapReasonBuilder.h"
+
+namespace clang {
+namespace CodeGen {
+
+TrapReasonBuilder::TrapReasonBuilder(DiagnosticsEngine *DiagObj,
+ unsigned DiagID, TrapReason &TR)
+ : DiagnosticBuilder(DiagObj, SourceLocation(), DiagID), TR(TR) {
+ assert(DiagObj->getDiagnosticIDs()->isTrapDiag(DiagID));
+}
+
+TrapReasonBuilder::~TrapReasonBuilder() {
+ // Store the trap message and category into the TrapReason object.
+ getMessage(TR.Message);
+ TR.Category = getCategory();
+
+ // Make sure that when `DiagnosticBuilder::~DiagnosticBuilder()`
+ // calls `Emit()` that it does nothing.
+ Clear();
+}
+
+void TrapReasonBuilder::getMessage(SmallVectorImpl<char> &Storage) {
+ // Render the Diagnostic
+ Diagnostic Info(getDiagnosticsEngine(), *this);
+ Info.FormatDiagnostic(Storage);
+}
+
+StringRef TrapReasonBuilder::getCategory() {
+ auto CategoryID =
+ getDiagnosticsEngine()->getDiagnosticIDs()->getCategoryNumberForDiag(
+ getDiagID());
+ if (CategoryID == 0)
+ return "";
+ return getDiagnosticsEngine()->getDiagnosticIDs()->getCategoryNameFromID(
+ CategoryID);
+}
+} // namespace CodeGen
+} // namespace clang
diff --git a/clang/lib/CodeGen/TrapReasonBuilder.h b/clang/lib/CodeGen/TrapReasonBuilder.h
new file mode 100644
index 0000000000000..4b4bddc4c7f0a
--- /dev/null
+++ b/clang/lib/CodeGen/TrapReasonBuilder.h
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declaration of TrapReasonBuilder and related classes.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_CODEGEN_TRAP_REASON_BUILDER_H
+#define LLVM_CLANG_CODEGEN_TRAP_REASON_BUILDER_H
+#include "clang/Basic/Diagnostic.h"
+
+namespace clang {
+namespace CodeGen {
+
+/// Helper class for \class TrapReasonBuilder. \class TrapReason stores the
+/// "trap reason" built by \class RuntimeTrapDiagnosticBuilder. This consists of
+/// a trap message and trap category.
+///
+/// It is intended that this object be allocated on the stack.
+class TrapReason {
+public:
+ TrapReason() {}
+ /// \return The trap message. Note the lifetime of the underlying storage for
+ /// the returned StringRef lives in this class which means the returned
+ /// StringRef should not be used after this class is destroyed.
+ StringRef getMessage() const { return Message; }
+
+ /// \return the trap category (e.g. "Undefined Behavior Sanitizer")
+ StringRef getCategory() const { return Category; }
+
+ bool isEmpty() const {
+ // Note both Message and Category are checked because it is legitimate for
+ // the Message to be empty but for the Category to be non-empty when the
+ // trap category is known but the specific reason is not available during
+ // codegen.
+ return Message.size() == 0 && Category.size() == 0;
+ }
+
+private:
+ llvm::SmallString<64> Message;
+ // The Category doesn't need its own storage because the StringRef points
+ // to a global constant string.
+ StringRef Category;
+
+ // Only this class can set the private fields.
+ friend class TrapReasonBuilder;
+};
+
+/// Class to make it convenient to initialize TrapReason objects which can be
+/// used to attach the "trap reason" to trap instructions.
+///
+/// Although this class inherits from \class DiagnosticBuilder it has slightly
+/// different semantics.
+///
+/// * This class should only be used with trap diagnostics (declared in
+/// `DiagnosticTrapKinds.td`).
+/// * The `RuntimeTrapDiagnosticBuilder` does not emit diagnostics to the normal
+/// diagnostics consumers on destruction like normal Diagnostic builders.
+/// Instead on destruction it assigns to the TrapReason object passed in to
+/// the constructor.
+///
+/// Given that this class inherits from `DiagnosticBuilder` it inherits all of
+/// its abilities to format diagnostic messages and consume various types in
+/// class (e.g. Type, Exprs, etc.). This makes it particularly suited to
+/// printing types and expressions from the AST while codegen-ing runtime
+/// checks.
+///
+///
+/// Example use via the `CodeGenModule::BuildTrapReason` helper.
+///
+/// \code
+/// {
+/// TrapReason TR;
+/// CGM.BuildTrapReason(diag::trap_diagnostic, TR) << 0 << SomeExpr;
+/// consume(&TR);
+/// }
+/// \endcode
+///
+///
+class TrapReasonBuilder : public DiagnosticBuilder {
+public:
+ TrapReasonBuilder(DiagnosticsEngine *DiagObj, unsigned DiagID,
+ TrapReason &TR);
+ ~TrapReasonBuilder();
+
+ // Prevent accidentally copying or assigning
+ TrapReasonBuilder &operator=(const TrapReasonBuilder &) = delete;
+ TrapReasonBuilder &operator=(const TrapReasonBuilder &&) = delete;
+ TrapReasonBuilder(const TrapReasonBuilder &) = delete;
+ TrapReasonBuilder(const TrapReasonBuilder &&) = delete;
+
+private:
+ /// \return Format the trap message into `Storage`.
+ void getMessage(SmallVectorImpl<char> &Storage);
+
+ /// \return Return the trap category. These are the `CategoryName` property
+ /// of `trap` diagnostics declared in `DiagnosticTrapKinds.td`.
+ StringRef getCategory();
+
+private:
+ TrapReason &TR;
+};
+
+} // namespace CodeGen
+} // namespace clang
+
+#endif
>From 4d7acf8aae644bf888e81ffd97b632ea32b68e21 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 16:05:44 -0700
Subject: [PATCH 14/26] Small clean ups
---
clang/include/clang/Basic/Diagnostic.h | 6 ------
clang/include/clang/Basic/DiagnosticTrap.h | 4 ++--
clang/include/clang/Basic/DiagnosticTrapKinds.td | 16 ++++++++--------
clang/lib/Basic/DiagnosticIDs.cpp | 2 --
clang/lib/CodeGen/CodeGenModule.h | 1 -
clang/lib/CodeGen/TrapReasonBuilder.h | 2 +-
6 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index 422b5bc3c84c6..af26a04d94889 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -1538,12 +1538,6 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
return Report(SourceLocation(), DiagID);
}
-//===----------------------------------------------------------------------===//
-// RuntimeTrapDiagnosticBuilder and helper classes
-//===----------------------------------------------------------------------===//
-
-
-
//===----------------------------------------------------------------------===//
// Diagnostic
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Basic/DiagnosticTrap.h b/clang/include/clang/Basic/DiagnosticTrap.h
index b93f9ebc6f34c..7a59f891dec40 100644
--- a/clang/include/clang/Basic/DiagnosticTrap.h
+++ b/clang/include/clang/Basic/DiagnosticTrap.h
@@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// This file contains the necessary includes for using
-/// `RuntimeTrapDiagnosticBuilder`.
+/// This file contains the necessary definitions of trap diagnostics to be used
+/// with `TrapReasonBuilder`.
///
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Basic/DiagnosticTrapKinds.td b/clang/include/clang/Basic/DiagnosticTrapKinds.td
index edf44e19465e7..c17a88d4fb4fb 100644
--- a/clang/include/clang/Basic/DiagnosticTrapKinds.td
+++ b/clang/include/clang/Basic/DiagnosticTrapKinds.td
@@ -1,19 +1,19 @@
-//==--- DiagnosticTrapKinds.td - CodeGen Diagnostics -------------------===//
+//==--- DiagnosticTrapKinds.td ------------------------ -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
-//===----------------------------------------------------------------------===//
-
//===----------------------------------------------------------------------===//
// Trap Diagnostics
//
-// These are diagnostics that are emitted into Debug Info, rather than to the
-// traditional consumers like the terminal. Their primary purpose is to make
-// debugging traps (e.g. `-fsanitize-trap=undefined`) easier by attaching
-// a trap category and reason to the trap instruction that tools like a debugger
-// can show.
+// These are diagnostics that are emitted into `TrapReason` objects using the
+// `TrapReasonBuilder` class. These `TrapReason` objects are then encoded into
+// debug info during codegen, rather than to the traditional diagnostic
+// consumers like the terminal. Their primary purpose is to make debugging traps
+// (e.g. `-fsanitize-trap=undefined`) easier by attaching a trap category and
+// message to the trap instruction that tools like a debugger can show.
+//
//===----------------------------------------------------------------------===//
let Component = "Trap" in {
let CategoryName = "Undefined Behavior Sanitizer" in {
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index 9ae0c2c5a4b19..a1d9d0f34d20d 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -395,8 +395,6 @@ unsigned DiagnosticIDs::getCustomDiagID(CustomDiagDesc Diag) {
}
bool DiagnosticIDs::isWarningOrExtension(unsigned DiagID) const {
- // FIXME: Returning `true` for notes, remarks, traps, and
- // CLASS_INVALID classes looks wrong.
return DiagID < diag::DIAG_UPPER_LIMIT
? getDiagClass(DiagID) != CLASS_ERROR
: CustomDiagInfo->getDescription(DiagID).GetClass() != CLASS_ERROR;
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index cbeb5634b9531..b4b3a17662045 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -24,7 +24,6 @@
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"
#include "clang/Basic/ABI.h"
-#include "clang/Basic/DiagnosticTrap.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/NoSanitizeList.h"
#include "clang/Basic/ProfileList.h"
diff --git a/clang/lib/CodeGen/TrapReasonBuilder.h b/clang/lib/CodeGen/TrapReasonBuilder.h
index 4b4bddc4c7f0a..8d1b481a4aff3 100644
--- a/clang/lib/CodeGen/TrapReasonBuilder.h
+++ b/clang/lib/CodeGen/TrapReasonBuilder.h
@@ -61,7 +61,7 @@ class TrapReason {
/// `DiagnosticTrapKinds.td`).
/// * The `RuntimeTrapDiagnosticBuilder` does not emit diagnostics to the normal
/// diagnostics consumers on destruction like normal Diagnostic builders.
-/// Instead on destruction it assigns to the TrapReason object passed in to
+/// Instead on destruction it assigns to the TrapReason object passed into
/// the constructor.
///
/// Given that this class inherits from `DiagnosticBuilder` it inherits all of
>From abfade44df483d5b718d906d1caf3a270cca83e4 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 16:29:07 -0700
Subject: [PATCH 15/26] Drop top-level comment in header
---
clang/include/clang/Basic/DiagnosticTrap.h | 7 -------
1 file changed, 7 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticTrap.h b/clang/include/clang/Basic/DiagnosticTrap.h
index 7a59f891dec40..da8bd257037e9 100644
--- a/clang/include/clang/Basic/DiagnosticTrap.h
+++ b/clang/include/clang/Basic/DiagnosticTrap.h
@@ -5,13 +5,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file contains the necessary definitions of trap diagnostics to be used
-/// with `TrapReasonBuilder`.
-///
-//===----------------------------------------------------------------------===//
-
#ifndef LLVM_CLANG_BASIC_DIAGNOSTICTRAP_H
#define LLVM_CLANG_BASIC_DIAGNOSTICTRAP_H
>From fc6b7cd73ac40d2cb2c81c5b7390e82bb85e39d5 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 16:42:24 -0700
Subject: [PATCH 16/26] Add missing include to unbreak the build
---
clang/lib/CodeGen/CGExprScalar.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index a7a26f1b125a2..914900f176165 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -30,6 +30,7 @@
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/DiagnosticTrap.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/APFixedPoint.h"
#include "llvm/IR/Argument.h"
>From 3a8b319f28936e4c2d2089784dc93d19168da083 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Mon, 25 Aug 2025 17:02:41 -0700
Subject: [PATCH 17/26] Add missing `isEmpty()` test to fix tests
---
clang/lib/CodeGen/CGExpr.cpp | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 23ee285e4d242..8656b5d0a974a 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4144,10 +4144,15 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];
llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
- llvm::StringRef TrapMessage =
- TR ? TR->getMessage() : GetUBSanTrapForHandler(CheckHandlerID);
- llvm::StringRef TrapCategory =
- TR ? TR->getCategory() : "Undefined Behavior Sanitizer";
+ llvm::StringRef TrapMessage;
+ llvm::StringRef TrapCategory;
+ if (TR && !TR->isEmpty()) {
+ TrapMessage = TR->getMessage();
+ TrapCategory = TR->getCategory();
+ } else {
+ TrapMessage = GetUBSanTrapForHandler(CheckHandlerID);
+ TrapCategory = "Undefined Behavior Sanitizer";
+ }
if (getDebugInfo() && !TrapMessage.empty() &&
CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) {
>From 8973973d16ce6b68f9fac6300cbb167a5bb44099 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 10:32:59 -0700
Subject: [PATCH 18/26] Fix inconsistent ordering
---
clang/lib/CodeGen/CGExprScalar.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 914900f176165..2eff3a387593c 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1845,8 +1845,8 @@ void ScalarExprEmitter::EmitBinOpCheck(
int ArithOverflowKind = 0;
switch (Opcode) {
case BO_Add: {
- ArithOverflowKind = diag::UBSanArithKind::Add;
Check = SanitizerHandler::AddOverflow;
+ ArithOverflowKind = diag::UBSanArithKind::Add;
break;
}
case BO_Sub: {
>From 4e3200f8b2c282c9be5f63e0d1ada445c991bd7b Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 10:34:04 -0700
Subject: [PATCH 19/26] Update clang/include/clang/Basic/Diagnostic.td
Co-authored-by: Sirraide <aeternalmail at gmail.com>
---
clang/include/clang/Basic/Diagnostic.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td
index b1d57d2b5ecc1..53b1db265ccd0 100644
--- a/clang/include/clang/Basic/Diagnostic.td
+++ b/clang/include/clang/Basic/Diagnostic.td
@@ -145,7 +145,7 @@ class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>;
class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>;
// Notes can provide supplementary information on errors, warnings, and remarks.
class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>;
-// Traps messages attached to traps in debug info.
+// Trap messages attached to traps in debug info.
class Trap<string str> : Diagnostic<str, CLASS_TRAP, SEV_Fatal/*ignored*/>;
class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; }
>From e5859beff4befe2923bec81ae86ea2794959fffc Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 10:34:14 -0700
Subject: [PATCH 20/26] Update clang/lib/CodeGen/TrapReasonBuilder.h
Co-authored-by: Sirraide <aeternalmail at gmail.com>
---
clang/lib/CodeGen/TrapReasonBuilder.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/TrapReasonBuilder.h b/clang/lib/CodeGen/TrapReasonBuilder.h
index 8d1b481a4aff3..7d12d56252bf5 100644
--- a/clang/lib/CodeGen/TrapReasonBuilder.h
+++ b/clang/lib/CodeGen/TrapReasonBuilder.h
@@ -24,7 +24,7 @@ namespace CodeGen {
/// It is intended that this object be allocated on the stack.
class TrapReason {
public:
- TrapReason() {}
+ TrapReason() = default;
/// \return The trap message. Note the lifetime of the underlying storage for
/// the returned StringRef lives in this class which means the returned
/// StringRef should not be used after this class is destroyed.
>From 58a897c6c5557ede7981a260ab1adea4061d017b Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 10:34:22 -0700
Subject: [PATCH 21/26] Update clang/lib/CodeGen/TrapReasonBuilder.h
Co-authored-by: Sirraide <aeternalmail at gmail.com>
---
clang/lib/CodeGen/TrapReasonBuilder.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/TrapReasonBuilder.h b/clang/lib/CodeGen/TrapReasonBuilder.h
index 7d12d56252bf5..f80582e69dc1f 100644
--- a/clang/lib/CodeGen/TrapReasonBuilder.h
+++ b/clang/lib/CodeGen/TrapReasonBuilder.h
@@ -59,7 +59,7 @@ class TrapReason {
///
/// * This class should only be used with trap diagnostics (declared in
/// `DiagnosticTrapKinds.td`).
-/// * The `RuntimeTrapDiagnosticBuilder` does not emit diagnostics to the normal
+/// * The `TrapReasonBuilder` does not emit diagnostics to the normal
/// diagnostics consumers on destruction like normal Diagnostic builders.
/// Instead on destruction it assigns to the TrapReason object passed into
/// the constructor.
>From 6c78086bc317aeb01d799874d97678f92a8eb0bc Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 10:43:20 -0700
Subject: [PATCH 22/26] Fix out-of-date comment
---
clang/lib/CodeGen/TrapReasonBuilder.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/TrapReasonBuilder.h b/clang/lib/CodeGen/TrapReasonBuilder.h
index f80582e69dc1f..b16cae482153a 100644
--- a/clang/lib/CodeGen/TrapReasonBuilder.h
+++ b/clang/lib/CodeGen/TrapReasonBuilder.h
@@ -18,7 +18,7 @@ namespace clang {
namespace CodeGen {
/// Helper class for \class TrapReasonBuilder. \class TrapReason stores the
-/// "trap reason" built by \class RuntimeTrapDiagnosticBuilder. This consists of
+/// "trap reason" built by \class TrapReasonBuilder. This consists of
/// a trap message and trap category.
///
/// It is intended that this object be allocated on the stack.
>From bc4842feb30de32ba7673da70bd8bacc7e89d895 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 14:42:44 -0700
Subject: [PATCH 23/26] Make `-fsanitize-debug-trap-reasons=` a parameterized
flag that takes either `none`, `basic`, or `detailed`.
The old boolean flags are included for compatibility and are aliases of
the new flag.
The `basic` mode is the behavior that existed before this patch, and
`detailed` is the newer more expressive trap reasons.
---
clang/docs/ReleaseNotes.rst | 28 ++++++---
clang/include/clang/Basic/CodeGenOptions.def | 2 +-
clang/include/clang/Basic/CodeGenOptions.h | 10 ++++
clang/include/clang/Driver/Options.td | 31 ++++++----
clang/lib/CodeGen/CGExpr.cpp | 9 ++-
clang/lib/Driver/SanitizerArgs.cpp | 6 +-
.../Generic/ubsan-trap-reason-add-overflow.c | 21 +++++--
.../Generic/ubsan-trap-reason-flag.c | 29 +++++++++-
.../Generic/ubsan-trap-reason-mul-overflow.c | 20 +++++--
.../Generic/ubsan-trap-reason-sub-overflow.c | 20 +++++--
.../Driver/fsanitize-debug-trap-reasons.c | 57 +++++++++++++++++++
.../Frontend/fsanitize-debug-trap-reasons.c | 6 ++
12 files changed, 195 insertions(+), 44 deletions(-)
create mode 100644 clang/test/Driver/fsanitize-debug-trap-reasons.c
create mode 100644 clang/test/Frontend/fsanitize-debug-trap-reasons.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3ad9e566a5d93..ea1c244c7dc01 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -141,14 +141,22 @@ Non-comprehensive list of changes in this release
- Added ``__builtin_elementwise_minnumnum`` and ``__builtin_elementwise_maxnumnum``.
-- Trapping UBSan (e.g. ``-fsanitize-trap=undefined``) now emits a string describing the reason for
- trapping into the generated debug info. This feature allows debuggers (e.g. LLDB) to display
- the reason for trapping if the trap is reached. The string is currently encoded in the debug
- info as an artificial frame that claims to be inlined at the trap location. The function used
- for the artificial frame is an artificial function whose name encodes the reason for trapping.
- The encoding used is currently the same as ``__builtin_verbose_trap`` but might change in the future.
- This feature is enabled by default but can be disabled by compiling with
- ``-fno-sanitize-annotate-debug-info-traps``.
+- Trapping UBSan (e.g. ``-fsanitize-trap=undefined``) now emits a string
+ describing the reason for trapping into the generated debug info. This feature
+ allows debuggers (e.g. LLDB) to display the reason for trapping if the trap is
+ reached. The string is currently encoded in the debug info as an artificial
+ frame that claims to be inlined at the trap location. The function used for
+ the artificial frame is an artificial function whose name encodes the reason
+ for trapping. The encoding used is currently the same as
+ ``__builtin_verbose_trap`` but might change in the future. This feature is
+ enabled by default but can be disabled by compiling with
+ ``-fno-sanitize-debug-trap-reasons``. The feature has a ``basic`` and
+ ``detailed`` mode (the default). The ``basic`` mode emits a hard-coded string
+ per trap kind (e.g. integer overflow) and the ``detailed`` mode emits a more
+ descriptive string describing each individual trap. The ``detailed`` mode
+ produces larger debug info than ``basic`` but is more helpful for debugging.
+ The ``-fsanitize-debug-trap-reasons=`` flag can be used to switch between the
+ different modes.
- ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` functions for integer types can
now be used in constant expressions.
@@ -185,7 +193,9 @@ Non-comprehensive list of changes in this release
New Compiler Flags
------------------
-- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
+- New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
+- New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
+
Lanai Support
^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index b96ec75068004..fda0da99b60c0 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -307,7 +307,7 @@ CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0, Benign) ///< Emit PCs for atomic
CODEGENOPT(SanitizeBinaryMetadataUAR, 1, 0, Benign) ///< Emit PCs for start of functions
///< that are subject for use-after-return checking.
CODEGENOPT(SanitizeStats , 1, 0, Benign) ///< Collect statistics for sanitizers.
-CODEGENOPT(SanitizeDebugTrapReasons, 1, 1 , Benign) ///< Enable UBSan trapping messages
+ENUM_CODEGENOPT(SanitizeDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info
CODEGENOPT(SimplifyLibCalls , 1, 1, Benign) ///< Set when -fbuiltin is enabled.
CODEGENOPT(SoftFloat , 1, 0, Benign) ///< -soft-float.
CODEGENOPT(SpeculativeLoadHardening, 1, 0, Benign) ///< Enable speculative load hardening.
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index cdeedd5b4eac6..c4964bc835c9b 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -198,6 +198,16 @@ class CodeGenOptions : public CodeGenOptionsBase {
Forced,
};
+ enum SanitizeDebugTrapReasonKind {
+ None, ///< Trap Messages are omitted. This offers the smallest debug info
+ ///< size but at the cost of making traps hard to debug.
+ Basic, ///< Trap Message is fixed per SanitizerKind. Produces smaller debug
+ ///< info than `Detailed` but is not as helpful for debugging.
+ Detailed, ///< Trap Message includes more context (e.g. the expression being
+ ///< overflowed). This is more helpful for debugging but produces
+ ///< larger debug info than `Basic`.
+ };
+
/// The code model to use (-mcmodel).
std::string CodeModel;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 82e8212bee12d..b1ae3cf6525b8 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2612,16 +2612,27 @@ def fsanitize_undefined_trap_on_error
def fno_sanitize_undefined_trap_on_error
: Flag<["-"], "fno-sanitize-undefined-trap-on-error">, Group<f_clang_Group>,
Alias<fno_sanitize_trap_EQ>, AliasArgs<["undefined"]>;
-defm sanitize_debug_trap_reasons
- : BoolFOption<
- "sanitize-debug-trap-reasons",
- CodeGenOpts<"SanitizeDebugTrapReasons">, DefaultTrue,
- PosFlag<SetTrue, [], [ClangOption, CC1Option],
- "Annotate trap blocks in debug info with UBSan trap reasons">,
- NegFlag<SetFalse, [], [ClangOption, CC1Option],
- "Do not annotate trap blocks in debug info with UBSan trap "
- "reasons">>;
-
+def fsanitize_debug_trap_reasons_EQ
+ : Joined<["-"], "fsanitize-debug-trap-reasons=">, Group<f_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Set how trap reasons are emitted. "
+ "`none` - Not emitted. This gives the smallest debug info; "
+ "`basic` - Emit a fixed trap message per check type. This increases the "
+ "debug info size but not as much as `detailed`; "
+ "`detailed` - Emit a more detailed trap message. This increases the "
+ "debug info size the most. Default is `detailed`.">,
+ Values<"none,basic,detailed">,
+ NormalizedValuesScope<"CodeGenOptions::SanitizeDebugTrapReasonKind">,
+ NormalizedValues<["None", "Basic", "Detailed"]>,
+ MarshallingInfoEnum<CodeGenOpts<"SanitizeDebugTrapReasons">, "Detailed">;
+def fsanitize_debug_trap_reasons
+ : Flag<["-"], "fsanitize-debug-trap-reasons">, Group<f_clang_Group>,
+ Alias<fsanitize_debug_trap_reasons_EQ>, AliasArgs<["detailed"]>,
+ HelpText<"Alias for -fsanitize-debug-trap-reasons=detailed">;
+def fno_sanitize_debug_trap_reasons
+ : Flag<["-"], "fno-sanitize-debug-trap-reasons">, Group<f_clang_Group>,
+ Alias<fsanitize_debug_trap_reasons_EQ>, AliasArgs<["none"]>,
+ HelpText<"Alias for -fsanitize-debug-trap-reasons=none">;
defm sanitize_minimal_runtime : BoolOption<"f", "sanitize-minimal-runtime",
CodeGenOpts<"SanitizeMinimalRuntime">, DefaultFalse,
PosFlag<SetTrue>,
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 8656b5d0a974a..844b445b98c1d 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4146,7 +4146,10 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
llvm::StringRef TrapMessage;
llvm::StringRef TrapCategory;
- if (TR && !TR->isEmpty()) {
+ auto DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons();
+ if (TR && !TR->isEmpty() &&
+ DebugTrapReasonKind ==
+ CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed) {
TrapMessage = TR->getMessage();
TrapCategory = TR->getCategory();
} else {
@@ -4155,7 +4158,9 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
}
if (getDebugInfo() && !TrapMessage.empty() &&
- CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) {
+ DebugTrapReasonKind !=
+ CodeGenOptions::SanitizeDebugTrapReasonKind::None &&
+ TrapLocation) {
TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
TrapLocation, TrapCategory, TrapMessage);
}
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 54f0e63b98070..7ce1afe6f2e6a 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -1384,11 +1384,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
CmdArgs.push_back(Args.MakeArgString("-fsanitize-annotate-debug-info=" +
toString(AnnotateDebugInfo)));
- if (const Arg *A =
- Args.getLastArg(options::OPT_fsanitize_debug_trap_reasons,
- options::OPT_fno_sanitize_debug_trap_reasons)) {
- CmdArgs.push_back(Args.MakeArgString(A->getAsString(Args)));
- }
+ Args.AddLastArg(CmdArgs, options::OPT_fsanitize_debug_trap_reasons_EQ);
addSpecialCaseListOpt(Args, CmdArgs,
"-fsanitize-ignorelist=", UserIgnorelistFiles);
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
index aefb92819d215..cbe21afb688b1 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
@@ -1,7 +1,13 @@
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
-// RUN: -emit-llvm %s -o - | FileCheck %s
+// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s
+
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-debug-trap-reasons=basic \
+// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s
int sadd_overflow(int a, int b) { return a + b; }
@@ -13,9 +19,14 @@ unsigned add_overflow(unsigned c, unsigned d) { return c + d; }
// CHECK-LABEL: @add_overflow
// CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
+// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
+// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'"
+// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer addition overflow in 'c + d'"
+
+// In "Basic" mode both the trap reason is shared by both functions.
+// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed"
+// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// CHECK: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
-// CHECK: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'"
-// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer addition overflow in 'c + d'"
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c
index f0e707e5804be..2968e6bd2ade4 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c
@@ -2,20 +2,45 @@
// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - \
// RUN: | FileCheck %s --check-prefix=ANNOTATE
+//==============================================================================
+// Detailed trap reasons
+//==============================================================================
+
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
+// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \
+// RUN: -fsanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,DETAILED
+
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
+// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \
+// RUN: -fsanitize-debug-trap-reasons=detailed -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,DETAILED
+
+//==============================================================================
+// Basic trap reasons
+//==============================================================================
+
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \
-// RUN: -fsanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefix=ANNOTATE
+// RUN: -fsanitize-debug-trap-reasons=basic -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,BASIC
+
+//==============================================================================
+// No trap reasons
+//==============================================================================
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \
// RUN: -fno-sanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefix=NO-ANNOTATE
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
+// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \
+// RUN: -fsanitize-debug-trap-reasons=none -emit-llvm %s -o - | FileCheck %s --check-prefix=NO-ANNOTATE
+
int add_overflow(int a, int b) { return a + b; }
// ANNOTATE-LABEL: @add_overflow
// ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
// ANNOTATE: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// ANNOTATE: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'"
+// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'"
+// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed"
// NO-ANNOTATE-LABEL: @add_overflow
// NO-ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
index b1a79b8a6cb24..80719610f3ee6 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
@@ -1,7 +1,13 @@
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
-// RUN: -emit-llvm %s -o - | FileCheck %s
+// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s
+
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-debug-trap-reasons=basic \
+// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s
int smul_overflow(int a, int b) { return a * b; }
@@ -13,8 +19,12 @@ unsigned mul_overflow(unsigned c, unsigned d) { return c * d; }
// CHECK-LABEL: @mul_overflow
// CHECK: call void @llvm.ubsantrap(i8 12) {{.*}}!dbg [[LOC:![0-9]+]]
-// CHECK: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
-// CHECK: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer multiplication overflow in 'a * b'"
+// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
+// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer multiplication overflow in 'a * b'"
+// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer multiplication overflow in 'c * d'"
-// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer multiplication overflow in 'c * d'"
+// In "Basic" mode both the trap reason is shared by both functions.
+// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer multiplication overflowed"
+// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
index f38a5b47e6a00..441a87ae32ce0 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
@@ -1,7 +1,13 @@
// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
-// RUN: -emit-llvm %s -o - | FileCheck %s
+// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s
+
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \
+// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \
+// RUN: -fsanitize-debug-trap-reasons=basic \
+// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s
int ssub_overflow(int a, int b) { return a - b; }
@@ -13,8 +19,12 @@ unsigned sub_overflow(unsigned c, unsigned d) { return c - d; }
// CHECK-LABEL: @sub_overflow
// CHECK: call void @llvm.ubsantrap(i8 21) {{.*}}!dbg [[LOC:![0-9]+]]
-// CHECK: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
-// CHECK: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer subtraction overflow in 'a - b'"
+// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}})
+// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer subtraction overflow in 'a - b'"
+// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer subtraction overflow in 'c - d'"
-// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
-// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer subtraction overflow in 'c - d'"
+// In "Basic" mode both the trap reason is shared by both functions.
+// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer subtraction overflowed"
+// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
diff --git a/clang/test/Driver/fsanitize-debug-trap-reasons.c b/clang/test/Driver/fsanitize-debug-trap-reasons.c
new file mode 100644
index 0000000000000..5a0ccde015939
--- /dev/null
+++ b/clang/test/Driver/fsanitize-debug-trap-reasons.c
@@ -0,0 +1,57 @@
+// =============================================================================
+// No Trap Reasons
+// =============================================================================
+
+// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \
+// RUN: -fsanitize-debug-trap-reasons=none %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=NONE %s
+
+// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \
+// RUN: -fno-sanitize-debug-trap-reasons %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=NONE %s
+
+// NONE: -fsanitize-debug-trap-reasons=none
+
+// =============================================================================
+// Basic Trap Reasons
+// =============================================================================
+
+// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \
+// RUN: -fsanitize-debug-trap-reasons=basic %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=BASIC %s
+// BASIC: -fsanitize-debug-trap-reasons=basic
+
+// =============================================================================
+// Detailed Trap Reasons
+// =============================================================================
+
+// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \
+// RUN: -fsanitize-debug-trap-reasons=detailed %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=DETAILED %s
+
+// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \
+// RUN: -fsanitize-debug-trap-reasons %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=DETAILED %s
+
+// DETAILED: -fsanitize-debug-trap-reasons=detailed
+
+// =============================================================================
+// Other cases
+// =============================================================================
+
+// By default the driver doesn't pass along any value and the default value is
+// whatever is the default in CodeGenOptions.
+// RUN: %clang %s -### 2>&1 | FileCheck --check-prefix=DEFAULT %s
+// DEFAULT-NOT: -fsanitize-debug-trap-reasons
+
+// Warning when not using UBSan
+// RUN: %clang -fsanitize-debug-trap-reasons=none %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=WARN %s
+// WARN: warning: argument unused during compilation: '-fsanitize-debug-trap-reasons=none'
+
+// Bad flag arguments are just passed along to the Frontend which handles rejecting
+// invalid values. See `clang/test/Frontend/fsanitize-debug-trap-reasons.c`
+// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \
+// RUN: -fsanitize-debug-trap-reasons=bad_value %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=BAD_VALUE %s
+// BAD_VALUE: -fsanitize-debug-trap-reasons=bad_value
diff --git a/clang/test/Frontend/fsanitize-debug-trap-reasons.c b/clang/test/Frontend/fsanitize-debug-trap-reasons.c
new file mode 100644
index 0000000000000..82b33eaf1cb27
--- /dev/null
+++ b/clang/test/Frontend/fsanitize-debug-trap-reasons.c
@@ -0,0 +1,6 @@
+// RUN: not %clang_cc1 -triple arm64-apple-macosx14.0.0 \
+// RUN: -fsanitize=signed-integer-overflow -fsanitize=signed-integer-overflow \
+// RUN: -fsanitize-debug-trap-reasons=bad_value 2>&1 | FileCheck %s
+
+// CHECK: error: invalid value 'bad_value' in '-fsanitize-debug-trap-reasons=bad_value'
+int test(void) { return 0;}
>From 85c4e42c313816b482d8d2b97ff0a8b9688f8a93 Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 14:52:23 -0700
Subject: [PATCH 24/26] Fix formatting
---
clang/include/clang/Basic/CodeGenOptions.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index c4964bc835c9b..5d5cf250b56b9 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -199,8 +199,8 @@ class CodeGenOptions : public CodeGenOptionsBase {
};
enum SanitizeDebugTrapReasonKind {
- None, ///< Trap Messages are omitted. This offers the smallest debug info
- ///< size but at the cost of making traps hard to debug.
+ None, ///< Trap Messages are omitted. This offers the smallest debug info
+ ///< size but at the cost of making traps hard to debug.
Basic, ///< Trap Message is fixed per SanitizerKind. Produces smaller debug
///< info than `Detailed` but is not as helpful for debugging.
Detailed, ///< Trap Message includes more context (e.g. the expression being
>From 783eca99319ce08e346990e342440180f5ee081f Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Tue, 26 Aug 2025 16:12:56 -0700
Subject: [PATCH 25/26] Fix typos in some test comments
---
clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c | 2 +-
clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c | 2 +-
clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
index cbe21afb688b1..862d434d291bc 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c
@@ -24,7 +24,7 @@ unsigned add_overflow(unsigned c, unsigned d) { return c + d; }
// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer addition overflow in 'c + d'"
-// In "Basic" mode both the trap reason is shared by both functions.
+// In "Basic" mode the trap reason is shared by both functions.
// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed"
// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
index 80719610f3ee6..ba3928d0c2e63 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c
@@ -24,7 +24,7 @@ unsigned mul_overflow(unsigned c, unsigned d) { return c * d; }
// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer multiplication overflow in 'c * d'"
-// In "Basic" mode both the trap reason is shared by both functions.
+// In "Basic" mode the trap reason is shared by both functions.
// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer multiplication overflowed"
// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
diff --git a/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
index 441a87ae32ce0..596d777fa4360 100644
--- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
+++ b/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c
@@ -24,7 +24,7 @@ unsigned sub_overflow(unsigned c, unsigned d) { return c - d; }
// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer subtraction overflow in 'c - d'"
-// In "Basic" mode both the trap reason is shared by both functions.
+// In "Basic" mode the trap reason is shared by both functions.
// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer subtraction overflowed"
// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
>From cd62acd1c9f3991307edeca5d92a9bb74df043be Mon Sep 17 00:00:00 2001
From: Dan Liew <dan at su-root.co.uk>
Date: Wed, 27 Aug 2025 12:13:57 -0700
Subject: [PATCH 26/26] Address some feedback for the release notes and also
explain about unoptimized-vs-optimized builds
---
clang/docs/ReleaseNotes.rst | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ea1c244c7dc01..84e499e5d0ab9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -141,22 +141,27 @@ Non-comprehensive list of changes in this release
- Added ``__builtin_elementwise_minnumnum`` and ``__builtin_elementwise_maxnumnum``.
-- Trapping UBSan (e.g. ``-fsanitize-trap=undefined``) now emits a string
- describing the reason for trapping into the generated debug info. This feature
- allows debuggers (e.g. LLDB) to display the reason for trapping if the trap is
- reached. The string is currently encoded in the debug info as an artificial
- frame that claims to be inlined at the trap location. The function used for
- the artificial frame is an artificial function whose name encodes the reason
- for trapping. The encoding used is currently the same as
- ``__builtin_verbose_trap`` but might change in the future. This feature is
+- Trapping UBSan (e.g. ``-fsanitize=undefined -fsanitize-trap=undefined``) now
+ emits a string describing the reason for trapping into the generated debug
+ info. This feature allows debuggers (e.g. LLDB) to display the reason for
+ trapping if the trap is reached. The string is currently encoded in the debug
+ info as an artificial frame that claims to be inlined at the trap location.
+ The function used for the artificial frame is an artificial function whose
+ name encodes the reason for trapping. The encoding used is currently the same
+ as ``__builtin_verbose_trap`` but might change in the future. This feature is
enabled by default but can be disabled by compiling with
``-fno-sanitize-debug-trap-reasons``. The feature has a ``basic`` and
``detailed`` mode (the default). The ``basic`` mode emits a hard-coded string
- per trap kind (e.g. integer overflow) and the ``detailed`` mode emits a more
- descriptive string describing each individual trap. The ``detailed`` mode
- produces larger debug info than ``basic`` but is more helpful for debugging.
- The ``-fsanitize-debug-trap-reasons=`` flag can be used to switch between the
- different modes.
+ per trap kind (e.g. ``Integer addition overflowed``) and the ``detailed`` mode
+ emits a more descriptive string describing each individual trap (e.g. ``signed
+ integer addition overflow in 'a + b'``). The ``detailed`` mode produces larger
+ debug info than ``basic`` but is more helpful for debugging. The
+ ``-fsanitize-debug-trap-reasons=`` flag can be used to switch between the
+ different modes or disable the feature entirely. Note due to trap merging in
+ optimized builds (i.e. in each function all traps of the same kind get merged
+ into the same trap instruction) the trap reasons might be removed. To prevent
+ this build without optimizations (i.e. use `-O0` or use the `optnone` function
+ attribute) or use the `fno-sanitize-merge=` flag in optimized builds.
- ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` functions for integer types can
now be used in constant expressions.
More information about the lldb-commits
mailing list