From 9868ea764f31b0fd4ec250867807aa0ad7958abf Mon Sep 17 00:00:00 2001 From: jasonliu Date: Fri, 11 Sep 2020 14:26:26 +0000 Subject: [PATCH] [XCOFF][AIX] Handle TOC entries that could not be reached by positive range in small code model Summary: In small code model, AIX assembler could not deal with labels that could not be reached within the [-0x8000, 0x8000) range from TOC base. So when generating the assembly, we would need to help the assembler by subtracting an offset from the label to keep the actual value within [-0x8000, 0x8000). Reviewed By: hubert.reinterpretcast, Xiangling_L Differential Revision: https://reviews.llvm.org/D86879 --- llvm/lib/MC/XCOFFObjectWriter.cpp | 16 +++-- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 52 +++++++++++---- llvm/test/CodeGen/PowerPC/aix-overflow-toc.py | 66 +++++++++++++++++++ llvm/test/CodeGen/PowerPC/lit.local.cfg | 2 + 4 files changed, 116 insertions(+), 20 deletions(-) create mode 100644 llvm/test/CodeGen/PowerPC/aix-overflow-toc.py diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp index 5047b5041aa75d..d6cee3bb59bb8a 100644 --- a/llvm/lib/MC/XCOFFObjectWriter.cpp +++ b/llvm/lib/MC/XCOFFObjectWriter.cpp @@ -49,7 +49,6 @@ namespace { constexpr unsigned DefaultSectionAlign = 4; constexpr int16_t MaxSectionIndex = INT16_MAX; -constexpr uint16_t MaxTOCSizeInARegion = UINT16_MAX; // Packs the csect's alignment and type into a byte. uint8_t getEncodedType(const MCSectionXCOFF *); @@ -431,12 +430,15 @@ void XCOFFObjectWriter::recordRelocation(MCAssembler &Asm, FixedValue = getVirtualAddress(SymA, SymASec) + Target.getConstant(); else if (Type == XCOFF::RelocationType::R_TOC || Type == XCOFF::RelocationType::R_TOCL) { - // The FixedValue should be the TC entry offset from TOC-base. - FixedValue = SectionMap[SymASec]->Address - TOCCsects.front().Address; - if (FixedValue >= MaxTOCSizeInARegion) - report_fatal_error( - "handling of TOC entries could not fit in the initial TOC " - "entry region is not yet supported"); + // The FixedValue should be the TOC entry offset from the TOC-base plus any + // constant offset value. + const int64_t TOCEntryOffset = SectionMap[SymASec]->Address - + TOCCsects.front().Address + + Target.getConstant(); + if (Type == XCOFF::RelocationType::R_TOC && !isInt<16>(TOCEntryOffset)) + report_fatal_error("TOCEntryOffset overflows in small code model mode"); + + FixedValue = TOCEntryOffset; } assert( diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index 8f1477012bfddf..f950e748158f5d 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -579,6 +579,38 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) { } } #endif + + auto getTOCRelocAdjustedExprForXCOFF = [this](const MCExpr *Expr, + ptrdiff_t OriginalOffset) { + // Apply an offset to the TOC-based expression such that the adjusted + // notional offset from the TOC base (to be encoded into the instruction's D + // or DS field) is the signed 16-bit truncation of the original notional + // offset from the TOC base. + // This is consistent with the treatment used both by XL C/C++ and + // by AIX ld -r. + ptrdiff_t Adjustment = + OriginalOffset - llvm::SignExtend32<16>(OriginalOffset); + return MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(-Adjustment, OutContext), OutContext); + }; + + auto getTOCEntryLoadingExprForXCOFF = + [IsPPC64, getTOCRelocAdjustedExprForXCOFF, + this](const MCSymbol *MOSymbol, const MCExpr *Expr) -> const MCExpr * { + const unsigned EntryByteSize = IsPPC64 ? 8 : 4; + const auto TOCEntryIter = TOC.find(MOSymbol); + assert(TOCEntryIter != TOC.end() && + "Could not find the TOC entry for this symbol."); + const ptrdiff_t EntryDistanceFromTOCBase = + (TOCEntryIter - TOC.begin()) * EntryByteSize; + constexpr int16_t PositiveTOCRange = INT16_MAX; + + if (EntryDistanceFromTOCBase > PositiveTOCRange) + return getTOCRelocAdjustedExprForXCOFF(Expr, EntryDistanceFromTOCBase); + + return Expr; + }; + // Lower multi-instruction pseudo operations. switch (MI->getOpcode()) { default: break; @@ -725,6 +757,7 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) { assert( TM.getCodeModel() == CodeModel::Small && "This pseudo should only be selected for 32-bit small code model."); + Exp = getTOCEntryLoadingExprForXCOFF(MOSymbol, Exp); TmpInst.getOperand(1) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); return; @@ -753,17 +786,20 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) { assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand!"); + // Map the operand to its corresponding MCSymbol. + const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); + // Map the machine operand to its corresponding MCSymbol, then map the // global address operand to be a reference to the TOC entry we will // synthesize later. - MCSymbol *TOCEntry = - lookUpOrCreateTOCEntry(getMCSymbolForTOCPseudoMO(MO, *this)); + MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol); const MCSymbolRefExpr::VariantKind VK = IsAIX ? MCSymbolRefExpr::VK_None : MCSymbolRefExpr::VK_PPC_TOC; const MCExpr *Exp = MCSymbolRefExpr::create(TOCEntry, VK, OutContext); - TmpInst.getOperand(1) = MCOperand::createExpr(Exp); + TmpInst.getOperand(1) = MCOperand::createExpr( + IsAIX ? getTOCEntryLoadingExprForXCOFF(MOSymbol, Exp) : Exp); EmitToStreamer(*OutStreamer, TmpInst); return; } @@ -1821,16 +1857,6 @@ void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) { PPCTargetStreamer *TS = static_cast(OutStreamer->getTargetStreamer()); - const unsigned EntryByteSize = Subtarget->isPPC64() ? 8 : 4; - const unsigned TOCEntriesByteSize = TOC.size() * EntryByteSize; - // TODO: If TOC entries' size is larger than 32768, then we run out of - // positive displacement to reach the TOC entry. We need to decide how to - // handle entries' size larger than that later. - if (TOCEntriesByteSize > 32767) { - report_fatal_error("Handling of TOC entry displacement larger than 32767 " - "is not yet implemented."); - } - for (auto &I : TOC) { // Setup the csect for the current TC entry. MCSectionXCOFF *TCEntry = cast( diff --git a/llvm/test/CodeGen/PowerPC/aix-overflow-toc.py b/llvm/test/CodeGen/PowerPC/aix-overflow-toc.py new file mode 100644 index 00000000000000..5e56b6f9fa250b --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-overflow-toc.py @@ -0,0 +1,66 @@ +# RUN: python %s > %t.ll +# RUN: llc -mtriple powerpc-ibm-aix-xcoff -code-model=small -mcpu=pwr4 -mattr=-altivec -O0 < %t.ll | \ +# RUN: FileCheck --check-prefix=ASM32 %s + +# RUN: llc -mtriple powerpc64-ibm-aix-xcoff -code-model=small -mcpu=pwr4 -mattr=-altivec -O0 < %t.ll | \ +# RUN: FileCheck --check-prefix=ASM64 %s + +# RUN: llc -mtriple powerpc-ibm-aix-xcoff -code-model=small -mcpu=pwr4 -mattr=-altivec -O0 \ +# RUN: -filetype=obj -o %t.o < %t.ll +# RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck --check-prefix=DIS32 %s + +# RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff \ +# RUN: -mcpu=pwr4 -mattr=-altivec -filetype=obj -o %t.o 2>&1 < %t.ll | \ +# RUN: FileCheck --check-prefix=XCOFF64 %s +# XCOFF64: LLVM ERROR: 64-bit XCOFF object files are not supported yet. + +numentries = 12290 +for x in range(0, numentries): + print("@a%d = global i32 0, align 4" % (x)) + +print("define void @foo() {") +print("entry:") +for x in range(0, numentries): + print("store i32 1, i32* @a%d, align 4" % (x)) +print("ret void") +print("}") + +# 32-bit assembly check +# ASM32: lwz 3, L..C0(2) +# ASM32: lwz 3, L..C1(2) + +# ASM32: lwz 3, L..C8191(2) +# ASM32: lwz 3, L..C8192-65536(2) +# ASM32: lwz 3, L..C8193-65536(2) + +# ASM32: lwz 3, L..C12288-65536(2) +# ASM32: lwz 3, L..C12289-65536(2) + +# 64-bit assembly check +# ASM64: ld 3, L..C0(2) +# ASM64: ld 3, L..C1(2) + +# ASM64: ld 3, L..C4095(2) +# ASM64: ld 3, L..C4096-65536(2) +# ASM64: ld 3, L..C4097-65536(2) + +# ASM64: ld 3, L..C12287-65536(2) +# ASM64: ld 3, L..C12288-131072(2) +# ASM64: ld 3, L..C12289-131072(2) + +# DIS32: 0: 80 62 00 00 lwz 3, 0(2) +# DIS32: 00000002: R_TOC (idx: 24590) a0[TC] +# DIS32: c: 80 62 00 04 lwz 3, 4(2) +# DIS32: 0000000e: R_TOC (idx: 24592) a1[TC] + +# DIS32: fffc: 80 62 7f fc lwz 3, 32764(2) +# DIS32: 0000fffe: R_TOC (idx: 40972) a8191[TC] +# DIS32: 10004: 80 62 80 00 lwz 3, -32768(2) +# DIS32: 00010006: R_TOC (idx: 40974) a8192[TC] +# DIS32: 1000c: 80 62 80 04 lwz 3, -32764(2) +# DIS32: 0001000e: R_TOC (idx: 40976) a8193[TC] + +# DIS32: 18004: 80 62 c0 00 lwz 3, -16384(2) +# DIS32: 00018006: R_TOC (idx: 49166) a12288[TC] +# DIS32: 1800c: 80 62 c0 04 lwz 3, -16380(2) +# DIS32: 0001800e: R_TOC (idx: 49168) a12289[TC] diff --git a/llvm/test/CodeGen/PowerPC/lit.local.cfg b/llvm/test/CodeGen/PowerPC/lit.local.cfg index 091332439b1867..1dbbf92fcf5e3c 100644 --- a/llvm/test/CodeGen/PowerPC/lit.local.cfg +++ b/llvm/test/CodeGen/PowerPC/lit.local.cfg @@ -1,2 +1,4 @@ if not 'PowerPC' in config.root.targets: config.unsupported = True + +config.suffixes.add('.py')