#include "clang/Lex/Preprocessor.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
}
void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) {
- if (const Decl *DE = Gen->GetDeclForMangledName(D.getFunctionName()))
- if (const auto *FD = dyn_cast<FunctionDecl>(DE)) {
- assert(FD->hasAttr<ErrorAttr>() &&
- "expected error or warning function attribute");
-
- if (const auto *EA = FD->getAttr<ErrorAttr>()) {
- assert((EA->isError() || EA->isWarning()) &&
- "ErrorAttr neither error or warning");
-
- SourceLocation LocCookie =
- SourceLocation::getFromRawEncoding(D.getLocCookie());
-
- // FIXME: we can't yet diagnose indirect calls. When/if we can, we
- // should instead assert that LocCookie.isValid().
- if (!LocCookie.isValid())
- return;
-
- Diags.Report(LocCookie, EA->isError()
- ? diag::err_fe_backend_error_attr
- : diag::warn_fe_backend_warning_attr)
- << FD << EA->getUserDiagnostic();
- }
- }
- // TODO: assert if DE or FD were nullptr?
+ SourceLocation LocCookie =
+ SourceLocation::getFromRawEncoding(D.getLocCookie());
+
+ // FIXME: we can't yet diagnose indirect calls. When/if we can, we
+ // should instead assert that LocCookie.isValid().
+ if (!LocCookie.isValid())
+ return;
+
+ Diags.Report(LocCookie, D.getSeverity() == DiagnosticSeverity::DS_Error
+ ? diag::err_fe_backend_error_attr
+ : diag::warn_fe_backend_warning_attr)
+ << llvm::demangle(D.getFunctionName().str()) << D.getNote();
}
/// This function is invoked when the backend needs
else if (const auto *SA = FD->getAttr<SectionAttr>())
F->setSection(SA->getName());
- if (FD->hasAttr<ErrorAttr>())
- F->addFnAttr("dontcall");
+ if (const auto *EA = FD->getAttr<ErrorAttr>()) {
+ if (EA->isError())
+ F->addFnAttr("dontcall-error", EA->getUserDiagnostic());
+ else if (EA->isWarning())
+ F->addFnAttr("dontcall-warn", EA->getUserDiagnostic());
+ }
// If we plan on emitting this inline builtin, we can't treat it as a builtin.
if (FD->isInlineBuiltinDeclaration()) {
// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
// CHECK: declare{{.*}} void @foo() [[ATTR:#[0-9]+]]
-// CHECK: attributes [[ATTR]] = {{{.*}}"dontcall"
+// CHECK: attributes [[ATTR]] = {{{.*}}"dontcall-error"="oh no"
// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
// CHECK: declare{{.*}} void @foo() [[ATTR:#[0-9]+]]
-// CHECK: attributes [[ATTR]] = {{{.*}}"dontcall"
+// CHECK: attributes [[ATTR]] = {{{.*}}"dontcall-warn"="oh no"
// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
return 8 % 2 == 1;
}
void baz(void) {
- foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+ foo(); // expected-error {{call to foo declared with 'error' attribute: oh no foo}}
if (x())
bar();
}
// RUN: %clang_cc1 -verify=expected,enabled -emit-codegen-only %s
-// RUN: %clang_cc1 -verify=expected,enabled -emit-codegen-only %s -x c++
// RUN: %clang_cc1 -verify -emit-codegen-only -Wno-attribute-warning %s
-// RUN: %clang_cc1 -verify -emit-codegen-only -Wno-attribute-warning %s -x c++
__attribute__((error("oh no foo"))) void foo(void);
duplicate_warnings(void);
void baz(void) {
- foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
+ foo(); // expected-error {{call to foo declared with 'error' attribute: oh no foo}}
if (x())
- bar(); // expected-error {{call to 'bar' declared with 'error' attribute: oh no bar}}
+ bar(); // expected-error {{call to bar declared with 'error' attribute: oh no bar}}
- quux(); // enabled-warning {{call to 'quux' declared with 'warning' attribute: oh no quux}}
- __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455' declared with 'error' attribute: demangle me}}
- duplicate_errors(); // expected-error {{call to 'duplicate_errors' declared with 'error' attribute: two}}
- duplicate_warnings(); // enabled-warning {{call to 'duplicate_warnings' declared with 'warning' attribute: two}}
+ quux(); // enabled-warning {{call to quux declared with 'warning' attribute: oh no quux}}
+ __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455 declared with 'error' attribute: demangle me}}
+ duplicate_errors(); // expected-error {{call to duplicate_errors declared with 'error' attribute: two}}
+ duplicate_warnings(); // enabled-warning {{call to duplicate_warnings declared with 'warning' attribute: two}}
}
-
-#ifdef __cplusplus
-template <typename T>
-__attribute__((error("demangle me, too")))
-T
-nocall(T t);
-
-struct Widget {
- __attribute__((warning("don't call me!")))
- operator int() { return 42; }
-};
-
-void baz_cpp(void) {
- foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}}
- if (x())
- bar(); // expected-error {{call to 'bar' declared with 'error' attribute: oh no bar}}
- quux(); // enabled-warning {{call to 'quux' declared with 'warning' attribute: oh no quux}}
-
- // 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}}
- nocall<int>(42); // expected-error {{call to 'nocall<int>' declared with 'error' attribute: demangle me, too}}
-
- Widget W;
- int w = W; // enabled-warning {{'operator int' declared with 'warning' attribute: don't call me!}}
-}
-#endif
--- /dev/null
+// RUN: %clang_cc1 -verify=expected,enabled -emit-codegen-only %s
+// RUN: %clang_cc1 -verify -emit-codegen-only -Wno-attribute-warning %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+ return 8 % 2 == 1;
+}
+
+__attribute__((warning("oh no quux"))) void quux(void);
+
+__attribute__((error("demangle me"))) void __compiletime_assert_455(void);
+
+__attribute__((error("one"), error("two"))) // expected-warning {{attribute 'error' is already applied with different arguments}}
+void // expected-note@-1 {{previous attribute is here}}
+duplicate_errors(void);
+
+__attribute__((warning("one"), warning("two"))) // expected-warning {{attribute 'warning' is already applied with different arguments}}
+void // expected-note@-1 {{previous attribute is here}}
+duplicate_warnings(void);
+
+void baz(void) {
+ foo(); // expected-error {{call to foo() declared with 'error' attribute: oh no foo}}
+ if (x())
+ bar(); // expected-error {{call to bar() declared with 'error' attribute: oh no bar}}
+
+ quux(); // enabled-warning {{call to quux() declared with 'warning' attribute: oh no quux}}
+ __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455() declared with 'error' attribute: demangle me}}
+ duplicate_errors(); // expected-error {{call to duplicate_errors() declared with 'error' attribute: two}}
+ duplicate_warnings(); // enabled-warning {{call to duplicate_warnings() declared with 'warning' attribute: two}}
+}
+
+#ifdef __cplusplus
+template <typename T>
+__attribute__((error("demangle me, too")))
+T
+nocall(T t);
+
+struct Widget {
+ __attribute__((warning("don't call me!")))
+ operator int() { return 42; }
+};
+
+void baz_cpp(void) {
+ foo(); // expected-error {{call to foo() declared with 'error' attribute: oh no foo}}
+ if (x())
+ bar(); // expected-error {{call to bar() declared with 'error' attribute: oh no bar}}
+ quux(); // enabled-warning {{call to quux() declared with 'warning' attribute: oh no quux}}
+
+ // 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}}
+ nocall<int>(42); // expected-error {{call to int nocall<int>(int) declared with 'error' attribute: demangle me, too}}
+
+ Widget W;
+ int w = W; // enabled-warning {{Widget::operator int() declared with 'warning' attribute: don't call me!}}
+}
+#endif
``disable_sanitizer_instrumentation`` disables all kinds of instrumentation,
taking precedence over the ``sanitize_<name>`` attributes and other compiler
flags.
-``"dontcall"``
- This attribute denotes that a 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.
+``"dontcall-error"``
+ This attribute denotes that an error 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.
+``"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.
``"frame-pointer"``
This attribute tells the code generator whether the function
should keep the frame pointer. The code generator may emit the frame pointer
// Forward declarations.
class DiagnosticPrinter;
+class CallInst;
class Function;
class Instruction;
class InstructionCost;
}
};
+void diagnoseDontCall(const CallInst &CI);
+
class DiagnosticInfoDontCall : public DiagnosticInfo {
StringRef CalleeName;
+ StringRef Note;
unsigned LocCookie;
public:
- DiagnosticInfoDontCall(StringRef CalleeName, unsigned LocCookie)
- : DiagnosticInfo(DK_DontCall, DS_Error), CalleeName(CalleeName),
+ DiagnosticInfoDontCall(StringRef CalleeName, StringRef Note,
+ DiagnosticSeverity DS, unsigned LocCookie)
+ : DiagnosticInfo(DK_DontCall, DS), CalleeName(CalleeName), Note(Note),
LocCookie(LocCookie) {}
StringRef getFunctionName() const { return CalleeName; }
+ StringRef getNote() const { return Note; }
unsigned getLocCookie() const { return LocCookie; }
void print(DiagnosticPrinter &DP) const override;
static bool classof(const DiagnosticInfo *DI) {
if (CI.isInlineAsm())
return translateInlineAsm(CI, MIRBuilder);
- if (F && F->hasFnAttribute("dontcall")) {
- unsigned LocCookie = 0;
- if (MDNode *MD = CI.getMetadata("srcloc"))
- LocCookie =
- mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue();
- DiagnosticInfoDontCall D(F->getName(), LocCookie);
- F->getContext().diagnose(D);
- }
+ diagnoseDontCall(CI);
Intrinsic::ID ID = Intrinsic::not_intrinsic;
if (F && F->isIntrinsic()) {
CLI.setCallee(RetTy, FuncTy, CI->getCalledOperand(), std::move(Args), *CI)
.setTailCall(IsTailCall);
- if (const Function *F = CI->getCalledFunction())
- if (F->hasFnAttribute("dontcall")) {
- unsigned LocCookie = 0;
- if (MDNode *MD = CI->getMetadata("srcloc"))
- LocCookie =
- mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue();
- DiagnosticInfoDontCall D(F->getName(), LocCookie);
- F->getContext().diagnose(D);
- }
+ diagnoseDontCall(*CI);
return lowerCallTo(CLI);
}
}
if (Function *F = I.getCalledFunction()) {
- if (F->hasFnAttribute("dontcall")) {
- unsigned LocCookie = 0;
- if (MDNode *MD = I.getMetadata("srcloc"))
- LocCookie =
- mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue();
- DiagnosticInfoDontCall D(F->getName(), LocCookie);
- DAG.getContext()->diagnose(D);
- }
+ diagnoseDontCall(I);
if (F->isDeclaration()) {
// Is this an LLVM intrinsic or a target-specific intrinsic?
void OptimizationRemarkAnalysisFPCommute::anchor() {}
void OptimizationRemarkAnalysisAliasing::anchor() {}
+void llvm::diagnoseDontCall(const CallInst &CI) {
+ auto *F = CI.getCalledFunction();
+ if (!F)
+ return;
+
+ for (int i = 0; i != 2; ++i) {
+ auto AttrName = i == 0 ? "dontcall-error" : "dontcall-warn";
+ auto Sev = i == 0 ? DS_Error : DS_Warning;
+
+ if (F->hasFnAttribute(AttrName)) {
+ unsigned LocCookie = 0;
+ auto A = F->getFnAttribute(AttrName);
+ if (MDNode *MD = CI.getMetadata("srcloc"))
+ LocCookie =
+ mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue();
+ DiagnosticInfoDontCall D(F->getName(), A.getValueAsString(), Sev,
+ LocCookie);
+ F->getContext().diagnose(D);
+ }
+ }
+}
+
void DiagnosticInfoDontCall::print(DiagnosticPrinter &DP) const {
- DP << "call to " << getFunctionName() << " marked \"dontcall\"";
+ DP << "call to " << getFunctionName() << " marked \"dontcall-";
+ if (getSeverity() == DiagnosticSeverity::DS_Error)
+ DP << "error\"";
+ else
+ DP << "warn\"";
+ if (!getNote().empty())
+ DP << ": " << getNote();
}
; RUN: not llc -mtriple=x86_64 -global-isel=0 -fast-isel=1 -stop-after=finalize-isel < %s 2>&1 | FileCheck %s
; RUN: not llc -mtriple=x86_64 -global-isel=1 -fast-isel=0 -stop-after=irtranslator -global-isel-abort=0 < %s 2>&1 | FileCheck %s
-declare void @foo() "dontcall"
+declare void @foo() "dontcall-error"="e"
define void @bar() {
call void @foo()
ret void
}
-; CHECK: error: call to foo marked "dontcall"
+declare void @foo2() "dontcall-warn"="w"
+define void @bar2() {
+ call void @foo2()
+ ret void
+}
+
+declare void @foo3() "dontcall-warn"
+define void @bar3() {
+ call void @foo3()
+ ret void
+}
+
+; CHECK: error: call to foo marked "dontcall-error": e
+; CHECK: warning: call to foo2 marked "dontcall-warn": w
+; CHECK: warning: call to foo3 marked "dontcall-warn"{{$}}
; RUN: -r=%t/b.bc,caller,px
; TODO: As part of LTO, we check that types match, but *we don't yet check that
-; attributes match!!! What should happen if we remove "dontcall" from the
+; attributes match!!! What should happen if we remove "dontcall-error" from the
; definition or declaration of @callee?
-; CHECK: call to callee marked "dontcall"
+; CHECK: call to callee marked "dontcall-error"
;--- a.s
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
-define i32 @callee() "dontcall" noinline {
+define i32 @callee() "dontcall-error" noinline {
ret i32 42
}
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
-declare i32 @callee() "dontcall"
+declare i32 @callee() "dontcall-error"
define i32 @caller() {
entry: