From c1ba3a634ff711ce65de6b6c44506115d5f4795a Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Thu, 9 Oct 2025 17:07:20 -0700 Subject: [PATCH 1/2] [NFC][BoundsSafety] Adopt `TrapReason` in `CodeGenFunction::EmitBoundsSafetyTrapCheck` This is the first step in adopting the new trap reasons infrastructure. This patch introduces the `trap_bs_fallback` diagnostic and this just uses the existing infrastructure for computing trap reason reasons. Thus there is no user visible change in behavior with this patch. Future patches will start introducing new diagnostics so more specific trap reason messages can be created. rdar://158623471 --- .../Basic/DiagnosticBoundsSafetyTrapKinds.td | 19 ++++++++ .../clang/Basic/DiagnosticTrapKinds.td | 4 ++ clang/lib/CodeGen/CGExpr.cpp | 45 ++++++++++--------- clang/lib/CodeGen/CodeGenFunction.h | 5 ++- 4 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td diff --git a/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td b/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td new file mode 100644 index 0000000000000..39d7f16ba1d78 --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td @@ -0,0 +1,19 @@ +//==--- DiagnosticBoundsSafetyTrapKinds.td --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// These are the trap diagnostics used by -fbounds-safety +//===----------------------------------------------------------------------===// + +let Component = "Trap" in { +let CategoryName = "Bounds check failed" in { + +// Used for falling back to the legacy infrastructure for constructing +// trap reason strings. +def trap_bs_fallback : Trap<"%0">; + +} +} diff --git a/clang/include/clang/Basic/DiagnosticTrapKinds.td b/clang/include/clang/Basic/DiagnosticTrapKinds.td index c17a88d4fb4fb..f0031c53814c4 100644 --- a/clang/include/clang/Basic/DiagnosticTrapKinds.td +++ b/clang/include/clang/Basic/DiagnosticTrapKinds.td @@ -28,3 +28,7 @@ def trap_ubsan_arith_overflow : Trap< } } + +// TO_UPSTREAM(BoundsSafety) ON +include "DiagnosticBoundsSafetyTrapKinds.td" +// TO_UPSTREAM(BoundsSafety) OFF diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 03acae1ca170e..3b677545d2395 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -36,6 +36,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/DiagnosticTrap.h" // TO_UPSTREAM(BoundsSafety) #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/STLExtras.h" @@ -4855,8 +4856,7 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) { void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, bool NoMerge, const TrapReason *TR, - StringRef Annotation, - StringRef BoundsSafetyTrapMessage) { + StringRef Annotation) { llvm::BasicBlock *Cont = createBasicBlock("cont"); // If we're optimizing, collapse all calls to trap down to just one per @@ -4870,26 +4870,24 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::StringRef TrapMessage; llvm::StringRef TrapCategory; auto DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons(); + + /* TO_UPSTREAM(BoundsSafety) ON*/ if (TR && !TR->isEmpty() && - DebugTrapReasonKind == - CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed) { + (DebugTrapReasonKind == + CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed || + CheckHandlerID == SanitizerHandler::BoundsSafety)) { TrapMessage = TR->getMessage(); TrapCategory = TR->getCategory(); } else { - /* TO_UPSTREAM(BoundsSafety) ON*/ - // FIXME: Move to using `TrapReason` (rdar://158623471). - if (CheckHandlerID == SanitizerHandler::BoundsSafety) { - TrapMessage = BoundsSafetyTrapMessage; - TrapCategory = GetBoundsSafetyTrapMessagePrefix(); - } else { - TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); - TrapCategory = "Undefined Behavior Sanitizer"; - } + assert(CheckHandlerID != SanitizerHandler::BoundsSafety); + TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); + TrapCategory = "Undefined Behavior Sanitizer"; } if (getDebugInfo() && !(TrapMessage.empty() && TrapCategory.empty()) && - DebugTrapReasonKind != - CodeGenOptions::SanitizeDebugTrapReasonKind::None && + (DebugTrapReasonKind != + CodeGenOptions::SanitizeDebugTrapReasonKind::None || + CheckHandlerID == SanitizerHandler::BoundsSafety) && TrapLocation) { TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( TrapLocation, TrapCategory, TrapMessage); @@ -4979,19 +4977,26 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, EmitBlock(Cont); } -void CodeGenFunction::EmitBoundsSafetyTrapCheck(llvm::Value *Checked, - BoundsSafetyTrapKind kind, - BoundsSafetyTrapCtx::Kind TrapCtx) { +void CodeGenFunction::EmitBoundsSafetyTrapCheck( + llvm::Value *Checked, BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx, TrapReason *TR) { auto OptRemark = GetBoundsSafetyOptRemarkForTrap(kind); assert(BoundsSafetyOptRemarkScope::InScope(this, OptRemark)); + // Fallback: If a TrapReason object isn't provided use the legacy approach + // for constructing + TrapReason TempTR; + if (!TR) { + CGM.BuildTrapReason(diag::trap_bs_fallback, TempTR) + << GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx); + } + // We still need to pass `OptRemark` because not all emitted instructions // can be covered by BoundsSafetyOptRemarkScope. This is because EmitTrapCheck // caches basic blocks that contain instructions that need annotating. EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, /*NoMerge=*/CGM.getCodeGenOpts().BoundsSafetyUniqueTraps, - /*TR=*/nullptr, GetBoundsSafetyOptRemarkString(OptRemark), - GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx)); + TR ? TR : &TempTR, GetBoundsSafetyOptRemarkString(OptRemark)); } llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 5b027c67e961b..cb6597e72159a 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5425,7 +5425,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// conditional branch to it, for the -ftrapv checks. void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, bool NoMerge = false, const TrapReason *TR = nullptr, - StringRef Annotation = "", StringRef TrapMessage = ""); + StringRef Annotation = ""); /* TO_UPSTREAM(BoundsSafety) ON*/ /// Create a basic block that will call the trap intrinsic for -fbounds-safety, and @@ -5435,7 +5435,8 @@ class CodeGenFunction : public CodeGenTypeCache { /// must be in scope when this method is called. void EmitBoundsSafetyTrapCheck( llvm::Value *Checked, BoundsSafetyTrapKind kind, - BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN, + TrapReason *TR = nullptr); /* TO_UPSTREAM(BoundsSafety) OFF*/ /// Emit a call to trap or debugtrap and attach function attribute From 35338df1ab3130a346c5ef2b9d9be1fc7802f8fc Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 10 Oct 2025 16:54:02 -0700 Subject: [PATCH 2/2] [BoundsSafety] Move traps reasons for indexing to new trap reasons infrastructure This patch moves generation of the trap reason strings for indexing into pointers into the new trap reason infrastructure. We will need to move many other trap reasons over to the new infrastructure but this is the first we are moving over. Previously array indexing emitted these very unspecifc trap reason messages: - `Dereferencing above bounds` - `Deferencing below bounds` Now we emit trap reasons that look like - `indexing above upper bound in ''` - `indexing below lower bound in ''` - `indexing overflows address space in ''` where `` is the ArraySubscriptExpr printed as a string (see test cases for example). There are several improvements here: 1. We say indexing rather than dereferencing which is more specific. 2. We emit a specific trap reason for address space overflow. Previously there was no distinction between the upper bound trap and the address space overflow trap. 3. We emit the textual representation of the ArraySubscriptExpr that triggered the bounds check failed. This makes the message very specific. This new approach to emitting trap reasons will likely increase the size of debug info. To give users control a new flag `-fbounds-safety-debug-trap-trap-reasons` has been added which is analogous to `-fsanitize-debug-trap-reasons` for UBSan. The flag takes three values: * `none` - Dont' emit any trap reasons * `detailed` - Emit the new more detailed trap reasons (the default) * `basic` - Emit the less descriptive trap reasons using the legacy infrastructure. While working on this it became clear that emission of trap diagnostics is more complicated than for UBSan become some of the context for where we are emitting the trap exists in a different stackframe than the location where we can actually emit the trap diagnostic. In `EmitWidePtrArraySubscriptExpr` we don't know if we are emitting a lower/upper bound/address space check. In `EmitBoundsSafetyBoundsCheck` we don't know we are emitting a check for an ArraySubscriptExpr so there isn't a function where we can emit the trap diagnostic that has all the necessary information. The solution used in this patch is to re-use the `PartialDiagnostic` class which essentially lets us partially construct a diagnostic in one function and then pass it along to another function to the actual where the trap diagnostic can be fully constructed. Note in this implementation the "detailed" trap reason is always constructed even if it later gets thrown away. There are several reasons for doing this: * For clang's diagnostics normally we typically don't write guards around them to check they are enabled (e.g. the warning might be actually disabled). * While technically we could write guards around all the code that builds the TrapReason objects this will become repetitive very quickly. It's cleaner to just put the guard in this function. * I'm also planning to use these TrapReason objects for the upcoming soft trap mode and I didn't want to put guards around their creation until I've figured out exactly how this is going to be implemented. * This is also how its implemented for UBSan's trapping diagnostics right now. This is not a particularly strong argument because I'm the one who implemented that but at least upstream didn't object to me doing it this way. rdar://158623471 --- clang/include/clang/Basic/CodeGenOptions.def | 2 + .../Basic/DiagnosticBoundsSafetyTrapKinds.td | 13 ++++ clang/include/clang/Driver/Options.td | 14 ++++ clang/lib/CodeGen/CGExpr.cpp | 74 ++++++++++++++----- clang/lib/CodeGen/CodeGenFunction.h | 3 +- clang/lib/CodeGen/CodeGenModule.h | 9 +++ clang/lib/Driver/ToolChains/Clang.cpp | 2 + .../CodeGen/opt-remarks/ptr-bounds-argc-O0.c | 4 +- .../CodeGen/opt-remarks/ptr-bounds-const-O0.c | 4 +- ..._ge_upper_bound-deref-array_subscript-O0.c | 15 +++- .../CodeGen/debug-trap-reasons-flag.c | 40 ++++++++++ .../CodeGen/opt-remarks/ptr-bounds-argc-O0.c | 27 +++++-- .../CodeGen/opt-remarks/ptr-bounds-const-O0.c | 27 +++++-- ..._ge_upper_bound-deref-array_subscript-O0.c | 69 +++++++++++++++-- ..._lt_lower_bound-deref-array_subscript-O0.c | 39 +++++++++- .../Driver/debug-trap-reasons-flag.c | 11 +++ 16 files changed, 309 insertions(+), 44 deletions(-) create mode 100644 clang/test/BoundsSafety/CodeGen/debug-trap-reasons-flag.c create mode 100644 clang/test/BoundsSafety/Driver/debug-trap-reasons-flag.c diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 12c5685c7dc40..9ddcc851e3225 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -343,6 +343,8 @@ CODEGENOPT(BoundsSafetyUniqueTraps, 1, 0, Benign) ///< When true, merging of /// -fbounds-safety trap /// instructions will be /// prevented. + +ENUM_CODEGENOPT(BoundsSafetyDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info /* TO_UPSTREAM(BoundsSafety) OFF*/ /// Treat loops as finite: language, always, never. diff --git a/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td b/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td index 39d7f16ba1d78..add4c79ca5935 100644 --- a/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td +++ b/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td @@ -15,5 +15,18 @@ let CategoryName = "Bounds check failed" in { // trap reason strings. def trap_bs_fallback : Trap<"%0">; +// TODO: Split these up once we know what the diagnostics strings needs to be. +// Ideally we want to be able to generate trap codes and opt-remark strings +// from these definitions too so we'll need to split them up when we do that. +def trap_bs_upper_lower_overflow_bound : Trap< + "%enum_select{" + "%Indexing{indexing}|" + "%TODO{TODO}" + "}0 %enum_select{" + "%Upper{above upper bound}|" + "%Overflow{overflows address space}|" + "%Lower{below lower bound" + "}}2 in %1">; + } } diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 31ed20425bda9..d73263c61890b 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2060,6 +2060,20 @@ defm bounds_safety_legacy_unique_traps : BoolOptionWithoutMarshalling<"f", "uniq NegFlag>; +def fbounds_safety_debug_trap_reasons_EQ + : Joined<["-"], "fbounds-safety-debug-trap-reasons=">, Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Set how trap reasons are emitted. " + "`none` - Not emitted. This gives the smallest debug info; " + "`basic` - Emit a fixed trap message per check type. This increases the " + "debug info size but not as much as `detailed`; " + "`detailed` - Emit a more detailed trap message. This increases the " + "debug info size the most. Default is `detailed`.">, + Values<"none,basic,detailed">, + NormalizedValuesScope<"CodeGenOptions::SanitizeDebugTrapReasonKind">, + NormalizedValues<["None", "Basic", "Detailed"]>, + MarshallingInfoEnum, "Detailed">; + // TO_UPSTREAM(BoundsSafety) OFF defm lifetime_safety : BoolFOption< diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 3b677545d2395..ce9dacec2a383 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -123,8 +123,8 @@ void CodeGenFunction::EmitPtrCastLECheck(llvm::Value *LHS, llvm::Value *RHS, void CodeGenFunction::EmitBoundsSafetyBoundsCheck( llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper, - llvm::Value *Lower, bool AcceptNullPtr, - BoundsSafetyTrapCtx::Kind TrapCtx) { + llvm::Value *Lower, bool AcceptNullPtr, BoundsSafetyTrapCtx::Kind TrapCtx, + PartialDiagnostic *PD) { if (!Upper && !Lower) return; assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); @@ -159,6 +159,18 @@ void CodeGenFunction::EmitBoundsSafetyBoundsCheck( BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GT_UPPER_BOUND); llvm::Value *OnePastTheEndPtr = Builder.CreateGEP(ElemTy, Ptr, llvm::ConstantInt::get(SizeTy, 1)); + + TrapReason OverflowTR; + TrapReason UpperBoundTR; + if (PD) { + CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound, + UpperBoundTR) + << *PD << diag::BoundsSafetyPtrCheckKind::Upper; + CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound, + OverflowTR) + << *PD << diag::BoundsSafetyPtrCheckKind::Overflow; + } + // Emitting the upper bound check first since it's more // optimization-friendly. This is because the upper bound calculation (ptr // + size) is often marked 'inbounds' if 'ptr' is '__counted_by' or an @@ -166,9 +178,11 @@ void CodeGenFunction::EmitBoundsSafetyBoundsCheck( // this information to infer subsequent pointer arithmetic (ptr + i; where // 'i <= size') doesn't wrap. EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(OnePastTheEndPtr, Upper), - BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx, + PD ? &UpperBoundTR : nullptr); EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(Ptr, OnePastTheEndPtr), - BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx, + PD ? &OverflowTR : nullptr); } else { // Path where the size of the access is assumed to be 1 byte. This is used // for @@ -190,14 +204,31 @@ void CodeGenFunction::EmitBoundsSafetyBoundsCheck( // BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GE_UPPER_BOUND); llvm::Value *Check = Builder.CreateICmpULT(Ptr, Upper); - EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx); + + TrapReason UpperTR; + if (PD) { + CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound, UpperTR) + << *PD << diag::BoundsSafetyPtrCheckKind::Upper; + } + + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx, + PD ? &UpperTR : nullptr); } } if (Lower) { BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_LT_LOWER_BOUND); llvm::Value *Check = Builder.CreateICmpUGE(Ptr, Lower); - EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx); + + TrapReason LowerBoundTR; + if (PD) { + CGM.BuildTrapReason(diag::trap_bs_upper_lower_overflow_bound, + LowerBoundTR) + << *PD << diag::BoundsSafetyPtrCheckKind::Lower; + } + + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx, + PD ? &LowerBoundTR : nullptr); } if (NullCheckBranch) NullCheckBranch->setSuccessor(1, Builder.GetInsertBlock()); @@ -4869,9 +4900,15 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); llvm::StringRef TrapMessage; llvm::StringRef TrapCategory; - auto DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons(); - /* TO_UPSTREAM(BoundsSafety) ON*/ + CodeGenOptions::SanitizeDebugTrapReasonKind DebugTrapReasonKind; + if (CheckHandlerID == SanitizerHandler::BoundsSafety) { + DebugTrapReasonKind = + CGM.getCodeGenOpts().getBoundsSafetyDebugTrapReasons(); + } else { + DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons(); + } + if (TR && !TR->isEmpty() && (DebugTrapReasonKind == CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed || @@ -4885,9 +4922,8 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, } if (getDebugInfo() && !(TrapMessage.empty() && TrapCategory.empty()) && - (DebugTrapReasonKind != - CodeGenOptions::SanitizeDebugTrapReasonKind::None || - CheckHandlerID == SanitizerHandler::BoundsSafety) && + DebugTrapReasonKind != + CodeGenOptions::SanitizeDebugTrapReasonKind::None && TrapLocation) { TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( TrapLocation, TrapCategory, TrapMessage); @@ -4983,10 +5019,11 @@ void CodeGenFunction::EmitBoundsSafetyTrapCheck( auto OptRemark = GetBoundsSafetyOptRemarkForTrap(kind); assert(BoundsSafetyOptRemarkScope::InScope(this, OptRemark)); - // Fallback: If a TrapReason object isn't provided use the legacy approach - // for constructing + // Fallback: If a TrapReason object isn't provided or we are asked to provide + // a "basic" description. TrapReason TempTR; - if (!TR) { + if (!TR || CGM.getCodeGenOpts().getBoundsSafetyDebugTrapReasons() == + CodeGenOptions::SanitizeDebugTrapReasonKind::Basic) { CGM.BuildTrapReason(diag::trap_bs_fallback, TempTR) << GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx); } @@ -4996,7 +5033,8 @@ void CodeGenFunction::EmitBoundsSafetyTrapCheck( // caches basic blocks that contain instructions that need annotating. EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, /*NoMerge=*/CGM.getCodeGenOpts().BoundsSafetyUniqueTraps, - TR ? TR : &TempTR, GetBoundsSafetyOptRemarkString(OptRemark)); + TempTR.isEmpty() ? TR : &TempTR, + GetBoundsSafetyOptRemarkString(OptRemark)); } llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) { @@ -5331,9 +5369,11 @@ CodeGenFunction::EmitWidePtrArraySubscriptExpr(const ArraySubscriptExpr *E, } assert(!!Upper && !!Lower); llvm::Type *ElemTy = ConvertTypeForMem(E->getType()); + auto PD = CGM.BuildPartialTrapReason(); + PD << diag::BoundsCheckContextKind::Indexing << E; EmitBoundsSafetyBoundsCheck(ElemTy, Addr.getBasePointer(), Upper, Lower, - /*AcceptNullPtr=*/false, - /*TrapCtx=*/BoundsSafetyTrapCtx::DEREF); + /*AcceptNullPtr=*/false, + /*TrapCtx=*/BoundsSafetyTrapCtx::DEREF, &PD); } return MakeAddrLValue(Addr, E->getType(), BaseInfo, TBAAInfo); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index cb6597e72159a..c124562ff11aa 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2977,7 +2977,8 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitBoundsSafetyBoundsCheck( llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper, llvm::Value *Lower, bool AcceptNullPt = false, - BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN, + PartialDiagnostic *PD = nullptr); void EmitBoundsSafetyRangeCheck( llvm::Value *LowerBound, llvm::Value *LowerAccess, llvm::Value *UpperAccess, llvm::Value *UpperBound, diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 6e02edac9a74d..d012d5be14b16 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1841,6 +1841,15 @@ class CodeGenModule : public CodeGenTypeCache { return TrapReasonBuilder(&getDiags(), DiagID, TR); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + PartialDiagnostic BuildPartialTrapReason() { + // When building trap reasons we sometimes don't know exactly + // which diagnostic is going to be emitted so we just specify + // `0` as place holder. + return PartialDiagnostic(0, getContext().getDiagAllocator()); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + private: bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index d42e7f291cc2c..3fc01869e4b3d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7076,6 +7076,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job, CmdArgs.push_back("-fbounds-safety-unique-traps"); } } + + Args.AddLastArg(CmdArgs, options::OPT_fbounds_safety_debug_trap_reasons_EQ); /* TO_UPSTREAM(BoundsSafety) OFF*/ // Handle -f[no-]wrapv and -f[no-]strict-overflow, which are used by both diff --git a/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-argc-O0.c b/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-argc-O0.c index 1741e2a00aaa9..b5a87e3695d9b 100644 --- a/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-argc-O0.c +++ b/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-argc-O0.c @@ -63,9 +63,9 @@ int main(int argc, char **argv) { // IR-DAG: ![[LOC_10_16]] = !DILocation(line: 10, column: 16{{.*}}) // IR-DAG: ![[LT_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]]) -// IR-DAG: ![[LT_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds" +// IR-DAG: ![[LT_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[idx]'" // IR-DAG: ![[GE_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[GE_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]]) -// IR-DAG: ![[GE_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds" +// IR-DAG: ![[GE_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing below lower bound in 'array[idx]'" // // IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5 // IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]]) diff --git a/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-const-O0.c b/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-const-O0.c index 95b104d1ae7dc..0bec761a5fba1 100644 --- a/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-const-O0.c +++ b/clang/test/BoundsSafety-legacy-checks/CodeGen/opt-remarks/ptr-bounds-const-O0.c @@ -63,9 +63,9 @@ int main() { // IR-DAG: ![[LOC_10_14]] = !DILocation(line: 10, column: 14{{.*}}) // IR-DAG: ![[LT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]]) -// IR-DAG: ![[LT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds" +// IR-DAG: ![[LT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[6]'" // IR-DAG: ![[GT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[GT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]]) -// IR-DAG: ![[GT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds" +// IR-DAG: ![[GT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing below lower bound in 'array[6]'" // IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5 // IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]]) // IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" diff --git a/clang/test/BoundsSafety-legacy-checks/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c b/clang/test/BoundsSafety-legacy-checks/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c index 9088cb3a42c54..b54ccf24edebe 100644 --- a/clang/test/BoundsSafety-legacy-checks/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c +++ b/clang/test/BoundsSafety-legacy-checks/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c @@ -1,5 +1,11 @@ -// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=basic \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=detailed \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s int operation(int index) { int array[] = {0, 1, 2}; @@ -15,5 +21,8 @@ int operation(int index) { // CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] // CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) -// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} -// CHECK-DAG: [[LOC]] = !DILocation(line: 6, column: 10, scope: {{![0-9]+}}) + +// BASIC-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// DETAILED-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[index]'", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} + +// CHECK-DAG: [[LOC]] = !DILocation(line: 12, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/debug-trap-reasons-flag.c b/clang/test/BoundsSafety/CodeGen/debug-trap-reasons-flag.c new file mode 100644 index 0000000000000..8990e8a0c2c24 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/debug-trap-reasons-flag.c @@ -0,0 +1,40 @@ +#include +//============================================================================== +// Detailed trap reasons +//============================================================================== + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -emit-llvm %s -o - \ +// RUN: | FileCheck %s --check-prefixes=ANNOTATE,DETAILED + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety \ +// RUN: -fbounds-safety-debug-trap-reasons=detailed -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,DETAILED + +//============================================================================== +// Basic trap reasons +//============================================================================== + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety \ +// RUN: -fbounds-safety-debug-trap-reasons=basic -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,BASIC + +//============================================================================== +// No trap reasons +//============================================================================== + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety \ +// RUN: -fbounds-safety-debug-trap-reasons=none -emit-llvm %s -o - | FileCheck %s --check-prefix=NO-ANNOTATE + +int read(int* __bidi_indexable ptr, int idx) { return ptr[idx]; } + +// ANNOTATE-LABEL: @read +// ANNOTATE: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[LOC:![0-9]+]] +// ANNOTATE: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'ptr[idx]'" +// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds" + +// NO-ANNOTATE-LABEL: @read +// NO-ANNOTATE: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[LOC:![0-9]+]] +// NO-ANNOTATE-NOT: __clang_trap_msg diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c index 5f03c4aaedf21..87cdcc6c7572b 100644 --- a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c @@ -156,7 +156,7 @@ int main(int argc, char **argv) { // OPT-REM-NEXT: Function: foo // OPT-REM-NEXT: Args: // OPT-REM-NEXT: - String: 'Inserted ' -// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - count: '2' // OPT-REM-NEXT: - String: ' LLVM IR instruction' // OPT-REM-NEXT: - String: s // OPT-REM-NEXT: - String: "\n" @@ -166,11 +166,28 @@ int main(int argc, char **argv) { // OPT-REM-NEXT: {{^[ ]+$}} // OPT-REM-NEXT: {{^[ ]+$}} // OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-le-upper-bound // OPT-REM-NEXT: - String: | -// OPT-REM-NEXT: trap (LLVM IR 'call') -// OPT-REM-NEXT: other (LLVM IR 'unreachable') -// OPT-REM-NEXT: trap (LLVM IR 'call') -// OPT-REM-NEXT: other (LLVM IR 'unreachable') +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" // OPT-REM-NEXT: ... // OPT-REM-NEXT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c index 9400288238a73..e217ef9314e8a 100644 --- a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c @@ -158,7 +158,7 @@ int main() { // OPT-REM-NEXT: Function: foo // OPT-REM-NEXT: Args: // OPT-REM-NEXT: - String: 'Inserted ' -// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - count: '2' // OPT-REM-NEXT: - String: ' LLVM IR instruction' // OPT-REM-NEXT: - String: s // OPT-REM-NEXT: - String: "\n" @@ -168,11 +168,28 @@ int main() { // OPT-REM-NEXT: {{^[ ]+$}} // OPT-REM-NEXT: {{^[ ]+$}} // OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-le-upper-bound // OPT-REM-NEXT: - String: | -// OPT-REM-NEXT: trap (LLVM IR 'call') -// OPT-REM-NEXT: other (LLVM IR 'unreachable') -// OPT-REM-NEXT: trap (LLVM IR 'call') -// OPT-REM-NEXT: other (LLVM IR 'unreachable') +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" // OPT-REM-NEXT: ... // OPT-REM-NEXT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c index fa7fa5b5b7760..56ea60b969066 100644 --- a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c @@ -1,21 +1,77 @@ - -// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll // RUN: echo "; __SEPARATOR__" > %t.sep + +// ============================================================================= +// Array access without a macro +// ============================================================================= + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=basic \ +// RUN: -emit-llvm %s -o %t.ll +// // RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll -// RUN: FileCheck %s --input-file=%t.repeated.ll +// RUN: FileCheck %s --check-prefixes=CHECK,BASIC,PLAIN \ +// RUN: --input-file=%t.repeated.ll + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=detailed \ +// RUN: -emit-llvm %s -o %t2.ll +// +// RUN: cat %t2.ll %t.sep %t2.ll > %t2.repeated.ll +// RUN: FileCheck %s --check-prefixes=CHECK,DETAILED,PLAIN \ +// RUN: --input-file=%t2.repeated.ll + +// ============================================================================= +// Array access through a macro +// ============================================================================= +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=basic \ +// RUN: -emit-llvm -DARRAY_ACCESS_THROUGH_MACRO %s -o %t3.ll +// +// RUN: cat %t3.ll %t.sep %t3.ll > %t3.repeated.ll +// RUN: FileCheck %s --check-prefixes=CHECK,BASIC,MACRO \ +// RUN: --input-file=%t3.repeated.ll + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=detailed \ +// RUN: -emit-llvm -DARRAY_ACCESS_THROUGH_MACRO %s -o %t4.ll +// +// RUN: cat %t4.ll %t.sep %t4.ll > %t4.repeated.ll +// RUN: FileCheck %s --check-prefixes=CHECK,DETAILED,MACRO \ +// RUN: --input-file=%t4.repeated.ll + + +#define ARRAY(__ARR, __IDX) __ARR[__IDX] int operation(int index) { int array[] = {0, 1, 2}; +#ifndef ARRAY_ACCESS_THROUGH_MACRO return array[index]; +#else + return ARRAY(array, index); +#endif } // In first copy of the file // CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-le-upper-bound"} -// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} + +// BASIC-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// DETAILED-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[index]'", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} + // CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) -// CHECK-DAG: [[SRC_LOC]] = !DILocation(line: 10, column: 10, scope: {{![0-9]+}}) +// PLAIN-DAG: [[SRC_LOC]] = !DILocation(line: 49, column: 10, scope: {{![0-9]+}}) +// MACRO-DAG: [[SRC_LOC]] = !DILocation(line: 51, column: 10, scope: {{![0-9]+}}) + +// In the detailed mode the address space overflow gets its own trap reason. +// FIXME: Basic mode should probably be making this distinction too. + +// DETAILED-DAG: [[TRAP_SCOPE_2:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing overflows address space in 'array[index]'", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// DETAILED-DAG: [[TRAP_LOC_2:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE_2]], inlinedAt: [[SRC_LOC:![0-9]+]]) + + + + // CHECK-LABEL: ; __SEPARATOR__ // In second copy of the file @@ -29,4 +85,5 @@ int operation(int index) { // CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL_1:[a-z0-9]+]], !dbg [[SRC_LOC]], !prof !{{[0-9]+}}, !annotation [[OPT_REMARK]] // CHECK: [[TRAP_LABEL_1]]: -// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] +// BASIC-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] +// DETAILED-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC_2]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c index 5659a9ed17bef..f0a796c79e7aa 100644 --- a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c @@ -1,9 +1,38 @@ +// ============================================================================= +// Array access without a macro +// ============================================================================= -// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=basic \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC,PLAIN %s + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=detailed \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED,PLAIN %s + +// ============================================================================= +// Array access through a macro +// ============================================================================= + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=basic \ +// RUN: -emit-llvm %s -o - -DARRAY_ACCESS_THROUGH_MACRO | \ +// RUN: FileCheck --check-prefixes=CHECK,BASIC,MACRO %s + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fbounds-safety -fbounds-safety-debug-trap-reasons=detailed \ +// RUN: -emit-llvm %s -o - -DARRAY_ACCESS_THROUGH_MACRO | \ +// RUN: FileCheck --check-prefixes=CHECK,DETAILED,MACRO %s + +#define ARRAY(__ARR, __IDX) __ARR[__IDX] int operation(int index) { int array[] = {0, 1, 2}; +#ifndef ARRAY_ACCESS_THROUGH_MACRO return array[index]; +#else + return ARRAY(array, index); +#endif } // CHECK-LABEL: @operation @@ -15,5 +44,9 @@ int operation(int index) { // CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] // CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) -// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} -// CHECK-DAG: [[LOC]] = !DILocation(line: 6, column: 10, scope: {{![0-9]+}}) + +// BASIC-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// DETAILED-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$indexing below lower bound in 'array[index]'", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} + +// PLAIN-DAG: [[LOC]] = !DILocation(line: 32, column: 10, scope: {{![0-9]+}}) +// MACRO-DAG: [[LOC]] = !DILocation(line: 34, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/Driver/debug-trap-reasons-flag.c b/clang/test/BoundsSafety/Driver/debug-trap-reasons-flag.c new file mode 100644 index 0000000000000..457affd96b903 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/debug-trap-reasons-flag.c @@ -0,0 +1,11 @@ +// RUN: %clang -fbounds-safety -### %s 2>&1 | FileCheck --check-prefix=DEFAULT %s + +// Simple use of new flag +// RUN: %clang -fbounds-safety -fbounds-safety-debug-trap-reasons=detailed -### %s 2>&1 | FileCheck --check-prefix=DETAILED %s +// RUN: %clang -fbounds-safety -fbounds-safety-debug-trap-reasons=basic -### %s 2>&1 | FileCheck --check-prefix=BASIC %s +// RUN: %clang -fbounds-safety -fbounds-safety-debug-trap-reasons=none -### %s 2>&1 | FileCheck --check-prefix=NONE %s + +// DEFAULT-NOT: -fbounds-safety-debug-trap-reasons= +// DETAILED: -fbounds-safety-debug-trap-reasons=detailed +// BASIC: -fbounds-safety-debug-trap-reasons=basic +// NONE: -fbounds-safety-debug-trap-reasons=none