Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang] report inlining decisions with -Wattribute-{warning|error} #73552

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

nickdesaulniers
Copy link
Member

@nickdesaulniers nickdesaulniers commented Nov 27, 2023

Due to inlining, descovering which specific call site to a function with
the attribute "warning" or "error" is painful.

In the IR record inlining decisions in a new metadata tuple "inlined.from" when inlining a callee
that itself contains a call to a dontcall-error or dontcall-warn fn.

Print this info so that it's clearer which call site is problematic.

There's still some limitations with this approach; macro expansion is
not recorded.

Fixes: ClangBuiltLinux/linux#1571

Differential Revision: https://reviews.llvm.org/D141451

Due to inlining, descovering which specific call site to a function with
the attribute "warning" or "error" is painful.

In the IR record inlining decisions in metadata when inlining a callee
that itself contains a call to a dontcall-error or dontcall-warn fn.

Print this info so that it's clearer which call site is problematic.

There's still some limitations with this approach; macro expansion is
not recorded.

Fixes: ClangBuiltLinux/linux#1571

Differential Revision: https://reviews.llvm.org/D141451
@nickdesaulniers nickdesaulniers marked this pull request as draft November 27, 2023 18:05
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen llvm:ir llvm:transforms labels Nov 27, 2023
@nickdesaulniers
Copy link
Member Author

(Initial import from https://reviews.llvm.org/D141451)

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 27, 2023

@llvm/pr-subscribers-llvm-ir
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Nick Desaulniers (nickdesaulniers)

Changes

Due to inlining, descovering which specific call site to a function with
the attribute "warning" or "error" is painful.

In the IR record inlining decisions in metadata when inlining a callee
that itself contains a call to a dontcall-error or dontcall-warn fn.

Print this info so that it's clearer which call site is problematic.

There's still some limitations with this approach; macro expansion is
not recorded.

Fixes: ClangBuiltLinux/linux#1571

Differential Revision: https://reviews.llvm.org/D141451


Full diff: https://github.com/llvm/llvm-project/pull/73552.diff

10 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticFrontendKinds.td (+2)
  • (modified) clang/lib/CodeGen/CodeGenAction.cpp (+10)
  • (modified) clang/test/Frontend/backend-attribute-error-warning-optimize.c (+21)
  • (modified) clang/test/Frontend/backend-attribute-error-warning.c (+6)
  • (modified) clang/test/Frontend/backend-attribute-error-warning.cpp (+12)
  • (modified) llvm/docs/LangRef.rst (+6-2)
  • (modified) llvm/include/llvm/IR/DiagnosticInfo.h (+10-4)
  • (modified) llvm/lib/IR/DiagnosticInfo.cpp (+21-2)
  • (modified) llvm/lib/Transforms/Utils/InlineFunction.cpp (+13)
  • (added) llvm/test/Transforms/Inline/dontcall-attributes.ll (+84)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 715e0c0dc8fa84e..0909b1f59175be9 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -93,6 +93,8 @@ 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'">;
+def note_fe_backend_inlined : Note<"inlined by function '%0'">;
 
 def err_fe_invalid_code_complete_file : Error<
     "cannot locate code-completion file %0">, DefaultFatal;
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index a31a271ed77d1ca..66e040741e2718d 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -52,6 +52,8 @@
 #include "llvm/Transforms/Utils/Cloning.h"
 
 #include <optional>
+#include <string>
+
 using namespace clang;
 using namespace llvm;
 
@@ -794,6 +796,14 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) {
                               ? diag::err_fe_backend_error_attr
                               : diag::warn_fe_backend_warning_attr)
       << llvm::demangle(D.getFunctionName()) << D.getNote();
+
+  SmallVector<std::string, 4> InliningDecisions;
+  D.getInliningDecisions(InliningDecisions);
+  InliningDecisions.push_back(D.getCaller().str());
+  for (auto Dec : llvm::enumerate(InliningDecisions))
+    Diags.Report(Dec.index() ? diag::note_fe_backend_inlined
+                             : diag::note_fe_backend_in)
+        << llvm::demangle(Dec.value());
 }
 
 void BackendConsumer::MisExpectDiagHandler(
diff --git a/clang/test/Frontend/backend-attribute-error-warning-optimize.c b/clang/test/Frontend/backend-attribute-error-warning-optimize.c
index d3951e3b6b1f57d..0bfc50ff8985c39 100644
--- a/clang/test/Frontend/backend-attribute-error-warning-optimize.c
+++ b/clang/test/Frontend/backend-attribute-error-warning-optimize.c
@@ -9,6 +9,7 @@ int x(void) {
 }
 void baz(void) {
   foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz'}}
   if (x())
     bar();
 }
@@ -20,3 +21,23 @@ void indirect(void) {
   quux = foo;
   quux();
 }
+
+static inline void a(int x) {
+    if (x == 10)
+        foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+               // expected-note@* {{called by function 'a'}}
+               // expected-note@* {{inlined by function 'b'}}
+               // expected-note@* {{inlined by function 'd'}}
+}
+
+static inline void b() {
+    a(10);
+}
+
+void c() {
+    a(9);
+}
+
+void d() {
+  b();
+}
diff --git a/clang/test/Frontend/backend-attribute-error-warning.c b/clang/test/Frontend/backend-attribute-error-warning.c
index c3c7803479aac96..c87a47053e5c0f8 100644
--- a/clang/test/Frontend/backend-attribute-error-warning.c
+++ b/clang/test/Frontend/backend-attribute-error-warning.c
@@ -23,11 +23,17 @@ duplicate_warnings(void);
 
 void baz(void) {
   foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz'}}
   if (x())
     bar(); // expected-error {{call to 'bar' declared with 'error' attribute: oh no bar}}
+           // expected-note@* {{called by function 'baz'}}
 
   quux();                     // enabled-warning {{call to 'quux' declared with 'warning' attribute: oh no quux}}
+                              // enabled-note@* {{called by function 'baz'}}
   __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455' declared with 'error' attribute: demangle me}}
+                              // expected-note@* {{called by function 'baz'}}
   duplicate_errors();         // expected-error {{call to 'duplicate_errors' declared with 'error' attribute: two}}
+                              // expected-note@* {{called by function 'baz'}}
   duplicate_warnings();       // enabled-warning {{call to 'duplicate_warnings' declared with 'warning' attribute: two}}
+                              // enabled-note@* {{called by function 'baz'}}
 }
diff --git a/clang/test/Frontend/backend-attribute-error-warning.cpp b/clang/test/Frontend/backend-attribute-error-warning.cpp
index 1ed549c271241ab..537e798649f7508 100644
--- a/clang/test/Frontend/backend-attribute-error-warning.cpp
+++ b/clang/test/Frontend/backend-attribute-error-warning.cpp
@@ -24,13 +24,19 @@ duplicate_warnings(void);
 
 void baz(void) {
   foo(); // expected-error {{call to 'foo()' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz()'}}
   if (x())
     bar(); // expected-error {{call to 'bar()' declared with 'error' attribute: oh no bar}}
+           // expected-note@* {{called by function 'baz()'}}
 
   quux();                     // enabled-warning {{call to 'quux()' declared with 'warning' attribute: oh no quux}}
+                              // enabled-note@* {{called by function 'baz()'}}
   __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455()' declared with 'error' attribute: demangle me}}
+                              // expected-note@* {{called by function 'baz()'}}
   duplicate_errors();         // expected-error {{call to 'duplicate_errors()' declared with 'error' attribute: two}}
+                              // expected-note@* {{called by function 'baz()'}}
   duplicate_warnings();       // enabled-warning {{call to 'duplicate_warnings()' declared with 'warning' attribute: two}}
+                              // enabled-note@* {{called by function 'baz()'}}
 }
 
 #ifdef __cplusplus
@@ -46,15 +52,21 @@ struct Widget {
 
 void baz_cpp(void) {
   foo(); // expected-error {{call to 'foo()' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz_cpp()'}}
   if (x())
     bar(); // expected-error {{call to 'bar()' declared with 'error' attribute: oh no bar}}
+           // expected-note@* {{called by function 'baz_cpp()'}}
   quux();  // enabled-warning {{call to 'quux()' declared with 'warning' attribute: oh no quux}}
+           // enabled-note@* {{called by function 'baz_cpp()'}}
 
   // Test that we demangle correctly in the diagnostic for C++.
   __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455()' declared with 'error' attribute: demangle me}}
+                              // expected-note@* {{called by function 'baz_cpp()'}}
   nocall<int>(42);            // expected-error {{call to 'int nocall<int>(int)' declared with 'error' attribute: demangle me, too}}
+                              // expected-note@* {{called by function 'baz_cpp()'}}
 
   Widget W;
   int w = W; // enabled-warning {{call to 'Widget::operator int()' declared with 'warning' attribute: don't call me!}}
+             // enabled-note@* {{called by function 'baz_cpp()'}}
 }
 #endif
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index e448c5ed5c5d947..c3127dcaf1ef80a 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1803,13 +1803,17 @@ example:
     call of a function with this attribute is not eliminated via optimization.
     Front ends can provide optional ``srcloc`` metadata nodes on call sites of
     such callees to attach information about where in the source language such a
-    call came from. A string value can be provided as a note.
+    call came from. A string value can be provided as a note. The optimizer may
+    add the optional ``inlined.from`` metadata to call sites which front ends
+    might consume to display more precise diagnostics.
 ``"dontcall-warn"``
     This attribute denotes that a warning diagnostic should be emitted when a
     call of a function with this attribute is not eliminated via optimization.
     Front ends can provide optional ``srcloc`` metadata nodes on call sites of
     such callees to attach information about where in the source language such a
-    call came from. A string value can be provided as a note.
+    call came from. A string value can be provided as a note. The optimizer may
+    add the optional ``inlined.from`` metadata to call sites which front ends
+    might consume to display more precise diagnostics.
 ``fn_ret_thunk_extern``
     This attribute tells the code generator that returns from functions should
     be replaced with jumps to externally-defined architecture-specific symbols.
diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h
index 628445fe9fb2cca..3f64c0acd293ed5 100644
--- a/llvm/include/llvm/IR/DiagnosticInfo.h
+++ b/llvm/include/llvm/IR/DiagnosticInfo.h
@@ -1099,15 +1099,19 @@ class DiagnosticInfoSrcMgr : public DiagnosticInfo {
 void diagnoseDontCall(const CallInst &CI);
 
 class DiagnosticInfoDontCall : public DiagnosticInfo {
+  StringRef CallerName;
   StringRef CalleeName;
   StringRef Note;
   unsigned LocCookie;
+  MDNode *MDN;
 
 public:
-  DiagnosticInfoDontCall(StringRef CalleeName, StringRef Note,
-                         DiagnosticSeverity DS, unsigned LocCookie)
-      : DiagnosticInfo(DK_DontCall, DS), CalleeName(CalleeName), Note(Note),
-        LocCookie(LocCookie) {}
+  DiagnosticInfoDontCall(StringRef CallerName, StringRef CalleeName,
+                         StringRef Note, DiagnosticSeverity DS,
+                         unsigned LocCookie, MDNode *MDN)
+      : DiagnosticInfo(DK_DontCall, DS), CallerName(CallerName),
+        CalleeName(CalleeName), Note(Note), LocCookie(LocCookie), MDN(MDN) {}
+  StringRef getCaller() const { return CallerName; }
   StringRef getFunctionName() const { return CalleeName; }
   StringRef getNote() const { return Note; }
   unsigned getLocCookie() const { return LocCookie; }
@@ -1115,6 +1119,8 @@ class DiagnosticInfoDontCall : public DiagnosticInfo {
   static bool classof(const DiagnosticInfo *DI) {
     return DI->getKind() == DK_DontCall;
   }
+  void
+  getInliningDecisions(SmallVectorImpl<std::string> &InliningDecisions) const;
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp
index 342c4cbbc39d65f..bacb4c01a1342ca 100644
--- a/llvm/lib/IR/DiagnosticInfo.cpp
+++ b/llvm/lib/IR/DiagnosticInfo.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/iterator_range.h"
@@ -433,8 +434,9 @@ void llvm::diagnoseDontCall(const CallInst &CI) {
       if (MDNode *MD = CI.getMetadata("srcloc"))
         LocCookie =
             mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue();
-      DiagnosticInfoDontCall D(F->getName(), A.getValueAsString(), Sev,
-                               LocCookie);
+      DiagnosticInfoDontCall D(CI.getParent()->getParent()->getName(),
+                               F->getName(), A.getValueAsString(), Sev,
+                               LocCookie, CI.getMetadata("inlined.from"));
       F->getContext().diagnose(D);
     }
   }
@@ -449,3 +451,20 @@ void DiagnosticInfoDontCall::print(DiagnosticPrinter &DP) const {
   if (!getNote().empty())
     DP << ": " << getNote();
 }
+
+void DiagnosticInfoDontCall::getInliningDecisions(
+    SmallVectorImpl<std::string> &InliningDecisions) const {
+  if (!MDN)
+    return;
+
+  const MDOperand &MO = MDN->getOperand(0);
+  if (auto *MDT = dyn_cast<MDTuple>(MO)) {
+    for (const MDOperand &MO : MDT->operands()) {
+      if (auto *S = dyn_cast<MDString>(MO)) {
+        InliningDecisions.push_back(S->getString().str());
+      }
+    }
+  } else if (auto *S = dyn_cast<MDString>(MO)) {
+    InliningDecisions.push_back(S->getString().str());
+  }
+}
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 39d5f6e53c1de48..81ffc416ffd5c0a 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -2520,6 +2520,19 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
         // inlining, commonly when the callee is an intrinsic.
         if (MarkNoUnwind && !CI->doesNotThrow())
           CI->setDoesNotThrow();
+
+        const Function *Callee = CI->getCalledFunction();
+        if (Callee && (Callee->hasFnAttribute("dontcall-error") ||
+                       Callee->hasFnAttribute("dontcall-warn"))) {
+          Metadata *MD = MDString::get(CI->getContext(), CalledFunc->getName());
+          if (MDNode *N = CI->getMetadata("inlined.from")) {
+            TempMDTuple Temp = cast<MDTuple>(N)->clone();
+            Temp->push_back(MD);
+            MD = MDNode::replaceWithUniqued(std::move(Temp));
+          }
+          MDTuple *MDT = MDNode::get(CI->getContext(), {MD});
+          CI->setMetadata("inlined.from", MDT);
+        }
       }
     }
   }
diff --git a/llvm/test/Transforms/Inline/dontcall-attributes.ll b/llvm/test/Transforms/Inline/dontcall-attributes.ll
new file mode 100644
index 000000000000000..acf8c6a62aad619
--- /dev/null
+++ b/llvm/test/Transforms/Inline/dontcall-attributes.ll
@@ -0,0 +1,84 @@
+; RUN: opt -S -o - -passes=inline %s \
+; RUN:  | FileCheck %s --check-prefixes=CHECK-BOTH,CHECK
+; RUN: opt -S -o - -passes=always-inline %s \
+; RUN:  | FileCheck %s --check-prefixes=CHECK-BOTH,CHECK-ALWAYS
+
+declare void @foo() "dontcall-warn"="oh no"
+declare void @fof() "dontcall-error"="oh no"
+
+define void @bar(i32 %x) {
+  %cmp = icmp eq i32 %x, 10
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  call void @foo()
+  br label %if.end
+
+if.end:
+  ret void
+}
+
+define void @quux() {
+  call void @bar(i32 9)
+  ret void
+}
+
+; Test that @baz's call to @foo has metadata with inlining info.
+define void @baz() {
+; CHECK-LABEL: @baz(
+; CHECK-NEXT:    call void @foo(), !inlined.from !0
+;
+  call void @bar(i32 10)
+  ret void
+}
+
+; Test that @zing's call to @foo has unique metadata from @baz's call to @foo.
+define void @zing() {
+; CHECK-LABEL: @zing(
+; CHECK-NEXT:    call void @foo(), !inlined.from !1
+;
+  call void @baz()
+  ret void
+}
+
+; Same test but @fof has fn attr "dontcall-error"="..." rather than
+; "dontcall-warn"="...".
+define void @a() {
+  call void @fof()
+  ret void
+}
+define void @b() {
+; CHECK-LABEL: @b(
+; CHECK-NEXT: call void @fof(), !inlined.from !3
+  call void @a()
+  ret void
+}
+
+; Add some tests for alwaysinline.
+define void @always_callee() alwaysinline {
+  call void @fof()
+  ret void
+}
+define void @always_caller() alwaysinline {
+; CHECK-BOTH-LABEL: @always_caller(
+; CHECK-NEXT: call void @fof(), !inlined.from !4
+; CHECK-ALWAYS-NEXT: call void @fof(), !inlined.from !0
+  call void @always_callee()
+  ret void
+}
+define void @always_caller2() alwaysinline {
+; CHECK-BOTH-LABEL: @always_caller2(
+; CHECK-NEXT: call void @fof(), !inlined.from !5
+; CHECK-ALWAYS-NEXT: call void @fof(), !inlined.from !1
+  call void @always_caller()
+  ret void
+}
+
+; CHECK: !0 = !{!"bar"}
+; CHECK-NEXT: !1 = !{!2}
+; CHECK-NEXT: !2 = !{!"bar", !"baz"}
+; CHECK-NEXT: !3 = !{!"a"}
+; CHECK-NEXT: !4 = !{!"always_callee"}
+; CHECK-ALWAYS: !0 = !{!"always_callee"}
+; CHECK-ALWAYS-NEXT: !1 = !{!2}
+; CHECK-ALWAYS-NEXT: !2 = !{!"always_callee", !"always_caller"}

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 27, 2023

@llvm/pr-subscribers-llvm-transforms

Author: Nick Desaulniers (nickdesaulniers)

Changes

Due to inlining, descovering which specific call site to a function with
the attribute "warning" or "error" is painful.

In the IR record inlining decisions in metadata when inlining a callee
that itself contains a call to a dontcall-error or dontcall-warn fn.

Print this info so that it's clearer which call site is problematic.

There's still some limitations with this approach; macro expansion is
not recorded.

Fixes: ClangBuiltLinux/linux#1571

Differential Revision: https://reviews.llvm.org/D141451


Full diff: https://github.com/llvm/llvm-project/pull/73552.diff

10 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticFrontendKinds.td (+2)
  • (modified) clang/lib/CodeGen/CodeGenAction.cpp (+10)
  • (modified) clang/test/Frontend/backend-attribute-error-warning-optimize.c (+21)
  • (modified) clang/test/Frontend/backend-attribute-error-warning.c (+6)
  • (modified) clang/test/Frontend/backend-attribute-error-warning.cpp (+12)
  • (modified) llvm/docs/LangRef.rst (+6-2)
  • (modified) llvm/include/llvm/IR/DiagnosticInfo.h (+10-4)
  • (modified) llvm/lib/IR/DiagnosticInfo.cpp (+21-2)
  • (modified) llvm/lib/Transforms/Utils/InlineFunction.cpp (+13)
  • (added) llvm/test/Transforms/Inline/dontcall-attributes.ll (+84)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 715e0c0dc8fa84e..0909b1f59175be9 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -93,6 +93,8 @@ 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'">;
+def note_fe_backend_inlined : Note<"inlined by function '%0'">;
 
 def err_fe_invalid_code_complete_file : Error<
     "cannot locate code-completion file %0">, DefaultFatal;
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index a31a271ed77d1ca..66e040741e2718d 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -52,6 +52,8 @@
 #include "llvm/Transforms/Utils/Cloning.h"
 
 #include <optional>
+#include <string>
+
 using namespace clang;
 using namespace llvm;
 
@@ -794,6 +796,14 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) {
                               ? diag::err_fe_backend_error_attr
                               : diag::warn_fe_backend_warning_attr)
       << llvm::demangle(D.getFunctionName()) << D.getNote();
+
+  SmallVector<std::string, 4> InliningDecisions;
+  D.getInliningDecisions(InliningDecisions);
+  InliningDecisions.push_back(D.getCaller().str());
+  for (auto Dec : llvm::enumerate(InliningDecisions))
+    Diags.Report(Dec.index() ? diag::note_fe_backend_inlined
+                             : diag::note_fe_backend_in)
+        << llvm::demangle(Dec.value());
 }
 
 void BackendConsumer::MisExpectDiagHandler(
diff --git a/clang/test/Frontend/backend-attribute-error-warning-optimize.c b/clang/test/Frontend/backend-attribute-error-warning-optimize.c
index d3951e3b6b1f57d..0bfc50ff8985c39 100644
--- a/clang/test/Frontend/backend-attribute-error-warning-optimize.c
+++ b/clang/test/Frontend/backend-attribute-error-warning-optimize.c
@@ -9,6 +9,7 @@ int x(void) {
 }
 void baz(void) {
   foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz'}}
   if (x())
     bar();
 }
@@ -20,3 +21,23 @@ void indirect(void) {
   quux = foo;
   quux();
 }
+
+static inline void a(int x) {
+    if (x == 10)
+        foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+               // expected-note@* {{called by function 'a'}}
+               // expected-note@* {{inlined by function 'b'}}
+               // expected-note@* {{inlined by function 'd'}}
+}
+
+static inline void b() {
+    a(10);
+}
+
+void c() {
+    a(9);
+}
+
+void d() {
+  b();
+}
diff --git a/clang/test/Frontend/backend-attribute-error-warning.c b/clang/test/Frontend/backend-attribute-error-warning.c
index c3c7803479aac96..c87a47053e5c0f8 100644
--- a/clang/test/Frontend/backend-attribute-error-warning.c
+++ b/clang/test/Frontend/backend-attribute-error-warning.c
@@ -23,11 +23,17 @@ duplicate_warnings(void);
 
 void baz(void) {
   foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz'}}
   if (x())
     bar(); // expected-error {{call to 'bar' declared with 'error' attribute: oh no bar}}
+           // expected-note@* {{called by function 'baz'}}
 
   quux();                     // enabled-warning {{call to 'quux' declared with 'warning' attribute: oh no quux}}
+                              // enabled-note@* {{called by function 'baz'}}
   __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455' declared with 'error' attribute: demangle me}}
+                              // expected-note@* {{called by function 'baz'}}
   duplicate_errors();         // expected-error {{call to 'duplicate_errors' declared with 'error' attribute: two}}
+                              // expected-note@* {{called by function 'baz'}}
   duplicate_warnings();       // enabled-warning {{call to 'duplicate_warnings' declared with 'warning' attribute: two}}
+                              // enabled-note@* {{called by function 'baz'}}
 }
diff --git a/clang/test/Frontend/backend-attribute-error-warning.cpp b/clang/test/Frontend/backend-attribute-error-warning.cpp
index 1ed549c271241ab..537e798649f7508 100644
--- a/clang/test/Frontend/backend-attribute-error-warning.cpp
+++ b/clang/test/Frontend/backend-attribute-error-warning.cpp
@@ -24,13 +24,19 @@ duplicate_warnings(void);
 
 void baz(void) {
   foo(); // expected-error {{call to 'foo()' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz()'}}
   if (x())
     bar(); // expected-error {{call to 'bar()' declared with 'error' attribute: oh no bar}}
+           // expected-note@* {{called by function 'baz()'}}
 
   quux();                     // enabled-warning {{call to 'quux()' declared with 'warning' attribute: oh no quux}}
+                              // enabled-note@* {{called by function 'baz()'}}
   __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455()' declared with 'error' attribute: demangle me}}
+                              // expected-note@* {{called by function 'baz()'}}
   duplicate_errors();         // expected-error {{call to 'duplicate_errors()' declared with 'error' attribute: two}}
+                              // expected-note@* {{called by function 'baz()'}}
   duplicate_warnings();       // enabled-warning {{call to 'duplicate_warnings()' declared with 'warning' attribute: two}}
+                              // enabled-note@* {{called by function 'baz()'}}
 }
 
 #ifdef __cplusplus
@@ -46,15 +52,21 @@ struct Widget {
 
 void baz_cpp(void) {
   foo(); // expected-error {{call to 'foo()' declared with 'error' attribute: oh no foo}}
+         // expected-note@* {{called by function 'baz_cpp()'}}
   if (x())
     bar(); // expected-error {{call to 'bar()' declared with 'error' attribute: oh no bar}}
+           // expected-note@* {{called by function 'baz_cpp()'}}
   quux();  // enabled-warning {{call to 'quux()' declared with 'warning' attribute: oh no quux}}
+           // enabled-note@* {{called by function 'baz_cpp()'}}
 
   // Test that we demangle correctly in the diagnostic for C++.
   __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455()' declared with 'error' attribute: demangle me}}
+                              // expected-note@* {{called by function 'baz_cpp()'}}
   nocall<int>(42);            // expected-error {{call to 'int nocall<int>(int)' declared with 'error' attribute: demangle me, too}}
+                              // expected-note@* {{called by function 'baz_cpp()'}}
 
   Widget W;
   int w = W; // enabled-warning {{call to 'Widget::operator int()' declared with 'warning' attribute: don't call me!}}
+             // enabled-note@* {{called by function 'baz_cpp()'}}
 }
 #endif
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index e448c5ed5c5d947..c3127dcaf1ef80a 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1803,13 +1803,17 @@ example:
     call of a function with this attribute is not eliminated via optimization.
     Front ends can provide optional ``srcloc`` metadata nodes on call sites of
     such callees to attach information about where in the source language such a
-    call came from. A string value can be provided as a note.
+    call came from. A string value can be provided as a note. The optimizer may
+    add the optional ``inlined.from`` metadata to call sites which front ends
+    might consume to display more precise diagnostics.
 ``"dontcall-warn"``
     This attribute denotes that a warning diagnostic should be emitted when a
     call of a function with this attribute is not eliminated via optimization.
     Front ends can provide optional ``srcloc`` metadata nodes on call sites of
     such callees to attach information about where in the source language such a
-    call came from. A string value can be provided as a note.
+    call came from. A string value can be provided as a note. The optimizer may
+    add the optional ``inlined.from`` metadata to call sites which front ends
+    might consume to display more precise diagnostics.
 ``fn_ret_thunk_extern``
     This attribute tells the code generator that returns from functions should
     be replaced with jumps to externally-defined architecture-specific symbols.
diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h
index 628445fe9fb2cca..3f64c0acd293ed5 100644
--- a/llvm/include/llvm/IR/DiagnosticInfo.h
+++ b/llvm/include/llvm/IR/DiagnosticInfo.h
@@ -1099,15 +1099,19 @@ class DiagnosticInfoSrcMgr : public DiagnosticInfo {
 void diagnoseDontCall(const CallInst &CI);
 
 class DiagnosticInfoDontCall : public DiagnosticInfo {
+  StringRef CallerName;
   StringRef CalleeName;
   StringRef Note;
   unsigned LocCookie;
+  MDNode *MDN;
 
 public:
-  DiagnosticInfoDontCall(StringRef CalleeName, StringRef Note,
-                         DiagnosticSeverity DS, unsigned LocCookie)
-      : DiagnosticInfo(DK_DontCall, DS), CalleeName(CalleeName), Note(Note),
-        LocCookie(LocCookie) {}
+  DiagnosticInfoDontCall(StringRef CallerName, StringRef CalleeName,
+                         StringRef Note, DiagnosticSeverity DS,
+                         unsigned LocCookie, MDNode *MDN)
+      : DiagnosticInfo(DK_DontCall, DS), CallerName(CallerName),
+        CalleeName(CalleeName), Note(Note), LocCookie(LocCookie), MDN(MDN) {}
+  StringRef getCaller() const { return CallerName; }
   StringRef getFunctionName() const { return CalleeName; }
   StringRef getNote() const { return Note; }
   unsigned getLocCookie() const { return LocCookie; }
@@ -1115,6 +1119,8 @@ class DiagnosticInfoDontCall : public DiagnosticInfo {
   static bool classof(const DiagnosticInfo *DI) {
     return DI->getKind() == DK_DontCall;
   }
+  void
+  getInliningDecisions(SmallVectorImpl<std::string> &InliningDecisions) const;
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp
index 342c4cbbc39d65f..bacb4c01a1342ca 100644
--- a/llvm/lib/IR/DiagnosticInfo.cpp
+++ b/llvm/lib/IR/DiagnosticInfo.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/iterator_range.h"
@@ -433,8 +434,9 @@ void llvm::diagnoseDontCall(const CallInst &CI) {
       if (MDNode *MD = CI.getMetadata("srcloc"))
         LocCookie =
             mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue();
-      DiagnosticInfoDontCall D(F->getName(), A.getValueAsString(), Sev,
-                               LocCookie);
+      DiagnosticInfoDontCall D(CI.getParent()->getParent()->getName(),
+                               F->getName(), A.getValueAsString(), Sev,
+                               LocCookie, CI.getMetadata("inlined.from"));
       F->getContext().diagnose(D);
     }
   }
@@ -449,3 +451,20 @@ void DiagnosticInfoDontCall::print(DiagnosticPrinter &DP) const {
   if (!getNote().empty())
     DP << ": " << getNote();
 }
+
+void DiagnosticInfoDontCall::getInliningDecisions(
+    SmallVectorImpl<std::string> &InliningDecisions) const {
+  if (!MDN)
+    return;
+
+  const MDOperand &MO = MDN->getOperand(0);
+  if (auto *MDT = dyn_cast<MDTuple>(MO)) {
+    for (const MDOperand &MO : MDT->operands()) {
+      if (auto *S = dyn_cast<MDString>(MO)) {
+        InliningDecisions.push_back(S->getString().str());
+      }
+    }
+  } else if (auto *S = dyn_cast<MDString>(MO)) {
+    InliningDecisions.push_back(S->getString().str());
+  }
+}
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 39d5f6e53c1de48..81ffc416ffd5c0a 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -2520,6 +2520,19 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
         // inlining, commonly when the callee is an intrinsic.
         if (MarkNoUnwind && !CI->doesNotThrow())
           CI->setDoesNotThrow();
+
+        const Function *Callee = CI->getCalledFunction();
+        if (Callee && (Callee->hasFnAttribute("dontcall-error") ||
+                       Callee->hasFnAttribute("dontcall-warn"))) {
+          Metadata *MD = MDString::get(CI->getContext(), CalledFunc->getName());
+          if (MDNode *N = CI->getMetadata("inlined.from")) {
+            TempMDTuple Temp = cast<MDTuple>(N)->clone();
+            Temp->push_back(MD);
+            MD = MDNode::replaceWithUniqued(std::move(Temp));
+          }
+          MDTuple *MDT = MDNode::get(CI->getContext(), {MD});
+          CI->setMetadata("inlined.from", MDT);
+        }
       }
     }
   }
diff --git a/llvm/test/Transforms/Inline/dontcall-attributes.ll b/llvm/test/Transforms/Inline/dontcall-attributes.ll
new file mode 100644
index 000000000000000..acf8c6a62aad619
--- /dev/null
+++ b/llvm/test/Transforms/Inline/dontcall-attributes.ll
@@ -0,0 +1,84 @@
+; RUN: opt -S -o - -passes=inline %s \
+; RUN:  | FileCheck %s --check-prefixes=CHECK-BOTH,CHECK
+; RUN: opt -S -o - -passes=always-inline %s \
+; RUN:  | FileCheck %s --check-prefixes=CHECK-BOTH,CHECK-ALWAYS
+
+declare void @foo() "dontcall-warn"="oh no"
+declare void @fof() "dontcall-error"="oh no"
+
+define void @bar(i32 %x) {
+  %cmp = icmp eq i32 %x, 10
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  call void @foo()
+  br label %if.end
+
+if.end:
+  ret void
+}
+
+define void @quux() {
+  call void @bar(i32 9)
+  ret void
+}
+
+; Test that @baz's call to @foo has metadata with inlining info.
+define void @baz() {
+; CHECK-LABEL: @baz(
+; CHECK-NEXT:    call void @foo(), !inlined.from !0
+;
+  call void @bar(i32 10)
+  ret void
+}
+
+; Test that @zing's call to @foo has unique metadata from @baz's call to @foo.
+define void @zing() {
+; CHECK-LABEL: @zing(
+; CHECK-NEXT:    call void @foo(), !inlined.from !1
+;
+  call void @baz()
+  ret void
+}
+
+; Same test but @fof has fn attr "dontcall-error"="..." rather than
+; "dontcall-warn"="...".
+define void @a() {
+  call void @fof()
+  ret void
+}
+define void @b() {
+; CHECK-LABEL: @b(
+; CHECK-NEXT: call void @fof(), !inlined.from !3
+  call void @a()
+  ret void
+}
+
+; Add some tests for alwaysinline.
+define void @always_callee() alwaysinline {
+  call void @fof()
+  ret void
+}
+define void @always_caller() alwaysinline {
+; CHECK-BOTH-LABEL: @always_caller(
+; CHECK-NEXT: call void @fof(), !inlined.from !4
+; CHECK-ALWAYS-NEXT: call void @fof(), !inlined.from !0
+  call void @always_callee()
+  ret void
+}
+define void @always_caller2() alwaysinline {
+; CHECK-BOTH-LABEL: @always_caller2(
+; CHECK-NEXT: call void @fof(), !inlined.from !5
+; CHECK-ALWAYS-NEXT: call void @fof(), !inlined.from !1
+  call void @always_caller()
+  ret void
+}
+
+; CHECK: !0 = !{!"bar"}
+; CHECK-NEXT: !1 = !{!2}
+; CHECK-NEXT: !2 = !{!"bar", !"baz"}
+; CHECK-NEXT: !3 = !{!"a"}
+; CHECK-NEXT: !4 = !{!"always_callee"}
+; CHECK-ALWAYS: !0 = !{!"always_callee"}
+; CHECK-ALWAYS-NEXT: !1 = !{!2}
+; CHECK-ALWAYS-NEXT: !2 = !{!"always_callee", !"always_caller"}

@nickdesaulniers
Copy link
Member Author

Note to reviewers, I imported this to a GH PR from https://reviews.llvm.org/D141451, and addressed comments from https://reviews.llvm.org/D141451#4311399 down.

I did have a related RFC, but the proposal from @rnk is a yak shave that I don't intend to pursue.

Upon rereviewing https://reviews.llvm.org/D141451, it seems there was just some minor nits to resolve. Bringing this back up because @kees reminded me how painful this is.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only a few comments on the CFE, most of the work seems LLVM related.

@@ -93,6 +93,8 @@ 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'">;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right file for this? It seems weird to do codegen in a 'frontend kinds' diagnostics list, though I'm not convinced I know enough to ask this question well enough.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where should the diagnostic strings go if not here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK, that is why I asked. It SOUNDS wrong, so I'm hopeful someone else can come along and tell us.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the diagnostics for backend warnings are defined in this file. (see existing lines 66-95).

As is, this note is emitted alongside the err/warn from existing lines 91-95, so it makes sense to put the new notes immediately after those existing definitions.

If my newly added backend warning notes belong in a new file, then all of the above belong in a new file. But that seems orthogonal to my patch.

@@ -794,6 +796,13 @@ void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) {
? diag::err_fe_backend_error_attr
: diag::warn_fe_backend_warning_attr)
<< llvm::demangle(D.getFunctionName()) << D.getNote();

SmallVector<std::string> InliningDecisions = D.getInliningDecisions();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use StringRef here instead? It seems to make more sense to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in e7ed56d PTAL


SmallVector<std::string> InliningDecisions = D.getInliningDecisions();
InliningDecisions.push_back(D.getCaller().str());
for (auto [index, value] : llvm::enumerate(InliningDecisions))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (auto [index, value] : llvm::enumerate(InliningDecisions))
for (const auto &[index, value] : llvm::enumerate(InliningDecisions))

Seems we don't modify either, and value is a string, so requiring a copy of the pair is expensive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in d3a221a PTAL

SmallVector<StringRef> InliningDecisions = D.getInliningDecisions();
InliningDecisions.push_back(D.getCaller());
for (const auto &[index, value] : llvm::enumerate(InliningDecisions))
Diags.Report(LocCookie, index ? diag::note_fe_backend_inlined
Copy link
Member Author

@nickdesaulniers nickdesaulniers Nov 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, the use of LocCookie is problematic here.

Take this example (from the RFC):

#include <string.h>
__attribute__((error("bad memcpy"))) void bad(void);

static void *my_memcpy(void *restrict dest, size_t dest_size,
                       const void *restrict src, size_t src_size,
                       size_t n) {
    if (n > dest_size || n > src_size)
        bad();
    return memcpy(dest, src, n);
}

void my_driver (void) {
    unsigned char src [42], dst [42];
    my_memcpy(dst, 42, src, 42, 0);
    my_memcpy(dst, 42, src, 42, 42);
    my_memcpy(dst, 42, src, 42, 4096);
    my_memcpy(dst, 42, src, 42, 1);
}

No LocCookie:

$ clang /tmp/x.c -O2
/tmp/x.c:8:9: error: call to 'bad' declared with 'error' attribute: bad memcpy
    8 |         bad();
      |         ^
note: called by function 'my_memcpy'
note: inlined by function 'my_driver'
1 error generated.

w/ LocCookie:

$ clang /tmp/x.c -O2
/tmp/x.c:8:9: error: call to 'bad' declared with 'error' attribute: bad memcpy
    8 |         bad();
      |         ^
/tmp/x.c:8:9: note: called by function 'my_memcpy'
/tmp/x.c:8:9: note: inlined by function 'my_driver'
1 error generated.

The LocCookie is pointing to the function call to bad();, not the call to my_memcpy because that information was not retained. (We don't retain such debug info unless debug info (or optimization remarks) were requested; I discuss this in the RFC. I think it's preferable to have no line+col info on the note rather than wrong line+col info).

cc @MaskRay since this change was in response to https://reviews.llvm.org/D141451#inline-1445409

Comment on lines 458 to 469
if (MDN) {
const MDOperand &MO = MDN->getOperand(0);
if (auto *MDT = dyn_cast<MDTuple>(MO)) {
for (const MDOperand &MO : MDT->operands()) {
if (auto *S = dyn_cast<MDString>(MO)) {
if (MDN)
if (auto *MDT = dyn_cast<MDTuple>(MDN->getOperand(0)))
for (const MDOperand &MO : MDT->operands())
if (auto *S = dyn_cast<MDString>(MO))
InliningDecisions.push_back(S->getString().str());
}
}
} else if (auto *S = dyn_cast<MDString>(MO)) {
InliningDecisions.push_back(S->getString().str());
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is wrong; it seems I don't have this captured in the existing tests, but doing a local integration test:

#include <string.h>
__attribute__((error("bad memcpy"))) void bad(void);

static void *my_memcpy(void *restrict dest, size_t dest_size,
                       const void *restrict src, size_t src_size,
                       size_t n) {
    if (n > dest_size || n > src_size)
        bad();
    return memcpy(dest, src, n);
}

void my_driver (void) {
    unsigned char src [42], dst [42];
    my_memcpy(dst, 42, src, 42, 0);
    my_memcpy(dst, 42, src, 42, 42);
    my_memcpy(dst, 42, src, 42, 4096);
    my_memcpy(dst, 42, src, 42, 1);
}

Before this change:

/tmp/x.c:8:9: error: call to 'bad' declared with 'error' attribute: bad memcpy
    8 |         bad();
      |         ^
/tmp/x.c:8:9: note: called by function 'my_memcpy'
/tmp/x.c:8:9: note: inlined by function 'my_driver'

After this change:

$ /tmp/x.c:8:9: error: call to 'bad' declared with 'error' attribute: bad memcpy
    8 |         bad();
      |         ^
/tmp/x.c:8:9: note: called by function 'my_memcpy'
1 error generated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want it to read like "before", yes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes; note:

  1. github is having some kind of outtage currently. https://www.githubstatus.com/ currently mentions an issue with PRs. I have pushed a revert but it's not yet being reflected in the GH UI.
  2. I need to fix the issue pointed out https://reviews.llvm.org/D141451#inline-1446137, which I haven't done yet. The revert was incorrect.

@nickdesaulniers nickdesaulniers marked this pull request as draft November 27, 2023 19:49
This reverts commit 239ab3e.
@dwblaikie
Copy link
Collaborator

I did have a related RFC, but the proposal from @rnk is a yak shave that I don't intend to pursue.

I'd push on that a bit more. As I mentioned on the phab review, I think it'd be unfortunate to build a second system for tracking inlining apart from the debug info.

Guess it comes down to whether some sufficient quorum insist on that being necessary yak shaving here or not.


if (MDN) {
const MDOperand &MO = MDN->getOperand(0);
if (auto *MDT = dyn_cast<MDTuple>(MO)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copying @aeubanks 's comment from the phab review:

seems simpler if this is always a MDTuple instead of special casing one entry to be MDString

For the life of me I cannot figure out why when an MDTuple is created via:

Metadata *MD = MDString::get(CI->getContext(), CalledFunc->getName());
MDTuple *MDT = MDNode::get(CI->getContext(), {MD});
CI->setMetadata("inlined.from", MDT);

you get

!10 = !{!"my_memcpy"}

but when you copy a MDTuple and then append to it, you get a layer of indirection:

Metadata *MD = MDString::get(CI->getContext(), CalledFunc->getName());                                                                                                                                                   
if (MDNode *N = CI->getMetadata("inlined.from")) {                                                                                                                                                                       
  TempMDTuple Temp = cast<MDTuple>(N)->clone();                                                                                                                                                                          
  Temp->push_back(MD);                                                                                                                                                                                                   
  MD = MDNode::replaceWithUniqued(std::move(Temp));                                                                                                                                                                      
}                                                                                                                                                                                                                        
MDTuple *MDT = MDNode::get(CI->getContext(), {MD});                                                                                                                                                                      
CI->setMetadata("inlined.from", MDT);
!10 = !{!11}
!11 = !{!"my_memcpy", !"my_driver"}

when I would have expected:

!10 = !{!"my_memcpy", !"my_driver"}

am I holding the API wrong??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category llvm:ir llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Report inlining location when using __attribute__((__warning__(msg)))
5 participants