diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -122,7 +122,8 @@ C Language Changes in Clang --------------------------- -- ... +- Support for ``__attribute__((error("")))`` and + ``__attribute__((warning("")))`` function attributes have been added. C++ Language Changes in Clang ----------------------------- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3817,3 +3817,17 @@ let Documentation = [EnforceTCBLeafDocs]; bit InheritEvenIfAlreadyPresent = 1; } + +def Error : Attr { + let Spellings = [GCC<"error">]; + let Args = [StringArgument<"UserDiagnostic">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [ErrorAttrDocs]; +} + +def Warning : Attr { + let Spellings = [GCC<"warning">]; + let Args = [StringArgument<"UserDiagnostic">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [WarningAttrDocs]; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6045,3 +6045,24 @@ - ``enforce_tcb_leaf(Name)`` indicates that this function is a part of the TCB named ``Name`` }]; } + +def ErrorAttrDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ + The ``error`` function attribute can be used to specify a custom diagnostic + to be emitted when a call to such a function is not eliminated via + optimizations. This can be used to create compile time assertions that + depend on optimizations, while providing diagnostics pointing to precise + locations of the call site in the source. + }]; +} +def WarningAttrDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ + The ``warning`` function attribute can be used to specify a custom + diagnostic to be emitted when a call to such a function is not eliminated + via optimizations. This can be used to create compile time assertions that + depend on optimizations, while providing diagnostics pointing to precise + locations of the call site in the source. + }]; +} 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 @@ -72,6 +72,12 @@ def err_fe_backend_unsupported : Error<"%0">, BackendInfo; def warn_fe_backend_unsupported : Warning<"%0">, BackendInfo; +def err_fe_backend_error_attr : + Error<"call to %0 declared with attribute error: %1">, BackendInfo; +def warn_fe_backend_warning_attr : + Warning<"call to %0 declared with attribute warning: %1">, BackendInfo, + InGroup; + def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; def err_fe_dependency_file_requires_MT : Error< diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1208,6 +1208,7 @@ def BackendOptimizationRemarkMissed : DiagGroup<"pass-missed">; def BackendOptimizationRemarkAnalysis : DiagGroup<"pass-analysis">; def BackendOptimizationFailure : DiagGroup<"pass-failed">; +def BackendUserDiagnostic : DiagGroup<"user-diagnostic">; // Instrumentation based profiling warnings. def ProfileInstrMissing : DiagGroup<"profile-instr-missing">; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5306,6 +5306,17 @@ TargetDecl->hasAttr()) getDebugInfo()->addHeapAllocSiteMetadata(CI, RetTy->getPointeeType(), Loc); + // Add metadata if calling an __attribute__((error(""))) or warning fn. + if (TargetDecl) + if (TargetDecl->hasAttr() || + TargetDecl->hasAttr()) { + llvm::ConstantInt *Line = + llvm::ConstantInt::get(Int32Ty, Loc.getRawEncoding()); + llvm::ConstantAsMetadata *MD = llvm::ConstantAsMetadata::get(Line); + llvm::MDTuple *MDT = llvm::MDNode::get(getLLVMContext(), {MD}); + CI->setMetadata("srcloc", MDT); + } + // 4. Finish the call. // If the call doesn't return, finish the basic block and clear the 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 @@ -401,6 +401,7 @@ const llvm::OptimizationRemarkAnalysisAliasing &D); void OptimizationFailureHandler( const llvm::DiagnosticInfoOptimizationFailure &D); + void DontCallDiagHandler(const DiagnosticInfoDontCall &D); }; void BackendConsumer::anchor() {} @@ -758,6 +759,31 @@ EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure); } +void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) { + unsigned DiagID = diag::err_fe_backend_error_attr; + SourceLocation LocCookie = + SourceLocation::getFromRawEncoding(D.getLocCookie()); + assert(LocCookie.isValid() && "invalid location of dontcall call"); + + if (const Decl *DE = Gen->GetDeclForMangledName(D.getFunctionName())) { + if (const auto *FD = dyn_cast(DE)) { + assert((FD->hasAttr() || FD->hasAttr()) && + "expected error or warning function attribute"); + + llvm::StringRef Msg; + if (const auto *EA = FD->getAttr()) { + Msg = EA->getUserDiagnostic(); + } else if (const auto *WA = FD->getAttr()) { + Msg = WA->getUserDiagnostic(); + DiagID = diag::warn_fe_backend_warning_attr; + } + + Diags.Report(LocCookie, DiagID) << FD->getDeclName() << Msg; + } + } + // TODO: assert if D or FD were NULL? +} + /// This function is invoked when the backend needs /// to report something to the user. void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { @@ -833,6 +859,9 @@ case llvm::DK_Unsupported: UnsupportedDiagHandler(cast(DI)); return; + case llvm::DK_DontCall: + DontCallDiagHandler(cast(DI)); + return; default: // Plugin IDs are not bound to any value as they are set dynamically. ComputeDiagRemarkID(Severity, backend_plugin, DiagID); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2172,6 +2172,11 @@ CalleeIdx, PayloadIndices, /* VarArgsArePassed */ false)})); } + + // TODO: move to CodeGenModule::ConstructAttributeList() or + // CodeGenFunction::EmitCall() ??? + if (FD->hasAttr() || FD->hasAttr()) + F->addFnAttr("dontcall"); } void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -946,6 +946,19 @@ D->addAttr(::new (S.Context) EnableIfAttr(S.Context, AL, Cond, Msg)); } +static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef Str; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) + return; + D->addAttr(::new (S.Context) ErrorAttr(S.Context, AL, Str)); +} +static void handleWarningAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef Str; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) + return; + D->addAttr(::new (S.Context) WarningAttr(S.Context, AL, Str)); +} + namespace { /// Determines if a given Expr references any of the given function's /// ParmVarDecls, or the function's implicit `this` parameter (if applicable). @@ -7863,6 +7876,9 @@ case ParsedAttr::AT_EnableIf: handleEnableIfAttr(S, D, AL); break; + case ParsedAttr::AT_Error: + handleErrorAttr(S, D, AL); + break; case ParsedAttr::AT_DiagnoseIf: handleDiagnoseIfAttr(S, D, AL); break; @@ -8078,6 +8094,9 @@ case ParsedAttr::AT_TypeVisibility: handleVisibilityAttr(S, D, AL, true); break; + case ParsedAttr::AT_Warning: + handleWarningAttr(S, D, AL); + break; case ParsedAttr::AT_WarnUnusedResult: handleWarnUnusedResult(S, D, AL); break; diff --git a/clang/test/CodeGen/attr-error.c b/clang/test/CodeGen/attr-error.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/attr-error.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s +__attribute__((error("oh no"))) void foo(void); + +void bar(void) { + foo(); +} + +// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]] +// CHECK: declare void @foo() [[ATTR:#[0-9]+]] +// CHECK: attributes [[ATTR]] = {{{.*}}"dontcall" +// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}} diff --git a/clang/test/CodeGen/attr-warning.c b/clang/test/CodeGen/attr-warning.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/attr-warning.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s +__attribute__((warning("oh no"))) void foo(void); + +void bar(void) { + foo(); +} + +// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]] +// CHECK: declare void @foo() [[ATTR:#[0-9]+]] +// CHECK: attributes [[ATTR]] = {{{.*}}"dontcall" +// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}} diff --git a/clang/test/Frontend/backend-attribute-error-warning-optimize.c b/clang/test/Frontend/backend-attribute-error-warning-optimize.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/backend-attribute-error-warning-optimize.c @@ -0,0 +1,15 @@ +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -O2 -verify -emit-codegen-only %s + +__attribute__((error("oh no foo"))) void foo(void); + +__attribute__((error("oh no bar"))) void bar(void); + +int x(void) { + return 8 % 2 == 1; +} +void baz(void) { + foo(); // expected-error {{call to 'foo' declared with attribute error: oh no foo}} + if (x()) + bar(); +} diff --git a/clang/test/Frontend/backend-attribute-error-warning.c b/clang/test/Frontend/backend-attribute-error-warning.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/backend-attribute-error-warning.c @@ -0,0 +1,22 @@ +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -verify -emit-codegen-only %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); + +void baz(void) { + foo(); // expected-error {{call to 'foo' declared with attribute error: oh no foo}} + if (x()) + bar(); // expected-error {{call to 'bar' declared with attribute error: oh no bar}} + quux(); // expected-warning {{call to 'quux' declared with attribute warning: oh no quux}} + __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455' declared with attribute error: demangle me}} +} diff --git a/clang/test/Frontend/backend-attribute-error-warning.cpp b/clang/test/Frontend/backend-attribute-error-warning.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/backend-attribute-error-warning.cpp @@ -0,0 +1,37 @@ +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -verify -emit-codegen-only %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); + +template +__attribute__((error("demangle me, too"))) +T +nocall(T t); + +struct Widget { + __attribute__((warning("don't call me!"))) + operator int() { return 42; } +}; + +void baz(void) { + // Test that we demangle correctly in the diagnostic for C++. + foo(); // expected-error {{call to 'foo' declared with attribute error: oh no foo}} + if (x()) + bar(); // expected-error {{call to 'bar' declared with attribute error: oh no bar}} + quux(); // expected-warning {{call to 'quux' declared with attribute warning: oh no quux}} + __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455' declared with attribute error: demangle me}} + nocall(42); // expected-error {{call to 'nocall' declared with attribute error: demangle me, too}} + + Widget W; + int w = W; // expected-warning {{'operator int' declared with attribute warning: don't call me!}} +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -61,6 +61,7 @@ // CHECK-NEXT: EnforceTCB (SubjectMatchRule_function) // CHECK-NEXT: EnforceTCBLeaf (SubjectMatchRule_function) // CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum) +// CHECK-NEXT: Error (SubjectMatchRule_function) // CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) // CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum) @@ -177,6 +178,7 @@ // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function) // CHECK-NEXT: WarnUnused (SubjectMatchRule_record) // CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType) +// CHECK-NEXT: Warning (SubjectMatchRule_function) // CHECK-NEXT: Weak (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) // CHECK-NEXT: WeakRef (SubjectMatchRule_variable, SubjectMatchRule_function) // CHECK-NEXT: WebAssemblyExportName (SubjectMatchRule_function) diff --git a/clang/test/Sema/attr-error.c b/clang/test/Sema/attr-error.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-error.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +#if !__has_attribute(error) +#error "error attribute missing" +#endif + +__attribute__((error("don't call me!"))) int foo(void); + +__attribute__((error)) // expected-error {{'error' attribute takes one argument}} +int +bad0(void); + +int bad1(__attribute__((error("bad1"))) int param); // expected-error {{'error' attribute only applies to functions}} + +int bad2(void) { + __attribute__((error("bad2"))); // expected-error {{'error' attribute cannot be applied to a statement}} +} + +__attribute__((error(3))) // expected-error {{'error' attribute requires a string}} +int +bad3(void); diff --git a/clang/test/Sema/attr-warning.c b/clang/test/Sema/attr-warning.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-warning.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +#if !__has_attribute(warning) +#warning "warning attribute missing" +#endif + +__attribute__((warning("don't call me!"))) int foo(void); + +__attribute__((warning)) // expected-error {{'warning' attribute takes one argument}} +int +bad0(void); + +int bad1(__attribute__((warning("bad1"))) int param); // expected-warning {{'warning' attribute only applies to functions}} + +int bad2(void) { + __attribute__((warning("bad2"))); // expected-error {{'warning' attribute cannot be applied to a statement}} +} + +__attribute__((warning(3))) // expected-error {{'warning' attribute requires a string}} +int +bad3(void); diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1576,6 +1576,12 @@ can prove that the function does not execute any convergent operations. Similarly, the optimizer may remove ``convergent`` on calls/invokes when it can prove that the call/invoke cannot call a convergent function. +``"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. ``"frame-pointer"`` This attribute tells the code generator whether the function should keep the frame pointer. The code generator may emit the frame pointer 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 @@ -79,6 +79,7 @@ DK_PGOProfile, DK_Unsupported, DK_SrcMgr, + DK_DontCall, DK_FirstPluginKind // Must be last value to work with // getNextAvailablePluginDiagnosticKind }; @@ -1070,6 +1071,23 @@ } }; +class DiagnosticInfoDontCall : public DiagnosticInfo { + StringRef CalleeName; + unsigned LocCookie; + +public: + // TODO: make LocCookie optional + DiagnosticInfoDontCall(StringRef CalleeName, unsigned LocCookie) + : DiagnosticInfo(DK_DontCall, DS_Error), CalleeName(CalleeName), + LocCookie(LocCookie) {} + StringRef getFunctionName() const { return CalleeName; } + unsigned getLocCookie() const { return LocCookie; } + void print(DiagnosticPrinter &DP) const override; + static bool classof(const DiagnosticInfo *DI) { + return DI->getKind() == DK_DontCall; + } +}; + } // end namespace llvm #endif // LLVM_IR_DIAGNOSTICINFO_H diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -47,6 +47,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/InlineAsm.h" @@ -2321,6 +2322,15 @@ if (CI.isInlineAsm()) return translateInlineAsm(CI, MIRBuilder); + if (F && F->hasFnAttribute("dontcall")) { + unsigned LocCookie = 0; + if (MDNode *MD = CI.getMetadata("srcloc")) + LocCookie = + mdconst::extract(MD->getOperand(0))->getZExtValue(); + DiagnosticInfoDontCall D(F->getName(), LocCookie); + F->getContext().diagnose(D); + } + Intrinsic::ID ID = Intrinsic::not_intrinsic; if (F && F->isIntrinsic()) { ID = F->getIntrinsicID(); diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -75,6 +75,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/GlobalValue.h" @@ -1151,6 +1152,16 @@ 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(MD->getOperand(0))->getZExtValue(); + DiagnosticInfoDontCall D(F->getName(), LocCookie); + F->getContext().diagnose(D); + } + return lowerCallTo(CLI); } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -69,6 +69,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/InlineAsm.h" @@ -7918,6 +7919,15 @@ } if (Function *F = I.getCalledFunction()) { + if (F->hasFnAttribute("dontcall")) { + unsigned LocCookie = 0; + if (MDNode *MD = I.getMetadata("srcloc")) + LocCookie = + mdconst::extract(MD->getOperand(0))->getZExtValue(); + DiagnosticInfoDontCall D(F->getName(), LocCookie); + DAG.getContext()->diagnose(D); + } + if (F->isDeclaration()) { // Is this an LLVM intrinsic or a target-specific intrinsic? unsigned IID = F->getIntrinsicID(); 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 @@ -401,3 +401,7 @@ void OptimizationRemarkAnalysisFPCommute::anchor() {} void OptimizationRemarkAnalysisAliasing::anchor() {} + +void DiagnosticInfoDontCall::print(DiagnosticPrinter &DP) const { + DP << "call to " << getFunctionName() << " marked \"dontcall\""; +} diff --git a/llvm/test/CodeGen/X86/attr-dontcall.ll b/llvm/test/CodeGen/X86/attr-dontcall.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/attr-dontcall.ll @@ -0,0 +1,11 @@ +; RUN: not llc -global-isel=0 -fast-isel=0 -stop-after=finalize-isel %s 2>&1 | FileCheck %s +; RUN: not llc -global-isel=0 -fast-isel=1 -stop-after=finalize-isel %s 2>&1 | FileCheck %s +; RUN: not llc -global-isel=1 -fast-isel=0 -stop-after=irtranslator %s 2>&1 | FileCheck %s + +declare void @foo() "dontcall" +define void @bar() { + call void @foo() + ret void +} + +; CHECK: error: call to foo marked "dontcall"