diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h --- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -246,7 +246,7 @@ const Constant *C, Align &Alignment) const override; - static XCOFF::StorageClass getStorageClassForGlobal(const GlobalObject *GO); + static XCOFF::StorageClass getStorageClassForGlobal(const GlobalValue *GV); MCSection * getSectionForFunctionDescriptor(const Function *F, @@ -263,7 +263,7 @@ MCSymbol *getTargetSymbol(const GlobalValue *GV, const TargetMachine &TM) const override; - MCSymbol *getFunctionEntryPointSymbol(const Function *F, + MCSymbol *getFunctionEntryPointSymbol(const GlobalValue *Func, const TargetMachine &TM) const override; }; diff --git a/llvm/include/llvm/Target/TargetLoweringObjectFile.h b/llvm/include/llvm/Target/TargetLoweringObjectFile.h --- a/llvm/include/llvm/Target/TargetLoweringObjectFile.h +++ b/llvm/include/llvm/Target/TargetLoweringObjectFile.h @@ -247,7 +247,8 @@ /// If supported, return the function entry point symbol. /// Otherwise, returns nulltpr. - virtual MCSymbol *getFunctionEntryPointSymbol(const Function *F, + /// Func must be a function or an alias which has a function as base object. + virtual MCSymbol *getFunctionEntryPointSymbol(const GlobalValue *Func, const TargetMachine &TM) const { return nullptr; } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1390,16 +1390,7 @@ void AsmPrinter::emitGlobalIndirectSymbol(Module &M, const GlobalIndirectSymbol& GIS) { MCSymbol *Name = getSymbol(&GIS); - - if (GIS.hasExternalLinkage() || !MAI->getWeakRefDirective()) - OutStreamer->emitSymbolAttribute(Name, MCSA_Global); - else if (GIS.hasWeakLinkage() || GIS.hasLinkOnceLinkage()) - OutStreamer->emitSymbolAttribute(Name, MCSA_WeakReference); - else - assert(GIS.hasLocalLinkage() && "Invalid alias or ifunc linkage"); - bool IsFunction = GIS.getValueType()->isFunctionTy(); - // Treat bitcasts of functions as functions also. This is important at least // on WebAssembly where object and function addresses can't alias each other. if (!IsFunction) @@ -1408,6 +1399,30 @@ IsFunction = CE->getOperand(0)->getType()->getPointerElementType()->isFunctionTy(); + // AIX's assembly directive `.set` is not usable for aliasing purpose, + // so AIX has to use the extra-label-at-definition strategy. At this + // point, all the extra label is emitted, we just have to emit linkage for + // those labels. + if (TM.getTargetTriple().isOSBinFormatXCOFF()) { + assert(!isa(GIS) && "IFunc is not supported on AIX."); + assert(MAI->hasVisibilityOnlyWithLinkage() && + "Visibility should be handled with emitLinkage() on AIX."); + emitLinkage(&GIS, Name); + // If it's a function, also emit linkage for aliases of function entry + // point. + if (IsFunction) + emitLinkage(&GIS, + getObjFileLowering().getFunctionEntryPointSymbol(&GIS, TM)); + return; + } + + if (GIS.hasExternalLinkage() || !MAI->getWeakRefDirective()) + OutStreamer->emitSymbolAttribute(Name, MCSA_Global); + else if (GIS.hasWeakLinkage() || GIS.hasLinkOnceLinkage()) + OutStreamer->emitSymbolAttribute(Name, MCSA_WeakReference); + else + assert(GIS.hasLocalLinkage() && "Invalid alias or ifunc linkage"); + // Set the symbol type to function if the alias has a function type. // This affects codegen when the aliasee is not a function. if (IsFunction) diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2125,9 +2125,11 @@ report_fatal_error("XCOFF not yet implemented."); } -XCOFF::StorageClass TargetLoweringObjectFileXCOFF::getStorageClassForGlobal( - const GlobalObject *GO) { - switch (GO->getLinkage()) { +XCOFF::StorageClass +TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(const GlobalValue *GV) { + assert(!isa(GV) && "GlobalIFunc is not supported on AIX."); + + switch (GV->getLinkage()) { case GlobalValue::InternalLinkage: case GlobalValue::PrivateLinkage: return XCOFF::C_HIDEXT; @@ -2149,10 +2151,16 @@ } MCSymbol *TargetLoweringObjectFileXCOFF::getFunctionEntryPointSymbol( - const Function *F, const TargetMachine &TM) const { + const GlobalValue *Func, const TargetMachine &TM) const { + assert( + isa(Func) || + (isa(Func) && + isa_and_nonnull(cast(Func)->getBaseObject())) && + "Func must be a function or an alias which has a function as base " + "object."); SmallString<128> NameStr; NameStr.push_back('.'); - getNameWithPrefix(NameStr, F, TM); + getNameWithPrefix(NameStr, Func, TM); return getContext().getOrCreateSymbol(NameStr); } diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -154,6 +154,10 @@ SmallPtrSet ExtSymSDNodeSymbols; static void ValidateGV(const GlobalVariable *GV); + // Record a list of GlobalAlias associated with a GlobalObject. + // This is used for AIX's extra-label-at-definition aliasing strategy. + DenseMap> + GOAliasMap; public: PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -173,6 +177,8 @@ void emitFunctionDescriptor() override; + void emitFunctionEntryLabel() override; + void emitEndOfAsmFile(Module &) override; void emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const override; @@ -1729,6 +1735,10 @@ emitLinkage(GV, EmittedInitSym); emitAlignment(getGVAlignment(GV, DL), GV); OutStreamer->emitLabel(EmittedInitSym); + // Emit aliasing label for global variable. + llvm::for_each(GOAliasMap[GV], [this](const GlobalAlias *Alias) { + OutStreamer->emitLabel(getSymbol(Alias)); + }); emitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); } @@ -1740,6 +1750,13 @@ // Emit function descriptor. OutStreamer->SwitchSection( cast(CurrentFnDescSym)->getRepresentedCsect()); + + // Emit aliasing label for function descriptor csect. + llvm::for_each(GOAliasMap[&MF->getFunction()], + [this](const GlobalAlias *Alias) { + OutStreamer->emitLabel(getSymbol(Alias)); + }); + // Emit function entry point address. OutStreamer->emitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), PointerSize); @@ -1755,6 +1772,16 @@ OutStreamer->SwitchSection(Current.first, Current.second); } +void PPCAIXAsmPrinter::emitFunctionEntryLabel() { + PPCAsmPrinter::emitFunctionEntryLabel(); + // Emit aliasing label for function entry point label. + llvm::for_each( + GOAliasMap[&MF->getFunction()], [this](const GlobalAlias *Alias) { + OutStreamer->emitLabel( + getObjFileLowering().getFunctionEntryPointSymbol(Alias, TM)); + }); +} + void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) { // If there are no functions in this module, we will never need to reference // the TOC base. @@ -1790,10 +1817,6 @@ } bool PPCAIXAsmPrinter::doInitialization(Module &M) { - if (M.alias_size() > 0u) - report_fatal_error( - "module has aliases, which LLVM does not yet support for AIX"); - const bool Result = PPCAsmPrinter::doInitialization(M); auto setCsectAlignment = [this](const GlobalObject *GO) { @@ -1819,6 +1842,15 @@ for (const auto &F : M) setCsectAlignment(&F); + // Construct an aliasing list for each GlobalObject. + for (const auto &Alias : M.aliases()) { + const GlobalObject *Base = Alias.getBaseObject(); + if (!Base) + report_fatal_error( + "alias without a base object is not yet supported on AIX"); + GOAliasMap[Base].push_back(&Alias); + } + return Result; } diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -5372,10 +5372,9 @@ UsePlt ? PPCII::MO_PLT : 0); assert(!isa(GV) && "IFunc is not supported on AIX."); - const GlobalObject *GO = cast(GV); const XCOFF::StorageClass SC = - TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GO); - return getAIXFuncEntryPointSymbolSDNode(GO->getName(), GO->isDeclaration(), + TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GV); + return getAIXFuncEntryPointSymbolSDNode(GV->getName(), GV->isDeclaration(), SC); } diff --git a/llvm/test/CodeGen/PowerPC/aix-alias-unsupported.ll b/llvm/test/CodeGen/PowerPC/aix-alias-unsupported.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-alias-unsupported.ll @@ -0,0 +1,6 @@ +; RUN: not --crash llc < %s -mtriple powerpc-ibm-aix-xcoff 2>&1 | FileCheck %s +; RUN: not --crash llc < %s -mtriple powerpc64-ibm-aix-xcoff 2>&1 | FileCheck %s +; CHECK: ERROR: alias without a base object is not yet supported on AIX + +@bar = global i32 42 +@test = alias i32, inttoptr(i32 42 to i32*) diff --git a/llvm/test/CodeGen/PowerPC/aix-alias.ll b/llvm/test/CodeGen/PowerPC/aix-alias.ll --- a/llvm/test/CodeGen/PowerPC/aix-alias.ll +++ b/llvm/test/CodeGen/PowerPC/aix-alias.ll @@ -1,10 +1,116 @@ -; RUN: not --crash llc < %s -mtriple powerpc-ibm-aix-xcoff 2>&1 | FileCheck %s -; RUN: not --crash llc < %s -mtriple powerpc64-ibm-aix-xcoff 2>&1 | FileCheck %s +; TODO: Add object generation test when visibility for object generation +; is implemnted. -; Check that, while generation of aliases on AIX remains unimplemented, llc dies -; with an appropriate message instead of generating incorrect output when an -; alias is encountered. +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=-altivec < %s | \ +; RUN: FileCheck --check-prefix=ASM %s +; RUN: llc -verify-machineinstrs -mtriple powerpc64-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=-altivec < %s | \ +; RUN: FileCheck --check-prefix=ASM %s -define i32 @a() { ret i32 0 } -; CHECK: ERROR: module has aliases -@b = internal alias i32 (), i32 ()* @a +@var = global i32 42 +@var1 = alias i32, i32* @var +@var2 = alias i32, i32* @var1 +@var_l = linkonce_odr alias i32, i32* @var +@var_i = internal alias i32, i32* @var +@var_h = hidden alias i32, i32* @var +@var_p = protected alias i32, i32* @var + +@array = global [2 x i32] [i32 1, i32 2], align 4 +@x = global i32* bitcast (i8* getelementptr (i8, i8* bitcast ([2 x i32]* @array to i8*), i64 4) to i32*), align 4 +@bitcast_alias = alias i32*, i32** @x + +define i32 @fun() { + ret i32 0 +} + +%FunTy = type i32() +@fun_weak = weak alias %FunTy, %FunTy* @fun +@fun_hidden = hidden alias %FunTy, %FunTy* @fun +@fun_ptr = global i32()* @fun_weak + +define i32 @test() { +entry: + %tmp = load i32, i32* @var1 + %tmp1 = load i32, i32* @var2 + %tmp0 = load i32, i32* @var_i + %tmp2 = call i32 @fun() + %tmp3 = add i32 %tmp, %tmp2 + %tmp4 = call i32 @fun_weak() + %tmp5 = add i32 %tmp3, %tmp4 + %tmp6 = add i32 %tmp1, %tmp5 + %tmp7 = add i32 %tmp6, %tmp0 + %fun_ptr1 = alloca i32 ()* + store i32 ()* @fun_weak, i32 ()** %fun_ptr1 + %callee.knr.cast = bitcast i32 ()** %fun_ptr1 to i32 ()* + %tmp8 = call i32 %callee.knr.cast() + %tmp9 = call i32 @fun_hidden() + %tmp10 = add i32 %tmp7, %tmp8 + %tmp11 = add i32 %tmp10, %tmp9 + ret i32 %tmp11 +} + +; ASM: .globl fun[DS] +; ASM-NEXT: .globl .fun +; ASM-NEXT: .align 4 +; ASM-NEXT: .csect fun[DS] +; ASM-NEXT: fun_weak: # @fun +; ASM-NEXT: fun_hidden: +; ASM: .csect .text[PR],2 +; ASM-NEXT: .fun: +; ASM-NEXT: .fun_weak: +; ASM-NEXT: .fun_hidden: +; ASM-NEXT: # %bb.0: +; ASM-NEXT: li 3, 0 +; ASM-NEXT: blr +; ASM-NEXT: # -- End function +; ASM: .csect .text[PR],2 +; ASM-NEXT: .test: +; ASM-NEXT: # %bb.0: # %entry +; ASM: bl .fun +; ASM-NEXT: nop +; ASM: bl .fun_weak +; ASM-NEXT: nop +; ASM: bl .fun_hidden +; ASM: # -- End function +; ASM-NEXT: .csect .data[RW] +; ASM-NEXT: .globl var +; ASM: var: +; ASM-NEXT: var1: +; ASM-NEXT: var2: +; ASM-NEXT: var_l: +; ASM-NEXT: var_i: +; ASM-NEXT: var_h: +; ASM-NEXT: var_p: +; ASM-NEXT: .vbyte 4, 42 +; ASM-NEXT: .globl array +; ASM: array: +; ASM-NEXT: .vbyte 4, 1 # 0x1 +; ASM-NEXT: .vbyte 4, 2 # 0x2 +; ASM-NEXT: .globl x +; ASM: x: +; ASM-NEXT: bitcast_alias: +; ASM-NEXT: .vbyte {{[0-9]+}}, array+4 +; ASM-NEXT: .globl fun_ptr +; ASM: fun_ptr: +; ASM-NEXT: .vbyte {{[0-9]+}}, fun_weak +; ASM-NEXT: .globl var1 +; ASM-NEXT: .globl var2 +; ASM-NEXT: .weak var_l +; ASM-NEXT: .lglobl var_i +; ASM-NEXT: .globl var_h,hidden +; ASM-NEXT: .globl var_p,protected +; ASM-NEXT: .globl bitcast_alias +; ASM-NEXT: .weak fun_weak +; ASM-NEXT: .weak .fun_weak +; ASM-NEXT: .globl fun_hidden,hidden +; ASM-NEXT: .globl .fun_hidden,hidden +; ASM-NEXT: .toc +; ASM-NEXT: L..C0: +; ASM-NEXT: .tc var1[TC],var1 +; ASM-NEXT: L..C1: +; ASM-NEXT: .tc var2[TC],var2 +; ASM-NEXT: L..C2: +; ASM-NEXT: .tc var_i[TC],var_i +; ASM-NEXT: L..C3: +; ASM-NEXT: .tc fun_weak[TC],fun_weak