[XCOFF] Enable symbol alias for AIX

Summary:
AIX assembly's .set directive is not usable for aliasing purpose.
We need to use extra-label-at-defintion strategy to generate symbol
aliasing on AIX.

Reviewed By: DiggerLin, Xiangling_L

Differential Revision: https://reviews.llvm.org/D83252
This commit is contained in:
jasonliu 2020-07-17 18:40:02 +00:00
parent 2a6c871596
commit b98b1700ef
8 changed files with 199 additions and 32 deletions

View File

@ -246,7 +246,7 @@ public:
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 @@ public:
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;
};

View File

@ -247,7 +247,8 @@ public:
/// 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;
}

View File

@ -1390,16 +1390,7 @@ void AsmPrinter::emitGlobalGOTEquivs() {
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 @@ void AsmPrinter::emitGlobalIndirectSymbol(Module &M,
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<GlobalIFunc>(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)

View File

@ -2125,9 +2125,11 @@ const MCExpr *TargetLoweringObjectFileXCOFF::lowerRelativeReference(
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<GlobalIFunc>(GV) && "GlobalIFunc is not supported on AIX.");
switch (GV->getLinkage()) {
case GlobalValue::InternalLinkage:
case GlobalValue::PrivateLinkage:
return XCOFF::C_HIDEXT;
@ -2149,10 +2151,16 @@ XCOFF::StorageClass TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(
}
MCSymbol *TargetLoweringObjectFileXCOFF::getFunctionEntryPointSymbol(
const Function *F, const TargetMachine &TM) const {
const GlobalValue *Func, const TargetMachine &TM) const {
assert(
isa<Function>(Func) ||
(isa<GlobalAlias>(Func) &&
isa_and_nonnull<Function>(cast<GlobalAlias>(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);
}

View File

@ -154,6 +154,10 @@ private:
SmallPtrSet<MCSymbol *, 8> 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<const GlobalObject *, SmallVector<const GlobalAlias *, 1>>
GOAliasMap;
public:
PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
@ -173,6 +177,8 @@ public:
void emitFunctionDescriptor() override;
void emitFunctionEntryLabel() override;
void emitEndOfAsmFile(Module &) override;
void emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const override;
@ -1729,6 +1735,10 @@ void PPCAIXAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
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 @@ void PPCAIXAsmPrinter::emitFunctionDescriptor() {
// Emit function descriptor.
OutStreamer->SwitchSection(
cast<MCSymbolXCOFF>(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 @@ void PPCAIXAsmPrinter::emitFunctionDescriptor() {
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 @@ void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
}
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 @@ bool PPCAIXAsmPrinter::doInitialization(Module &M) {
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;
}

View File

@ -5372,10 +5372,9 @@ static SDValue transformCallee(const SDValue &Callee, SelectionDAG &DAG,
UsePlt ? PPCII::MO_PLT : 0);
assert(!isa<GlobalIFunc>(GV) && "IFunc is not supported on AIX.");
const GlobalObject *GO = cast<GlobalObject>(GV);
const XCOFF::StorageClass SC =
TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GO);
return getAIXFuncEntryPointSymbolSDNode(GO->getName(), GO->isDeclaration(),
TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GV);
return getAIXFuncEntryPointSymbolSDNode(GV->getName(), GV->isDeclaration(),
SC);
}

View File

@ -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*)

View File

@ -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