diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -90,6 +90,8 @@ def warn_fe_backend_warning_attr : Warning<"call to '%0' declared with 'warning' attribute: %1">, BackendInfo, InGroup; +def note_fe_backend_in : Note<"In function '%0'">; +def note_fe_backend_inlined : Note<"\twhich inlined 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 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -851,6 +851,14 @@ ? diag::err_fe_backend_error_attr : diag::warn_fe_backend_warning_attr) << llvm::demangle(D.getFunctionName().str()) << D.getNote(); + + Diags.Report(diag::note_fe_backend_in) << D.getCaller(); + + SmallVector InliningDecisions; + D.getInliningDecisions(InliningDecisions); + for (StringRef S : InliningDecisions) { + Diags.Report(diag::note_fe_backend_inlined) << S; + } } void BackendConsumer::MisExpectDiagHandler( diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h --- a/llvm/include/llvm/IR/DiagnosticInfo.h +++ b/llvm/include/llvm/IR/DiagnosticInfo.h @@ -1099,15 +1099,19 @@ 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 @@ static bool classof(const DiagnosticInfo *DI) { return DI->getKind() == DK_DontCall; } + void + getInliningDecisions(SmallVectorImpl &InliningDecisions) const; }; } // end namespace llvm diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp --- 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" @@ -430,8 +431,9 @@ if (MDNode *MD = CI.getMetadata("srcloc")) LocCookie = mdconst::extract(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); } } @@ -446,3 +448,20 @@ if (!getNote().empty()) DP << ": " << getNote(); } + +void DiagnosticInfoDontCall::getInliningDecisions( + SmallVectorImpl &InliningDecisions) const { + if (!MDN) + return; + + const MDOperand &MO = MDN->getOperand(0); + if (auto *MDT = dyn_cast(MO)) { + for (unsigned i = MDT->getNumOperands(); i; --i) { + if (auto *S = dyn_cast(MDT->getOperand(i - 1))) { + InliningDecisions.push_back(S->getString()); + } + } + } else if (auto *S = dyn_cast(MO)) { + InliningDecisions.push_back(S->getString()); + } +} diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -2463,6 +2463,19 @@ // 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(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 --- /dev/null +++ b/llvm/test/Transforms/Inline/dontcall-attributes.ll @@ -0,0 +1,57 @@ +; RUN: opt -S -o - -passes=inline %s | FileCheck %s + +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 +} + +; CHECK: !0 = !{!"bar"} +; CHECK: !1 = !{!2} +; CHECK: !2 = !{!"bar", !"baz"} +; CHECK: !3 = !{!"a"}