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 new file mode 100644 index 0000000000000..add4c79ca5935 --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticBoundsSafetyTrapKinds.td @@ -0,0 +1,32 @@ +//==--- 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">; + +// 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/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/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 03acae1ca170e..ce9dacec2a383 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" @@ -122,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); @@ -158,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 @@ -165,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 @@ -189,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()); @@ -4855,8 +4887,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 @@ -4869,22 +4900,25 @@ 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) { + (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()) && @@ -4979,19 +5013,28 @@ 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 or we are asked to provide + // a "basic" description. + TrapReason TempTR; + if (!TR || CGM.getCodeGenOpts().getBoundsSafetyDebugTrapReasons() == + CodeGenOptions::SanitizeDebugTrapReasonKind::Basic) { + 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)); + TempTR.isEmpty() ? TR : &TempTR, + GetBoundsSafetyOptRemarkString(OptRemark)); } llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) { @@ -5326,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 5b027c67e961b..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, @@ -5425,7 +5426,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 +5436,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 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