mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-14 14:56:47 +00:00
[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:
parent
bbffeac569
commit
85eda12d09
@ -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.
|
||||
|
@ -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]>;
|
||||
|
@ -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.
|
||||
}];
|
||||
}
|
||||
|
@ -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 "
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
41
clang/test/CodeGen/ifunc.c
Normal file
41
clang/test/CodeGen/ifunc.c
Normal 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()
|
@ -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}}
|
||||
|
43
clang/test/Sema/attr-ifunc.c
Normal file
43
clang/test/Sema/attr-ifunc.c
Normal 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
|
Loading…
x
Reference in New Issue
Block a user