DmitryPolukhin created this revision.
DmitryPolukhin added reviewers: aaron.ballman, rjmccall.
DmitryPolukhin added a subscriber: cfe-commits.

This patch add support for GCC __attribute__((ifunc("resolver"))) for targets 
that use ELF as object file format. In general ifunc is a special kind of 
function alias with type @gnu_indirect_function. LLVM patch will be sent 
separate.

http://reviews.llvm.org/D15524

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/Decl.cpp
  lib/CodeGen/CodeGenModule.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/CodeGen/ifunc.c
  test/Sema/attr-ifunc.c

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}}
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: 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<FunctionDecl>(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<FunctionDecl>(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<VarDecl>(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: 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<AliasAttr>(NewAttribute)) {
+    if (isa<AliasAttr>(NewAttribute) || isa<IFuncAttr>(NewAttribute)) {
       if (FunctionDecl *FD = dyn_cast<FunctionDecl>(New)) {
         Sema::SkipBodyInfo SkipBody;
         S.CheckForFunctionRedefinition(FD, cast<FunctionDecl>(Def), &SkipBody);
@@ -5420,7 +5420,7 @@
       if (const auto *Attr = VD->getAttr<AliasAttr>()) {
         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<AliasAttr>();
       }
     }
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<ValueDecl>(GD.getDecl());
-    const AliasAttr *AA = D->getAttr<AliasAttr>();
+    SourceLocation Location;
+    if (const AliasAttr *AA = D->getAttr<AliasAttr>())
+      Location = AA->getLocation();
+    else if (const IFuncAttr *IFA = D->getAttr<IFuncAttr>())
+      Location = IFA->getLocation();
+    else
+      llvm_unreachable("Not an aliasi or ifunc?");
     StringRef MangledName = getMangledName(GD);
     llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
     auto *Alias = cast<llvm::GlobalAlias>(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<llvm::GlobalAlias>(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<AliasAttr>())
+  // emit it now. IFunc is an alias for function with special type.
+  if (Global->hasAttr<AliasAttr>() || Global->hasAttr<IFuncAttr>())
     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<ValueDecl>(GD.getDecl());
-  const AliasAttr *AA = D->getAttr<AliasAttr>();
-  assert(AA && "Not an alias?");
+  StringRef AliaseeName;
+  SourceLocation Location;
+  if (const AliasAttr *AA = D->getAttr<AliasAttr>()) {
+    AliaseeName = AA->getAliasee();
+    Location = AA->getLocation();
+  } else if (const IFuncAttr *IFA = D->getAttr<IFuncAttr>()) {
+    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<llvm::FunctionType>(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<IFuncAttr>()) {
+    GA->setIFunc(true);
+    GA->setLinkage(llvm::GlobalValue::LinkOnceAnyLinkage);
+  }
+
   setAliasAttributes(D, GA);
 }
 
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -1940,7 +1940,7 @@
   if (hasInit())
     return Definition;
 
-  if (hasAttr<AliasAttr>())
+  if (hasAttr<AliasAttr>() || hasAttr<IFuncAttr>())
     return Definition;
 
   if (const auto *SAA = getAttr<SelectAnyAttr>())
@@ -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<AliasAttr>()) {
+        I->hasAttr<AliasAttr>() || I->hasAttr<IFuncAttr>()) {
       Definition = I->IsDeleted ? I->getCanonicalDecl() : I;
       return true;
     }
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: 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/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]>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to