https://github.com/JustinStitt updated https://github.com/llvm/llvm-project/pull/174892
>From ea02427b27288e70acaf87b0c2ad53d897f13e80 Mon Sep 17 00:00:00 2001 From: Justin Stitt <[email protected]> Date: Tue, 6 Jan 2026 15:36:33 -0800 Subject: [PATCH 1/2] [Clang] Add -fdiagnostics-show-inlining-chain for warning/error attributes When functions marked with __attribute__((warning/error)) are called through inlined functions, Clang now optionally shows the inlining chain that led to the call. Three modes are supported: - ``none`` is the default and results in no inline notes being shown (matches the behavior before this change). - ``heuristic`` tries to guess which functions will be inlined to minimize the amount of source locations kept in memory and visible in LLVM IR. - ``debug`` leverages minimal debug directive tracking infrastructure to get more reliable source locations over the heuristic mode while having more compile-time overhead. Fixes: https://github.com/ClangBuiltLinux/linux/issues/1571 Based-on: https://github.com/llvm/llvm-project/pull/73552 Signed-off-by: Justin Stitt <[email protected]> --- clang/docs/ReleaseNotes.rst | 6 + clang/include/clang/Basic/AttrDocs.td | 7 ++ clang/include/clang/Basic/CodeGenOptions.def | 2 + clang/include/clang/Basic/CodeGenOptions.h | 8 ++ .../clang/Basic/DiagnosticFrontendKinds.td | 6 + clang/include/clang/Options/Options.td | 15 +++ clang/lib/CodeGen/CGCall.cpp | 23 ++-- clang/lib/CodeGen/CodeGenAction.cpp | 31 +++++ clang/lib/Driver/ToolChains/Clang.cpp | 7 ++ clang/lib/Frontend/CompilerInvocation.cpp | 8 ++ .../backend-attribute-inlining-cross-tu.c | 64 ++++++++++ ...nd-attribute-inlining-debug-vs-heuristic.c | 28 +++++ .../backend-attribute-inlining-modes.c | 47 ++++++++ .../Frontend/backend-attribute-inlining.c | 109 ++++++++++++++++++ llvm/include/llvm/IR/DiagnosticInfo.h | 26 ++++- llvm/lib/IR/DiagnosticInfo.cpp | 38 +++++- llvm/lib/Transforms/Utils/InlineFunction.cpp | 44 +++++++ 17 files changed, 459 insertions(+), 10 deletions(-) create mode 100644 clang/test/Frontend/backend-attribute-inlining-cross-tu.c create mode 100644 clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c create mode 100644 clang/test/Frontend/backend-attribute-inlining-modes.c create mode 100644 clang/test/Frontend/backend-attribute-inlining.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8ef6564bd80e6..cf17f8a052cbb 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -348,6 +348,12 @@ New Compiler Flags - New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``. - The ``-resource-dir`` option is now displayed in the list of options shown by ``--help``. - New option ``-fmatrix-memory-layout`` added to control the memory layout of Clang matrix types. (e.g. ``-fmatrix-memory-layout=column-major`` or ``-fmatrix-memory-layout=row-major``). +- New option ``-fdiagnostics-show-inlining-chain=`` added to show inlining chain + notes for ``__attribute__((warning))`` and ``__attribute__((error))`` + diagnostics. When a function with these attributes is called from an inlined + context, Clang can now show which functions were inlined to reach the call. + Modes: ``none`` (default), ``heuristic`` (uses metadata tracking), ``debug`` + (uses debug info for accurate source locations). Lanai Support ^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 812b48058d189..97331b1175cc9 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8397,6 +8397,13 @@ pointing to precise locations of the call site in the source. dontcall(); // No Warning sizeof(dontcall()); // No Warning } + +When the call occurs through inlined functions, the +``-fdiagnostics-show-inlining-chain=`` option can be used to show the +inlining chain that led to the call. This helps identify which call site +triggered the diagnostic when the attributed function is called from +multiple locations through inline functions. See ``clang --help`` for +available modes. }]; } diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 3784844ef1028..223e803a22c6c 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -76,6 +76,8 @@ CODEGENOPT(CallGraphSection, 1, 0, Benign) ///< Emit a call graph section into t ///< object file. CODEGENOPT(EmitCallSiteInfo, 1, 0, Benign) ///< Emit call site info only in the case of ///< '-g' + 'O>0' level. +/// Tracking inlining chain in __attribute__((warning)) and __attribute__((error)) diagnostics +ENUM_CODEGENOPT(InliningChain, InliningChainKind, 2, Inlining_None, Benign) CODEGENOPT(IndirectTlsSegRefs, 1, 0, Benign) ///< Set when -mno-tls-direct-seg-refs ///< is specified. CODEGENOPT(DisableTailCalls , 1, 0, Benign) ///< Do not emit tail calls. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index c60ca507ff917..f34de98126768 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -120,6 +120,14 @@ class CodeGenOptions : public CodeGenOptionsBase { Embed_Marker // Embed a marker as a placeholder for bitcode. }; + /// Inlining chain tracking __attribute__((warning)) or __attribute__((error)) + /// diagnostics + enum InliningChainKind { + Inlining_Heuristic, /// Track via srcloc metadata on inline/static calls. + Inlining_Debug, /// Track via debug info (DILocation inlinedAt chain). + Inlining_None /// No tracking, no inlining notes shown (default). + }; + enum class ExtendVariableLivenessKind { None, This, diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index e2b257ceae80d..3a7b7ccc73ab8 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -94,6 +94,12 @@ def err_fe_backend_error_attr : def warn_fe_backend_warning_attr : Warning<"call to '%0' declared with 'warning' attribute: %1">, BackendInfo, InGroup<BackendWarningAttributes>; +def note_fe_backend_in : Note<"called by function '%0'">, BackendInfo; +def note_fe_backend_inlined : Note<"inlined by function '%0'">, BackendInfo; +def warn_fe_inlining_debug_requires_g + : Warning<"'-fdiagnostics-show-inlining-chain=debug' requires at least " + "'-gline-directives-only'; falling back to 'heuristic' mode">, + InGroup<BackendWarningAttributes>; def warn_toc_unsupported_type : Warning<"-mtocdata option is ignored " "for %0 because %1">, InGroup<BackendWarningAttributes>; diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index dd039fd474263..92bbb34e6a1eb 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -2158,6 +2158,21 @@ defm diagnostics_show_note_include_stack : BoolFOption<"diagnostics-show-note-in DiagnosticOpts<"ShowNoteIncludeStack">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption], "Display include stacks for diagnostic notes">, NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>; +def fdiagnostics_show_inlining_chain_EQ + : Joined<["-"], "fdiagnostics-show-inlining-chain=">, + Group<f_Group>, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Show inlining chain notes for __attribute__((warning/error)) " + "diagnostics: " + "'none' (default) shows no chain, 'heuristic' uses best-effort " + "metadata tracking, " + "'debug' uses debug info for accurate locations with increased " + "compile-time overhead">, + Values<"heuristic,debug,none">, + NormalizedValuesScope<"CodeGenOptions">, + NormalizedValues<["Inlining_Heuristic", "Inlining_Debug", + "Inlining_None"]>, + MarshallingInfoEnum<CodeGenOpts<"InliningChain">, "Inlining_None">; def fdiagnostics_format_EQ : Joined<["-"], "fdiagnostics-format=">, Group<f_clang_Group>; def fdiagnostics_show_category_EQ : Joined<["-"], "fdiagnostics-show-category=">, Group<f_clang_Group>; def fdiagnostics_show_template_tree : Flag<["-"], "fdiagnostics-show-template-tree">, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index d7bdeb3981cf8..2bc23d9737f5d 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -6048,13 +6048,22 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (getDebugInfo() && TargetDecl && TargetDecl->hasAttr<MSAllocatorAttr>()) getDebugInfo()->addHeapAllocSiteMetadata(CI, RetTy->getPointeeType(), Loc); - // Add metadata if calling an __attribute__((error(""))) or warning fn. - if (TargetDecl && TargetDecl->hasAttr<ErrorAttr>()) { - llvm::ConstantInt *Line = - llvm::ConstantInt::get(Int64Ty, Loc.getRawEncoding()); - llvm::ConstantAsMetadata *MD = llvm::ConstantAsMetadata::get(Line); - llvm::MDTuple *MDT = llvm::MDNode::get(getLLVMContext(), {MD}); - CI->setMetadata("srcloc", MDT); + // Add srcloc metadata for __attribute__((error/warning)) diagnostics. + // In heuristic mode, also track inline/static calls for inlining chain. + if (TargetDecl) { + bool NeedSrcLoc = TargetDecl->hasAttr<ErrorAttr>(); + if (!NeedSrcLoc && CGM.getCodeGenOpts().getInliningChain() == + CodeGenOptions::Inlining_Heuristic) { + if (const auto *FD = dyn_cast<FunctionDecl>(TargetDecl)) + NeedSrcLoc = FD->isInlined() || FD->isInlineSpecified() || + FD->hasAttr<AlwaysInlineAttr>() || + FD->getStorageClass() == SC_Static; + } + if (NeedSrcLoc) { + auto *Line = llvm::ConstantInt::get(Int64Ty, Loc.getRawEncoding()); + auto *MD = llvm::ConstantAsMetadata::get(Line); + CI->setMetadata("srcloc", llvm::MDNode::get(getLLVMContext(), {MD})); + } } // 4. Finish the call. diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index 60d6b7fa009e7..57adcd626839f 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -734,6 +734,37 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) { ? diag::err_fe_backend_error_attr : diag::warn_fe_backend_warning_attr) << llvm::demangle(D.getFunctionName()) << D.getNote(); + + if (CodeGenOpts.getInliningChain() == CodeGenOptions::Inlining_None) + return; + + auto EmitNote = [&](SourceLocation Loc, StringRef FuncName, bool IsFirst) { + if (!Loc.isValid()) + Loc = LocCookie; + unsigned DiagID = + IsFirst ? diag::note_fe_backend_in : diag::note_fe_backend_inlined; + Diags.Report(Loc, DiagID) << llvm::demangle(FuncName.str()); + }; + + if (CodeGenOpts.getInliningChain() == CodeGenOptions::Inlining_Debug) { + SourceManager &SM = Context->getSourceManager(); + FileManager &FM = SM.getFileManager(); + for (const auto &[I, Info] : llvm::enumerate(D.getDebugInlineChain())) { + SourceLocation Loc; + if (Info.Line > 0) + if (auto FE = FM.getOptionalFileRef(Info.Filename)) + Loc = SM.translateFileLineCol(*FE, Info.Line, + Info.Column ? Info.Column : 1); + EmitNote(Loc, Info.FuncName, I == 0); + } + return; + } + + for (const auto &[I, Entry] : llvm::enumerate(D.getInliningDecisions())) { + SourceLocation Loc = + I == 0 ? LocCookie : SourceLocation::getFromRawEncoding(Entry.second); + EmitNote(Loc, Entry.first, I == 0); + } } void BackendConsumer::MisExpectDiagHandler( diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 9ead69bd2fe48..2f232d296ce3d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4241,6 +4241,13 @@ static void RenderDiagnosticsOptions(const Driver &D, const ArgList &Args, CmdArgs.push_back(Args.MakeArgString(Opt)); } + if (const Arg *A = + Args.getLastArg(options::OPT_fdiagnostics_show_inlining_chain_EQ)) { + std::string Opt = + std::string("-fdiagnostics-show-inlining-chain=") + A->getValue(); + CmdArgs.push_back(Args.MakeArgString(Opt)); + } + if (const Arg *A = Args.getLastArg(options::OPT_fdiagnostics_format_EQ)) { CmdArgs.push_back("-fdiagnostics-format"); CmdArgs.push_back(A->getValue()); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 477406f2526c0..91f5aa160dd47 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2253,6 +2253,14 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, Opts.getDebugInfo() == llvm::codegenoptions::NoDebugInfo) Opts.setDebugInfo(llvm::codegenoptions::LocTrackingOnly); + // Debug mode for inlining chain diagnostics requires at least + // -gline-directives-only to track inlining locations via DILocation. + if (Opts.getInliningChain() == CodeGenOptions::Inlining_Debug && + Opts.getDebugInfo() < llvm::codegenoptions::DebugDirectivesOnly) { + Diags.Report(diag::warn_fe_inlining_debug_requires_g); + Opts.setInliningChain(CodeGenOptions::Inlining_Heuristic); + } + // Parse -fsanitize-recover= arguments. // FIXME: Report unrecoverable sanitizers incorrectly specified here. parseSanitizerKinds("-fsanitize-recover=", diff --git a/clang/test/Frontend/backend-attribute-inlining-cross-tu.c b/clang/test/Frontend/backend-attribute-inlining-cross-tu.c new file mode 100644 index 0000000000000..6c1cee3e9e212 --- /dev/null +++ b/clang/test/Frontend/backend-attribute-inlining-cross-tu.c @@ -0,0 +1,64 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: not %clang -O2 -fdiagnostics-show-inlining-chain=heuristic -S %t/main.c -I%t -o /dev/null 2>&1 | FileCheck %s + +// Cross-TU inlining: header functions inlined into source file. + +//--- overflow.h +__attribute__((warning("write overflow"))) +void __write_overflow(void); + +__attribute__((error("read overflow"))) +void __read_overflow(void); + +static inline void check_write(int size) { + if (size > 100) + __write_overflow(); +} + +static inline void check_read(int size) { + if (size > 50) + __read_overflow(); +} + +static inline void check_both(int size) { + check_write(size); + check_read(size); +} + +//--- main.c +#include "overflow.h" + +void test_simple_cross_tu(void) { + check_write(200); +} +// CHECK: warning: call to '__write_overflow' declared with 'warning' attribute: write overflow +// CHECK: note: called by function 'check_write' +// CHECK: main.c:{{.*}}: note: inlined by function 'test_simple_cross_tu' + +// Nested cross-TU inlining (header -> header -> source). +static inline void local_wrapper(int x) { + check_both(x); +} + +void test_nested_cross_tu(void) { + local_wrapper(200); +} +// CHECK: warning: call to '__write_overflow' declared with 'warning' attribute: write overflow +// CHECK: note: called by function 'check_write' +// CHECK: overflow.h:{{.*}}: note: inlined by function 'check_both' +// CHECK: main.c:{{.*}}: note: inlined by function 'local_wrapper' +// CHECK: main.c:{{.*}}: note: inlined by function 'test_nested_cross_tu' + +// CHECK: error: call to '__read_overflow' declared with 'error' attribute: read overflow +// CHECK: note: called by function 'check_read' +// CHECK: overflow.h:{{.*}}: note: inlined by function 'check_both' +// CHECK: main.c:{{.*}}: note: inlined by function 'local_wrapper' +// CHECK: main.c:{{.*}}: note: inlined by function 'test_nested_cross_tu' + +void test_error_cross_tu(void) { + check_read(100); +} +// CHECK: error: call to '__read_overflow' declared with 'error' attribute: read overflow +// CHECK: note: called by function 'check_read' +// CHECK: main.c:{{.*}}: note: inlined by function 'test_error_cross_tu' diff --git a/clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c b/clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c new file mode 100644 index 0000000000000..284b23d5cace2 --- /dev/null +++ b/clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=heuristic %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=HEURISTIC +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=debug -debug-info-kind=line-directives-only %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DEBUG + +__attribute__((warning("dangerous function"))) +void dangerous(void); + +// Non-static, non-inline functions that get inlined at -O2. +void wrapper(void) { + dangerous(); +} + +void middle(void) { + wrapper(); +} + +void caller(void) { + middle(); +} + +// HEURISTIC: :9:{{.*}}: warning: call to 'dangerous' +// HEURISTIC: :9:{{.*}}: note: called by function 'wrapper' +// HEURISTIC: :9:{{.*}}: note: inlined by function 'middle' +// HEURISTIC: :9:{{.*}}: note: inlined by function 'caller' + +// DEBUG: :9:{{.*}}: warning: call to 'dangerous' +// DEBUG: :9:{{.*}}: note: called by function 'wrapper' +// DEBUG: :13:{{.*}}: note: inlined by function 'middle' +// DEBUG: :17:{{.*}}: note: inlined by function 'caller' diff --git a/clang/test/Frontend/backend-attribute-inlining-modes.c b/clang/test/Frontend/backend-attribute-inlining-modes.c new file mode 100644 index 0000000000000..ebee1208a17dd --- /dev/null +++ b/clang/test/Frontend/backend-attribute-inlining-modes.c @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -O2 -emit-obj %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=NONE-DEFAULT +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=none %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=NONE-EXPLICIT +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=heuristic %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=HEURISTIC +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=debug -debug-info-kind=line-directives-only %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DEBUG +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=debug %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=FALLBACK + +// Tests all three modes plus fallback behavior when debug info +// (-gline-directives-only) is missing. + +__attribute__((warning("do not call"))) +void bad_func(void); + +static inline void level1(void) { + bad_func(); +} + +static inline void level2(void) { + level1(); +} + +void entry(void) { + level2(); +} + +// none (default): warning only, no inlining notes. +// NONE-DEFAULT: warning: call to 'bad_func' +// NONE-DEFAULT-NOT: note: + +// none (explicit): same as default. +// NONE-EXPLICIT: warning: call to 'bad_func' +// NONE-EXPLICIT-NOT: note: + +// HEURISTIC: :14:{{.*}}: warning: call to 'bad_func' +// HEURISTIC: :14:{{.*}}: note: called by function 'level1' +// HEURISTIC: :18:{{.*}}: note: inlined by function 'level2' +// HEURISTIC: :22:{{.*}}: note: inlined by function 'entry' + +// DEBUG: :14:{{.*}}: warning: call to 'bad_func' +// DEBUG: :14:{{.*}}: note: called by function 'level1' +// DEBUG: :18:{{.*}}: note: inlined by function 'level2' +// DEBUG: :22:{{.*}}: note: inlined by function 'entry' + +// FALLBACK: warning: '-fdiagnostics-show-inlining-chain=debug' requires at least '-gline-directives-only'; falling back to 'heuristic' mode +// FALLBACK: :14:{{.*}}: warning: call to 'bad_func' +// FALLBACK: :14:{{.*}}: note: called by function 'level1' +// FALLBACK: :18:{{.*}}: note: inlined by function 'level2' +// FALLBACK: :22:{{.*}}: note: inlined by function 'entry' diff --git a/clang/test/Frontend/backend-attribute-inlining.c b/clang/test/Frontend/backend-attribute-inlining.c new file mode 100644 index 0000000000000..5f91a38ebdc57 --- /dev/null +++ b/clang/test/Frontend/backend-attribute-inlining.c @@ -0,0 +1,109 @@ +// RUN: not %clang -O2 -fdiagnostics-show-inlining-chain=heuristic -S %s -o /dev/null 2>&1 | FileCheck %s + +// Single-level inlining with warning attribute. +__attribute__((warning("do not call directly"))) +void __warn_single(void); + +static inline void warn_wrapper(void) { + __warn_single(); +} + +void test_single_level(void) { + warn_wrapper(); +} +// CHECK: warning: call to '__warn_single' declared with 'warning' attribute: do not call directly +// CHECK: note: called by function 'warn_wrapper' +// CHECK: :12:{{.*}}: note: inlined by function 'test_single_level' + +// Error attribute with inlining. +__attribute__((error("never call this"))) +void __error_func(void); + +static inline void error_wrapper(void) { + __error_func(); +} + +void test_error_inlined(void) { + error_wrapper(); +} +// CHECK: error: call to '__error_func' declared with 'error' attribute: never call this +// CHECK: note: called by function 'error_wrapper' +// CHECK: :27:{{.*}}: note: inlined by function 'test_error_inlined' + +// Deep nesting (5 levels). +__attribute__((warning("deep call"))) +void __warn_deep(void); + +static inline void deep1(void) { __warn_deep(); } +static inline void deep2(void) { deep1(); } +static inline void deep3(void) { deep2(); } +static inline void deep4(void) { deep3(); } +static inline void deep5(void) { deep4(); } + +void test_deep_nesting(void) { + deep5(); +} +// CHECK: warning: call to '__warn_deep' declared with 'warning' attribute: deep call +// CHECK: note: called by function 'deep1' +// CHECK: :38:{{.*}}: note: inlined by function 'deep2' +// CHECK: :39:{{.*}}: note: inlined by function 'deep3' +// CHECK: :40:{{.*}}: note: inlined by function 'deep4' +// CHECK: :41:{{.*}}: note: inlined by function 'deep5' +// CHECK: :44:{{.*}}: note: inlined by function 'test_deep_nesting' + +// Multiple call sites produce distinct diagnostics. +__attribute__((warning("deprecated"))) +void __warn_multi(void); + +static inline void multi_wrapper(void) { + __warn_multi(); +} + +void call_site_a(void) { multi_wrapper(); } +void call_site_b(void) { multi_wrapper(); } +void call_site_c(void) { multi_wrapper(); } + +// CHECK: warning: call to '__warn_multi' declared with 'warning' attribute: deprecated +// CHECK: note: called by function 'multi_wrapper' +// CHECK: :62:{{.*}}: note: inlined by function 'call_site_a' + +// CHECK: warning: call to '__warn_multi' declared with 'warning' attribute: deprecated +// CHECK: note: called by function 'multi_wrapper' +// CHECK: :63:{{.*}}: note: inlined by function 'call_site_b' + +// CHECK: warning: call to '__warn_multi' declared with 'warning' attribute: deprecated +// CHECK: note: called by function 'multi_wrapper' +// CHECK: :64:{{.*}}: note: inlined by function 'call_site_c' + +// Different nesting depths from same inner function. +__attribute__((warning("mixed depth"))) +void __warn_mixed(void); + +static inline void mixed_inner(void) { __warn_mixed(); } +static inline void mixed_middle(void) { mixed_inner(); } + +void shallow(void) { mixed_inner(); } +void deep(void) { mixed_middle(); } + +// CHECK: warning: call to '__warn_mixed' declared with 'warning' attribute: mixed depth +// CHECK: note: called by function 'mixed_inner' +// CHECK: :85:{{.*}}: note: inlined by function 'shallow' + +// CHECK: warning: call to '__warn_mixed' declared with 'warning' attribute: mixed depth +// CHECK: note: called by function 'mixed_inner' +// CHECK: :83:{{.*}}: note: inlined by function 'mixed_middle' +// CHECK: :86:{{.*}}: note: inlined by function 'deep' + +// Incidental inlining (function not marked inline/static). +// The "inlined by" note has no location since heuristic mode doesn't track it. +__attribute__((warning("incidental"))) +void __warn_incidental(void); + +void not_marked_inline(void) { __warn_incidental(); } + +void test_incidental(void) { not_marked_inline(); } + +// CHECK: warning: call to '__warn_incidental' declared with 'warning' attribute: incidental +// CHECK: note: called by function 'not_marked_inline' +// CHECK: note: inlined by function 'test_incidental' +// CHECK-NOT: :{{.*}}: note: inlined by function 'test_incidental' diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h index 8f6fb4da0c839..0daaa462ba715 100644 --- a/llvm/include/llvm/IR/DiagnosticInfo.h +++ b/llvm/include/llvm/IR/DiagnosticInfo.h @@ -1192,19 +1192,41 @@ class LLVM_ABI DiagnosticInfoSrcMgr : public DiagnosticInfo { LLVM_ABI void diagnoseDontCall(const CallInst &CI); +/// Inlining location extracted from debug info. +struct DebugInlineInfo { + StringRef FuncName; + StringRef Filename; + unsigned Line; + unsigned Column; +}; + class LLVM_ABI DiagnosticInfoDontCall : public DiagnosticInfo { StringRef CalleeName; StringRef Note; uint64_t LocCookie; + MDNode *InlinedFromMD = nullptr; + SmallVector<DebugInlineInfo, 4> DebugInlineChain; public: DiagnosticInfoDontCall(StringRef CalleeName, StringRef Note, - DiagnosticSeverity DS, uint64_t LocCookie) + DiagnosticSeverity DS, uint64_t LocCookie, + MDNode *InlinedFromMD = nullptr) : DiagnosticInfo(DK_DontCall, DS), CalleeName(CalleeName), Note(Note), - LocCookie(LocCookie) {} + LocCookie(LocCookie), InlinedFromMD(InlinedFromMD) {} + StringRef getFunctionName() const { return CalleeName; } StringRef getNote() const { return Note; } uint64_t getLocCookie() const { return LocCookie; } + MDNode *getInlinedFromMD() const { return InlinedFromMD; } + SmallVector<std::pair<StringRef, uint64_t>> getInliningDecisions() const; + + void setDebugInlineChain(SmallVector<DebugInlineInfo, 4> &&Chain) { + DebugInlineChain = std::move(Chain); + } + ArrayRef<DebugInlineInfo> getDebugInlineChain() const { + return DebugInlineChain; + } + void print(DiagnosticPrinter &DP) const override; static bool classof(const DiagnosticInfo *DI) { return DI->getKind() == DK_DontCall; diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp index 8e6d654f6afb3..10ee9f27058f8 100644 --- a/llvm/lib/IR/DiagnosticInfo.cpp +++ b/llvm/lib/IR/DiagnosticInfo.cpp @@ -487,8 +487,27 @@ void llvm::diagnoseDontCall(const CallInst &CI) { if (MDNode *MD = CI.getMetadata("srcloc")) LocCookie = mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue(); + MDNode *InlinedFromMD = CI.getMetadata("inlined.from"); DiagnosticInfoDontCall D(F->getName(), A.getValueAsString(), Sev, - LocCookie); + LocCookie, InlinedFromMD); + + if (const DebugLoc &DL = CI.getDebugLoc()) { + SmallVector<DebugInlineInfo, 4> DebugChain; + auto AddLocation = [&](const DILocation *Loc) { + if (auto *Scope = Loc->getScope()) + if (auto *SP = Scope->getSubprogram()) + DebugChain.push_back({SP->getName(), Loc->getFilename(), + Loc->getLine(), Loc->getColumn()}); + }; + if (const DILocation *Loc = DL.get()) { + AddLocation(Loc); + for (const DILocation *InlinedAt = Loc->getInlinedAt(); InlinedAt; + InlinedAt = InlinedAt->getInlinedAt()) + AddLocation(InlinedAt); + } + D.setDebugInlineChain(std::move(DebugChain)); + } + F->getContext().diagnose(D); } } @@ -503,3 +522,20 @@ void DiagnosticInfoDontCall::print(DiagnosticPrinter &DP) const { if (!getNote().empty()) DP << ": " << getNote(); } + +SmallVector<std::pair<StringRef, uint64_t>> +DiagnosticInfoDontCall::getInliningDecisions() const { + SmallVector<std::pair<StringRef, uint64_t>> Chain; + if (!InlinedFromMD) + return Chain; + + for (unsigned I = 0, E = InlinedFromMD->getNumOperands(); I + 1 < E; I += 2) { + auto *NameMD = dyn_cast<MDString>(InlinedFromMD->getOperand(I)); + auto *LocMD = + mdconst::dyn_extract<ConstantInt>(InlinedFromMD->getOperand(I + 1)); + if (NameMD && !NameMD->getString().empty()) + Chain.emplace_back(NameMD->getString(), + LocMD ? LocMD->getZExtValue() : 0); + } + return Chain; +} diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp index f49fbf8807bac..7d23c9ba80f86 100644 --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -974,6 +974,46 @@ static void PropagateCallSiteMetadata(CallBase &CB, Function::iterator FStart, } } +/// Track inlining chain via inlined.from metadata for dontcall diagnostics. +static void PropagateInlinedFromMetadata(CallBase &CB, StringRef CalledFuncName, + StringRef CallerFuncName, + Function::iterator FStart, + Function::iterator FEnd) { + LLVMContext &Ctx = CB.getContext(); + uint64_t InlineSiteLoc = 0; + if (auto *MD = CB.getMetadata("srcloc")) + if (auto *CI = mdconst::dyn_extract<ConstantInt>(MD->getOperand(0))) + InlineSiteLoc = CI->getZExtValue(); + + auto *I64Ty = Type::getInt64Ty(Ctx); + auto MakeMDInt = [&](uint64_t V) { + return ConstantAsMetadata::get(ConstantInt::get(I64Ty, V)); + }; + + for (BasicBlock &BB : make_range(FStart, FEnd)) { + for (Instruction &I : BB) { + auto *CI = dyn_cast<CallInst>(&I); + if (!CI || !CI->getMetadata("srcloc")) + continue; + auto *Callee = CI->getCalledFunction(); + if (!Callee || (!Callee->hasFnAttribute("dontcall-error") && + !Callee->hasFnAttribute("dontcall-warn"))) + continue; + + SmallVector<Metadata *, 8> Ops; + if (MDNode *Existing = CI->getMetadata("inlined.from")) + append_range(Ops, Existing->operands()); + else { + Ops.push_back(MDString::get(Ctx, CalledFuncName)); + Ops.push_back(MakeMDInt(0)); + } + Ops.push_back(MDString::get(Ctx, CallerFuncName)); + Ops.push_back(MakeMDInt(InlineSiteLoc)); + CI->setMetadata("inlined.from", MDNode::get(Ctx, Ops)); + } + } +} + /// Bundle operands of the inlined function must be added to inlined call sites. static void PropagateOperandBundles(Function::iterator InlinedBB, Instruction *CallSiteEHPad) { @@ -2830,6 +2870,10 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI, // Propagate metadata on the callsite if necessary. PropagateCallSiteMetadata(CB, FirstNewBlock, Caller->end()); + // Propagate inlined.from metadata for dontcall diagnostics. + PropagateInlinedFromMetadata(CB, CalledFunc->getName(), Caller->getName(), + FirstNewBlock, Caller->end()); + // Register any cloned assumptions. if (IFI.GetAssumptionCache) for (BasicBlock &NewBlock : >From e866945f1c61774e745ec3a793629104ef473fee Mon Sep 17 00:00:00 2001 From: Justin Stitt <[email protected]> Date: Thu, 8 Jan 2026 10:27:15 -0800 Subject: [PATCH 2/2] use scoped enum, binary flag, better docs based on review: 1) use scoped enum 2) switch over to a binary flag instead of a ternary one. 3) improve docs and help text 4) add note regarding -gline-directives-only 5) use modern attribute spellings Signed-off-by: Justin Stitt <[email protected]> --- clang/docs/ReleaseNotes.rst | 13 +++-- clang/include/clang/Basic/AttrDocs.td | 11 +++- clang/include/clang/Basic/CodeGenOptions.def | 4 +- clang/include/clang/Basic/CodeGenOptions.h | 8 --- .../clang/Basic/DiagnosticFrontendKinds.td | 8 +-- clang/include/clang/Options/Options.td | 22 +++---- clang/lib/CodeGen/CGCall.cpp | 11 ++-- clang/lib/CodeGen/CodeGenAction.cpp | 16 +++++- clang/lib/Driver/ToolChains/Clang.cpp | 10 ++-- clang/lib/Frontend/CompilerInvocation.cpp | 8 --- .../backend-attribute-inlining-cross-tu.c | 9 ++- ...nd-attribute-inlining-debug-vs-heuristic.c | 31 ++++++---- .../backend-attribute-inlining-modes.c | 57 +++++++++---------- .../Frontend/backend-attribute-inlining.c | 17 +++--- 14 files changed, 115 insertions(+), 110 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cf17f8a052cbb..7bdf72e22948b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -348,12 +348,13 @@ New Compiler Flags - New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``. - The ``-resource-dir`` option is now displayed in the list of options shown by ``--help``. - New option ``-fmatrix-memory-layout`` added to control the memory layout of Clang matrix types. (e.g. ``-fmatrix-memory-layout=column-major`` or ``-fmatrix-memory-layout=row-major``). -- New option ``-fdiagnostics-show-inlining-chain=`` added to show inlining chain - notes for ``__attribute__((warning))`` and ``__attribute__((error))`` - diagnostics. When a function with these attributes is called from an inlined - context, Clang can now show which functions were inlined to reach the call. - Modes: ``none`` (default), ``heuristic`` (uses metadata tracking), ``debug`` - (uses debug info for accurate source locations). +- New option ``-fdiagnostics-show-inlining-chain`` added to show inlining chain + notes for ``[[gnu::warning]]`` and ``[[gnu::error]]`` diagnostics. When a + function with these attributes is called from an inlined context, Clang can + now show which functions were inlined to reach the call. When debug info is + available (``-gline-directives-only`` or higher), accurate source locations + are used; otherwise, a heuristic fallback is used with a note suggesting + how to enable debug info for better accuracy. Lanai Support ^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 97331b1175cc9..f9c6d6ccd606f 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8399,11 +8399,16 @@ pointing to precise locations of the call site in the source. } When the call occurs through inlined functions, the -``-fdiagnostics-show-inlining-chain=`` option can be used to show the +``-fdiagnostics-show-inlining-chain`` option can be used to show the inlining chain that led to the call. This helps identify which call site triggered the diagnostic when the attributed function is called from -multiple locations through inline functions. See ``clang --help`` for -available modes. +multiple locations through inline functions. + +When enabled, this option automatically uses debug info for accurate source +locations if available (requires at least ``-gline-directives-only``), or +falls back to a heuristic based on metadata tracking. When falling back, a +note is emitted suggesting ``-gline-directives-only`` for more accurate +locations. }]; } diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 223e803a22c6c..30e6612d84e55 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -76,8 +76,8 @@ CODEGENOPT(CallGraphSection, 1, 0, Benign) ///< Emit a call graph section into t ///< object file. CODEGENOPT(EmitCallSiteInfo, 1, 0, Benign) ///< Emit call site info only in the case of ///< '-g' + 'O>0' level. -/// Tracking inlining chain in __attribute__((warning)) and __attribute__((error)) diagnostics -ENUM_CODEGENOPT(InliningChain, InliningChainKind, 2, Inlining_None, Benign) +/// Show inlining chain notes for [[gnu::warning/error]] diagnostics. +CODEGENOPT(ShowInliningChain, 1, 0, Benign) CODEGENOPT(IndirectTlsSegRefs, 1, 0, Benign) ///< Set when -mno-tls-direct-seg-refs ///< is specified. CODEGENOPT(DisableTailCalls , 1, 0, Benign) ///< Do not emit tail calls. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index f34de98126768..c60ca507ff917 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -120,14 +120,6 @@ class CodeGenOptions : public CodeGenOptionsBase { Embed_Marker // Embed a marker as a placeholder for bitcode. }; - /// Inlining chain tracking __attribute__((warning)) or __attribute__((error)) - /// diagnostics - enum InliningChainKind { - Inlining_Heuristic, /// Track via srcloc metadata on inline/static calls. - Inlining_Debug, /// Track via debug info (DILocation inlinedAt chain). - Inlining_None /// No tracking, no inlining notes shown (default). - }; - enum class ExtendVariableLivenessKind { None, This, diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 3a7b7ccc73ab8..ff924b3ace3dd 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -96,10 +96,10 @@ def warn_fe_backend_warning_attr : InGroup<BackendWarningAttributes>; def note_fe_backend_in : Note<"called by function '%0'">, BackendInfo; def note_fe_backend_inlined : Note<"inlined by function '%0'">, BackendInfo; -def warn_fe_inlining_debug_requires_g - : Warning<"'-fdiagnostics-show-inlining-chain=debug' requires at least " - "'-gline-directives-only'; falling back to 'heuristic' mode">, - InGroup<BackendWarningAttributes>; +def note_fe_backend_inlining_debug_info + : Note<"use '-gline-directives-only' or higher for more accurate " + "inlining chain locations">, + BackendInfo; def warn_toc_unsupported_type : Warning<"-mtocdata option is ignored " "for %0 because %1">, InGroup<BackendWarningAttributes>; diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 92bbb34e6a1eb..89a72abaa2a50 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -2158,21 +2158,13 @@ defm diagnostics_show_note_include_stack : BoolFOption<"diagnostics-show-note-in DiagnosticOpts<"ShowNoteIncludeStack">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption], "Display include stacks for diagnostic notes">, NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>; -def fdiagnostics_show_inlining_chain_EQ - : Joined<["-"], "fdiagnostics-show-inlining-chain=">, - Group<f_Group>, - Visibility<[ClangOption, CC1Option]>, - HelpText<"Show inlining chain notes for __attribute__((warning/error)) " - "diagnostics: " - "'none' (default) shows no chain, 'heuristic' uses best-effort " - "metadata tracking, " - "'debug' uses debug info for accurate locations with increased " - "compile-time overhead">, - Values<"heuristic,debug,none">, - NormalizedValuesScope<"CodeGenOptions">, - NormalizedValues<["Inlining_Heuristic", "Inlining_Debug", - "Inlining_None"]>, - MarshallingInfoEnum<CodeGenOpts<"InliningChain">, "Inlining_None">; +defm diagnostics_show_inlining_chain + : BoolFOption<"diagnostics-show-inlining-chain", + CodeGenOpts<"ShowInliningChain">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], + "Show inlining chain notes for " + "[[gnu::warning/error]] diagnostics">, + NegFlag<SetFalse, [], [ClangOption, CC1Option]>>; def fdiagnostics_format_EQ : Joined<["-"], "fdiagnostics-format=">, Group<f_clang_Group>; def fdiagnostics_show_category_EQ : Joined<["-"], "fdiagnostics-show-category=">, Group<f_clang_Group>; def fdiagnostics_show_template_tree : Flag<["-"], "fdiagnostics-show-template-tree">, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 2bc23d9737f5d..76e504a5aa47b 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -6048,16 +6048,17 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (getDebugInfo() && TargetDecl && TargetDecl->hasAttr<MSAllocatorAttr>()) getDebugInfo()->addHeapAllocSiteMetadata(CI, RetTy->getPointeeType(), Loc); - // Add srcloc metadata for __attribute__((error/warning)) diagnostics. - // In heuristic mode, also track inline/static calls for inlining chain. + // Add srcloc metadata for [[gnu::error/warning]] diagnostics. + // When ShowInliningChain is enabled, also track inline/static calls for the + // heuristic fallback when debug info is not available. if (TargetDecl) { bool NeedSrcLoc = TargetDecl->hasAttr<ErrorAttr>(); - if (!NeedSrcLoc && CGM.getCodeGenOpts().getInliningChain() == - CodeGenOptions::Inlining_Heuristic) { + if (!NeedSrcLoc && CGM.getCodeGenOpts().ShowInliningChain) { if (const auto *FD = dyn_cast<FunctionDecl>(TargetDecl)) NeedSrcLoc = FD->isInlined() || FD->isInlineSpecified() || FD->hasAttr<AlwaysInlineAttr>() || - FD->getStorageClass() == SC_Static; + FD->getStorageClass() == SC_Static || + FD->isInAnonymousNamespace(); } if (NeedSrcLoc) { auto *Line = llvm::ConstantInt::get(Int64Ty, Loc.getRawEncoding()); diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index 57adcd626839f..513767a6e6af0 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -735,7 +735,7 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) { : diag::warn_fe_backend_warning_attr) << llvm::demangle(D.getFunctionName()) << D.getNote(); - if (CodeGenOpts.getInliningChain() == CodeGenOptions::Inlining_None) + if (!CodeGenOpts.ShowInliningChain) return; auto EmitNote = [&](SourceLocation Loc, StringRef FuncName, bool IsFirst) { @@ -746,7 +746,8 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) { Diags.Report(Loc, DiagID) << llvm::demangle(FuncName.str()); }; - if (CodeGenOpts.getInliningChain() == CodeGenOptions::Inlining_Debug) { + // Try debug info first for accurate source locations. + if (!D.getDebugInlineChain().empty()) { SourceManager &SM = Context->getSourceManager(); FileManager &FM = SM.getFileManager(); for (const auto &[I, Info] : llvm::enumerate(D.getDebugInlineChain())) { @@ -760,11 +761,20 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) { return; } - for (const auto &[I, Entry] : llvm::enumerate(D.getInliningDecisions())) { + // Fall back to heuristic (srcloc metadata) when debug info is unavailable. + auto InliningDecisions = D.getInliningDecisions(); + if (InliningDecisions.empty()) + return; + + for (const auto &[I, Entry] : llvm::enumerate(InliningDecisions)) { SourceLocation Loc = I == 0 ? LocCookie : SourceLocation::getFromRawEncoding(Entry.second); EmitNote(Loc, Entry.first, I == 0); } + + // Suggest enabling debug info (at least -gline-directives-only) for more + // accurate locations. + Diags.Report(LocCookie, diag::note_fe_backend_inlining_debug_info); } void BackendConsumer::MisExpectDiagHandler( diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 2f232d296ce3d..8eb85414664ae 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4242,10 +4242,12 @@ static void RenderDiagnosticsOptions(const Driver &D, const ArgList &Args, } if (const Arg *A = - Args.getLastArg(options::OPT_fdiagnostics_show_inlining_chain_EQ)) { - std::string Opt = - std::string("-fdiagnostics-show-inlining-chain=") + A->getValue(); - CmdArgs.push_back(Args.MakeArgString(Opt)); + Args.getLastArg(options::OPT_fdiagnostics_show_inlining_chain, + options::OPT_fno_diagnostics_show_inlining_chain)) { + if (A->getOption().matches(options::OPT_fdiagnostics_show_inlining_chain)) + CmdArgs.push_back("-fdiagnostics-show-inlining-chain"); + else + CmdArgs.push_back("-fno-diagnostics-show-inlining-chain"); } if (const Arg *A = Args.getLastArg(options::OPT_fdiagnostics_format_EQ)) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 91f5aa160dd47..477406f2526c0 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2253,14 +2253,6 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, Opts.getDebugInfo() == llvm::codegenoptions::NoDebugInfo) Opts.setDebugInfo(llvm::codegenoptions::LocTrackingOnly); - // Debug mode for inlining chain diagnostics requires at least - // -gline-directives-only to track inlining locations via DILocation. - if (Opts.getInliningChain() == CodeGenOptions::Inlining_Debug && - Opts.getDebugInfo() < llvm::codegenoptions::DebugDirectivesOnly) { - Diags.Report(diag::warn_fe_inlining_debug_requires_g); - Opts.setInliningChain(CodeGenOptions::Inlining_Heuristic); - } - // Parse -fsanitize-recover= arguments. // FIXME: Report unrecoverable sanitizers incorrectly specified here. parseSanitizerKinds("-fsanitize-recover=", diff --git a/clang/test/Frontend/backend-attribute-inlining-cross-tu.c b/clang/test/Frontend/backend-attribute-inlining-cross-tu.c index 6c1cee3e9e212..cf259cc77f232 100644 --- a/clang/test/Frontend/backend-attribute-inlining-cross-tu.c +++ b/clang/test/Frontend/backend-attribute-inlining-cross-tu.c @@ -1,14 +1,14 @@ // RUN: rm -rf %t // RUN: split-file %s %t -// RUN: not %clang -O2 -fdiagnostics-show-inlining-chain=heuristic -S %t/main.c -I%t -o /dev/null 2>&1 | FileCheck %s +// RUN: not %clang -O2 -fdiagnostics-show-inlining-chain -S %t/main.c -I%t -o /dev/null 2>&1 | FileCheck %s // Cross-TU inlining: header functions inlined into source file. //--- overflow.h -__attribute__((warning("write overflow"))) +[[gnu::warning("write overflow")]] void __write_overflow(void); -__attribute__((error("read overflow"))) +[[gnu::error("read overflow")]] void __read_overflow(void); static inline void check_write(int size) { @@ -62,3 +62,6 @@ void test_error_cross_tu(void) { // CHECK: error: call to '__read_overflow' declared with 'error' attribute: read overflow // CHECK: note: called by function 'check_read' // CHECK: main.c:{{.*}}: note: inlined by function 'test_error_cross_tu' + +// Fallback note should appear (no debug info). +// CHECK: note: use '-gline-directives-only' or higher for more accurate inlining chain locations diff --git a/clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c b/clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c index 284b23d5cace2..4760b95f49c39 100644 --- a/clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c +++ b/clang/test/Frontend/backend-attribute-inlining-debug-vs-heuristic.c @@ -1,7 +1,14 @@ -// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=heuristic %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=HEURISTIC -// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=debug -debug-info-kind=line-directives-only %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DEBUG +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=HEURISTIC +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain -debug-info-kind=line-directives-only %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DEBUG -__attribute__((warning("dangerous function"))) +// Verify auto-selection works between debug info and heuristic fallback. When +// we have at least -gline-directives-only we can use DILocation for accurate +// inline locations. + +// Without that debug info we fall back to a heuristic approach using srcloc +// metadata. + +[[gnu::warning("dangerous function")]] void dangerous(void); // Non-static, non-inline functions that get inlined at -O2. @@ -17,12 +24,14 @@ void caller(void) { middle(); } -// HEURISTIC: :9:{{.*}}: warning: call to 'dangerous' -// HEURISTIC: :9:{{.*}}: note: called by function 'wrapper' -// HEURISTIC: :9:{{.*}}: note: inlined by function 'middle' -// HEURISTIC: :9:{{.*}}: note: inlined by function 'caller' +// HEURISTIC: :16:{{.*}}: warning: call to 'dangerous' +// HEURISTIC: :16:{{.*}}: note: called by function 'wrapper' +// HEURISTIC: :16:{{.*}}: note: inlined by function 'middle' +// HEURISTIC: :16:{{.*}}: note: inlined by function 'caller' +// HEURISTIC: note: use '-gline-directives-only' or higher for more accurate inlining chain locations -// DEBUG: :9:{{.*}}: warning: call to 'dangerous' -// DEBUG: :9:{{.*}}: note: called by function 'wrapper' -// DEBUG: :13:{{.*}}: note: inlined by function 'middle' -// DEBUG: :17:{{.*}}: note: inlined by function 'caller' +// DEBUG: :16:{{.*}}: warning: call to 'dangerous' +// DEBUG: :16:{{.*}}: note: called by function 'wrapper' +// DEBUG: :20:{{.*}}: note: inlined by function 'middle' +// DEBUG: :24:{{.*}}: note: inlined by function 'caller' +// DEBUG-NOT: note: use '-gline-directives-only' diff --git a/clang/test/Frontend/backend-attribute-inlining-modes.c b/clang/test/Frontend/backend-attribute-inlining-modes.c index ebee1208a17dd..9768845458754 100644 --- a/clang/test/Frontend/backend-attribute-inlining-modes.c +++ b/clang/test/Frontend/backend-attribute-inlining-modes.c @@ -1,13 +1,13 @@ -// RUN: %clang_cc1 -O2 -emit-obj %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=NONE-DEFAULT -// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=none %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=NONE-EXPLICIT -// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=heuristic %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=HEURISTIC -// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=debug -debug-info-kind=line-directives-only %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DEBUG -// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain=debug %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=FALLBACK +// RUN: %clang_cc1 -O2 -emit-obj %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ENABLED-HEURISTIC +// RUN: %clang_cc1 -O2 -emit-obj -fdiagnostics-show-inlining-chain -debug-info-kind=line-directives-only %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ENABLED-DEBUG -// Tests all three modes plus fallback behavior when debug info -// (-gline-directives-only) is missing. +// Test -fdiagnostics-show-inlining-chain behavior: +// - Disabled (default): warning only, no inlining notes. +// - Enabled without debug info: heuristic fallback + suggestion note. +// - Enabled with debug info: accurate locations from DILocation. -__attribute__((warning("do not call"))) +[[gnu::warning("do not call")]] void bad_func(void); static inline void level1(void) { @@ -22,26 +22,21 @@ void entry(void) { level2(); } -// none (default): warning only, no inlining notes. -// NONE-DEFAULT: warning: call to 'bad_func' -// NONE-DEFAULT-NOT: note: - -// none (explicit): same as default. -// NONE-EXPLICIT: warning: call to 'bad_func' -// NONE-EXPLICIT-NOT: note: - -// HEURISTIC: :14:{{.*}}: warning: call to 'bad_func' -// HEURISTIC: :14:{{.*}}: note: called by function 'level1' -// HEURISTIC: :18:{{.*}}: note: inlined by function 'level2' -// HEURISTIC: :22:{{.*}}: note: inlined by function 'entry' - -// DEBUG: :14:{{.*}}: warning: call to 'bad_func' -// DEBUG: :14:{{.*}}: note: called by function 'level1' -// DEBUG: :18:{{.*}}: note: inlined by function 'level2' -// DEBUG: :22:{{.*}}: note: inlined by function 'entry' - -// FALLBACK: warning: '-fdiagnostics-show-inlining-chain=debug' requires at least '-gline-directives-only'; falling back to 'heuristic' mode -// FALLBACK: :14:{{.*}}: warning: call to 'bad_func' -// FALLBACK: :14:{{.*}}: note: called by function 'level1' -// FALLBACK: :18:{{.*}}: note: inlined by function 'level2' -// FALLBACK: :22:{{.*}}: note: inlined by function 'entry' +// Disabled (default): warning only, no inlining notes. +// DISABLED: warning: call to 'bad_func' +// DISABLED-NOT: note: + +// Enabled without debug info: heuristic fallback. +// All notes point to original call site (:14). +// ENABLED-HEURISTIC: :14:{{.*}}: warning: call to 'bad_func' +// ENABLED-HEURISTIC: :14:{{.*}}: note: called by function 'level1' +// ENABLED-HEURISTIC: :18:{{.*}}: note: inlined by function 'level2' +// ENABLED-HEURISTIC: :22:{{.*}}: note: inlined by function 'entry' +// ENABLED-HEURISTIC: note: use '-gline-directives-only' or higher for more accurate inlining chain locations + +// Enabled with debug info: accurate locations. +// ENABLED-DEBUG: :14:{{.*}}: warning: call to 'bad_func' +// ENABLED-DEBUG: :14:{{.*}}: note: called by function 'level1' +// ENABLED-DEBUG: :18:{{.*}}: note: inlined by function 'level2' +// ENABLED-DEBUG: :22:{{.*}}: note: inlined by function 'entry' +// ENABLED-DEBUG-NOT: note: use '-gline-directives-only' diff --git a/clang/test/Frontend/backend-attribute-inlining.c b/clang/test/Frontend/backend-attribute-inlining.c index 5f91a38ebdc57..45a8e601e805f 100644 --- a/clang/test/Frontend/backend-attribute-inlining.c +++ b/clang/test/Frontend/backend-attribute-inlining.c @@ -1,7 +1,7 @@ -// RUN: not %clang -O2 -fdiagnostics-show-inlining-chain=heuristic -S %s -o /dev/null 2>&1 | FileCheck %s +// RUN: not %clang -O2 -fdiagnostics-show-inlining-chain -S %s -o /dev/null 2>&1 | FileCheck %s // Single-level inlining with warning attribute. -__attribute__((warning("do not call directly"))) +[[gnu::warning("do not call directly")]] void __warn_single(void); static inline void warn_wrapper(void) { @@ -16,7 +16,7 @@ void test_single_level(void) { // CHECK: :12:{{.*}}: note: inlined by function 'test_single_level' // Error attribute with inlining. -__attribute__((error("never call this"))) +[[gnu::error("never call this")]] void __error_func(void); static inline void error_wrapper(void) { @@ -31,7 +31,7 @@ void test_error_inlined(void) { // CHECK: :27:{{.*}}: note: inlined by function 'test_error_inlined' // Deep nesting (5 levels). -__attribute__((warning("deep call"))) +[[gnu::warning("deep call")]] void __warn_deep(void); static inline void deep1(void) { __warn_deep(); } @@ -52,7 +52,7 @@ void test_deep_nesting(void) { // CHECK: :44:{{.*}}: note: inlined by function 'test_deep_nesting' // Multiple call sites produce distinct diagnostics. -__attribute__((warning("deprecated"))) +[[gnu::warning("deprecated")]] void __warn_multi(void); static inline void multi_wrapper(void) { @@ -76,7 +76,7 @@ void call_site_c(void) { multi_wrapper(); } // CHECK: :64:{{.*}}: note: inlined by function 'call_site_c' // Different nesting depths from same inner function. -__attribute__((warning("mixed depth"))) +[[gnu::warning("mixed depth")]] void __warn_mixed(void); static inline void mixed_inner(void) { __warn_mixed(); } @@ -96,7 +96,7 @@ void deep(void) { mixed_middle(); } // Incidental inlining (function not marked inline/static). // The "inlined by" note has no location since heuristic mode doesn't track it. -__attribute__((warning("incidental"))) +[[gnu::warning("incidental")]] void __warn_incidental(void); void not_marked_inline(void) { __warn_incidental(); } @@ -107,3 +107,6 @@ void test_incidental(void) { not_marked_inline(); } // CHECK: note: called by function 'not_marked_inline' // CHECK: note: inlined by function 'test_incidental' // CHECK-NOT: :{{.*}}: note: inlined by function 'test_incidental' + +// Fallback note should appear (no debug info). +// CHECK: note: use '-gline-directives-only' or higher for more accurate inlining chain locations _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
