diff --git a/llvm/include/llvm/MC/MCSymbol.h b/llvm/include/llvm/MC/MCSymbol.h --- a/llvm/include/llvm/MC/MCSymbol.h +++ b/llvm/include/llvm/MC/MCSymbol.h @@ -48,6 +48,7 @@ SymbolKindELF, SymbolKindMachO, SymbolKindWasm, + SymbolKindXCOFF, }; /// A symbol can contain an Offset, or Value, or be Common, but never more @@ -285,6 +286,8 @@ bool isWasm() const { return Kind == SymbolKindWasm; } + bool isXCOFF() const { return Kind == SymbolKindXCOFF; } + /// @} /// \name Variable Symbols /// @{ diff --git a/llvm/include/llvm/MC/MCSymbolXCOFF.h b/llvm/include/llvm/MC/MCSymbolXCOFF.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/MC/MCSymbolXCOFF.h @@ -0,0 +1,35 @@ +//===- MCSymbolXCOFF.h - ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_MC_MCSYMBOLXCOFF_H +#define LLVM_MC_MCSYMBOLXCOFF_H + +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/IR/GlobalValue.h" + +namespace llvm { + +class MCSymbolXCOFF : public MCSymbol { + // The IR symbol this MCSymbolXCOFF is based on. It is set on function + // entry point symbols when they are the callee operand of a direct call + // SDNode. + const GlobalValue *GV = nullptr; + +public: + MCSymbolXCOFF(const StringMapEntry *Name, bool isTemporary) + : MCSymbol(SymbolKindXCOFF, Name, isTemporary) {} + + void setGlobalValue(const GlobalValue *G) { GV = G; } + const GlobalValue *getGlobalValue() const { return GV; } + + static bool classof(const MCSymbol *S) { return S->isXCOFF(); } +}; + +} // end namespace llvm + +#endif // LLVM_MC_MCSYMBOLXCOFF_H diff --git a/llvm/lib/CodeGen/LLVMTargetMachine.cpp b/llvm/lib/CodeGen/LLVMTargetMachine.cpp --- a/llvm/lib/CodeGen/LLVMTargetMachine.cpp +++ b/llvm/lib/CodeGen/LLVMTargetMachine.cpp @@ -201,6 +201,15 @@ return true; if (!TargetPassConfig::willCompleteCodeGenPipeline()) { + if (this->getTargetTriple().isOSAIX()) { + // On AIX , we replace GlobalAddressSDNode with MCSymbolSDNode in SDAG for + // the calle of a direct function call. And to create the MCSymbolXCOFF + // contained in the MCSymbolSDNode, we always lower target object file for + // MIR output on AIX. + MCContext &Ctx = MMI->getContext(); + const_cast(*this->getObjFileLowering()) + .Initialize(Ctx, *this); + } PM.add(createPrintMIRPass(Out)); } else if (addAsmPrinter(PM, Out, DwoOut, FileType, MMI->getContext())) return true; diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1875,6 +1875,13 @@ return false; case Instruction::Call: + // On AIX, call lowering uses the DAG-ISEL path currently so that the + // callee of the direct function call instruction will be mapped to the + // symbol for the function's entry point, which is distinct from the + // function descriptor symbol. The latter is the symbol whose XCOFF symbol + // name is the C-linkage name of the source level function. + if (TM.getTargetTriple().isOSAIX()) + return false; return selectCall(I); case Instruction::BitCast: diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp --- a/llvm/lib/MC/MCContext.cpp +++ b/llvm/lib/MC/MCContext.cpp @@ -32,6 +32,7 @@ #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCSymbolMachO.h" #include "llvm/MC/MCSymbolWasm.h" +#include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/MC/SectionKind.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" @@ -162,8 +163,7 @@ case MCObjectFileInfo::IsWasm: return new (Name, *this) MCSymbolWasm(Name, IsTemporary); case MCObjectFileInfo::IsXCOFF: - // TODO: Need to implement class MCSymbolXCOFF. - break; + return new (Name, *this) MCSymbolXCOFF(Name, IsTemporary); } } return new (Name, *this) MCSymbol(MCSymbol::SymbolKindUnset, Name, diff --git a/llvm/lib/Target/PowerPC/PPCFastISel.cpp b/llvm/lib/Target/PowerPC/PPCFastISel.cpp --- a/llvm/lib/Target/PowerPC/PPCFastISel.cpp +++ b/llvm/lib/Target/PowerPC/PPCFastISel.cpp @@ -1964,6 +1964,13 @@ case Instruction::Sub: return SelectBinaryIntOp(I, ISD::SUB); case Instruction::Call: + // On AIX, call lowering uses the DAG-ISEL path currently so that the + // callee of the direct function call instruction will be mapped to the + // symbol for the function's entry point, which is distinct from the + // function descriptor symbol. The latter is the symbol whose XCOFF symbol + // name is the C-linkage name of the source level function. + if (TM.getTargetTriple().isOSAIX()) + break; return selectCall(I); case Instruction::Ret: return SelectRet(I); 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 @@ -44,6 +44,7 @@ #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineLoopInfo.h" #include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/RuntimeLibcalls.h" @@ -69,8 +70,10 @@ #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/Value.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/Support/AtomicOrdering.h" #include "llvm/Support/BranchProbability.h" #include "llvm/Support/Casting.h" @@ -4402,9 +4405,23 @@ static bool callsShareTOCBase(const Function *Caller, SDValue Callee, const TargetMachine &TM) { - // If !G, Callee can be an external symbol. - GlobalAddressSDNode *G = dyn_cast(Callee); - if (!G) + // Need a GlobalValue to determine if a Caller and Callee share the same + // TOCBase. + const GlobalValue *GV = nullptr; + + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + GV = G->getGlobal(); + } else if (MCSymbolSDNode *M = dyn_cast(Callee)) { + // On AIX only, we replace GlobalAddressSDNode with MCSymbolSDNode for + // the callee of a direct function call. The MCSymbolSDNode contains the + // MCSymbol for the funtion entry point. + const auto *S = cast(M->getMCSymbol()); + GV = S->getGlobalValue(); + } + + // If we failed to get a GlobalValue, then pessimistically assume they do not + // share a TOCBase. + if (!GV) return false; // The medium and large code models are expected to provide a sufficiently @@ -4413,13 +4430,12 @@ // only need to check that caller and callee don't cross dso boundaries. if (CodeModel::Medium == TM.getCodeModel() || CodeModel::Large == TM.getCodeModel()) - return TM.shouldAssumeDSOLocal(*Caller->getParent(), G->getGlobal()); + return TM.shouldAssumeDSOLocal(*Caller->getParent(), GV); // Otherwise we need to ensure callee and caller are in the same section, // since the linker may allocate multiple TOCs, and we don't know which // sections will belong to the same TOC base. - const GlobalValue *GV = G->getGlobal(); if (!GV->isStrongDefinitionForLinker()) return false; @@ -4890,7 +4906,8 @@ // we're building with the leopard linker or later, which automatically // synthesizes these stubs. const TargetMachine &TM = DAG.getTarget(); - const Module *Mod = DAG.getMachineFunction().getFunction().getParent(); + MachineFunction &MF = DAG.getMachineFunction(); + const Module *Mod = MF.getFunction().getParent(); const GlobalValue *GV = nullptr; if (auto *G = dyn_cast(Callee)) GV = G->getGlobal(); @@ -4899,17 +4916,29 @@ if (isFunctionGlobalAddress(Callee)) { GlobalAddressSDNode *G = cast(Callee); - // A call to a TLS address is actually an indirect call to a - // thread-specific pointer. - unsigned OpFlags = 0; - if (UsePlt) - OpFlags = PPCII::MO_PLT; - // If the callee is a GlobalAddress/ExternalSymbol node (quite common, - // every direct call is) turn it into a TargetGlobalAddress / - // TargetExternalSymbol node so that legalize doesn't hack it. - Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, - Callee.getValueType(), 0, OpFlags); + if (TM.getTargetTriple().isOSAIX()) { + // Direct function calls reference the symbol for the function's entry + // point, which is named by inserting a "." before the function's + // C-linkage name. + auto &Context = MF.getMMI().getContext(); + MCSymbol *S = Context.getOrCreateSymbol(Twine(".") + + Twine(G->getGlobal()->getName())); + cast(S)->setGlobalValue(GV); + Callee = DAG.getMCSymbol(S, PtrVT); + } else { + // A call to a TLS address is actually an indirect call to a + // thread-specific pointer. + unsigned OpFlags = 0; + if (UsePlt) + OpFlags = PPCII::MO_PLT; + + // If the callee is a GlobalAddress/ExternalSymbol node (quite common, + // every direct call is) turn it into a TargetGlobalAddress / + // TargetExternalSymbol node so that legalize doesn't hack it. + Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, + Callee.getValueType(), 0, OpFlags); + } needIndirectCall = false; } diff --git a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td --- a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td +++ b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td @@ -192,6 +192,12 @@ def : Pat<(PPCcall_nop (i64 texternalsym:$dst)), (BL8_NOP texternalsym:$dst)>; +// Calls for AIX +def : Pat<(PPCcall (i64 mcsym:$dst)), + (BL8 mcsym:$dst)>; +def : Pat<(PPCcall_nop (i64 mcsym:$dst)), + (BL8_NOP mcsym:$dst)>; + // Atomic operations // FIXME: some of these might be used with constant operands. This will result // in constant materialization instructions that may be redundant. We currently diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td @@ -3032,12 +3032,16 @@ // Calls def : Pat<(PPCcall (i32 tglobaladdr:$dst)), (BL tglobaladdr:$dst)>; -def : Pat<(PPCcall_nop (i32 tglobaladdr:$dst)), - (BL_NOP tglobaladdr:$dst)>; def : Pat<(PPCcall (i32 texternalsym:$dst)), (BL texternalsym:$dst)>; +// Calls for AIX only +def : Pat<(PPCcall (i32 mcsym:$dst)), + (BL mcsym:$dst)>; +def : Pat<(PPCcall_nop (i32 mcsym:$dst)), + (BL_NOP mcsym:$dst)>; + def : Pat<(PPCtc_return (i32 tglobaladdr:$dst), imm:$imm), (TCRETURNdi tglobaladdr:$dst, imm:$imm)>; diff --git a/llvm/test/CodeGen/PowerPC/test_call_aix.ll b/llvm/test/CodeGen/PowerPC/test_call_aix.ll --- a/llvm/test/CodeGen/PowerPC/test_call_aix.ll +++ b/llvm/test/CodeGen/PowerPC/test_call_aix.ll @@ -9,11 +9,11 @@ define void @test_call() { entry: ; 32BIT: ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1 -; 32BIT: BL_NOP @foo, csr_aix32, implicit-def dead $lr, implicit $rm, implicit-def $r1 +; 32BIT: BL_NOP , csr_aix32, implicit-def dead $lr, implicit $rm, implicit-def $r1 ; 32BIT: ADJCALLSTACKUP 56, 0, implicit-def dead $r1, implicit $r1 ; 64BIT: ADJCALLSTACKDOWN 112, 0, implicit-def dead $r1, implicit $r1 -; 64BIT: BL8_NOP @foo, csr_aix64, implicit-def dead $lr8, implicit $rm, implicit-def $r1 +; 64BIT: BL8_NOP , csr_aix64, implicit-def dead $lr8, implicit $rm, implicit-def $r1 ; 64BIT: ADJCALLSTACKUP 112, 0, implicit-def dead $r1, implicit $r1 call void bitcast (void (...)* @foo to void ()*)() @@ -28,11 +28,11 @@ define void @test_local_call() { entry: ; 32BIT: ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1 -; 32BIT: BL @foo_local, csr_aix32, implicit-def dead $lr, implicit $rm, implicit-def $r1 +; 32BIT: BL , csr_aix32, implicit-def dead $lr, implicit $rm, implicit-def $r1 ; 32BIT: ADJCALLSTACKUP 56, 0, implicit-def dead $r1, implicit $r1 ; 64BIT: ADJCALLSTACKDOWN 112, 0, implicit-def dead $r1, implicit $r1 -; 64BIT: BL8 @foo_local, csr_aix64, implicit-def dead $lr8, implicit $rm, implicit-def $r1 +; 64BIT: BL8 , csr_aix64, implicit-def dead $lr8, implicit $rm, implicit-def $r1 ; 64BIT: ADJCALLSTACKUP 112, 0, implicit-def dead $r1, implicit $r1 call void @foo_local()