Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -818,6 +818,13 @@ let Documentation = [Undocumented]; } +def IFunc : Attr { + let Spellings = [GCC<"ifunc">]; + let Args = [StringArgument<"Resolver">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [IFuncDocs]; +} + def Restrict : InheritableAttr { let Spellings = [Declspec<"restrict">, GCC<"malloc">]; let Subjects = SubjectList<[Function]>; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1859,3 +1859,11 @@ }]; } + +def IFuncDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The attribute ``__attribute__((ifunc("resolver")))`` is used to mark a function as an indirect function using the STT_GNU_IFUNC symbol type extension to the ELF standard. For more information, see GCC ifunc attribute documentation https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html + }]; +} + Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4127,7 +4127,7 @@ def err_alias_after_tentative : Error<"alias definition of %0 after tentative definition">; def err_alias_is_definition : - Error<"definition %0 cannot also be an alias">; + Error<"definition %0 cannot also be an %select{alias|ifunc}1">; def err_definition_of_implicitly_declared_member : Error< "definition of implicitly declared %select{default constructor|copy " "constructor|move constructor|copy assignment operator|move assignment " Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1940,7 +1940,7 @@ if (hasInit()) return Definition; - if (hasAttr()) + if (hasAttr() || hasAttr()) return Definition; if (const auto *SAA = getAttr()) @@ -2401,7 +2401,7 @@ bool FunctionDecl::isDefined(const FunctionDecl *&Definition) const { for (auto I : redecls()) { if (I->IsDeleted || I->IsDefaulted || I->Body || I->IsLateTemplateParsed || - I->hasAttr()) { + I->hasAttr() || I->hasAttr()) { Definition = I->IsDeleted ? I->getCanonicalDecl() : I; return true; } Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -283,17 +283,23 @@ DiagnosticsEngine &Diags = getDiags(); for (const GlobalDecl &GD : Aliases) { const auto *D = cast(GD.getDecl()); - const AliasAttr *AA = D->getAttr(); + SourceLocation Location; + if (const AliasAttr *AA = D->getAttr()) + Location = AA->getLocation(); + else if (const IFuncAttr *IFA = D->getAttr()) + Location = IFA->getLocation(); + else + llvm_unreachable("Not an aliasi or ifunc?"); StringRef MangledName = getMangledName(GD); llvm::GlobalValue *Entry = GetGlobalValue(MangledName); auto *Alias = cast(Entry); const llvm::GlobalValue *GV = getAliasedGlobal(*Alias); if (!GV) { Error = true; - Diags.Report(AA->getLocation(), diag::err_cyclic_alias); + Diags.Report(Location, diag::err_cyclic_alias); } else if (GV->isDeclaration()) { Error = true; - Diags.Report(AA->getLocation(), diag::err_alias_to_undefined); + Diags.Report(Location, diag::err_alias_to_undefined); } llvm::Constant *Aliasee = Alias->getAliasee(); @@ -317,7 +323,7 @@ // expecting the link to be weak. if (auto GA = dyn_cast(AliaseeGV)) { if (GA->mayBeOverridden()) { - Diags.Report(AA->getLocation(), diag::warn_alias_to_weak_alias) + Diags.Report(Location, diag::warn_alias_to_weak_alias) << GV->getName() << GA->getName(); Aliasee = llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast( GA->getAliasee(), Alias->getType()); @@ -1414,8 +1420,8 @@ return; // If this is an alias definition (which otherwise looks like a declaration) - // emit it now. - if (Global->hasAttr()) + // emit it now. IFunc is an alias for function with special type. + if (Global->hasAttr() || Global->hasAttr()) return EmitAliasDefinition(GD); // If this is CUDA, be selective about which declarations we emit. @@ -2668,13 +2674,21 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) { const auto *D = cast(GD.getDecl()); - const AliasAttr *AA = D->getAttr(); - assert(AA && "Not an alias?"); + StringRef AliaseeName; + SourceLocation Location; + if (const AliasAttr *AA = D->getAttr()) { + AliaseeName = AA->getAliasee(); + Location = AA->getLocation(); + } else if (const IFuncAttr *IFA = D->getAttr()) { + AliaseeName = IFA->getResolver(); + Location = IFA->getLocation(); + } else + llvm_unreachable("Not an aliasi or ifunc?"); StringRef MangledName = getMangledName(GD); - if (AA->getAliasee() == MangledName) { - Diags.Report(AA->getLocation(), diag::err_cyclic_alias); + if (AliaseeName == MangledName) { + Diags.Report(Location, diag::err_cyclic_alias); return; } @@ -2692,10 +2706,10 @@ // if a deferred decl. llvm::Constant *Aliasee; if (isa(DeclTy)) - Aliasee = GetOrCreateLLVMFunction(AA->getAliasee(), DeclTy, GD, + Aliasee = GetOrCreateLLVMFunction(AliaseeName, DeclTy, GD, /*ForVTable=*/false); else - Aliasee = GetOrCreateLLVMGlobal(AA->getAliasee(), + Aliasee = GetOrCreateLLVMGlobal(AliaseeName, llvm::PointerType::getUnqual(DeclTy), /*D=*/nullptr); @@ -2705,7 +2719,7 @@ if (Entry) { if (GA->getAliasee() == Entry) { - Diags.Report(AA->getLocation(), diag::err_cyclic_alias); + Diags.Report(Location, diag::err_cyclic_alias); return; } @@ -2739,6 +2753,11 @@ if (VD->getTLSKind()) setTLSMode(GA, *VD); + if (D->hasAttr()) { + GA->setIFunc(true); + GA->setLinkage(llvm::GlobalValue::LinkOnceAnyLinkage); + } + setAliasAttributes(D, GA); } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2297,7 +2297,7 @@ for (unsigned I = 0, E = NewAttributes.size(); I != E;) { const Attr *NewAttribute = NewAttributes[I]; - if (isa(NewAttribute)) { + if (isa(NewAttribute) || isa(NewAttribute)) { if (FunctionDecl *FD = dyn_cast(New)) { Sema::SkipBodyInfo SkipBody; S.CheckForFunctionRedefinition(FD, cast(Def), &SkipBody); @@ -5420,7 +5420,7 @@ if (const auto *Attr = VD->getAttr()) { assert(VD->isThisDeclarationADefinition() && !VD->isExternallyVisible() && "Broken AliasAttr handled late!"); - S.Diag(Attr->getLocation(), diag::err_alias_is_definition) << VD; + S.Diag(Attr->getLocation(), diag::err_alias_is_definition) << VD << 0; VD->dropAttr(); } } Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1529,6 +1529,29 @@ Attr.getAttributeSpellingListIndex())); } +static void handleIFuncAttr(Sema &S, Decl *D, const AttributeList &Attr) { + StringRef Str; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + return; + + // Aliases should be on declarations, not definitions. + if (const auto *FD = dyn_cast(D)) { + if (FD->isThisDeclarationADefinition()) { + S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << FD << 1; + return; + } + if (S.Context.getTargetInfo().getTriple().getObjectFormat() != + llvm::Triple::ELF) { + S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); + return; + } + } else + llvm_unreachable("ifunc must be used for function declaration"); + + D->addAttr(::new (S.Context) IFuncAttr(Attr.getRange(), S.Context, Str, + Attr.getAttributeSpellingListIndex())); +} + static void handleAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) { StringRef Str; if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) @@ -1542,13 +1565,13 @@ // Aliases should be on declarations, not definitions. if (const auto *FD = dyn_cast(D)) { if (FD->isThisDeclarationADefinition()) { - S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << FD; + S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << FD << 0; return; } } else { const auto *VD = cast(D); if (VD->isThisDeclarationADefinition() && VD->isExternallyVisible()) { - S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << VD; + S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << VD << 0; return; } } @@ -4975,6 +4998,9 @@ case AttributeList::AT_IBOutletCollection: handleIBOutletCollection(S, D, Attr); break; + case AttributeList::AT_IFunc: + handleIFuncAttr(S, D, Attr); + break; case AttributeList::AT_Alias: handleAliasAttr(S, D, Attr); break; Index: test/CodeGen/ifunc.c =================================================================== --- /dev/null +++ test/CodeGen/ifunc.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s + +int foo (int) __attribute__ ((ifunc("foo_ifunc"))); + +static int f1(int i) { + return i + 1; +} + +static int f2(int i) { + return i + 2; +} + +typedef int (*foo_t)(int); + +extern int global; + +static foo_t foo_ifunc() { + return global ? f1 : f2; +} + +int bar() { + return foo(1); +} + +// CHECK: @foo = linkonce alias ifunc i32 (i32), bitcast (i32 (i32)* ()* @foo_ifunc to i32 (i32)*) +// CHECK: call i32 @foo(i32 Index: test/Sema/attr-ifunc.c =================================================================== --- /dev/null +++ test/Sema/attr-ifunc.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -triple x86_64-windows -fsyntax-only -verify %s + +void g() {} + +void f() __attribute__((ifunc("g"))); +//expected-warning@-1 {{'ifunc' attribute ignored}}