diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -137,6 +137,19 @@ } }; +class DeclOrStmtAttr : public Attr { +protected: + DeclOrStmtAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed) + : Attr(Context, CommonInfo, AK, IsLateParsed) {} + +public: + static bool classof(const Attr *A) { + return A->getKind() >= attr::FirstDeclOrStmtAttr && + A->getKind() <= attr::LastDeclOrStmtAttr; + } +}; + class InheritableAttr : public Attr { protected: InheritableAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, 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 @@ -555,6 +555,9 @@ /// A stmt attribute is not processed on a declaration or a type. class StmtAttr : Attr; +/// A attribute is either a declaration attribute or a statement attribute. +class DeclOrStmtAttr : Attr; + /// An inheritable attribute is inherited by later redeclarations. class InheritableAttr : Attr { // Set to true if this attribute can be duplicated on a subject when inheriting @@ -1312,7 +1315,7 @@ let Documentation = [LikelihoodDocs]; } -def NoMerge : StmtAttr { +def NoMerge : DeclOrStmtAttr { let Spellings = [Clang<"nomerge">]; let Documentation = [NoMergeDocs]; } 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 @@ -1966,6 +1966,8 @@ FuncAttrs.addAttribute(llvm::Attribute::NoReturn); NBA = Fn->getAttr(); } + if (!AttrOnCallSite && TargetDecl->hasAttr()) + FuncAttrs.addAttribute(llvm::Attribute::NoMerge); } // 'const', 'pure' and 'noalias' attributed functions are also nounwind. @@ -4873,11 +4875,13 @@ Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, llvm::Attribute::StrictFP); - // Add call-site nomerge attribute if exists. - if (InNoMergeAttributedStmt) - Attrs = - Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoMerge); + // Add nomerge attribute to the call-site if the callee function doesn't have + // the attribute. + if (const FunctionDecl *FD = dyn_cast_or_null(TargetDecl)) + if (!FD->hasAttr() && InNoMergeAttributedStmt) + Attrs = Attrs.addAttribute(getLLVMContext(), + llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoMerge); // Apply some call-site-specific attributes. // TODO: work this into building the attribute set. 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 @@ -2182,6 +2182,16 @@ D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority)); } +static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!isFunctionOrMethod(D)) { + S.Diag(AL.getLoc(), diag::warn_attribute_wrong_decl_type) + << AL << ExpectedFunctionOrMethod; + return; + } + + D->addAttr(::new (S.Context) NoMergeAttr(S.Context, AL)); +} + template static void handleAttrWithMessage(Sema &S, Decl *D, const ParsedAttr &AL) { // Handle the case where the attribute has a text message. @@ -7743,6 +7753,9 @@ handleSimpleAttributeWithExclusions(S, D, AL); break; + case ParsedAttr::AT_NoMerge: + handleNoMergeAttr(S, D, AL); + break; case ParsedAttr::AT_Visibility: handleVisibilityAttr(S, D, AL, false); break; diff --git a/clang/test/CodeGen/attr-nomerge.cpp b/clang/test/CodeGen/attr-nomerge.cpp --- a/clang/test/CodeGen/attr-nomerge.cpp +++ b/clang/test/CodeGen/attr-nomerge.cpp @@ -1,9 +1,23 @@ // RUN: %clang_cc1 -S -emit-llvm %s -triple x86_64-unknown-linux-gnu -o - | FileCheck %s -bool bar(); -void f(bool, bool); +class A { +public: + [[clang::nomerge]] A(); + [[clang::nomerge]] ~A(); + [[clang::nomerge]] void f(); + [[clang::nomerge]] virtual void g(); + [[clang::nomerge]] static void f1(); +}; -void foo(int i) { +class B : public A { +public: + void g() override; +}; + +[[clang::nomerge]] bool bar(); +[[clang::nomerge]] void f(bool, bool); + +void foo(int i, A *ap, B *bp) { [[clang::nomerge]] bar(); [[clang::nomerge]] (i = 4, bar()); [[clang::nomerge]] (void)(bar()); @@ -12,18 +26,48 @@ [[clang::nomerge]] for (bar(); bar(); bar()) {} [[clang::nomerge]] { asm("nop"); } bar(); + + ap->g(); + bp->g(); + + A a; + a.f(); + a.g(); + A::f1(); + + B b; + b.g(); } -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR:[0-9]+]] -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] -// CHECK: call void @_Z1fbb({{.*}}) #[[NOMERGEATTR]] -// CHECK: call void @"_ZZ3fooiENK3$_0clEv"(%class.anon* {{[^,]*}} %ref.tmp) #[[NOMERGEATTR]] -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] -// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] -// CHECK: call void asm {{.*}} #[[NOMERGEATTR2:[0-9]+]] -// CHECK: call zeroext i1 @_Z3barv() -// CHECK: attributes #[[NOMERGEATTR]] = { nomerge } -// CHECK: attributes #[[NOMERGEATTR2]] = { nomerge nounwind } + +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call void @_Z1fbb({{.*}}){{$}} +// CHECK: call void @"_ZZ3fooiP1AP1BENK3$_0clEv"{{.*}} #[[ATTR0:[0-9]+]] +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: call void asm sideeffect "nop"{{.*}} #[[ATTR1:[0-9]+]] +// CHECK: call zeroext i1 @_Z3barv(){{$}} +// CHECK: %[[AG:.*]] = load void (%class.A*)*, void (%class.A*)** +// CHECK-NEXT: call void %[[AG]](%class.A* nonnull dereferenceable +// CHECK: %[[BG:.*]] = load void (%class.B*)*, void (%class.B*)** +// CHECK-NEXT: call void %[[BG]](%class.B* nonnull dereferenceable + + +// CHECK-DAG: declare zeroext i1 @_Z3barv() #[[ATTR2:[0-9]+]] +// CHECK-DAG: declare void @_Z1fbb(i1 zeroext, i1 zeroext) #[[ATTR2]] +// CHECK-DAG: declare void @_ZN1AC1Ev{{.*}} #[[ATTR2]] +// CHECK-DAG: declare void @_ZN1A1fEv{{.*}} #[[ATTR2]] +// CHECK-DAG: declare void @_ZN1A1gEv{{.*}} #[[ATTR2]] +// CHECK-DAG: declare void @_ZN1A2f1Ev{{.*}} #[[ATTR2]] +// CHECK-DAG: declare void @_ZN1AC2Ev{{.*}} #[[ATTR2]] +// CHECK-DAG: declare void @_ZN1AD1Ev{{.*}} #[[ATTR3:[0-9]+]] +// CHECK-DAG: declare void @_ZN1AD2Ev{{.*}} #[[ATTR3]] + +// CHECK-DAG: attributes #[[ATTR0]] = {{{.*}}nomerge{{.*}}} +// CHECK-DAG: attributes #[[ATTR1]] = {{{.*}}nomerge{{.*}}} +// CHECK-DAG: attributes #[[ATTR2]] = {{{.*}}nomerge{{.*}}} +// CHECK-DAG: attributes #[[ATTR3]] = {{{.*}}nomerge{{.*}}} diff --git a/clang/test/Sema/attr-nomerge.cpp b/clang/test/Sema/attr-nomerge.cpp --- a/clang/test/Sema/attr-nomerge.cpp +++ b/clang/test/Sema/attr-nomerge.cpp @@ -8,10 +8,10 @@ int x; [[clang::nomerge]] x = 10; // expected-warning {{nomerge attribute is ignored because there exists no call expression inside the statement}} - [[clang::nomerge]] label: bar(); // expected-error {{'nomerge' attribute cannot be applied to a declaration}} + [[clang::nomerge]] label: bar(); // expected-warning {{'nomerge' attribute only applies to functions and methods}} } -int f(); +[[clang::nomerge]] int f(); -[[clang::nomerge]] static int i = f(); // expected-error {{'nomerge' attribute cannot be applied to a declaration}} +[[clang::nomerge]] static int i = f(); // expected-warning {{'nomerge' attribute only applies to functions and methods}} diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2682,6 +2682,7 @@ { "ATTR", "Attr" }, { "TYPE_ATTR", "TypeAttr" }, { "STMT_ATTR", "StmtAttr" }, + { "DECL_OR_STMT_ATTR", "DeclOrStmtAttr" }, { "INHERITABLE_ATTR", "InheritableAttr" }, { "DECL_OR_TYPE_ATTR", "DeclOrTypeAttr" }, { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" }, @@ -3767,7 +3768,8 @@ OS << (Attr.isSubClassOf("TypeAttr") || Attr.isSubClassOf("DeclOrTypeAttr")) << ";\n"; OS << " IsStmt = "; - OS << Attr.isSubClassOf("StmtAttr") << ";\n"; + OS << (Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr")) + << ";\n"; OS << " IsKnownToGCC = "; OS << IsKnownToGCC(Attr) << ";\n"; OS << " IsSupportedByPragmaAttribute = "; diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -121,7 +121,7 @@ /// Function is called early and/or often, so lazy binding isn't worthwhile. def NonLazyBind : EnumAttr<"nonlazybind">; -/// Disable merging for call sites +/// Disable merging for specified functions or call sites. def NoMerge : EnumAttr<"nomerge">; /// Pointer is known to be not null.