[clang] Add support for builtin_verbose_trap (PR #79230)

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 1 09:33:04 PDT 2024


https://github.com/ahatanak updated https://github.com/llvm/llvm-project/pull/79230

>From 44813fefa59b442abcc6cb23c2ac8d3f78e44efc Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Tue, 16 Jan 2024 13:18:09 -0800
Subject: [PATCH 01/11] Add support for builtin_verbose_trap

The builtin causes the program to stop its execution abnormally and shows a
human-readable description of the reason for the termination when a debugger is
attached or in a symbolicated crash log.

The motivation for the builtin is explained in the following RFC:

https://discourse.llvm.org/t/rfc-adding-builtin-verbose-trap-string-literal/75845
---
 clang/docs/LanguageExtensions.rst             | 48 ++++++++++++++++++
 clang/include/clang/AST/Expr.h                |  5 ++
 clang/include/clang/Basic/Builtins.td         |  6 +++
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/lib/AST/ExprConstant.cpp                | 18 +++++--
 clang/lib/CodeGen/CGBuiltin.cpp               | 12 +++++
 clang/lib/CodeGen/CGDebugInfo.cpp             | 42 ++++++++++++++++
 clang/lib/CodeGen/CGDebugInfo.h               | 20 ++++++++
 clang/lib/Sema/SemaChecking.cpp               | 22 +++++++++
 .../CodeGenCXX/debug-info-verbose-trap.cpp    | 49 +++++++++++++++++++
 clang/test/SemaCXX/verbose-trap.cpp           | 28 +++++++++++
 11 files changed, 249 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
 create mode 100644 clang/test/SemaCXX/verbose-trap.cpp

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 7b23e4d1c2f30c..4e4151e7803875 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3464,6 +3464,54 @@ Query for this feature with ``__has_builtin(__builtin_trap)``.
 
 ``__builtin_arm_trap`` is lowered to the ``llvm.aarch64.break`` builtin, and then to ``brk #payload``.
 
+``__builtin_verbose_trap``
+--------------------------
+
+``__builtin_verbose_trap`` causes the program to stop its execution abnormally
+and shows a human-readable description of the reason for the termination when a
+debugger is attached or in a symbolicated crash log.
+
+**Syntax**:
+
+.. code-block:: c++
+
+    __builtin_verbose_trap(const char *reason)
+
+**Description**
+
+``__builtin_verbose_trap`` is lowered to the ` ``llvm.trap`` <https://llvm.org/docs/LangRef.html#llvm-trap-intrinsic>`_ builtin.
+Additionally, clang emits debug metadata that represents an artificial inline
+frame whose name encodes the string passed to the builtin, prefixed by a "magic"
+prefix.
+
+For example, consider the following code:
+
+.. code-block:: c++
+
+    void foo(int* p) {
+      if (p == nullptr)
+        __builtin_verbose_trap("Argument_must_not_be_null");
+    }
+
+The debug metadata would look as if it were produced for the following code:
+
+.. code-block:: c++
+
+    __attribute__((always_inline))
+    inline void "__llvm_verbose_trap: Argument_must_not_be_null"() {
+      __builtin_trap();
+    }
+
+    void foo(int* p) {
+      if (p == nullptr)
+        "__llvm_verbose_trap: Argument_must_not_be_null"();
+    }
+
+However, the LLVM IR would not actually contain a call to the artificial
+function — it only exists in the debug metadata.
+
+Query for this feature with ``__has_builtin(__builtin_verbose_trap)``.
+
 ``__builtin_nondeterministic_value``
 ------------------------------------
 
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 6e153ebe024b42..789bedbd9724a6 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -787,6 +787,11 @@ class Expr : public ValueStmt {
                                  const Expr *PtrExpression, ASTContext &Ctx,
                                  EvalResult &Status) const;
 
+  /// If the current Expr can be evaluated to a pointer to a null-terminated
+  /// constant string, return the constant string (without the terminating null)
+  /// in Result. Return true if it succeeds.
+  bool tryEvaluateString(std::string &Result, ASTContext &Ctx) const;
+
   /// Enumeration used to describe the kind of Null pointer constant
   /// returned from \c isNullPointerConstant().
   enum NullPointerConstantKind {
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index f421223ff087de..df7b0460c1f21d 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1152,6 +1152,12 @@ def Trap : Builtin {
   let Prototype = "void()";
 }
 
+def VerboseTrap : Builtin {
+  let Spellings = ["__builtin_verbose_trap"];
+  let Attributes = [NoThrow, NoReturn];
+  let Prototype = "void(char const*)";
+}
+
 def Debugtrap : Builtin {
   let Spellings = ["__builtin_debugtrap"];
   let Attributes = [NoThrow];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index df57f5e6ce11ba..03f52c88ea1f1f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8863,6 +8863,8 @@ def err_expected_callable_argument : Error<
   "expected a callable expression as %ordinal0 argument to %1, found %2">;
 def note_building_builtin_dump_struct_call : Note<
   "in call to printing function with arguments '(%0)' while dumping struct">;
+def err_builtin_verbose_trap_arg : Error<
+  "argument to __builtin_verbose_trap must be a non-empty string literal">;
 
 def err_atomic_load_store_uses_lib : Error<
   "atomic %select{load|store}0 requires runtime support that is not "
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index dae8f32fc02951..127c0415f8feb8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1884,7 +1884,8 @@ static bool EvaluateAtomic(const Expr *E, const LValue *This, APValue &Result,
                            EvalInfo &Info);
 static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result);
 static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
-                                  EvalInfo &Info);
+                                  EvalInfo &Info,
+                                  std::string *StringResult = nullptr);
 
 /// Evaluate an integer or fixed point expression into an APResult.
 static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result,
@@ -16789,7 +16790,7 @@ bool Expr::tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx,
 }
 
 static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
-                                  EvalInfo &Info) {
+                                  EvalInfo &Info, std::string *StringResult) {
   if (!E->getType()->hasPointerRepresentation() || !E->isPRValue())
     return false;
 
@@ -16816,6 +16817,8 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
         Str = Str.substr(0, Pos);
 
       Result = Str.size();
+      if (StringResult)
+        *StringResult = Str;
       return true;
     }
 
@@ -16831,12 +16834,21 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
     if (!Char.getInt()) {
       Result = Strlen;
       return true;
-    }
+    } else if (StringResult)
+      StringResult->push_back(Char.getInt().getExtValue());
     if (!HandleLValueArrayAdjustment(Info, E, String, CharTy, 1))
       return false;
   }
 }
 
+bool Expr::tryEvaluateString(std::string &StringResult, ASTContext &Ctx) const {
+  Expr::EvalStatus Status;
+  EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
+  uint64_t Result;
+
+  return EvaluateBuiltinStrLen(this, Result, Info, &StringResult);
+}
+
 bool Expr::EvaluateCharRangeAsString(std::string &Result,
                                      const Expr *SizeExpression,
                                      const Expr *PtrExpression, ASTContext &Ctx,
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index bb007231c0b783..b9307331fc56c8 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3574,6 +3574,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   case Builtin::BI__builtin_trap:
     EmitTrapCall(Intrinsic::trap);
     return RValue::get(nullptr);
+  case Builtin::BI__builtin_verbose_trap: {
+    llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
+    if (getDebugInfo()) {
+      std::string Str;
+      E->getArg(0)->tryEvaluateString(Str, getContext());
+      TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
+          TrapLocation, "__llvm_verbose_trap", Str);
+    }
+    ApplyDebugLocation ApplyTrapDI(*this, TrapLocation);
+    EmitTrapCall(Intrinsic::trap);
+    return RValue::get(nullptr);
+  }
   case Builtin::BI__debugbreak:
     EmitTrapCall(Intrinsic::debugtrap);
     return RValue::get(nullptr);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 691fde8b0d8b82..81bb2854b80d3b 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1631,6 +1631,27 @@ llvm::DIType *CGDebugInfo::createFieldType(
                                    offsetInBits, flags, debugType, Annotations);
 }
 
+llvm::DISubprogram *
+CGDebugInfo::getFakeFuncSubprogram(const std::string &FakeFuncName) {
+  llvm::DISubprogram *&SP = FakeFuncMap[FakeFuncName];
+
+  if (!SP) {
+    auto FileScope = TheCU->getFile();
+    llvm::DISubroutineType *DIFnTy = DBuilder.createSubroutineType(nullptr);
+    // Note: We use `FileScope` rather than `TheCU` as the scope because that's
+    // what LLVM's inliner seems to do.
+    SP = DBuilder.createFunction(
+        /*Scope=*/FileScope, /*Name=*/FakeFuncName, /*LinkageName=*/StringRef(),
+        /*File=*/FileScope, /*LineNo=*/0, /*Ty=*/DIFnTy,
+        /*ScopeLine=*/0,
+        /*Flags=*/llvm::DINode::FlagArtificial,
+        /*SPFlags=*/llvm::DISubprogram::SPFlagDefinition,
+        /*TParams=*/nullptr, /*ThrownTypes=*/nullptr, /*Annotations=*/nullptr);
+  }
+
+  return SP;
+}
+
 void CGDebugInfo::CollectRecordLambdaFields(
     const CXXRecordDecl *CXXDecl, SmallVectorImpl<llvm::Metadata *> &elements,
     llvm::DIType *RecordTy) {
@@ -3427,6 +3448,27 @@ llvm::DIMacroFile *CGDebugInfo::CreateTempMacroFile(llvm::DIMacroFile *Parent,
   return DBuilder.createTempMacroFile(Parent, Line, FName);
 }
 
+llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
+    llvm::DebugLoc TrapLocation, StringRef Prefix, StringRef FailureMsg) {
+  // Create debug info that describes a fake function whose name is the failure
+  // message.
+  std::string FuncName(Prefix);
+  if (!FailureMsg.empty()) {
+    // A space in the function name identifies this as not being a real function
+    // because it's not a valid symbol name.
+    FuncName += ": ";
+    FuncName += FailureMsg;
+  }
+
+  assert(FuncName.size() > 0);
+  assert(FuncName.find(' ') != std::string::npos &&
+         "Fake function name must contain a space");
+
+  llvm::DISubprogram *TrapSP = getFakeFuncSubprogram(FuncName);
+  return llvm::DILocation::get(CGM.getLLVMContext(), /*Line=*/0, /*Column=*/0,
+                               /*Scope=*/TrapSP, /*InlinedAt=*/TrapLocation);
+}
+
 static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
   Qualifiers Quals;
   do {
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 7b60e94555d060..21edb553674260 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -346,6 +346,14 @@ class CGDebugInfo {
       const FieldDecl *BitFieldDecl, const llvm::DIDerivedType *BitFieldDI,
       llvm::ArrayRef<llvm::Metadata *> PreviousFieldsDI, const RecordDecl *RD);
 
+  // A cache that maps fake function names used for __builtin_verbose_trap to
+  // subprograms.
+  std::map<std::string, llvm::DISubprogram *> FakeFuncMap;
+
+  // A function that returns the subprogram corresponding to the fake function
+  // name.
+  llvm::DISubprogram *getFakeFuncSubprogram(const std::string &FakeFuncName);
+
   /// Helpers for collecting fields of a record.
   /// @{
   void CollectRecordLambdaFields(const CXXRecordDecl *CXXDecl,
@@ -602,6 +610,18 @@ class CGDebugInfo {
     return CoroutineParameterMappings;
   }
 
+  // Create a debug location from `TrapLocation` that adds a fake
+  // inline frame where the frame name is
+  //
+  // * `<Prefix>: <FailureMsg>` if `<FailureMsg>` is not empty.
+  // * `<Prefix>` if `<FailureMsg>` is empty. Note `<Prefix>` must
+  //   contain a space.
+  //
+  // This is used to store failure reasons for traps.
+  llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
+                                                StringRef Prefix,
+                                                StringRef FailureMsg);
+
 private:
   /// Emit call to llvm.dbg.declare for a variable declaration.
   /// Returns a pointer to the DILocalVariable associated with the
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 11401b6f56c0ea..32e4c5e0f19473 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -171,6 +171,23 @@ static bool checkArgCount(Sema &S, CallExpr *Call, unsigned DesiredArgCount) {
          << /*is non object*/ 0 << Call->getArg(1)->getSourceRange();
 }
 
+static bool checkBuiltinVerboseTrap(CallExpr *Call, Sema &S) {
+  Expr *Arg = Call->getArg(0);
+
+  if (Arg->isValueDependent())
+    return true;
+
+  // FIXME: Add more checks and reject strings that can't be handled by
+  // debuggers.
+  std::string Result;
+  if (!Arg->tryEvaluateString(Result, S.Context) || Result.empty()) {
+    S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg)
+        << Arg->getSourceRange();
+    return false;
+  }
+  return true;
+}
+
 static bool convertArgumentToType(Sema &S, Expr *&Value, QualType Ty) {
   if (Value->isTypeDependent())
     return false;
@@ -3205,6 +3222,11 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
   case Builtin::BI__builtin_matrix_column_major_store:
     return SemaBuiltinMatrixColumnMajorStore(TheCall, TheCallResult);
 
+  case Builtin::BI__builtin_verbose_trap:
+    if (!checkBuiltinVerboseTrap(TheCall, *this))
+      return ExprError();
+    break;
+
   case Builtin::BI__builtin_get_device_side_mangled_name: {
     auto Check = [](CallExpr *TheCall) {
       if (TheCall->getNumArgs() != 1)
diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
new file mode 100644
index 00000000000000..04fa68ae93ecb6
--- /dev/null
+++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -std=c++20 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s
+
+// CHECK-LABEL: define void @_Z2f0v()
+// CHECK: call void @llvm.trap(), !dbg ![[LOC17:.*]]
+
+// CHECK-LABEL: define void @_Z2f1v()
+// CHECK: call void @llvm.trap(), !dbg ![[LOC23:.*]]
+// CHECK: call void @llvm.trap(), !dbg ![[LOC25:.*]]
+
+// CHECK-LABEL: define void @_Z2f3v()
+// CHECK: call void @_Z2f2IXadsoKcL_ZL8constMsgEEEEvv()
+
+// CHECK-LABEL: define internal void @_Z2f2IXadsoKcL_ZL8constMsgEEEEvv()
+// CHECK: call void @llvm.trap(), !dbg ![[LOC36:.*]]
+
+// CHECK: ![[FILESCOPE:.*]] = !DIFile(filename:
+// CHECK: ![[SUBPROG14:.*]] = distinct !DISubprogram(name: "f0", linkageName: "_Z2f0v",
+// CHECK: ![[LOC17]] = !DILocation(line: 0, scope: ![[SUBPROG18:.*]], inlinedAt: ![[LOC20:.*]])
+// CHECK: ![[SUBPROG18]] = distinct !DISubprogram(name: "__llvm_verbose_trap: Argument_must_not_be_null", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}})
+// CHECK: ![[LOC20]] = !DILocation(line: 34, column: 3, scope: ![[SUBPROG14]])
+// CHECK: ![[SUBPROG22:.*]] = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v",
+// CHECK: ![[LOC23]] = !DILocation(line: 0, scope: ![[SUBPROG18]], inlinedAt: ![[LOC24:.*]])
+// CHECK: ![[LOC24]] = !DILocation(line: 38, column: 3, scope: ![[SUBPROG22]])
+// CHECK: ![[LOC25]] = !DILocation(line: 0, scope: ![[SUBPROG26:.*]], inlinedAt: ![[LOC27:.*]])
+// CHECK: ![[SUBPROG26]] = distinct !DISubprogram(name: "__llvm_verbose_trap: hello", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}})
+// CHECK: ![[LOC27]] = !DILocation(line: 39, column: 3, scope: ![[SUBPROG22]])
+// CHECK: ![[SUBPROG32:.*]] = distinct !DISubprogram(name: "f2<&constMsg[0]>", linkageName: "_Z2f2IXadsoKcL_ZL8constMsgEEEEvv",
+// CHECK: ![[LOC36]] = !DILocation(line: 0, scope: ![[SUBPROG26]], inlinedAt: ![[LOC37:.*]])
+// CHECK: ![[LOC37]] = !DILocation(line: 44, column: 3, scope: ![[SUBPROG32]])
+
+char const constMsg[] = "hello";
+
+void f0() {
+  __builtin_verbose_trap("Argument_must_not_be_null");
+}
+
+void f1() {
+  __builtin_verbose_trap("Argument_must_not_be_null");
+  __builtin_verbose_trap("hello");
+}
+
+template <const char * const str>
+void f2() {
+  __builtin_verbose_trap(str);
+}
+
+void f3() {
+  f2<constMsg>();
+}
diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp
new file mode 100644
index 00000000000000..7c152325d30815
--- /dev/null
+++ b/clang/test/SemaCXX/verbose-trap.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify %s
+
+#if !__has_builtin(__builtin_verbose_trap)
+#error
+#endif
+
+constexpr char const* constMsg1 = "hello";
+char const* const constMsg2 = "hello";
+char const constMsg3[] = "hello";
+
+template <const char * const str>
+void f(const char * arg) {
+  __builtin_verbose_trap("Argument_must_not_be_null");
+  __builtin_verbose_trap("hello" "world");
+  __builtin_verbose_trap(constMsg1);
+  __builtin_verbose_trap(constMsg2);
+  __builtin_verbose_trap(""); // expected-error {{argument to __builtin_verbose_trap must be a non-empty string literal}}
+  __builtin_verbose_trap(); // expected-error {{too few arguments}}
+  __builtin_verbose_trap(0); // expected-error {{argument to __builtin_verbose_trap must be a non-empty string literal}}
+  __builtin_verbose_trap(1); // expected-error {{cannot initialize a parameter of type 'const char *' with}}
+  __builtin_verbose_trap(arg); // expected-error {{argument to __builtin_verbose_trap must be a non-empty string literal}}
+  __builtin_verbose_trap(str);
+  __builtin_verbose_trap(u8"hello");
+}
+
+void test() {
+  f<constMsg3>(nullptr);
+}

>From c69b207ca52ac6524b6abb9c05770cb8e4cddbf8 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Tue, 23 Jan 2024 16:13:14 -0800
Subject: [PATCH 02/11] Fix diagnostic message

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 ++-
 clang/test/SemaCXX/verbose-trap.cpp              | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 03f52c88ea1f1f..1cd1813f18d85f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8864,7 +8864,8 @@ def err_expected_callable_argument : Error<
 def note_building_builtin_dump_struct_call : Note<
   "in call to printing function with arguments '(%0)' while dumping struct">;
 def err_builtin_verbose_trap_arg : Error<
-  "argument to __builtin_verbose_trap must be a non-empty string literal">;
+  "argument to __builtin_verbose_trap must be a pointer to a non-empty constant "
+  "string">;
 
 def err_atomic_load_store_uses_lib : Error<
   "atomic %select{load|store}0 requires runtime support that is not "
diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp
index 7c152325d30815..2cbfad6c817f82 100644
--- a/clang/test/SemaCXX/verbose-trap.cpp
+++ b/clang/test/SemaCXX/verbose-trap.cpp
@@ -14,11 +14,11 @@ void f(const char * arg) {
   __builtin_verbose_trap("hello" "world");
   __builtin_verbose_trap(constMsg1);
   __builtin_verbose_trap(constMsg2);
-  __builtin_verbose_trap(""); // expected-error {{argument to __builtin_verbose_trap must be a non-empty string literal}}
+  __builtin_verbose_trap(""); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a non-empty constant string}}
   __builtin_verbose_trap(); // expected-error {{too few arguments}}
-  __builtin_verbose_trap(0); // expected-error {{argument to __builtin_verbose_trap must be a non-empty string literal}}
+  __builtin_verbose_trap(0); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a non-empty constant string}}
   __builtin_verbose_trap(1); // expected-error {{cannot initialize a parameter of type 'const char *' with}}
-  __builtin_verbose_trap(arg); // expected-error {{argument to __builtin_verbose_trap must be a non-empty string literal}}
+  __builtin_verbose_trap(arg); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a non-empty constant string}}
   __builtin_verbose_trap(str);
   __builtin_verbose_trap(u8"hello");
 }

>From 96f853b84ff524a0930efefe2c535d1a288ce1ea Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Thu, 25 Jan 2024 14:15:16 -0800
Subject: [PATCH 03/11] Address review comments

---
 clang/docs/LanguageExtensions.rst   | 19 +++++++++++--------
 clang/lib/CodeGen/CGDebugInfo.cpp   | 19 +++++++++----------
 clang/lib/CodeGen/CGDebugInfo.h     | 16 ++++++++--------
 clang/test/SemaCXX/verbose-trap.cpp |  1 +
 4 files changed, 29 insertions(+), 26 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 4e4151e7803875..25d5f15e547368 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3480,9 +3480,9 @@ debugger is attached or in a symbolicated crash log.
 **Description**
 
 ``__builtin_verbose_trap`` is lowered to the ` ``llvm.trap`` <https://llvm.org/docs/LangRef.html#llvm-trap-intrinsic>`_ builtin.
-Additionally, clang emits debug metadata that represents an artificial inline
-frame whose name encodes the string passed to the builtin, prefixed by a "magic"
-prefix.
+Additionally, clang emits debugging information that represents an artificial
+inline frame whose name encodes the string passed to the builtin, prefixed by a
+"magic" prefix.
 
 For example, consider the following code:
 
@@ -3490,27 +3490,30 @@ For example, consider the following code:
 
     void foo(int* p) {
       if (p == nullptr)
-        __builtin_verbose_trap("Argument_must_not_be_null");
+        __builtin_verbose_trap("Argument must not be null!");
     }
 
-The debug metadata would look as if it were produced for the following code:
+The debugging information would look as if it were produced for the following code:
 
 .. code-block:: c++
 
     __attribute__((always_inline))
-    inline void "__llvm_verbose_trap: Argument_must_not_be_null"() {
+    inline void "__llvm_verbose_trap: Argument must not be null!"() {
       __builtin_trap();
     }
 
     void foo(int* p) {
       if (p == nullptr)
-        "__llvm_verbose_trap: Argument_must_not_be_null"();
+        "__llvm_verbose_trap: Argument must not be null!"();
     }
 
 However, the LLVM IR would not actually contain a call to the artificial
 function — it only exists in the debug metadata.
 
-Query for this feature with ``__has_builtin(__builtin_verbose_trap)``.
+Query for this feature with ``__has_builtin(__builtin_verbose_trap)``. Note that
+users need to enable debug information to enable this feature. A call to this
+builtin is equivalent to a call to ``__builtin_trap`` if debug information isn't
+enabled.
 
 ``__builtin_nondeterministic_value``
 ------------------------------------
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 81bb2854b80d3b..3fe91211d69795 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1632,8 +1632,8 @@ llvm::DIType *CGDebugInfo::createFieldType(
 }
 
 llvm::DISubprogram *
-CGDebugInfo::getFakeFuncSubprogram(const std::string &FakeFuncName) {
-  llvm::DISubprogram *&SP = FakeFuncMap[FakeFuncName];
+CGDebugInfo::createInlinedTrapSubprogram(const std::string &FuncName) {
+  llvm::DISubprogram *&SP = InlinedTrapFuncMap[FuncName];
 
   if (!SP) {
     auto FileScope = TheCU->getFile();
@@ -1641,7 +1641,7 @@ CGDebugInfo::getFakeFuncSubprogram(const std::string &FakeFuncName) {
     // Note: We use `FileScope` rather than `TheCU` as the scope because that's
     // what LLVM's inliner seems to do.
     SP = DBuilder.createFunction(
-        /*Scope=*/FileScope, /*Name=*/FakeFuncName, /*LinkageName=*/StringRef(),
+        /*Scope=*/FileScope, /*Name=*/FuncName, /*LinkageName=*/StringRef(),
         /*File=*/FileScope, /*LineNo=*/0, /*Ty=*/DIFnTy,
         /*ScopeLine=*/0,
         /*Flags=*/llvm::DINode::FlagArtificial,
@@ -3450,8 +3450,11 @@ llvm::DIMacroFile *CGDebugInfo::CreateTempMacroFile(llvm::DIMacroFile *Parent,
 
 llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
     llvm::DebugLoc TrapLocation, StringRef Prefix, StringRef FailureMsg) {
-  // Create debug info that describes a fake function whose name is the failure
-  // message.
+  assert((!FailureMsg.empty() || Prefix.find(' ') != std::string::npos) &&
+         "Prefix must contain a space when FailureMsg is empty");
+
+  // Create debug info that describes an inlined function whose name is the
+  // failure message.
   std::string FuncName(Prefix);
   if (!FailureMsg.empty()) {
     // A space in the function name identifies this as not being a real function
@@ -3460,11 +3463,7 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
     FuncName += FailureMsg;
   }
 
-  assert(FuncName.size() > 0);
-  assert(FuncName.find(' ') != std::string::npos &&
-         "Fake function name must contain a space");
-
-  llvm::DISubprogram *TrapSP = getFakeFuncSubprogram(FuncName);
+  llvm::DISubprogram *TrapSP = createInlinedTrapSubprogram(FuncName);
   return llvm::DILocation::get(CGM.getLLVMContext(), /*Line=*/0, /*Column=*/0,
                                /*Scope=*/TrapSP, /*InlinedAt=*/TrapLocation);
 }
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 21edb553674260..ff559153291836 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -346,13 +346,13 @@ class CGDebugInfo {
       const FieldDecl *BitFieldDecl, const llvm::DIDerivedType *BitFieldDI,
       llvm::ArrayRef<llvm::Metadata *> PreviousFieldsDI, const RecordDecl *RD);
 
-  // A cache that maps fake function names used for __builtin_verbose_trap to
-  // subprograms.
-  std::map<std::string, llvm::DISubprogram *> FakeFuncMap;
+  // A cache that maps artificial inlined function names used for
+  // __builtin_verbose_trap to subprograms.
+  std::map<std::string, llvm::DISubprogram *> InlinedTrapFuncMap;
 
-  // A function that returns the subprogram corresponding to the fake function
-  // name.
-  llvm::DISubprogram *getFakeFuncSubprogram(const std::string &FakeFuncName);
+  // A function that returns the subprogram corresponding to the artificial
+  // inlined function for traps.
+  llvm::DISubprogram *createInlinedTrapSubprogram(const std::string &FuncName);
 
   /// Helpers for collecting fields of a record.
   /// @{
@@ -610,8 +610,8 @@ class CGDebugInfo {
     return CoroutineParameterMappings;
   }
 
-  // Create a debug location from `TrapLocation` that adds a fake
-  // inline frame where the frame name is
+  // Create a debug location from `TrapLocation` that adds an artificial inline
+  // frame where the frame name is
   //
   // * `<Prefix>: <FailureMsg>` if `<FailureMsg>` is not empty.
   // * `<Prefix>` if `<FailureMsg>` is empty. Note `<Prefix>` must
diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp
index 2cbfad6c817f82..7968e999520957 100644
--- a/clang/test/SemaCXX/verbose-trap.cpp
+++ b/clang/test/SemaCXX/verbose-trap.cpp
@@ -10,6 +10,7 @@ char const constMsg3[] = "hello";
 
 template <const char * const str>
 void f(const char * arg) {
+  __builtin_verbose_trap("Arbitrary string literals can be used!");
   __builtin_verbose_trap("Argument_must_not_be_null");
   __builtin_verbose_trap("hello" "world");
   __builtin_verbose_trap(constMsg1);

>From 499acc7f47fe2e353378d31017060f30184718df Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Mon, 29 Jan 2024 07:43:50 -0800
Subject: [PATCH 04/11] Address review comments

1. Test long string argument.
2. Test c++20 unicode.
3. Allow empty string arguments.
4. #inlcude <map> and <string>.
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td |  3 +--
 clang/lib/CodeGen/CGDebugInfo.h                  |  2 ++
 clang/lib/Sema/SemaChecking.cpp                  |  2 +-
 clang/test/SemaCXX/verbose-trap.cpp              | 12 +++++++++---
 4 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1cd1813f18d85f..cefac72744f855 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8864,8 +8864,7 @@ def err_expected_callable_argument : Error<
 def note_building_builtin_dump_struct_call : Note<
   "in call to printing function with arguments '(%0)' while dumping struct">;
 def err_builtin_verbose_trap_arg : Error<
-  "argument to __builtin_verbose_trap must be a pointer to a non-empty constant "
-  "string">;
+  "argument to __builtin_verbose_trap must be a pointer to a constant string">;
 
 def err_atomic_load_store_uses_lib : Error<
   "atomic %select{load|store}0 requires runtime support that is not "
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index ff559153291836..17a49a0fb990a9 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -29,7 +29,9 @@
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/ValueHandle.h"
 #include "llvm/Support/Allocator.h"
+#include <map>
 #include <optional>
+#include <string>
 
 namespace llvm {
 class MDNode;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 32e4c5e0f19473..d906f7e7a1b3f2 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -180,7 +180,7 @@ static bool checkBuiltinVerboseTrap(CallExpr *Call, Sema &S) {
   // FIXME: Add more checks and reject strings that can't be handled by
   // debuggers.
   std::string Result;
-  if (!Arg->tryEvaluateString(Result, S.Context) || Result.empty()) {
+  if (!Arg->tryEvaluateString(Result, S.Context)) {
     S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg)
         << Arg->getSourceRange();
     return false;
diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp
index 7968e999520957..e10529cc7c5480 100644
--- a/clang/test/SemaCXX/verbose-trap.cpp
+++ b/clang/test/SemaCXX/verbose-trap.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify %s
 
 #if !__has_builtin(__builtin_verbose_trap)
 #error
@@ -15,13 +16,18 @@ void f(const char * arg) {
   __builtin_verbose_trap("hello" "world");
   __builtin_verbose_trap(constMsg1);
   __builtin_verbose_trap(constMsg2);
-  __builtin_verbose_trap(""); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a non-empty constant string}}
+  __builtin_verbose_trap("");
   __builtin_verbose_trap(); // expected-error {{too few arguments}}
-  __builtin_verbose_trap(0); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a non-empty constant string}}
+  __builtin_verbose_trap(0); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a constant string}}
   __builtin_verbose_trap(1); // expected-error {{cannot initialize a parameter of type 'const char *' with}}
-  __builtin_verbose_trap(arg); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a non-empty constant string}}
+  __builtin_verbose_trap(arg); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a constant string}}
   __builtin_verbose_trap(str);
   __builtin_verbose_trap(u8"hello");
+#if __cplusplus >= 202002L
+  // FIXME: Accept c++20 u8 string literals.
+  // expected-error at -3 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char8_t[6]'}}
+#endif
+  __builtin_verbose_trap("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd");
 }
 
 void test() {

>From ac7293707afade4c7fe99aa3de8ee03639a6c3f3 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Mon, 29 Jan 2024 17:36:05 -0800
Subject: [PATCH 05/11] Add triple to test

---
 clang/test/CodeGenCXX/debug-info-verbose-trap.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
index 04fa68ae93ecb6..54a21a96440001 100644
--- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
+++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++20 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++20 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s
 
 // CHECK-LABEL: define void @_Z2f0v()
 // CHECK: call void @llvm.trap(), !dbg ![[LOC17:.*]]

>From 4788ae36b53114b0f78ddfae1f339d45a5fd0071 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Thu, 1 Feb 2024 08:43:34 -0800
Subject: [PATCH 06/11] Address review comments

---
 clang/lib/CodeGen/CGBuiltin.cpp   |  4 ++--
 clang/lib/CodeGen/CGDebugInfo.cpp | 17 ++++++++---------
 clang/lib/CodeGen/CGDebugInfo.h   |  7 ++++---
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index b9307331fc56c8..7598b7148e0e1e 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3579,8 +3579,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     if (getDebugInfo()) {
       std::string Str;
       E->getArg(0)->tryEvaluateString(Str, getContext());
-      TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
-          TrapLocation, "__llvm_verbose_trap", Str);
+      TrapLocation =
+          getDebugInfo()->CreateTrapFailureMessageFor(TrapLocation, Str);
     }
     ApplyDebugLocation ApplyTrapDI(*this, TrapLocation);
     EmitTrapCall(Intrinsic::trap);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 3fe91211d69795..8d047fde086fdf 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1632,7 +1632,7 @@ llvm::DIType *CGDebugInfo::createFieldType(
 }
 
 llvm::DISubprogram *
-CGDebugInfo::createInlinedTrapSubprogram(const std::string &FuncName) {
+CGDebugInfo::createInlinedTrapSubprogram(StringRef FuncName) {
   llvm::DISubprogram *&SP = InlinedTrapFuncMap[FuncName];
 
   if (!SP) {
@@ -3448,14 +3448,13 @@ llvm::DIMacroFile *CGDebugInfo::CreateTempMacroFile(llvm::DIMacroFile *Parent,
   return DBuilder.createTempMacroFile(Parent, Line, FName);
 }
 
-llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
-    llvm::DebugLoc TrapLocation, StringRef Prefix, StringRef FailureMsg) {
-  assert((!FailureMsg.empty() || Prefix.find(' ') != std::string::npos) &&
-         "Prefix must contain a space when FailureMsg is empty");
-
-  // Create debug info that describes an inlined function whose name is the
-  // failure message.
-  std::string FuncName(Prefix);
+llvm::DILocation *
+CGDebugInfo::CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
+                                         StringRef FailureMsg) {
+  // Create a debug location from `TrapLocation` that adds an artificial inline
+  // frame.
+  const char *Prefix = "__llvm_verbose_trap";
+  SmallString<64> FuncName(Prefix);
   if (!FailureMsg.empty()) {
     // A space in the function name identifies this as not being a real function
     // because it's not a valid symbol name.
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 17a49a0fb990a9..e440b91b56a15b 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -350,11 +350,11 @@ class CGDebugInfo {
 
   // A cache that maps artificial inlined function names used for
   // __builtin_verbose_trap to subprograms.
-  std::map<std::string, llvm::DISubprogram *> InlinedTrapFuncMap;
+  llvm::StringMap<llvm::DISubprogram *> InlinedTrapFuncMap;
 
   // A function that returns the subprogram corresponding to the artificial
   // inlined function for traps.
-  llvm::DISubprogram *createInlinedTrapSubprogram(const std::string &FuncName);
+  llvm::DISubprogram *createInlinedTrapSubprogram(StringRef FuncName);
 
   /// Helpers for collecting fields of a record.
   /// @{
@@ -619,9 +619,10 @@ class CGDebugInfo {
   // * `<Prefix>` if `<FailureMsg>` is empty. Note `<Prefix>` must
   //   contain a space.
   //
+  // Currently `<Prefix>` is always "__llvm_verbose_trap".
+  //
   // This is used to store failure reasons for traps.
   llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
-                                                StringRef Prefix,
                                                 StringRef FailureMsg);
 
 private:

>From 50ffca2cfc4c942435e14fc6e2b89845a7757140 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Thu, 1 Feb 2024 11:12:18 -0800
Subject: [PATCH 07/11] Pass a better file scope when creating the subprogram
 for the artificial inlined function

`TheCU->getFile()` returns `<stdin>` when `-main-file-name` is empty.
---
 clang/lib/CodeGen/CGDebugInfo.cpp                 | 9 ++++-----
 clang/lib/CodeGen/CGDebugInfo.h                   | 3 ++-
 clang/test/CodeGenCXX/debug-info-verbose-trap.cpp | 2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 8d047fde086fdf..491033161bfed0 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1632,14 +1632,12 @@ llvm::DIType *CGDebugInfo::createFieldType(
 }
 
 llvm::DISubprogram *
-CGDebugInfo::createInlinedTrapSubprogram(StringRef FuncName) {
+CGDebugInfo::createInlinedTrapSubprogram(StringRef FuncName,
+                                         llvm::DIFile *FileScope) {
   llvm::DISubprogram *&SP = InlinedTrapFuncMap[FuncName];
 
   if (!SP) {
-    auto FileScope = TheCU->getFile();
     llvm::DISubroutineType *DIFnTy = DBuilder.createSubroutineType(nullptr);
-    // Note: We use `FileScope` rather than `TheCU` as the scope because that's
-    // what LLVM's inliner seems to do.
     SP = DBuilder.createFunction(
         /*Scope=*/FileScope, /*Name=*/FuncName, /*LinkageName=*/StringRef(),
         /*File=*/FileScope, /*LineNo=*/0, /*Ty=*/DIFnTy,
@@ -3462,7 +3460,8 @@ CGDebugInfo::CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
     FuncName += FailureMsg;
   }
 
-  llvm::DISubprogram *TrapSP = createInlinedTrapSubprogram(FuncName);
+  llvm::DISubprogram *TrapSP =
+      createInlinedTrapSubprogram(FuncName, TrapLocation->getFile());
   return llvm::DILocation::get(CGM.getLLVMContext(), /*Line=*/0, /*Column=*/0,
                                /*Scope=*/TrapSP, /*InlinedAt=*/TrapLocation);
 }
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index e440b91b56a15b..2f1d907692ede0 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -354,7 +354,8 @@ class CGDebugInfo {
 
   // A function that returns the subprogram corresponding to the artificial
   // inlined function for traps.
-  llvm::DISubprogram *createInlinedTrapSubprogram(StringRef FuncName);
+  llvm::DISubprogram *createInlinedTrapSubprogram(StringRef FuncName,
+                                                  llvm::DIFile *FileScope);
 
   /// Helpers for collecting fields of a record.
   /// @{
diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
index 54a21a96440001..0b156c23a54a24 100644
--- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
+++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
@@ -13,7 +13,7 @@
 // CHECK-LABEL: define internal void @_Z2f2IXadsoKcL_ZL8constMsgEEEEvv()
 // CHECK: call void @llvm.trap(), !dbg ![[LOC36:.*]]
 
-// CHECK: ![[FILESCOPE:.*]] = !DIFile(filename:
+// CHECK: ![[FILESCOPE:.*]] = !DIFile(filename: "{{.*}}debug-info-verbose-trap.cpp"
 // CHECK: ![[SUBPROG14:.*]] = distinct !DISubprogram(name: "f0", linkageName: "_Z2f0v",
 // CHECK: ![[LOC17]] = !DILocation(line: 0, scope: ![[SUBPROG18:.*]], inlinedAt: ![[LOC20:.*]])
 // CHECK: ![[SUBPROG18]] = distinct !DISubprogram(name: "__llvm_verbose_trap: Argument_must_not_be_null", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}})

>From 4224667fcc3ea1425c4c85ea8d56505649918b84 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Mon, 4 Mar 2024 21:31:49 -0800
Subject: [PATCH 08/11] Address review comments

---
 clang/docs/LanguageExtensions.rst             |  3 ++
 clang/include/clang/AST/Expr.h                |  6 ++--
 clang/lib/AST/ExprConstant.cpp                |  7 +++--
 clang/lib/CodeGen/CGBuiltin.cpp               |  6 ++--
 clang/lib/CodeGen/CGDebugInfo.cpp             |  5 ++--
 clang/lib/CodeGen/CGDebugInfo.h               |  3 +-
 clang/lib/Sema/SemaChecking.cpp               |  3 +-
 .../CodeGenCXX/debug-info-verbose-trap.cpp    | 30 +++++++++++--------
 clang/test/SemaCXX/verbose-trap.cpp           |  2 +-
 9 files changed, 36 insertions(+), 29 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 25d5f15e547368..cbed60aba6c58c 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3515,6 +3515,9 @@ users need to enable debug information to enable this feature. A call to this
 builtin is equivalent to a call to ``__builtin_trap`` if debug information isn't
 enabled.
 
+The optimizer can merge calls to trap with different messages, which degrades
+the debugging experience.
+
 ``__builtin_nondeterministic_value``
 ------------------------------------
 
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 789bedbd9724a6..78441f6edb315b 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -788,9 +788,9 @@ class Expr : public ValueStmt {
                                  EvalResult &Status) const;
 
   /// If the current Expr can be evaluated to a pointer to a null-terminated
-  /// constant string, return the constant string (without the terminating null)
-  /// in Result. Return true if it succeeds.
-  bool tryEvaluateString(std::string &Result, ASTContext &Ctx) const;
+  /// constant string, return the constant string (without the terminating
+  /// null).
+  std::optional<std::string> tryEvaluateString(ASTContext &Ctx) const;
 
   /// Enumeration used to describe the kind of Null pointer constant
   /// returned from \c isNullPointerConstant().
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 127c0415f8feb8..d020903312b85b 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -16841,12 +16841,15 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
   }
 }
 
-bool Expr::tryEvaluateString(std::string &StringResult, ASTContext &Ctx) const {
+std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const {
   Expr::EvalStatus Status;
   EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
   uint64_t Result;
+  std::string StringResult;
 
-  return EvaluateBuiltinStrLen(this, Result, Info, &StringResult);
+  if (EvaluateBuiltinStrLen(this, Result, Info, &StringResult))
+    return StringResult;
+  return {};
 }
 
 bool Expr::EvaluateCharRangeAsString(std::string &Result,
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 7598b7148e0e1e..a95275e2861d9d 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3577,10 +3577,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
   case Builtin::BI__builtin_verbose_trap: {
     llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
     if (getDebugInfo()) {
-      std::string Str;
-      E->getArg(0)->tryEvaluateString(Str, getContext());
-      TrapLocation =
-          getDebugInfo()->CreateTrapFailureMessageFor(TrapLocation, Str);
+      TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
+          TrapLocation, *E->getArg(0)->tryEvaluateString(getContext()));
     }
     ApplyDebugLocation ApplyTrapDI(*this, TrapLocation);
     EmitTrapCall(Intrinsic::trap);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 491033161bfed0..48980354fa0bcd 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1634,6 +1634,9 @@ llvm::DIType *CGDebugInfo::createFieldType(
 llvm::DISubprogram *
 CGDebugInfo::createInlinedTrapSubprogram(StringRef FuncName,
                                          llvm::DIFile *FileScope) {
+  // We are caching the subprogram because we don't want to duplicate
+  // subprograms with the same message. Note that `SPFlagDefinition` prevents
+  // subprograms from being uniqued.
   llvm::DISubprogram *&SP = InlinedTrapFuncMap[FuncName];
 
   if (!SP) {
@@ -3454,8 +3457,6 @@ CGDebugInfo::CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
   const char *Prefix = "__llvm_verbose_trap";
   SmallString<64> FuncName(Prefix);
   if (!FailureMsg.empty()) {
-    // A space in the function name identifies this as not being a real function
-    // because it's not a valid symbol name.
     FuncName += ": ";
     FuncName += FailureMsg;
   }
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 2f1d907692ede0..4aef93f76520ed 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -617,8 +617,7 @@ class CGDebugInfo {
   // frame where the frame name is
   //
   // * `<Prefix>: <FailureMsg>` if `<FailureMsg>` is not empty.
-  // * `<Prefix>` if `<FailureMsg>` is empty. Note `<Prefix>` must
-  //   contain a space.
+  // * `<Prefix>` if `<FailureMsg>` is empty.
   //
   // Currently `<Prefix>` is always "__llvm_verbose_trap".
   //
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index d906f7e7a1b3f2..05958e660e041a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -179,8 +179,7 @@ static bool checkBuiltinVerboseTrap(CallExpr *Call, Sema &S) {
 
   // FIXME: Add more checks and reject strings that can't be handled by
   // debuggers.
-  std::string Result;
-  if (!Arg->tryEvaluateString(Result, S.Context)) {
+  if (!Arg->tryEvaluateString(S.Context).has_value()) {
     S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg)
         << Arg->getSourceRange();
     return false;
diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
index 0b156c23a54a24..0854b76a4bdea4 100644
--- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
+++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp
@@ -3,6 +3,8 @@
 // CHECK-LABEL: define void @_Z2f0v()
 // CHECK: call void @llvm.trap(), !dbg ![[LOC17:.*]]
 
+// CHECK: declare void @llvm.trap() #[[ATTR1:.*]]
+
 // CHECK-LABEL: define void @_Z2f1v()
 // CHECK: call void @llvm.trap(), !dbg ![[LOC23:.*]]
 // CHECK: call void @llvm.trap(), !dbg ![[LOC25:.*]]
@@ -13,32 +15,34 @@
 // CHECK-LABEL: define internal void @_Z2f2IXadsoKcL_ZL8constMsgEEEEvv()
 // CHECK: call void @llvm.trap(), !dbg ![[LOC36:.*]]
 
+// CHECK: attributes #[[ATTR1]] = { cold {{.*}}}
+
 // CHECK: ![[FILESCOPE:.*]] = !DIFile(filename: "{{.*}}debug-info-verbose-trap.cpp"
-// CHECK: ![[SUBPROG14:.*]] = distinct !DISubprogram(name: "f0", linkageName: "_Z2f0v",
-// CHECK: ![[LOC17]] = !DILocation(line: 0, scope: ![[SUBPROG18:.*]], inlinedAt: ![[LOC20:.*]])
-// CHECK: ![[SUBPROG18]] = distinct !DISubprogram(name: "__llvm_verbose_trap: Argument_must_not_be_null", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}})
-// CHECK: ![[LOC20]] = !DILocation(line: 34, column: 3, scope: ![[SUBPROG14]])
-// CHECK: ![[SUBPROG22:.*]] = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v",
-// CHECK: ![[LOC23]] = !DILocation(line: 0, scope: ![[SUBPROG18]], inlinedAt: ![[LOC24:.*]])
-// CHECK: ![[LOC24]] = !DILocation(line: 38, column: 3, scope: ![[SUBPROG22]])
-// CHECK: ![[LOC25]] = !DILocation(line: 0, scope: ![[SUBPROG26:.*]], inlinedAt: ![[LOC27:.*]])
-// CHECK: ![[SUBPROG26]] = distinct !DISubprogram(name: "__llvm_verbose_trap: hello", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}})
-// CHECK: ![[LOC27]] = !DILocation(line: 39, column: 3, scope: ![[SUBPROG22]])
-// CHECK: ![[SUBPROG32:.*]] = distinct !DISubprogram(name: "f2<&constMsg[0]>", linkageName: "_Z2f2IXadsoKcL_ZL8constMsgEEEEvv",
-// CHECK: ![[LOC36]] = !DILocation(line: 0, scope: ![[SUBPROG26]], inlinedAt: ![[LOC37:.*]])
-// CHECK: ![[LOC37]] = !DILocation(line: 44, column: 3, scope: ![[SUBPROG32]])
 
 char const constMsg[] = "hello";
 
+// CHECK: ![[SUBPROG14:.*]] = distinct !DISubprogram(name: "f0", linkageName: "_Z2f0v",
+// CHECK: ![[LOC17]] = !DILocation(line: 0, scope: ![[SUBPROG18:.*]], inlinedAt: ![[LOC20:.*]])
+// CHECK: ![[SUBPROG18]] = distinct !DISubprogram(name: "__llvm_verbose_trap: Argument_must_not_be_null", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}})
+// CHECK: ![[LOC20]] = !DILocation(line: [[@LINE+2]], column: 3, scope: ![[SUBPROG14]])
 void f0() {
   __builtin_verbose_trap("Argument_must_not_be_null");
 }
 
+// CHECK: ![[SUBPROG22:.*]] = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v",
+// CHECK: ![[LOC23]] = !DILocation(line: 0, scope: ![[SUBPROG18]], inlinedAt: ![[LOC24:.*]])
+// CHECK: ![[LOC24]] = !DILocation(line: [[@LINE+5]], column: 3, scope: ![[SUBPROG22]])
+// CHECK: ![[LOC25]] = !DILocation(line: 0, scope: ![[SUBPROG26:.*]], inlinedAt: ![[LOC27:.*]])
+// CHECK: ![[SUBPROG26]] = distinct !DISubprogram(name: "__llvm_verbose_trap: hello", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}})
+// CHECK: ![[LOC27]] = !DILocation(line: [[@LINE+3]], column: 3, scope: ![[SUBPROG22]])
 void f1() {
   __builtin_verbose_trap("Argument_must_not_be_null");
   __builtin_verbose_trap("hello");
 }
 
+// CHECK: ![[SUBPROG32:.*]] = distinct !DISubprogram(name: "f2<&constMsg[0]>", linkageName: "_Z2f2IXadsoKcL_ZL8constMsgEEEEvv",
+// CHECK: ![[LOC36]] = !DILocation(line: 0, scope: ![[SUBPROG26]], inlinedAt: ![[LOC37:.*]])
+// CHECK: ![[LOC37]] = !DILocation(line: [[@LINE+3]], column: 3, scope: ![[SUBPROG32]])
 template <const char * const str>
 void f2() {
   __builtin_verbose_trap(str);
diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp
index e10529cc7c5480..86458a3957e7fc 100644
--- a/clang/test/SemaCXX/verbose-trap.cpp
+++ b/clang/test/SemaCXX/verbose-trap.cpp
@@ -19,7 +19,7 @@ void f(const char * arg) {
   __builtin_verbose_trap("");
   __builtin_verbose_trap(); // expected-error {{too few arguments}}
   __builtin_verbose_trap(0); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a constant string}}
-  __builtin_verbose_trap(1); // expected-error {{cannot initialize a parameter of type 'const char *' with}}
+  __builtin_verbose_trap(1); // expected-error {{cannot initialize a parameter of type 'const char *' with an rvalue of type 'int'}}
   __builtin_verbose_trap(arg); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a constant string}}
   __builtin_verbose_trap(str);
   __builtin_verbose_trap(u8"hello");

>From 1389cc8fd583fe48587c5bf091e1f900bdda98bb Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Tue, 12 Mar 2024 16:58:23 -0700
Subject: [PATCH 09/11] Define CLANG_VERBOSE_TRAP_PREFIX in public header

---
 clang/include/clang/CodeGen/ModuleBuilder.h | 3 +++
 clang/lib/CodeGen/CGDebugInfo.cpp           | 3 ++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h
index edacd82bf899db..69f4537330ba57 100644
--- a/clang/include/clang/CodeGen/ModuleBuilder.h
+++ b/clang/include/clang/CodeGen/ModuleBuilder.h
@@ -27,6 +27,9 @@ namespace llvm {
   }
 }
 
+// Prefix for __builtin_verbose_trap.
+#define CLANG_VERBOSE_TRAP_PREFIX "__llvm_verbose_trap"
+
 namespace clang {
   class CodeGenOptions;
   class CoverageSourceInfo;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 48980354fa0bcd..cc07b7b697bbd7 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -32,6 +32,7 @@
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/Version.h"
+#include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/FrontendOptions.h"
 #include "clang/Lex/HeaderSearchOptions.h"
 #include "clang/Lex/ModuleMap.h"
@@ -3454,7 +3455,7 @@ CGDebugInfo::CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
                                          StringRef FailureMsg) {
   // Create a debug location from `TrapLocation` that adds an artificial inline
   // frame.
-  const char *Prefix = "__llvm_verbose_trap";
+  const char *Prefix = CLANG_VERBOSE_TRAP_PREFIX;
   SmallString<64> FuncName(Prefix);
   if (!FailureMsg.empty()) {
     FuncName += ": ";

>From f4a38e0543731b8a217b76bc3c6d0bc578d7606b Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Tue, 19 Mar 2024 18:56:37 -0700
Subject: [PATCH 10/11] Address review comments

---
 clang/docs/LanguageExtensions.rst |  4 ++--
 clang/lib/CodeGen/CGBuiltin.cpp   |  1 +
 clang/lib/CodeGen/CGDebugInfo.h   | 22 +++++++++++-----------
 3 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index cbed60aba6c58c..8bfc859466d93e 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3507,8 +3507,8 @@ The debugging information would look as if it were produced for the following co
         "__llvm_verbose_trap: Argument must not be null!"();
     }
 
-However, the LLVM IR would not actually contain a call to the artificial
-function — it only exists in the debug metadata.
+However, the generated code would not actually contain a call to the artificial
+function — it only exists in the debugging information.
 
 Query for this feature with ``__has_builtin(__builtin_verbose_trap)``. Note that
 users need to enable debug information to enable this feature. A call to this
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index a95275e2861d9d..0a7da917b5d626 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3581,6 +3581,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
           TrapLocation, *E->getArg(0)->tryEvaluateString(getContext()));
     }
     ApplyDebugLocation ApplyTrapDI(*this, TrapLocation);
+    // Currently no attempt is made to prevent traps from being merged.
     EmitTrapCall(Intrinsic::trap);
     return RValue::get(nullptr);
   }
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 4aef93f76520ed..857a4711db16b9 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -348,8 +348,8 @@ class CGDebugInfo {
       const FieldDecl *BitFieldDecl, const llvm::DIDerivedType *BitFieldDI,
       llvm::ArrayRef<llvm::Metadata *> PreviousFieldsDI, const RecordDecl *RD);
 
-  // A cache that maps artificial inlined function names used for
-  // __builtin_verbose_trap to subprograms.
+  /// A cache that maps artificial inlined function names used for
+  /// __builtin_verbose_trap to subprograms.
   llvm::StringMap<llvm::DISubprogram *> InlinedTrapFuncMap;
 
   // A function that returns the subprogram corresponding to the artificial
@@ -613,15 +613,15 @@ class CGDebugInfo {
     return CoroutineParameterMappings;
   }
 
-  // Create a debug location from `TrapLocation` that adds an artificial inline
-  // frame where the frame name is
-  //
-  // * `<Prefix>: <FailureMsg>` if `<FailureMsg>` is not empty.
-  // * `<Prefix>` if `<FailureMsg>` is empty.
-  //
-  // Currently `<Prefix>` is always "__llvm_verbose_trap".
-  //
-  // This is used to store failure reasons for traps.
+  /// Create a debug location from `TrapLocation` that adds an artificial inline
+  /// frame where the frame name is
+  ///
+  /// * `<Prefix>: <FailureMsg>` if `<FailureMsg>` is not empty.
+  /// * `<Prefix>` if `<FailureMsg>` is empty.
+  ///
+  /// Currently `<Prefix>` is always "__llvm_verbose_trap".
+  ///
+  /// This is used to store failure reasons for traps.
   llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
                                                 StringRef FailureMsg);
 

>From faf771a9a33efdead7023a92c9ada9a7134398eb Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Wed, 20 Mar 2024 12:38:22 -0700
Subject: [PATCH 11/11] Use three forward slashes

---
 clang/lib/CodeGen/CGDebugInfo.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 857a4711db16b9..643cafae1db45a 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -352,8 +352,8 @@ class CGDebugInfo {
   /// __builtin_verbose_trap to subprograms.
   llvm::StringMap<llvm::DISubprogram *> InlinedTrapFuncMap;
 
-  // A function that returns the subprogram corresponding to the artificial
-  // inlined function for traps.
+  /// A function that returns the subprogram corresponding to the artificial
+  /// inlined function for traps.
   llvm::DISubprogram *createInlinedTrapSubprogram(StringRef FuncName,
                                                   llvm::DIFile *FileScope);
 



More information about the cfe-commits mailing list