From 95200f3bb3859738981240a9d8c503a13ede9601 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 16 Jan 2024 13:18:09 -0800 Subject: [PATCH 01/19] 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 | 49 ++++++++++++++++++- 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(+), 4 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 84fc4dee02fa80..1dd8d3bcec920d 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3467,6 +3467,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`` `_ 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_allow_runtime_check`` --------------------------------- @@ -3514,7 +3562,6 @@ guarded check. It's unused now. It will enable kind-specific lowering in future. E.g. a higher hotness cutoff can be used for more expensive kind of check. Query for this feature with ``__has_builtin(__builtin_allow_runtime_check)``. - ``__builtin_nondeterministic_value`` ------------------------------------ diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 2bfefeabc348be..9606a5fddd47e3 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 de721a87b3341d..5b3b9d04e576ab 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 63e951daec7477..417a24564cd009 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8875,6 +8875,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 de3c2a63913e94..c5ac10f2013dc7 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, @@ -16792,7 +16793,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; @@ -16819,6 +16820,8 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result, Str = Str.substr(0, Pos); Result = Str.size(); + if (StringResult) + *StringResult = Str; return true; } @@ -16834,12 +16837,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 7e5f2edfc732cc..ad19b4d24be5ac 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3586,6 +3586,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 539ded5cca5e1b..b42489746fdc68 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1692,6 +1692,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 &elements, llvm::DIType *RecordTy) { @@ -3488,6 +3509,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 d6db4d711366ac..217b99d448f758 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 PreviousFieldsDI, const RecordDecl *RD); + // A cache that maps fake function names used for __builtin_verbose_trap to + // subprograms. + std::map 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 + // + // * `: ` if `` is not empty. + // * `` if `` is empty. Note `` 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 51757f4cf727d6..fc8ff6bc340cc2 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -172,6 +172,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; @@ -3204,6 +3221,11 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, case Builtin::BI__builtin_matrix_column_major_store: return BuiltinMatrixColumnMajorStore(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 +void f2() { + __builtin_verbose_trap(str); +} + +void f3() { + f2(); +} 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 +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(nullptr); +} From 156e9debc3933b931befa5938e54ae832739fd75 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 23 Jan 2024 16:13:14 -0800 Subject: [PATCH 02/19] 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 417a24564cd009..60e0087c8d4d8c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8876,7 +8876,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 78b1c451ea17defd0ea6719fc4f8d862480a20e9 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 25 Jan 2024 14:15:16 -0800 Subject: [PATCH 03/19] 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 1dd8d3bcec920d..3acdfd5e6ca849 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3483,9 +3483,9 @@ debugger is attached or in a symbolicated crash log. **Description** ``__builtin_verbose_trap`` is lowered to the ` ``llvm.trap`` `_ 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: @@ -3493,27 +3493,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_allow_runtime_check`` --------------------------------- diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index b42489746fdc68..7fd9184113ce9a 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1693,8 +1693,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(); @@ -1702,7 +1702,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, @@ -3511,8 +3511,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 @@ -3521,11 +3524,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 217b99d448f758..08877a0413196a 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 PreviousFieldsDI, const RecordDecl *RD); - // A cache that maps fake function names used for __builtin_verbose_trap to - // subprograms. - std::map FakeFuncMap; + // A cache that maps artificial inlined function names used for + // __builtin_verbose_trap to subprograms. + std::map 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 // // * `: ` if `` is not empty. // * `` if `` is empty. Note `` 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 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 856fd3d950e0d85b503fc847c04292c20df2c4bf Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 29 Jan 2024 07:43:50 -0800 Subject: [PATCH 04/19] Address review comments 1. Test long string argument. 2. Test c++20 unicode. 3. Allow empty string arguments. 4. #inlcude and . --- 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 60e0087c8d4d8c..46f054617f430a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8876,8 +8876,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 08877a0413196a..8222ff37dd689f 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 #include +#include namespace llvm { class MDNode; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index fc8ff6bc340cc2..8e894c8a4b77e4 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -181,7 +181,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@-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 5efc51478e3313b3d0bab32e3786ed9b2ef5da57 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 29 Jan 2024 17:36:05 -0800 Subject: [PATCH 05/19] 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 0ff6ac0751227170cf75971ef855ed0cef0aa92d Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 1 Feb 2024 08:43:34 -0800 Subject: [PATCH 06/19] 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 ad19b4d24be5ac..3692b4bdb1ef3f 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3591,8 +3591,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 7fd9184113ce9a..36b3c7b941f0ba 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1693,7 +1693,7 @@ llvm::DIType *CGDebugInfo::createFieldType( } llvm::DISubprogram * -CGDebugInfo::createInlinedTrapSubprogram(const std::string &FuncName) { +CGDebugInfo::createInlinedTrapSubprogram(StringRef FuncName) { llvm::DISubprogram *&SP = InlinedTrapFuncMap[FuncName]; if (!SP) { @@ -3509,14 +3509,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 8222ff37dd689f..b0b2e0f3ab63c5 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 InlinedTrapFuncMap; + llvm::StringMap 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 { // * `` if `` is empty. Note `` must // contain a space. // + // Currently `` 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 206e55d9ff59066262b8552b3036964d468f92cd Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 1 Feb 2024 11:12:18 -0800 Subject: [PATCH 07/19] Pass a better file scope when creating the subprogram for the artificial inlined function `TheCU->getFile()` returns `` 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 36b3c7b941f0ba..07bcd1ad7df45c 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1693,14 +1693,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, @@ -3523,7 +3521,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 b0b2e0f3ab63c5..d112ae74a0fec6 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 4aa91cc8475ceb8ae2355579a3d4cdb5e9433c93 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 4 Mar 2024 21:31:49 -0800 Subject: [PATCH 08/19] Address review comments --- clang/docs/LanguageExtensions.rst | 4 +++ 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, 37 insertions(+), 29 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 3acdfd5e6ca849..5e63d43abba1b2 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3518,6 +3518,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_allow_runtime_check`` --------------------------------- @@ -3565,6 +3568,7 @@ guarded check. It's unused now. It will enable kind-specific lowering in future. E.g. a higher hotness cutoff can be used for more expensive kind of check. Query for this feature with ``__has_builtin(__builtin_allow_runtime_check)``. + ``__builtin_nondeterministic_value`` ------------------------------------ diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 9606a5fddd47e3..c7af59da241959 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 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 c5ac10f2013dc7..29ac550c212666 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -16844,12 +16844,15 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result, } } -bool Expr::tryEvaluateString(std::string &StringResult, ASTContext &Ctx) const { +std::optional 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 3692b4bdb1ef3f..2c3fa677facc89 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3589,10 +3589,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 07bcd1ad7df45c..0f1e634ef9085e 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1695,6 +1695,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) { @@ -3515,8 +3518,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 d112ae74a0fec6..10101da1c796a6 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 // // * `: ` if `` is not empty. - // * `` if `` is empty. Note `` must - // contain a space. + // * `` if `` is empty. // // Currently `` is always "__llvm_verbose_trap". // diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8e894c8a4b77e4..6b6e9235e29c46 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -180,8 +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)) { + 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 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 521fe9174c12164e38d6810d12a868e3787046a1 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 12 Mar 2024 16:58:23 -0700 Subject: [PATCH 09/19] 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 0f1e634ef9085e..d461e375f620a7 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" @@ -3515,7 +3516,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 c72fc1767c211816e58d364caa0b17809166b521 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 19 Mar 2024 18:56:37 -0700 Subject: [PATCH 10/19] 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 5e63d43abba1b2..4fc2bf3f1b7ed3 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3510,8 +3510,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 2c3fa677facc89..cb25c8e5ea8e77 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3593,6 +3593,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 10101da1c796a6..7e79adfb7653a7 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 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 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 - // - // * `: ` if `` is not empty. - // * `` if `` is empty. - // - // Currently `` 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 + /// + /// * `: ` if `` is not empty. + /// * `` if `` is empty. + /// + /// Currently `` is always "__llvm_verbose_trap". + /// + /// This is used to store failure reasons for traps. llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation, StringRef FailureMsg); From 5bbb01be39b997d475159ec1e6b3a44944cc0352 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Wed, 20 Mar 2024 12:38:22 -0700 Subject: [PATCH 11/19] 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 7e79adfb7653a7..5cc2f5978477ed 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 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); From 4896af087488c294c93d2123389fded32ea65b30 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 1 Apr 2024 13:08:29 -0700 Subject: [PATCH 12/19] Fix 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 0854b76a4bdea4..966fd2b7bced93 100644 --- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp +++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp @@ -40,7 +40,7 @@ void f1() { __builtin_verbose_trap("hello"); } -// CHECK: ![[SUBPROG32:.*]] = distinct !DISubprogram(name: "f2<&constMsg[0]>", linkageName: "_Z2f2IXadsoKcL_ZL8constMsgEEEEvv", +// CHECK: ![[SUBPROG32:.*]] = distinct !DISubprogram(name: "f2", linkageName: "_Z2f2IXadsoKcL_ZL8constMsgEEEEvv", // CHECK: ![[LOC36]] = !DILocation(line: 0, scope: ![[SUBPROG26]], inlinedAt: ![[LOC37:.*]]) // CHECK: ![[LOC37]] = !DILocation(line: [[@LINE+3]], column: 3, scope: ![[SUBPROG32]]) template From 74639861168357e3bc9f5a8b9d18e7dac9a3c6f8 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Wed, 24 Apr 2024 18:57:39 -0700 Subject: [PATCH 13/19] Pass category string to the builtin --- clang/docs/LanguageExtensions.rst | 12 +++--- clang/include/clang/Basic/Builtins.td | 2 +- clang/lib/CodeGen/CGBuiltin.cpp | 3 +- clang/lib/CodeGen/CGDebugInfo.cpp | 14 +++---- clang/lib/CodeGen/CGDebugInfo.h | 6 +-- clang/lib/Sema/SemaChecking.cpp | 25 ++++++----- .../CodeGenCXX/debug-info-verbose-trap.cpp | 23 ++++++----- clang/test/SemaCXX/verbose-trap.cpp | 41 ++++++++++++------- 8 files changed, 72 insertions(+), 54 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 4fc2bf3f1b7ed3..1b6ce2d22f4015 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3478,14 +3478,14 @@ debugger is attached or in a symbolicated crash log. .. code-block:: c++ - __builtin_verbose_trap(const char *reason) + __builtin_verbose_trap(const char *category, const char *reason) **Description** ``__builtin_verbose_trap`` is lowered to the ` ``llvm.trap`` `_ builtin. 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. +inline frame whose name encodes the category and reason strings passed to the builtin, +prefixed by a "magic" prefix. For example, consider the following code: @@ -3493,7 +3493,7 @@ For example, consider the following code: void foo(int* p) { if (p == nullptr) - __builtin_verbose_trap("Argument must not be null!"); + __builtin_verbose_trap("check null", "Argument must not be null!"); } The debugging information would look as if it were produced for the following code: @@ -3501,13 +3501,13 @@ The debugging information would look as if it were produced for the following co .. code-block:: c++ __attribute__((always_inline)) - inline void "__llvm_verbose_trap: Argument must not be null!"() { + inline void "__llvm_verbose_trap:check null: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:check null:Argument must not be null!"(); } However, the generated code would not actually contain a call to the artificial diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 5b3b9d04e576ab..1c4453d3c84e9e 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1155,7 +1155,7 @@ def Trap : Builtin { def VerboseTrap : Builtin { let Spellings = ["__builtin_verbose_trap"]; let Attributes = [NoThrow, NoReturn]; - let Prototype = "void(char const*)"; + let Prototype = "void(char const*, char const*)"; } def Debugtrap : Builtin { diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index cb25c8e5ea8e77..5c762c641235c2 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3590,7 +3590,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); if (getDebugInfo()) { TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( - TrapLocation, *E->getArg(0)->tryEvaluateString(getContext())); + TrapLocation, *E->getArg(0)->tryEvaluateString(getContext()), + *E->getArg(1)->tryEvaluateString(getContext())); } ApplyDebugLocation ApplyTrapDI(*this, TrapLocation); // Currently no attempt is made to prevent traps from being merged. diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index d461e375f620a7..f8dba8c212d850 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3511,17 +3511,17 @@ llvm::DIMacroFile *CGDebugInfo::CreateTempMacroFile(llvm::DIMacroFile *Parent, return DBuilder.createTempMacroFile(Parent, Line, FName); } -llvm::DILocation * -CGDebugInfo::CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation, - StringRef FailureMsg) { +llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor( + llvm::DebugLoc TrapLocation, StringRef Category, StringRef FailureMsg) { // Create a debug location from `TrapLocation` that adds an artificial inline // frame. const char *Prefix = CLANG_VERBOSE_TRAP_PREFIX; SmallString<64> FuncName(Prefix); - if (!FailureMsg.empty()) { - FuncName += ": "; - FuncName += FailureMsg; - } + + FuncName += ":"; + FuncName += Category; + FuncName += ":"; + FuncName += FailureMsg; llvm::DISubprogram *TrapSP = createInlinedTrapSubprogram(FuncName, TrapLocation->getFile()); diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 5cc2f5978477ed..7b5c9b0a81ba42 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -616,13 +616,13 @@ class CGDebugInfo { /// Create a debug location from `TrapLocation` that adds an artificial inline /// frame where the frame name is /// - /// * `: ` if `` is not empty. - /// * `` if `` is empty. + /// * `::` /// - /// Currently `` is always "__llvm_verbose_trap". + /// `` is "__llvm_verbose_trap". /// /// This is used to store failure reasons for traps. llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation, + StringRef Category, StringRef FailureMsg); private: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 6b6e9235e29c46..5d5b1d13e1ec9e 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -173,19 +173,24 @@ static bool checkArgCount(Sema &S, CallExpr *Call, unsigned DesiredArgCount) { } static bool checkBuiltinVerboseTrap(CallExpr *Call, Sema &S) { - Expr *Arg = Call->getArg(0); + bool HasError = false; - if (Arg->isValueDependent()) - return true; + for (int I = 0; I < Call->getNumArgs(); ++I) { + Expr *Arg = Call->getArg(I); - // FIXME: Add more checks and reject strings that can't be handled by - // debuggers. - if (!Arg->tryEvaluateString(S.Context).has_value()) { - S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg) - << Arg->getSourceRange(); - return false; + if (Arg->isValueDependent()) + continue; + + // FIXME: Add more checks and reject strings that can't be handled by + // debuggers. + if (!Arg->tryEvaluateString(S.Context).has_value()) { + S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg) + << Arg->getSourceRange(); + HasError = true; + } } - return true; + + return !HasError; } static bool convertArgumentToType(Sema &S, Expr *&Value, QualType Ty) { diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp index 966fd2b7bced93..948b967bbb9aad 100644 --- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp +++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp @@ -10,44 +10,45 @@ // CHECK: call void @llvm.trap(), !dbg ![[LOC25:.*]] // CHECK-LABEL: define void @_Z2f3v() -// CHECK: call void @_Z2f2IXadsoKcL_ZL8constMsgEEEEvv() +// CHECK: call void @_Z2f2IXadsoKcL_ZL8constCatEEEXadsoS0_L_ZL8constMsgEEEEvv() -// CHECK-LABEL: define internal void @_Z2f2IXadsoKcL_ZL8constMsgEEEEvv() +// CHECK-LABEL: define internal void @_Z2f2IXadsoKcL_ZL8constCatEEEXadsoS0_L_ZL8constMsgEEEEvv // CHECK: call void @llvm.trap(), !dbg ![[LOC36:.*]] // CHECK: attributes #[[ATTR1]] = { cold {{.*}}} // CHECK: ![[FILESCOPE:.*]] = !DIFile(filename: "{{.*}}debug-info-verbose-trap.cpp" +char const constCat[] = "category2"; 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: ![[SUBPROG18]] = distinct !DISubprogram(name: "__llvm_verbose_trap:category1: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"); + __builtin_verbose_trap("category1", "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: ![[SUBPROG26]] = distinct !DISubprogram(name: "__llvm_verbose_trap:category2: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"); + __builtin_verbose_trap("category1", "Argument_must_not_be_null"); + __builtin_verbose_trap("category2", "hello"); } -// CHECK: ![[SUBPROG32:.*]] = distinct !DISubprogram(name: "f2", linkageName: "_Z2f2IXadsoKcL_ZL8constMsgEEEEvv", +// CHECK: ![[SUBPROG32:.*]] = distinct !DISubprogram(name: "f2", linkageName: "_Z2f2IXadsoKcL_ZL8constCatEEEXadsoS0_L_ZL8constMsgEEEEvv", // CHECK: ![[LOC36]] = !DILocation(line: 0, scope: ![[SUBPROG26]], inlinedAt: ![[LOC37:.*]]) // CHECK: ![[LOC37]] = !DILocation(line: [[@LINE+3]], column: 3, scope: ![[SUBPROG32]]) -template +template void f2() { - __builtin_verbose_trap(str); + __builtin_verbose_trap(category, reason); } void f3() { - f2(); + f2(); } diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp index 86458a3957e7fc..a91dd01106cc1a 100644 --- a/clang/test/SemaCXX/verbose-trap.cpp +++ b/clang/test/SemaCXX/verbose-trap.cpp @@ -5,31 +5,42 @@ #error #endif +constexpr char const* constCat1 = "cat"; +char const* const constCat2 = "cat"; +char const constCat3[] = "cat"; + constexpr char const* constMsg1 = "hello"; char const* const constMsg2 = "hello"; char const constMsg3[] = "hello"; -template +template 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); - __builtin_verbose_trap(constMsg2); - __builtin_verbose_trap(""); + __builtin_verbose_trap("cat1", "Arbitrary string literals can be used!"); + __builtin_verbose_trap(" cat1 ", "Argument_must_not_be_null"); + __builtin_verbose_trap("cat" "egory1", "hello" "world"); + __builtin_verbose_trap(constCat1, constMsg1); + __builtin_verbose_trap(constCat2, constMsg2); + __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 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"); + __builtin_verbose_trap(""); // expected-error {{too few arguments}} + __builtin_verbose_trap("", "", ""); // expected-error {{too many 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 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(category, reason); + __builtin_verbose_trap(u8"cat1", u8"hello"); #if __cplusplus >= 202002L // FIXME: Accept c++20 u8 string literals. - // expected-error@-3 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char8_t[6]'}} + // expected-error@-3 {{cannot initialize a parameter of type 'const char *' with an lvalue of type 'const char8_t[5]'}} #endif - __builtin_verbose_trap("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"); + __builtin_verbose_trap("", "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"); +} + +template +void f2() { + __builtin_verbose_trap(category, 1); // expected-error {{cannot initialize a parameter of type 'const char *' with an rvalue of type 'int'}} } void test() { - f(nullptr); + f(nullptr); } From c7bd9145df6f51f3fa56d6c34b6eba0017062e70 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 25 Apr 2024 13:23:07 -0700 Subject: [PATCH 14/19] Use $ as the separator --- clang/docs/LanguageExtensions.rst | 4 ++-- clang/lib/CodeGen/CGDebugInfo.cpp | 4 ++-- clang/test/CodeGenCXX/debug-info-verbose-trap.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 1b6ce2d22f4015..dddeb960568252 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3501,13 +3501,13 @@ The debugging information would look as if it were produced for the following co .. code-block:: c++ __attribute__((always_inline)) - inline void "__llvm_verbose_trap:check null:Argument must not be null!"() { + inline void "__llvm_verbose_trap$check null$Argument must not be null!"() { __builtin_trap(); } void foo(int* p) { if (p == nullptr) - "__llvm_verbose_trap:check null:Argument must not be null!"(); + "__llvm_verbose_trap$check null$Argument must not be null!"(); } However, the generated code would not actually contain a call to the artificial diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index f8dba8c212d850..2a4707860bc460 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3518,9 +3518,9 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor( const char *Prefix = CLANG_VERBOSE_TRAP_PREFIX; SmallString<64> FuncName(Prefix); - FuncName += ":"; + FuncName += "$"; FuncName += Category; - FuncName += ":"; + FuncName += "$"; FuncName += FailureMsg; llvm::DISubprogram *TrapSP = diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp index 948b967bbb9aad..beb4a7673b7314 100644 --- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp +++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp @@ -24,7 +24,7 @@ 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:category1:Argument_must_not_be_null", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}}) +// CHECK: ![[SUBPROG18]] = distinct !DISubprogram(name: "__llvm_verbose_trap$category1$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("category1", "Argument_must_not_be_null"); @@ -34,7 +34,7 @@ void f0() { // 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:category2:hello", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}}) +// CHECK: ![[SUBPROG26]] = distinct !DISubprogram(name: "__llvm_verbose_trap$category2$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("category1", "Argument_must_not_be_null"); From 1db962811d35a0320b8078f28c0f2e6e114246ad Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 30 Apr 2024 17:19:39 -0700 Subject: [PATCH 15/19] Use a shorter prefix --- clang/docs/LanguageExtensions.rst | 4 ++-- clang/include/clang/CodeGen/ModuleBuilder.h | 4 ++-- clang/lib/CodeGen/CGDebugInfo.cpp | 2 +- clang/lib/CodeGen/CGDebugInfo.h | 5 ++--- clang/test/CodeGenCXX/debug-info-verbose-trap.cpp | 4 ++-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index dddeb960568252..7bde31114bbd92 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3501,13 +3501,13 @@ The debugging information would look as if it were produced for the following co .. code-block:: c++ __attribute__((always_inline)) - inline void "__llvm_verbose_trap$check null$Argument must not be null!"() { + inline void "__trap_msg$check null$Argument must not be null!"() { __builtin_trap(); } void foo(int* p) { if (p == nullptr) - "__llvm_verbose_trap$check null$Argument must not be null!"(); + "__trap_msg$check null$Argument must not be null!"(); } However, the generated code would not actually contain a call to the artificial diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h index 69f4537330ba57..4a6200ad25dea3 100644 --- a/clang/include/clang/CodeGen/ModuleBuilder.h +++ b/clang/include/clang/CodeGen/ModuleBuilder.h @@ -27,8 +27,8 @@ namespace llvm { } } -// Prefix for __builtin_verbose_trap. -#define CLANG_VERBOSE_TRAP_PREFIX "__llvm_verbose_trap" +// Prefix of the name of the artificial inline frame. +#define CLANG_TRAP_PREFIX "__trap_msg" namespace clang { class CodeGenOptions; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 2a4707860bc460..73747a70c445f4 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3515,7 +3515,7 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor( llvm::DebugLoc TrapLocation, StringRef Category, StringRef FailureMsg) { // Create a debug location from `TrapLocation` that adds an artificial inline // frame. - const char *Prefix = CLANG_VERBOSE_TRAP_PREFIX; + const char *Prefix = CLANG_TRAP_PREFIX; SmallString<64> FuncName(Prefix); FuncName += "$"; diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 7b5c9b0a81ba42..5b48155ef26aa1 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -348,8 +348,7 @@ class CGDebugInfo { const FieldDecl *BitFieldDecl, const llvm::DIDerivedType *BitFieldDI, llvm::ArrayRef PreviousFieldsDI, const RecordDecl *RD); - /// A cache that maps artificial inlined function names used for - /// __builtin_verbose_trap to subprograms. + /// A cache that maps names of artificial inlined functions to subprograms. llvm::StringMap InlinedTrapFuncMap; /// A function that returns the subprogram corresponding to the artificial @@ -618,7 +617,7 @@ class CGDebugInfo { /// /// * `::` /// - /// `` is "__llvm_verbose_trap". + /// `` is "__trap_msg". /// /// This is used to store failure reasons for traps. llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation, diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp index beb4a7673b7314..a8c78343b12a3b 100644 --- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp +++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp @@ -24,7 +24,7 @@ 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$category1$Argument_must_not_be_null", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}}) +// CHECK: ![[SUBPROG18]] = distinct !DISubprogram(name: "__trap_msg$category1$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("category1", "Argument_must_not_be_null"); @@ -34,7 +34,7 @@ void f0() { // 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$category2$hello", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}}) +// CHECK: ![[SUBPROG26]] = distinct !DISubprogram(name: "__trap_msg$category2$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("category1", "Argument_must_not_be_null"); From 6881d0d32ca877a21f8b83d4dc8c61017ee056c3 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 21 Jun 2024 10:46:11 -0700 Subject: [PATCH 16/19] Use as the prefix --- clang/docs/LanguageExtensions.rst | 4 ++-- clang/include/clang/CodeGen/ModuleBuilder.h | 2 +- clang/lib/CodeGen/CGDebugInfo.h | 2 +- clang/test/CodeGenCXX/debug-info-verbose-trap.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 7bde31114bbd92..7217f1d668bb78 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3501,13 +3501,13 @@ The debugging information would look as if it were produced for the following co .. code-block:: c++ __attribute__((always_inline)) - inline void "__trap_msg$check null$Argument must not be null!"() { + inline void "__clang_trap_msg$check null$Argument must not be null!"() { __builtin_trap(); } void foo(int* p) { if (p == nullptr) - "__trap_msg$check null$Argument must not be null!"(); + "__clang_trap_msg$check null$Argument must not be null!"(); } However, the generated code would not actually contain a call to the artificial diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h index 4a6200ad25dea3..6cccd13c0a0f92 100644 --- a/clang/include/clang/CodeGen/ModuleBuilder.h +++ b/clang/include/clang/CodeGen/ModuleBuilder.h @@ -28,7 +28,7 @@ namespace llvm { } // Prefix of the name of the artificial inline frame. -#define CLANG_TRAP_PREFIX "__trap_msg" +#define CLANG_TRAP_PREFIX "__clang_trap_msg" namespace clang { class CodeGenOptions; diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 5b48155ef26aa1..4e5ed8540dcc39 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -617,7 +617,7 @@ class CGDebugInfo { /// /// * `::` /// - /// `` is "__trap_msg". + /// `` is "__clang_trap_msg". /// /// This is used to store failure reasons for traps. llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation, diff --git a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp index a8c78343b12a3b..f492698ccab83d 100644 --- a/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp +++ b/clang/test/CodeGenCXX/debug-info-verbose-trap.cpp @@ -24,7 +24,7 @@ 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: "__trap_msg$category1$Argument_must_not_be_null", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}}) +// CHECK: ![[SUBPROG18]] = distinct !DISubprogram(name: "__clang_trap_msg$category1$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("category1", "Argument_must_not_be_null"); @@ -34,7 +34,7 @@ void f0() { // 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: "__trap_msg$category2$hello", scope: ![[FILESCOPE]], file: ![[FILESCOPE]], type: !{{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{.*}}) +// CHECK: ![[SUBPROG26]] = distinct !DISubprogram(name: "__clang_trap_msg$category2$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("category1", "Argument_must_not_be_null"); From 65671fff70b036e976bcb89cecb704b2975398c9 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 21 Jun 2024 11:04:47 -0700 Subject: [PATCH 17/19] Reject arguments using '$' --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaChecking.cpp | 16 +++++++++++----- clang/test/SemaCXX/verbose-trap.cpp | 1 + 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 46f054617f430a..c9fb81f4620f06 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8876,7 +8876,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 constant string">; + "argument to __builtin_verbose_trap must %select{be a pointer to a constant string|not contain $}0">; def err_atomic_load_store_uses_lib : Error< "atomic %select{load|store}0 requires runtime support that is not " diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 5d5b1d13e1ec9e..b5cc173f719f7a 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -175,17 +175,23 @@ static bool checkArgCount(Sema &S, CallExpr *Call, unsigned DesiredArgCount) { static bool checkBuiltinVerboseTrap(CallExpr *Call, Sema &S) { bool HasError = false; - for (int I = 0; I < Call->getNumArgs(); ++I) { + for (unsigned I = 0; I < Call->getNumArgs(); ++I) { Expr *Arg = Call->getArg(I); if (Arg->isValueDependent()) continue; - // FIXME: Add more checks and reject strings that can't be handled by - // debuggers. - if (!Arg->tryEvaluateString(S.Context).has_value()) { + std::optional ArgString = Arg->tryEvaluateString(S.Context); + int DiagMsgKind = -1; + // Arguments must be pointers to constant strings and cannot use '$'. + if (!ArgString.has_value()) + DiagMsgKind = 0; + else if (ArgString->find('$') != std::string::npos) + DiagMsgKind = 1; + + if (DiagMsgKind >= 0) { S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg) - << Arg->getSourceRange(); + << DiagMsgKind << Arg->getSourceRange(); HasError = true; } } diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp index a91dd01106cc1a..2503f9860d9c34 100644 --- a/clang/test/SemaCXX/verbose-trap.cpp +++ b/clang/test/SemaCXX/verbose-trap.cpp @@ -27,6 +27,7 @@ void f(const char * arg) { __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 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("cat$1", "hel$lo"); // expected-error 2 {{argument to __builtin_verbose_trap must not contain $}} __builtin_verbose_trap(category, reason); __builtin_verbose_trap(u8"cat1", u8"hello"); #if __cplusplus >= 202002L From ae3907da7894da7c1c1fcb640283155d06d0d71f Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 24 Jun 2024 10:03:29 -0700 Subject: [PATCH 18/19] Define CLANG_TRAP_PREFIX as an inline constexpr StringRef --- clang/include/clang/CodeGen/ModuleBuilder.h | 3 ++- clang/lib/CodeGen/CGDebugInfo.cpp | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h index 6cccd13c0a0f92..5aba80b84fb92e 100644 --- a/clang/include/clang/CodeGen/ModuleBuilder.h +++ b/clang/include/clang/CodeGen/ModuleBuilder.h @@ -15,6 +15,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" namespace llvm { class Constant; @@ -28,7 +29,7 @@ namespace llvm { } // Prefix of the name of the artificial inline frame. -#define CLANG_TRAP_PREFIX "__clang_trap_msg" +inline constexpr llvm::StringRef CLANG_TRAP_PREFIX = "__clang_trap_msg"; namespace clang { class CodeGenOptions; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 73747a70c445f4..c1f9fb9f9297f6 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3515,8 +3515,7 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor( llvm::DebugLoc TrapLocation, StringRef Category, StringRef FailureMsg) { // Create a debug location from `TrapLocation` that adds an artificial inline // frame. - const char *Prefix = CLANG_TRAP_PREFIX; - SmallString<64> FuncName(Prefix); + SmallString<64> FuncName(CLANG_TRAP_PREFIX); FuncName += "$"; FuncName += Category; From e13d85c68b793c19008dc06f94d6efd156a5a3ff Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 24 Jun 2024 15:36:25 -0700 Subject: [PATCH 19/19] Rename variable --- clang/include/clang/CodeGen/ModuleBuilder.h | 2 +- clang/lib/CodeGen/CGDebugInfo.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h index 5aba80b84fb92e..59b9840d02e086 100644 --- a/clang/include/clang/CodeGen/ModuleBuilder.h +++ b/clang/include/clang/CodeGen/ModuleBuilder.h @@ -29,7 +29,7 @@ namespace llvm { } // Prefix of the name of the artificial inline frame. -inline constexpr llvm::StringRef CLANG_TRAP_PREFIX = "__clang_trap_msg"; +inline constexpr llvm::StringRef ClangTrapPrefix = "__clang_trap_msg"; namespace clang { class CodeGenOptions; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index c1f9fb9f9297f6..8b6db6e8fa2704 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3515,7 +3515,7 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor( llvm::DebugLoc TrapLocation, StringRef Category, StringRef FailureMsg) { // Create a debug location from `TrapLocation` that adds an artificial inline // frame. - SmallString<64> FuncName(CLANG_TRAP_PREFIX); + SmallString<64> FuncName(ClangTrapPrefix); FuncName += "$"; FuncName += Category;