[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 11:57:28 PDT 2025
https://github.com/delcypher updated https://github.com/llvm/llvm-project/pull/154618
>From 8587f2a91d667758f80d1446fe06416a2a303afd 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/25] [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 e6d2c11dbc642134187170cc9328cd2efd914373 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/25] 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 3601a8134dd0d58b653c608c570bfa5add7836b6 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/25] 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 138dcce13b7f478fdb49a6967c749e96da74e989 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/25] 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 f5e147c12def1ac04fe31efed89be92e3eb5a2c7 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/25] 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 560e08057e2f09595d03daab2e26bad62be9c86a 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/25] 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 adffffdc99f2c1022e8da839fdfd432c0a7f0c08 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/25] 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 b150fdda4a4a69e79ec77dbef540c689814b6831 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/25] 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 8c94a6a567dcfb857c68c31be3ed062138c8115b 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/25] 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 e8fc8a16c20e1e1b6803b19681dc8099cb103e29 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/25] 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 7ba3c0ff9a340a8b4c8b87e4fd4ca85455c23933 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/25] 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 8667f4dcf3db4e6648b7b17d6c2894ab039ca8a2 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/25] 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 2cf96df7aad663a0b513c6772e3e09b5c82a9174 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/25] 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 e0bd29bce72e30883985e3455636c91175047414 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/25] 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 e1d99739a2a28ac370514b6774534b15ee9cea19 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/25] 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 d7100a82554d55cb1d3cba4c69844035d257ec23 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/25] 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 d073b17c0ac82ebbf9ef678849a8d96ece8661ca 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/25] 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 b302eed31773d6732252d349c2fe3bf751a7ccca 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/25] 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 310ef3f4f6fbd79c7feab798047e662f7ee955d8 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/25] 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 9518a07759d78e6407655203394d2d7c12687663 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/25] 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 e1d5f463735841915fc35099a3b64f9bb863db6b 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/25] 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 13501d9df62da32d2d188291a4f56cf23c56d3e8 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/25] 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 e9ca89f7b0a8f947241431abd3cbbdd9afeba8e4 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/25] 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 d8e12d252f69e4f9c6acfce56d7faebeee1b4751 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/25] 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 5a0749548da21b7ec9a7e5c1ed6152d09929a107 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/25] 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]+]], {{.+}})
More information about the lldb-commits
mailing list