Skip to content

Conversation

anthonyhatran
Copy link
Contributor

@anthonyhatran anthonyhatran commented Jun 11, 2025

GSoC 2025
@delcypher @Michael137

Ran the clang test suite before and after changes, and some (~5) test cases failed after my changes:
clang/test/CodeGen/ubsan-trap-debugloc.c
clang/test/CodeGen/bounds-checking-debuginfo.c
clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c
clang/test/CodeGen/cfi-icall-generalize-debuginfo.c
clang/test/Driver/clang_f_opts_withspaces.c

Still need to modify the hard-coded strings after further discussion

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Jun 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 11, 2025

@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: None (anthonyhatran)

Changes

GSoC 2025
@delcypher @Michael137

Ran the clang test suite before and after changes, and some (~5) test cases failed:
clang/test/CodeGen/ubsan-trap-debugloc.c
clang/test/CodeGen/bounds-checking-debuginfo.c
clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c
clang/test/CodeGen/cfi-icall-generalize-debuginfo.c
clang/test/Driver/clang_f_opts_withspaces.c

Still need to modify the hard-coded strings after further discussion


Patch is 21.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/143758.diff

22 Files Affected:

  • (modified) clang/lib/CodeGen/CGExpr.cpp (+114-3)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+2-1)
  • (added) clang/test/CodeGen/ubsan-trap-reason-add-overflow.c (+10)
  • (added) clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c (+12)
  • (added) clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c (+10)
  • (added) clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c (+10)
  • (added) clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c (+16)
  • (added) clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c (+13)
  • (added) clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c (+12)
  • (added) clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c (+15)
  • (added) clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp (+12)
  • (added) clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c (+10)
  • (added) clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c (+12)
  • (added) clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c (+17)
  • (added) clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c (+15)
  • (added) clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c (+19)
  • (added) clang/test/CodeGen/ubsan-trap-reason-nullability-return.c (+19)
  • (added) clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c (+12)
  • (added) clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c (+16)
  • (added) clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c (+12)
  • (added) clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c (+10)
  • (added) clang/test/CodeGen/ubsan-trap-reason-type-mismatch.c (+11)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6cb348ffdf55f..9100ef80c13f4 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -85,6 +85,98 @@ enum VariableTypeDescriptorKind : uint16_t {
 //                        Miscellaneous Helper Methods
 //===--------------------------------------------------------------------===//
 
+static llvm::StringRef GetTrapMessageForHandler(SanitizerHandler ID) {
+  switch (ID) {
+  case SanitizerHandler::AddOverflow:
+    return "The addition of two signed integers resulted in overflow.";
+
+  case SanitizerHandler::BuiltinUnreachable:
+    return "_builtin_unreachable encountered.";
+
+  case SanitizerHandler::CFICheckFail:
+    return "Control flow integrity check failed.";
+
+  case SanitizerHandler::DivremOverflow: // Unsure
+    return "stub";
+
+  case SanitizerHandler::DynamicTypeCacheMiss: // Unsure
+    return "Data requested for dynamic type not found in cache memory.";
+
+  case SanitizerHandler::FloatCastOverflow: // Pasted from LLVM docs, maybe
+                                            // something better to put here.
+    return "Conversion to, from, or between floating-point types which would "
+           "overflow the destination.";
+
+  case SanitizerHandler::FunctionTypeMismatch:
+    return "Function called with arguments of a different data type than "
+           "expected";
+
+  case SanitizerHandler::ImplicitConversion:
+    return "Implicit conversion occurred.";
+
+  case SanitizerHandler::InvalidBuiltin:
+    return "Built-in function or keyword not recognized.";
+
+  case SanitizerHandler::InvalidObjCCast:
+    return "Invalid Objective-C cast.";
+
+  case SanitizerHandler::LoadInvalidValue:
+    return "stub";
+
+  case SanitizerHandler::MissingReturn:
+    return "Function is missing a return.";
+
+  case SanitizerHandler::MulOverflow:
+    return "The multiplication of two signed integers resulted in overflow.";
+
+  case SanitizerHandler::NegateOverflow:
+    return "Underflow/negative overflow occurred.";
+
+  case SanitizerHandler::
+      NullabilityArg: // Next 4 pasted from
+                      // https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
+    return "Passing null as a function parameter which is annotated with "
+           "_Nonnull";
+
+  case SanitizerHandler::NullabilityReturn:
+    return "Returning null from a function with a return type annotated with "
+           "_Nonnull";
+
+  case SanitizerHandler::NonnullArg:
+    return "Passing null as a function parameter which is declared to never be "
+           "null";
+
+  case SanitizerHandler::NonnullReturn:
+    return "Returning null pointer from a function which is declared to never "
+           "be null";
+
+  case SanitizerHandler::OutOfBounds:
+    return "Out of bounds -- memory accessed outside of expected boundaries.";
+
+  case SanitizerHandler::PointerOverflow:
+    return "stub";
+
+  case SanitizerHandler::ShiftOutOfBounds:
+    return "Bit shift attempted to move bits beyond boundaries of data type's "
+           "bit size.";
+
+  case SanitizerHandler::SubOverflow:
+    return "The subtraction of two signed integers resulted in overflow.";
+
+  case SanitizerHandler::TypeMismatch:
+    return "Type mismatch -- value type used does not match type expected.";
+
+  case SanitizerHandler::AlignmentAssumption: // Help on bottom 2
+    return "stub";
+
+  case SanitizerHandler::VLABoundNotPositive:
+    return "stub";
+
+  default:
+    return "";
+  }
+}
+
 /// CreateTempAlloca - This creates a alloca and inserts it into the entry
 /// block.
 RawAddress
@@ -4041,7 +4133,8 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
 
 void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
                                     SanitizerHandler CheckHandlerID,
-                                    bool NoMerge) {
+                                    bool NoMerge, StringRef Annotation,
+                                    StringRef TrapMessage) {
   llvm::BasicBlock *Cont = createBasicBlock("cont");
 
   // If we're optimizing, collapse all calls to trap down to just one per
@@ -4051,6 +4144,14 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
 
   llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];
 
+  llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
+  llvm::StringRef Category = GetTrapMessageForHandler(CheckHandlerID);
+
+  if (getDebugInfo() && !Category.empty()) {
+    TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
+        TrapLocation, Category, TrapMessage);
+  }
+
   NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel ||
             (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
 
@@ -4059,8 +4160,16 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
     auto Call = TrapBB->begin();
     assert(isa<llvm::CallInst>(Call) && "Expected call in trap BB");
 
-    Call->applyMergedLocation(Call->getDebugLoc(),
-                              Builder.getCurrentDebugLocation());
+    // Call->applyMergedLocation(Call->getDebugLoc(),
+    //                           Builder.getCurrentDebugLocation());
+    Call->applyMergedLocation(Call->getDebugLoc(), TrapLocation);
+
+    auto Unreachable = ++TrapBB->begin();
+    if (isa<llvm::UnreachableInst>(Unreachable)) {
+      Unreachable->applyMergedLocation(Unreachable->getDebugLoc(),
+                                       TrapLocation);
+    }
+
     Builder.CreateCondBr(Checked, Cont, TrapBB,
                          MDHelper.createLikelyBranchWeights());
   } else {
@@ -4069,6 +4178,8 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
                          MDHelper.createLikelyBranchWeights());
     EmitBlock(TrapBB);
 
+    ApplyDebugLocation applyTrapDI(*this, TrapLocation);
+
     llvm::CallInst *TrapCall =
         Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::ubsantrap),
                            llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID));
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index a5ab9df01dba9..aa9c862511579 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5281,7 +5281,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 Annotation = "",
+                     StringRef TrapMessage = "");
 
   /// Emit a call to trap or debugtrap and attach function attribute
   /// "trap-func-name" if specified.
diff --git a/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c
new file mode 100644
index 0000000000000..822a1c003d16a
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c
@@ -0,0 +1,10 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=undefined \
+// RUN: -fsanitize-trap=undefined -emit-llvm -S -c %s -o - | FileCheck %s
+
+int add_overflow(int a, int b) {
+  return a + b;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
diff --git a/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c b/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c
new file mode 100644
index 0000000000000..ada4c372a92b7
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c
@@ -0,0 +1,12 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=unreachable \
+// RUN: -fsanitize-trap=unreachable -emit-llvm -S -c %s -o - | FileCheck %s
+
+int call_builtin_unreachable()
+{
+    __builtin_unreachable();
+}
+
+
+// CHECK: call void @llvm.ubsantrap(i8 1) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c
new file mode 100644
index 0000000000000..17b6dca16cc62
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c
@@ -0,0 +1,10 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=undefined \
+// RUN: -fsanitize-trap=undefined -emit-llvm -S -c %s -o - | FileCheck %s
+
+int div_rem_overflow(int a, int b) {
+    return a / b;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 3) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
diff --git a/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c
new file mode 100644
index 0000000000000..b761e638c5293
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O0 -debug-info-kind=standalone -dwarf-version=5 -fsanitize=float-cast-overflow \
+// RUN: -fsanitize-trap=float-cast-overflow -emit-llvm %s -o - | FileCheck %s
+
+int f(float x) { 
+  return (int)x; 
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 5) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c b/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c
new file mode 100644
index 0000000000000..25063b69a894c
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c
@@ -0,0 +1,16 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=undefined \
+// RUN: -fsanitize-trap=undefined -emit-llvm -S -c %s -o - | FileCheck %s
+
+void target() { }
+
+int function_type_mismatch() {
+    int (*fp_int)(int);
+
+    fp_int = (int (*)(int))(void *)target;
+
+    return fp_int(42);
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 6) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c b/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c
new file mode 100644
index 0000000000000..48eb86d09b51a
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c
@@ -0,0 +1,13 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=implicit-conversion \
+// RUN: -fsanitize-trap=implicit-conversion -emit-llvm -S -c %s -o - | FileCheck %s
+
+unsigned long long big; 
+
+unsigned implicit_conversion()
+{
+    return big;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 7) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c b/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c
new file mode 100644
index 0000000000000..cd136df2f0ed4
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c
@@ -0,0 +1,12 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=builtin \
+// RUN: -fsanitize-trap=builtin -emit-llvm -S -c %s -o - | FileCheck %s
+
+unsigned invalid_builtin(unsigned x)
+{
+    return __builtin_clz(x);
+}
+
+
+// CHECK: call void @llvm.ubsantrap(i8 8) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c b/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c
new file mode 100644
index 0000000000000..09d8d588f0a32
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c
@@ -0,0 +1,15 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=undefined \
+// RUN: -fsanitize-trap=undefined -emit-llvm -S -c %s -o - | FileCheck %s
+#include <stdbool.h> 
+
+unsigned char bad_byte;
+
+bool load_invalid_value()
+{
+    return *((bool *)&bad_byte);
+}
+
+
+// CHECK: call void @llvm.ubsantrap(i8 10) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp b/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp
new file mode 100644
index 0000000000000..7f0265abd2305
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=return \
+// RUN: -fsanitize-trap=return -emit-llvm -S -c %s -o - | FileCheck %s
+
+int missing_return(int x)
+{
+    if (x > 0)
+        return x;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 11) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c
new file mode 100644
index 0000000000000..e9f76b87455c9
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c
@@ -0,0 +1,10 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=undefined \
+// RUN: -fsanitize-trap=undefined -emit-llvm -S -c %s -o - | FileCheck %s
+
+int mul_overflow(int a, int b) {
+    return a * b;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 12) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
diff --git a/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c
new file mode 100644
index 0000000000000..5660c6bb08d03
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c
@@ -0,0 +1,12 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=undefined \
+// RUN: -fsanitize-trap=undefined -emit-llvm -S -c %s -o - | FileCheck %s
+
+int negate_overflow()
+{
+    int x;
+    return -x;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 13) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c b/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c
new file mode 100644
index 0000000000000..e648f91b86b27
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c
@@ -0,0 +1,17 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=nonnull-attribute \
+// RUN: -fsanitize-trap=nonnull-attribute -emit-llvm -S -c %s -o - | FileCheck %s
+
+__attribute__((nonnull))
+void nonnull_arg(int *p) { 
+    (void)p; 
+}
+
+void trigger_nonnull_arg()
+{
+    nonnull_arg(0);
+}
+
+
+// CHECK: call void @llvm.ubsantrap(i8 16) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c b/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c
new file mode 100644
index 0000000000000..9b14004d96a02
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c
@@ -0,0 +1,15 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=returns-nonnull-attribute \
+// RUN: -fsanitize-trap=returns-nonnull-attribute -emit-llvm -S -c %s -o - | FileCheck %s
+
+__attribute__((returns_nonnull))
+int* must_return_nonnull(int bad)
+{
+    if (bad)
+        return 0;
+    static int x = 1;
+    return &x;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 17) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c b/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c
new file mode 100644
index 0000000000000..e0d6a79b27c02
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c
@@ -0,0 +1,19 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=nullability-arg \
+// RUN: -fsanitize-trap=nullability-arg -emit-llvm -S -c %s -o - | FileCheck %s
+
+#include <stddef.h>
+
+int nullability_arg(int* _Nonnull p)
+{
+    return *p;
+}
+
+int trigger_nullability_arg()
+{
+    return nullability_arg(NULL);
+}
+
+
+// CHECK: call void @llvm.ubsantrap(i8 14) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c b/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c
new file mode 100644
index 0000000000000..e10fc5b225221
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c
@@ -0,0 +1,19 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=nullability-return \
+// RUN: -fsanitize-trap=nullability-return -emit-llvm -S -c %s -o - | FileCheck %s
+
+#include <stdbool.h>
+#include <stddef.h>
+
+int* _Nonnull nullability_return(bool fail)
+{
+    if (fail)
+        return NULL;
+
+    static int x = 0;
+    return &x;
+}
+
+
+// CHECK: call void @llvm.ubsantrap(i8 15) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c b/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c
new file mode 100644
index 0000000000000..9ed093fc528ed
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c
@@ -0,0 +1,12 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=bounds \
+// RUN: -fsanitize-trap=bounds -emit-llvm -S -c %s -o - | FileCheck %s
+
+int out_of_bounds()
+{
+    int a[1] = {0};
+    return a[1];
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 18) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c
new file mode 100644
index 0000000000000..93ae42208520f
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c
@@ -0,0 +1,16 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=pointer-overflow \
+// RUN: -fsanitize-trap=pointer-overflow -emit-llvm -S -c %s -o - | FileCheck %s
+
+#include <stddef.h>
+#include <stdint.h>
+
+int* pointer_overflow(void)
+{
+    int buf[4];
+    volatile size_t n = (SIZE_MAX / sizeof(int)) - 1;
+    return buf + n;
+}
+
+// CHECK: call void @llvm.ubsantrap(i8 19) {{.*}}!dbg [[LOC:![0-9]+]]
+// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
+// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
\ No newline at end of file
diff --git a/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c b/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c
new file mode 100644
index 0000000000000..6f5aff7cd197a
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c
@@ -0,0 +1,12 @@
+// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=shift \
+// RUN: -fsanitize-trap=shift -emi...
[truncated]

@anthonyhatran
Copy link
Contributor Author

Forgot to mention missing test cases:
SanitizerHandler::CFICheckFail (was able to get this working with -fsanitize=cfi-cast-strict on my build, but cfi-cast-strict couldn't be identified in the testing dir)
SanitizerHandler::AlignmentAssumption
SanitizerHandler::VLABoundNotPositive
SanitizerHandler::InvalidObjCCast

@@ -4041,7 +4133,8 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {

void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
SanitizerHandler CheckHandlerID,
bool NoMerge) {
bool NoMerge, StringRef Annotation,
StringRef TrapMessage) {
Copy link
Member

@Michael137 Michael137 Jun 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrapMessage parameter seems to not get passed from anywhere?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like something from the Swift fork of clang. We should remove it from this PR because it isn't needed for this PR.


// CHECK: call void @llvm.ubsantrap(i8 6) {{.*}}!dbg [[LOC:![0-9]+]]
// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets check that the "category" part of the fake frame name is correct too

@anthonyhatran anthonyhatran force-pushed the gsoc-ubsan-trap-reasons branch from 748cafd to e40a027 Compare June 23, 2025 10:19
Copy link
Contributor

@delcypher delcypher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. We're off to a good start. Once we have your test cases in a good shape we should look at the failing tests to figure out why they broke so we can fix them or adjust your implementation.

@@ -85,6 +85,92 @@ enum VariableTypeDescriptorKind : uint16_t {
// Miscellaneous Helper Methods
//===--------------------------------------------------------------------===//

static llvm::StringRef GetTrapMessageForHandler(SanitizerHandler ID) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably rename this function to mention UBSan in its name given that the current intention is to only handle UBSan traps. E.g. GetUBSanTrapForHandler(SanitizerHandler ID)

return "Alignment assumption violated";

case SanitizerHandler::VLABoundNotPositive:
return "Variable-length array bound is not positive";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we explicitly add

case SanitizerHandler::BoundsSafety:
  // -fbounds-safety traps are not UBSan traps
  return {}; 

So that no one is later tempted to try and add a case at a later date (they might think it's omission is a mistake).

?

@@ -4051,6 +4137,15 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,

llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];

llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
llvm::StringRef Category = "UBSan Trap Reason";
Copy link
Contributor

@delcypher delcypher Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that Category is only used in one place I would be tempted to inline its use. I.e.

TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
        TrapLocation,  "UBSan", TrapMessage);

I realize that might conflict more with the code in the Swift fork of Clang but I'm ok with this. Once we've handled the merge conflict we'll have a better sense of what follow up changes are needed to best resolve the conflict.

Nit: We may want to rename UBSan Trap Reason. I'm not sure it's necessary to mention Trap or Reason. I think UBSan or Undefined Behavior Sanitizer (if we wanted to be really verbose) would be sufficient.

@@ -4059,8 +4154,16 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
auto Call = TrapBB->begin();
assert(isa<llvm::CallInst>(Call) && "Expected call in trap BB");

Call->applyMergedLocation(Call->getDebugLoc(),
Builder.getCurrentDebugLocation());
// Call->applyMergedLocation(Call->getDebugLoc(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The commented out code needs to be removed.

// Builder.getCurrentDebugLocation());
Call->applyMergedLocation(Call->getDebugLoc(), TrapLocation);

auto Unreachable = ++TrapBB->begin();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusting the debug info on the unreachable instructions is a bug fix in the Apple fork of Clang that I never got around to upstreaming. Is this something you needed for writing tests?

If this isn't strictly needed I'd suggest dropping this and you can add it as a separate patch.


// CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]]
// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}})
// CHECK: distinct !DISubprogram(name: "__clang_trap_msg$UBSan Trap Reason
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests aren't matching the DISubprogram correctly. They are matching the category but they aren't matching the trap message. For example this test should be trying to match the Signed integer addition overflowed trap message.

static llvm::StringRef GetTrapMessageForHandler(SanitizerHandler ID) {
switch (ID) {
case SanitizerHandler::AddOverflow:
return "Signed integer addition overflowed.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A thought I just had about these. We should probably try running our test cases without -fsanitize-trap=undefined to see how the non-trapping UBSan runtime describes them. We might want to make the messages here match the wording that's currently used when possible. It won't always because the runtime will have runtime information available that we don't have here.

return "Signed integer addition overflowed.";

case SanitizerHandler::BuiltinUnreachable:
return "_builtin_unreachable() executed.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit. We probably should omit the . at the end of the strings. Clang diagnostics tend to do this so we should probably follow think convention unless we have a good reason to not to do this. I realize these aren't the same thing as clang diagnostics so maybe the best thing to do is see how these render in LLDB and decide if they should end with a . and then do this consistently.

@delcypher
Copy link
Contributor

@anthonyhatran Here's an example for SanitizerHandler::AlignmentAssumption

#include <stdint.h>
int32_t* get_int(void) __attribute__((assume_aligned(16)));

void retrieve_int(void) {
    int* i = get_int();
    *i = 7;
}

@delcypher
Copy link
Contributor

@anthonyhatran Here's an example for SanitizerHandler::InvalidObjCCast

// Build with -fsanitize=objc-cast -fsanitize-trap=objc-cast
@interface NSFastEnumerationState
@end

#define NSUInteger unsigned int

@interface NSArray
+(NSArray*) arrayWithObjects: (id) first, ...;
- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *) state 
                                   objects:(id[]) buffer 
                                     count:(NSUInteger) len;
-(unsigned) count;
@end
@interface NSString
-(const char*) cString;
@end

void receive_NSString(NSString*);

void t0(void) {
  NSArray *array = [NSArray arrayWithObjects: @"0", @"1", (void*)0];
  for (NSString *i in array) {
    receive_NSString(i);
  }
}

@@ -0,0 +1,10 @@
// RUN: %clang -O0 -g -debug-info-kind=standalone -dwarf-version=5 -fsanitize=undefined \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anthonyhatran We should probably use %clang_cc1 instead as a lot of codegen tests do that. That by-passes the clang driver (which is responsible for presenting a GCC compatible interface).

We should also hardcode a target triple here so that we get deterministic code generation. Otherwise your test will use the host to determine the target which might cause the tests to fail when running on other host platforms.

E.g.: %clang_cc1 -triple arm64-apple-macos

slinder1 and others added 15 commits June 26, 2025 12:46
Try to give a more complete description of what we call a "fragment",
and how debug records interact when fragments are involved.
Use the Twine version instead of manually building a string
We don't need const on these return types.
Note that llvm::interleaved constructs a string with the elements from
a given range with a given separator.
Fold (vp.splice (insert_elt poison, scalar, 0), vec, 0, mask, 1, vl)
to (vslide1up vec, scalar, mask, vl).

Fold (vp.splice (splat_vector scalar), vec, 0, mask, 1, vl)
to (vslide1up vec, scalar, mask, vl).
Similar to the existing implementations for X86 and PPC, support
symbolizing branch targets for AArch64. Do not omit the address for ADRP
as the target is typically not at an intended location.

Pull Request: llvm#145009
…5708)

The oldest supported version is now 3.1. In terms of semantic analysis
the compiler treats all versions <= 4.5 identically, and there is no
plan to add version-specific checks for older versions.

See discourse thread:

https://discourse.llvm.org/t/rfc-remove-openmp-versions-prior-to-3-1/86901
As far as I know binutils does not have a similar option and I don't
know of a reason we shouldn't accept the RVC hint instructions.

The wording in the spec in the past suggested that maybe these
weren't valid instruction names, but that's been modified recently.
…139583)

Update asan build configuration for AIX:
- Adds import lists
- Guards shared library code

Issue: llvm#138916

---------

Co-authored-by: Hubert Tong <hubert.reinterpretcast@gmail.com>
@anthonyhatran
Copy link
Contributor Author

Apologies for the ping everyone, I made a mistake and requested a mass review. You can ignore the request for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.