[GCC] Attribute ifunc support in clang

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 http://reviews.llvm.org/D15525

Differential Revision: http://reviews.llvm.org/D15524

llvm-svn: 265917
This commit is contained in:
Dmitry Polukhin 2016-04-11 07:48:59 +00:00
parent bbffeac569
commit 85eda12d09
13 changed files with 265 additions and 33 deletions

View File

@ -567,6 +567,13 @@ public:
return NextInContextAndBits.getInt() & ModulePrivateFlag;
}
/// Return true if this declaration has an attribute which acts as
/// definition of the entity, such as 'alias' or 'ifunc'.
bool hasDefiningAttr() const;
/// Return this declaration's defining attribute if it has one.
const Attr *getDefiningAttr() const;
protected:
/// \brief Specify whether this declaration was marked as being private
/// to the module in which it was defined.

View File

@ -855,6 +855,13 @@ def IBOutletCollection : InheritableAttr {
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]>;

View File

@ -2358,3 +2358,16 @@ to replace the deprecated name with a new name. Otherwise, when spelled as
string argument which is the message to display when emitting the warning.
}];
}
def IFuncDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
``__attribute__((ifunc("resolver")))`` is used to mark that the address of a declaration should be resolved at runtime by calling a resolver function.
The symbol name of the resolver function is given in quotes. A function with this name (after mangling) must be defined in the current translation unit; it may be ``static``. The resolver function should take no arguments and return a pointer.
The ``ifunc`` attribute may only be used on a function declaration. A function declaration with an ``ifunc`` attribute is considered to be a definition of the declared entity. The entity must not have weak linkage; for example, in C++, it cannot be applied to a declaration if a definition at that location would be considered inline.
Not all targets support this attribute. ELF targets support this attribute when using binutils v2.20.1 or higher and glibc v2.11.1 or higher. Non-ELF targets currently do not support this attribute.
}];
}

View File

@ -2464,17 +2464,21 @@ def err_attribute_weakref_without_alias : Error<
def err_alias_not_supported_on_darwin : Error <
"only weak aliases are supported on darwin">;
def err_alias_to_undefined : Error<
"alias must point to a defined variable or function">;
"%select{alias|ifunc}0 must point to a defined %select{variable or |}1function">;
def warn_alias_to_weak_alias : Warning<
"alias will always resolve to %0 even if weak definition of alias %1 is overridden">,
"%select{alias|ifunc}2 will always resolve to %0 even if weak definition of %1 is overridden">,
InGroup<IgnoredAttributes>;
def warn_alias_with_section : Warning<
"alias will not be in section '%0' but in the same section as the aliasee">,
"%select{alias|ifunc}1 will not be in section '%0' but in the same section as the %select{aliasee|resolver}2">,
InGroup<IgnoredAttributes>;
def err_duplicate_mangled_name : Error<
"definition with same mangled name as another definition">;
def err_cyclic_alias : Error<
"alias definition is part of a cycle">;
"%select{alias|ifunc}0 definition is part of a cycle">;
def err_ifunc_resolver_return : Error<
"ifunc resolver function must return a pointer">;
def err_ifunc_resolver_params : Error<
"ifunc resolver function must have no parameters">;
def warn_attribute_wrong_decl_type : Warning<
"%0 attribute only applies to %select{functions|unions|"
"variables and functions|functions and methods|parameters|"
@ -4196,7 +4200,7 @@ def err_redefinition : Error<"redefinition of %0">;
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 "

View File

@ -1946,7 +1946,7 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
if (hasInit())
return Definition;
if (hasAttr<AliasAttr>())
if (hasDefiningAttr())
return Definition;
if (const auto *SAA = getAttr<SelectAnyAttr>())
@ -2486,7 +2486,7 @@ bool FunctionDecl::hasTrivialBody() const
bool FunctionDecl::isDefined(const FunctionDecl *&Definition) const {
for (auto I : redecls()) {
if (I->IsDeleted || I->IsDefaulted || I->Body || I->IsLateTemplateParsed ||
I->hasAttr<AliasAttr>()) {
I->hasDefiningAttr()) {
Definition = I->IsDeleted ? I->getCanonicalDecl() : I;
return true;
}

View File

@ -362,6 +362,18 @@ bool Decl::isReferenced() const {
return false;
}
bool Decl::hasDefiningAttr() const {
return hasAttr<AliasAttr>() || hasAttr<IFuncAttr>();
}
const Attr *Decl::getDefiningAttr() const {
if (AliasAttr *AA = getAttr<AliasAttr>())
return AA;
if (IFuncAttr *IFA = getAttr<IFuncAttr>())
return IFA;
return nullptr;
}
/// \brief Determine the availability of the given declaration based on
/// the target platform.
///

View File

@ -274,20 +274,21 @@ void CodeGenModule::applyGlobalValReplacements() {
// This is only used in aliases that we created and we know they have a
// linear structure.
static const llvm::GlobalObject *getAliasedGlobal(const llvm::GlobalAlias &GA) {
llvm::SmallPtrSet<const llvm::GlobalAlias*, 4> Visited;
const llvm::Constant *C = &GA;
static const llvm::GlobalObject *getAliasedGlobal(
const llvm::GlobalIndirectSymbol &GIS) {
llvm::SmallPtrSet<const llvm::GlobalIndirectSymbol*, 4> Visited;
const llvm::Constant *C = &GIS;
for (;;) {
C = C->stripPointerCasts();
if (auto *GO = dyn_cast<llvm::GlobalObject>(C))
return GO;
// stripPointerCasts will not walk over weak aliases.
auto *GA2 = dyn_cast<llvm::GlobalAlias>(C);
if (!GA2)
auto *GIS2 = dyn_cast<llvm::GlobalIndirectSymbol>(C);
if (!GIS2)
return nullptr;
if (!Visited.insert(GA2).second)
if (!Visited.insert(GIS2).second)
return nullptr;
C = GA2->getAliasee();
C = GIS2->getIndirectSymbol();
}
}
@ -299,20 +300,35 @@ void CodeGenModule::checkAliases() {
DiagnosticsEngine &Diags = getDiags();
for (const GlobalDecl &GD : Aliases) {
const auto *D = cast<ValueDecl>(GD.getDecl());
const AliasAttr *AA = D->getAttr<AliasAttr>();
SourceLocation Location;
bool IsIFunc = D->hasAttr<IFuncAttr>();
if (const Attr *A = D->getDefiningAttr())
Location = A->getLocation();
else
llvm_unreachable("Not an alias or ifunc?");
StringRef MangledName = getMangledName(GD);
llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
auto *Alias = cast<llvm::GlobalAlias>(Entry);
auto *Alias = cast<llvm::GlobalIndirectSymbol>(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) << IsIFunc;
} else if (GV->isDeclaration()) {
Error = true;
Diags.Report(AA->getLocation(), diag::err_alias_to_undefined);
Diags.Report(Location, diag::err_alias_to_undefined)
<< IsIFunc << IsIFunc;
} else if (IsIFunc) {
// Check resolver function type.
llvm::FunctionType *FTy = dyn_cast<llvm::FunctionType>(
GV->getType()->getPointerElementType());
assert(FTy);
if (!FTy->getReturnType()->isPointerTy())
Diags.Report(Location, diag::err_ifunc_resolver_return);
if (FTy->getNumParams())
Diags.Report(Location, diag::err_ifunc_resolver_params);
}
llvm::Constant *Aliasee = Alias->getAliasee();
llvm::Constant *Aliasee = Alias->getIndirectSymbol();
llvm::GlobalValue *AliaseeGV;
if (auto CE = dyn_cast<llvm::ConstantExpr>(Aliasee))
AliaseeGV = cast<llvm::GlobalValue>(CE->getOperand(0));
@ -323,7 +339,7 @@ void CodeGenModule::checkAliases() {
StringRef AliasSection = SA->getName();
if (AliasSection != AliaseeGV->getSection())
Diags.Report(SA->getLocation(), diag::warn_alias_with_section)
<< AliasSection;
<< AliasSection << IsIFunc << IsIFunc;
}
// We have to handle alias to weak aliases in here. LLVM itself disallows
@ -331,13 +347,13 @@ void CodeGenModule::checkAliases() {
// compatibility with gcc we implement it by just pointing the alias
// to its aliasee's aliasee. We also warn, since the user is probably
// expecting the link to be weak.
if (auto GA = dyn_cast<llvm::GlobalAlias>(AliaseeGV)) {
if (auto GA = dyn_cast<llvm::GlobalIndirectSymbol>(AliaseeGV)) {
if (GA->isInterposable()) {
Diags.Report(AA->getLocation(), diag::warn_alias_to_weak_alias)
<< GV->getName() << GA->getName();
Diags.Report(Location, diag::warn_alias_to_weak_alias)
<< GV->getName() << GA->getName() << IsIFunc;
Aliasee = llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(
GA->getAliasee(), Alias->getType());
Alias->setAliasee(Aliasee);
GA->getIndirectSymbol(), Alias->getType());
Alias->setIndirectSymbol(Aliasee);
}
}
}
@ -347,7 +363,7 @@ void CodeGenModule::checkAliases() {
for (const GlobalDecl &GD : Aliases) {
StringRef MangledName = getMangledName(GD);
llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
auto *Alias = cast<llvm::GlobalAlias>(Entry);
auto *Alias = dyn_cast<llvm::GlobalIndirectSymbol>(Entry);
Alias->replaceAllUsesWith(llvm::UndefValue::get(Alias->getType()));
Alias->eraseFromParent();
}
@ -1538,6 +1554,10 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
if (Global->hasAttr<AliasAttr>())
return EmitAliasDefinition(GD);
// IFunc like an alias whose value is resolved at runtime by calling resolver.
if (Global->hasAttr<IFuncAttr>())
return emitIFuncDefinition(GD);
// If this is CUDA, be selective about which declarations we emit.
if (LangOpts.CUDA) {
if (LangOpts.CUDAIsDevice) {
@ -2901,7 +2921,7 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) {
StringRef MangledName = getMangledName(GD);
if (AA->getAliasee() == MangledName) {
Diags.Report(AA->getLocation(), diag::err_cyclic_alias);
Diags.Report(AA->getLocation(), diag::err_cyclic_alias) << 0;
return;
}
@ -2932,7 +2952,7 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) {
if (Entry) {
if (GA->getAliasee() == Entry) {
Diags.Report(AA->getLocation(), diag::err_cyclic_alias);
Diags.Report(AA->getLocation(), diag::err_cyclic_alias) << 0;
return;
}
@ -2969,6 +2989,65 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) {
setAliasAttributes(D, GA);
}
void CodeGenModule::emitIFuncDefinition(GlobalDecl GD) {
const auto *D = cast<ValueDecl>(GD.getDecl());
const IFuncAttr *IFA = D->getAttr<IFuncAttr>();
assert(IFA && "Not an ifunc?");
StringRef MangledName = getMangledName(GD);
if (IFA->getResolver() == MangledName) {
Diags.Report(IFA->getLocation(), diag::err_cyclic_alias) << 1;
return;
}
// Report an error if some definition overrides ifunc.
llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
if (Entry && !Entry->isDeclaration()) {
GlobalDecl OtherGD;
if (lookupRepresentativeDecl(MangledName, OtherGD) &&
DiagnosedConflictingDefinitions.insert(GD).second) {
Diags.Report(D->getLocation(), diag::err_duplicate_mangled_name);
Diags.Report(OtherGD.getDecl()->getLocation(),
diag::note_previous_definition);
}
return;
}
Aliases.push_back(GD);
llvm::Type *DeclTy = getTypes().ConvertTypeForMem(D->getType());
llvm::Constant *Resolver =
GetOrCreateLLVMFunction(IFA->getResolver(), DeclTy, GD,
/*ForVTable=*/false);
llvm::GlobalIFunc *GIF =
llvm::GlobalIFunc::create(DeclTy, 0, llvm::Function::ExternalLinkage,
"", Resolver, &getModule());
if (Entry) {
if (GIF->getResolver() == Entry) {
Diags.Report(IFA->getLocation(), diag::err_cyclic_alias) << 1;
return;
}
assert(Entry->isDeclaration());
// If there is a declaration in the module, then we had an extern followed
// by the ifunc, as in:
// extern int test();
// ...
// int test() __attribute__((ifunc("resolver")));
//
// Remove it and replace uses of it with the ifunc.
GIF->takeName(Entry);
Entry->replaceAllUsesWith(llvm::ConstantExpr::getBitCast(GIF,
Entry->getType()));
Entry->eraseFromParent();
} else
GIF->setName(MangledName);
SetCommonAttributes(D, GIF);
}
llvm::Function *CodeGenModule::getIntrinsic(unsigned IID,
ArrayRef<llvm::Type*> Tys) {
return llvm::Intrinsic::getDeclaration(&getModule(), (llvm::Intrinsic::ID)IID,

View File

@ -1175,6 +1175,7 @@ private:
void EmitGlobalFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV);
void EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative = false);
void EmitAliasDefinition(GlobalDecl GD);
void emitIFuncDefinition(GlobalDecl GD);
void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D);
void EmitObjCIvarInitializations(ObjCImplementationDecl *D);

View File

@ -2299,7 +2299,7 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
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);
@ -5464,7 +5464,7 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
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>();
}
}

View File

@ -1548,6 +1548,28 @@ static void handleWeakRefAttr(Sema &S, Decl *D, const AttributeList &Attr) {
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.
const auto *FD = cast<FunctionDecl>(D);
if (FD->isThisDeclarationADefinition()) {
S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << FD << 1;
return;
}
// FIXME: it should be handled as a target specific attribute.
if (S.Context.getTargetInfo().getTriple().getObjectFormat() !=
llvm::Triple::ELF) {
S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName();
return;
}
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))
@ -1564,13 +1586,13 @@ static void handleAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) {
// 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;
}
}
@ -5373,6 +5395,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
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;

View File

@ -0,0 +1,41 @@
// 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);
int global;
static foo_t foo_ifunc() {
return global ? f1 : f2;
}
int bar() {
return foo(1);
}
extern void goo(void);
void bar2(void) {
goo();
}
extern void goo(void) __attribute__ ((ifunc("goo_ifunc")));
void* goo_ifunc(void) {
return 0;
}
// CHECK: @foo = ifunc i32 (i32), bitcast (i32 (i32)* ()* @foo_ifunc to i32 (i32)*)
// CHECK: @goo = ifunc void (), bitcast (i8* ()* @goo_ifunc to void ()*)
// CHECK: call i32 @foo(i32
// CHECK: call void @goo()

View File

@ -55,7 +55,7 @@ typedef int b4;
void test2_bar() {}
void test2_foo() __attribute__((weak, alias("test2_bar")));
void test2_zed() __attribute__((alias("test2_foo"))); // expected-warning {{alias will always resolve to test2_bar even if weak definition of alias test2_foo is overridden}}
void test2_zed() __attribute__((alias("test2_foo"))); // expected-warning {{alias will always resolve to test2_bar even if weak definition of test2_foo is overridden}}
void test3_bar() { }
void test3_foo() __attribute__((section("test"))); // expected-warning {{alias will not be in section 'test' but in the same section as the aliasee}}

View File

@ -0,0 +1,43 @@
// RUN: %clang_cc1 -triple x86_64-windows -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple x86_64-linux -fsyntax-only -verify -emit-llvm -DCHECK_ALIASES %s
// RUN: %clang_cc1 -triple x86_64-linux -fsyntax-only -verify -emit-llvm %s
#if defined(_WIN32)
void foo() {}
void bar() __attribute__((ifunc("foo")));
//expected-warning@-1 {{'ifunc' attribute ignored}}
#else
#if defined(CHECK_ALIASES)
void* f1_ifunc();
void f1() __attribute__((ifunc("f1_ifunc")));
//expected-error@-1 {{ifunc must point to a defined function}}
void* f2_a() __attribute__((ifunc("f2_b")));
//expected-error@-1 {{ifunc definition is part of a cycle}}
void* f2_b() __attribute__((ifunc("f2_a")));
//expected-error@-1 {{ifunc definition is part of a cycle}}
void* f3_a() __attribute__((ifunc("f3_b")));
//expected-warning@-1 {{ifunc will always resolve to f3_c even if weak definition of f3_b is overridden}}
void* f3_b() __attribute__((weak, alias("f3_c")));
void* f3_c() { return 0; }
void f4_ifunc() {}
void f4() __attribute__((ifunc("f4_ifunc")));
//expected-error@-1 {{ifunc resolver function must return a pointer}}
void* f5_ifunc(int i) { return 0; }
void f5() __attribute__((ifunc("f5_ifunc")));
//expected-error@-1 {{ifunc resolver function must have no parameters}}
#else
void f1a() __asm("f1");
void f1a() {}
//expected-note@-1 {{previous definition is here}}
void f1() __attribute__((ifunc("f1_ifunc")));
//expected-error@-1 {{definition with same mangled name as another definition}}
void* f1_ifunc() { return 0; }
#endif
#endif