diff --git a/llvm/include/llvm/Support/TargetRegistry.h b/llvm/include/llvm/Support/TargetRegistry.h --- a/llvm/include/llvm/Support/TargetRegistry.h +++ b/llvm/include/llvm/Support/TargetRegistry.h @@ -183,6 +183,12 @@ std::unique_ptr &&TAB, std::unique_ptr &&OW, std::unique_ptr &&Emitter, bool RelaxAll); + using XCOFFStreamerCtorTy = + MCStreamer *(*)(const Triple &T, MCContext &Ctx, + std::unique_ptr &&TAB, + std::unique_ptr &&OW, + std::unique_ptr &&Emitter, bool RelaxAll); + using NullTargetStreamerCtorTy = MCTargetStreamer *(*)(MCStreamer &S); using AsmTargetStreamerCtorTy = MCTargetStreamer *(*)( MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter *InstPrint, @@ -270,6 +276,7 @@ MachOStreamerCtorTy MachOStreamerCtorFn = nullptr; ELFStreamerCtorTy ELFStreamerCtorFn = nullptr; WasmStreamerCtorTy WasmStreamerCtorFn = nullptr; + XCOFFStreamerCtorTy XCOFFStreamerCtorFn = nullptr; /// Construction function for this target's null TargetStreamer, if /// registered (default = nullptr). @@ -513,8 +520,12 @@ case Triple::GOFF: report_fatal_error("GOFF MCObjectStreamer not implemented yet"); case Triple::XCOFF: - S = createXCOFFStreamer(Ctx, std::move(TAB), std::move(OW), - std::move(Emitter), RelaxAll); + if (XCOFFStreamerCtorFn) + S = XCOFFStreamerCtorFn(T, Ctx, std::move(TAB), std::move(OW), + std::move(Emitter), RelaxAll); + else + S = createXCOFFStreamer(Ctx, std::move(TAB), std::move(OW), + std::move(Emitter), RelaxAll); break; } if (ObjectTargetStreamerCtorFn) @@ -868,6 +879,10 @@ T.WasmStreamerCtorFn = Fn; } + static void RegisterXCOFFStreamer(Target &T, Target::XCOFFStreamerCtorTy Fn) { + T.XCOFFStreamerCtorFn = Fn; + } + static void RegisterNullTargetStreamer(Target &T, Target::NullTargetStreamerCtorTy Fn) { T.NullTargetStreamerCtorFn = Fn; diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/PowerPC/MCTargetDesc/CMakeLists.txt --- a/llvm/lib/Target/PowerPC/MCTargetDesc/CMakeLists.txt +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/CMakeLists.txt @@ -9,6 +9,7 @@ PPCELFObjectWriter.cpp PPCXCOFFObjectWriter.cpp PPCELFStreamer.cpp + PPCXCOFFStreamer.cpp LINK_COMPONENTS MC diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp @@ -15,6 +15,7 @@ #include "MCTargetDesc/PPCMCAsmInfo.h" #include "PPCELFStreamer.h" #include "PPCTargetStreamer.h" +#include "PPCXCOFFStreamer.h" #include "TargetInfo/PowerPCTargetInfo.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" @@ -112,15 +113,23 @@ return MAI; } -static MCStreamer *createPPCMCStreamer(const Triple &T, MCContext &Context, - std::unique_ptr &&MAB, - std::unique_ptr &&OW, - std::unique_ptr &&Emitter, - bool RelaxAll) { +static MCStreamer * +createPPCELFStreamer(const Triple &T, MCContext &Context, + std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&Emitter, bool RelaxAll) { return createPPCELFStreamer(Context, std::move(MAB), std::move(OW), std::move(Emitter)); } +static MCStreamer *createPPCXCOFFStreamer( + const Triple &T, MCContext &Context, std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&Emitter, bool RelaxAll) { + return createPPCXCOFFStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter)); +} + namespace { class PPCTargetAsmStreamer : public PPCTargetStreamer { @@ -377,7 +386,10 @@ TargetRegistry::RegisterMCAsmBackend(*T, createPPCAsmBackend); // Register the elf streamer. - TargetRegistry::RegisterELFStreamer(*T, createPPCMCStreamer); + TargetRegistry::RegisterELFStreamer(*T, createPPCELFStreamer); + + // Register the XCOFF streamer. + TargetRegistry::RegisterXCOFFStreamer(*T, createPPCXCOFFStreamer); // Register the object target streamer. TargetRegistry::RegisterObjectTargetStreamer(*T, diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCXCOFFStreamer.h b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCXCOFFStreamer.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCXCOFFStreamer.h @@ -0,0 +1,39 @@ +//===- PPCXCOFFStreamer.h - XCOFF Object Output -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a custom MCXCOFFStreamer for PowerPC. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_PPC_MCXCOFFSTREAMER_PPCXCOFFSTREAMER_H +#define LLVM_LIB_TARGET_PPC_MCXCOFFSTREAMER_PPCXCOFFSTREAMER_H + +#include "llvm/MC/MCXCOFFStreamer.h" + +namespace llvm { + +class PPCXCOFFStreamer : public MCXCOFFStreamer { +public: + PPCXCOFFStreamer(MCContext &Context, std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter); + + void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; + +private: + void emitPrefixedInstruction(const MCInst &Inst, const MCSubtargetInfo &STI); +}; + +MCXCOFFStreamer *createPPCXCOFFStreamer(MCContext &Context, + std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter); +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_PPC_MCXCOFFSTREAMER_PPCXCOFFSTREAMER_H diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCXCOFFStreamer.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCXCOFFStreamer.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCXCOFFStreamer.cpp @@ -0,0 +1,78 @@ +//===-------- PPCXCOFFStreamer.cpp - XCOFF Object Output ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a custom MCXCOFFStreamer for PowerPC. +// +// The purpose of the custom XCOFF streamer is to allow us to intercept +// instructions as they are being emitted and align all 8 byte instructions +// to a 64 byte boundary if required (by adding a 4 byte nop). This is important +// because 8 byte instructions are not allowed to cross 64 byte boundaries +// and by aligning anything that is within 4 bytes of the boundary we can +// guarantee that the 8 byte instructions do not cross that boundary. +// +//===----------------------------------------------------------------------===// + +#include "PPCXCOFFStreamer.h" +#include "PPCMCCodeEmitter.h" +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSectionXCOFF.h" +#include "llvm/MC/MCSymbolXCOFF.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +PPCXCOFFStreamer::PPCXCOFFStreamer(MCContext &Context, + std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter) + : MCXCOFFStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter)) {} + +void PPCXCOFFStreamer::emitPrefixedInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI) { + // Prefixed instructions must not cross a 64-byte boundary (i.e. prefix is + // before the boundary and the remaining 4-bytes are after the boundary). In + // order to achieve this, a nop is added prior to any such boundary-crossing + // prefixed instruction. Align to 64 bytes if possible but add a maximum of 4 + // bytes when trying to do that. If alignment requires adding more than 4 + // bytes then the instruction won't be aligned. + emitCodeAlignment(64, 4); + + // Emit the instruction. + // Since the previous emit created a new fragment then adding this instruction + // also forces the addition of a new fragment. Inst is now the first + // instruction in that new fragment. + MCXCOFFStreamer::emitInstruction(Inst, STI); +} + +void PPCXCOFFStreamer::emitInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI) { + PPCMCCodeEmitter *Emitter = + static_cast(getAssembler().getEmitterPtr()); + + // Special handling is only for prefixed instructions. + if (!Emitter->isPrefixedInstruction(Inst)) { + MCXCOFFStreamer::emitInstruction(Inst, STI); + return; + } + emitPrefixedInstruction(Inst, STI); +} + +MCXCOFFStreamer * +llvm::createPPCXCOFFStreamer(MCContext &Context, + std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter) { + return new PPCXCOFFStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter)); +} diff --git a/llvm/test/CodeGen/PowerPC/aix-prefixed-instruction-boundary.mir b/llvm/test/CodeGen/PowerPC/aix-prefixed-instruction-boundary.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-prefixed-instruction-boundary.mir @@ -0,0 +1,74 @@ +# RUN: llc -verify-machineinstrs -mcpu=pwr4 -mtriple powerpc-ibm-aix-xcoff -x mir -verify-machineinstrs \ +# RUN: -xcoff-traceback-table=false -start-before=ppc-branch-select -filetype=obj -o %t.o < %s +# RUN: llvm-objdump -D -r --mcpu=pwr10 %t.o | FileCheck --check-prefix=DIS %s + +--- +name: aix-prefixed-instruction-boundary +alignment: 16 +tracksRegLiveness: true +liveins: + - { reg: '$x3', virtual-reg: '' } +body: | + bb.0.entry: + liveins: $x3 + renamable $x3 = LI8 2 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = PADDI8 $x3, 13 + renamable $x3 = LI8 2 + + renamable $x3 = PADDI8 $x3, 13 + BLR8 implicit $lr8, implicit $rm, implicit killed $x3 +... + +# DIS: Disassembly of section .text: +# DIS: 00000000 <.text>: +# DIS-NEXT: 0: 38 60 00 02 li 3, 2 +# DIS-NEXT: 4: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: c: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 14: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 1c: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 24: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 2c: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 34: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 3c: 60 00 00 00 nop +# DIS-NEXT: 40: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 48: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 50: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 58: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 60: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 68: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 70: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 78: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 80: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 88: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 90: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: 98: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: a0: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: a8: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: b0: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: b8: 38 60 00 02 li 3, 2 +# DIS-NEXT: bc: 60 00 00 00 nop +# DIS-NEXT: c0: 06 00 00 00 38 63 00 0d paddi 3, 3, 13, 0 +# DIS-NEXT: c8: 4e 80 00 20 blr