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,4 +9,5 @@ PPCMachObjectWriter.cpp PPCELFObjectWriter.cpp PPCXCOFFObjectWriter.cpp + PPCELFStreamer.cpp ) diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFStreamer.h b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFStreamer.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFStreamer.h @@ -0,0 +1,52 @@ +//===- PPCELFStreamer.h - ELF 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 MCELFStreamer for PowerPC. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_PPC_MCELFSTREAMER_PPCELFSTREAMER_H +#define LLVM_LIB_TARGET_PPC_MCELFSTREAMER_PPCELFSTREAMER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCELFStreamer.h" +#include + +namespace llvm { + +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCSubtargetInfo; + +class PPCELFStreamer : public MCELFStreamer { + // We need to keep track of the last label we emitted (only one) because + // depending on whether the label is on the same line as an aligned + // instruction or not, the label may refer to the instruction or the nop. + MCSymbol *LastLabel; + SMLoc LastLabelLoc; + +public: + PPCELFStreamer(MCContext &Context, std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter); + + void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; + + // EmitLabel updates LastLabel and LastLabelLoc when a new label is emitted. + void EmitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; +}; + +MCELFStreamer *createPPCELFStreamer(MCContext &Context, + std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter); +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_PPC_MCELFSTREAMER_PPCELFSTREAMER_H diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFStreamer.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFStreamer.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFStreamer.cpp @@ -0,0 +1,108 @@ +//===-------- PPCELFStreamer.cpp - ELF 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 MCELFStreamer for PowerPC. +// +// The purpose of the custom ELF 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 aliging anything that is within 4 bytes of the boundary we can +// guarantee that the 8 byte instructions do not cross that boundary. +// +//===----------------------------------------------------------------------===// + + +#include "PPCELFStreamer.h" +#include "PPCInstrInfo.h" +#include "PPCMCCodeEmitter.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/SourceMgr.h" + +using namespace llvm; + +PPCELFStreamer::PPCELFStreamer(MCContext &Context, + std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter) + : MCELFStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter)), LastLabel(NULL) { +} + +void PPCELFStreamer::EmitInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI) { + PPCMCCodeEmitter *Emitter = + static_cast(getAssembler().getEmitterPtr()); + + // Special handling is only for prefixed instructions. + if (!Emitter->isPrefixedInstruction(Inst)) { + MCELFStreamer::EmitInstruction(Inst, STI); + return; + } + + // 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. When emitting a code alignment + // a new fragment is created for this alignment. This fragment will contain + // all of the nops required as part of the alignment operation. In the cases + // when no nops are added then The fragment is still created but it remains + // empty. + 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. + MCELFStreamer::EmitInstruction(Inst, STI); + + // The above instruction is forced to start a new fragment because it + // comes after a code alignment fragment. Get that new fragment. + MCFragment *InstructionFragment = getCurrentFragment(); + SMLoc InstLoc = Inst.getLoc(); + // Check if there was a last label emitted. + if (LastLabel && !LastLabel->isUnset() && LastLabelLoc.isValid() && + InstLoc.isValid()) { + const SourceMgr *SourceManager = getContext().getSourceManager(); + unsigned InstLine = SourceManager->FindLineNumber(InstLoc); + unsigned LabelLine = SourceManager->FindLineNumber(LastLabelLoc); + // If the Label and the Instruction are on the same line then move the + // label to the top of the fragment containing the aligned instruction that + // was just added. + if (InstLine == LabelLine) { + AssignFragment(LastLabel, InstructionFragment); + LastLabel->setOffset(0); + } + } +} + +void PPCELFStreamer::EmitLabel(MCSymbol *Symbol, SMLoc Loc) { + LastLabel = Symbol; + LastLabelLoc = Loc; + MCELFStreamer::EmitLabel(Symbol); +} + +MCELFStreamer *llvm::createPPCELFStreamer( + MCContext &Context, std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter) { + return new PPCELFStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter)); +} diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h @@ -103,6 +103,9 @@ // Get the number of bytes used to encode the given MCInst. unsigned getInstSizeInBytes(const MCInst &MI) const; + // Is this instruction a prefixed instruction. + bool isPrefixedInstruction(const MCInst &MI) const; + private: FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) const; void diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp @@ -347,5 +347,11 @@ return Desc.getSize(); } +bool PPCMCCodeEmitter::isPrefixedInstruction(const MCInst &MI) const { + unsigned Opcode = MI.getOpcode(); + const PPCInstrInfo *InstrInfo = static_cast(&MCII); + return InstrInfo->isPrefixed(Opcode); +} + #define ENABLE_INSTR_PREDICATE_VERIFIER #include "PPCGenMCCodeEmitter.inc" 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 @@ -13,6 +13,7 @@ #include "MCTargetDesc/PPCMCTargetDesc.h" #include "MCTargetDesc/PPCInstPrinter.h" #include "MCTargetDesc/PPCMCAsmInfo.h" +#include "PPCELFStreamer.h" #include "PPCTargetStreamer.h" #include "TargetInfo/PowerPCTargetInfo.h" #include "llvm/ADT/SmallPtrSet.h" @@ -20,11 +21,14 @@ #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCELFStreamer.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -97,6 +101,15 @@ return MAI; } +static MCStreamer *createPPCMCStreamer(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)); +} + namespace { class PPCTargetAsmStreamer : public PPCTargetStreamer { @@ -313,6 +326,9 @@ // Register the asm backend. TargetRegistry::RegisterMCAsmBackend(*T, createPPCAsmBackend); + // Register the elf streamer. + TargetRegistry::RegisterELFStreamer(*T, createPPCMCStreamer); + // Register the object target streamer. TargetRegistry::RegisterObjectTargetStreamer(*T, createObjectTargetStreamer); diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.h b/llvm/lib/Target/PowerPC/PPCInstrInfo.h --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.h +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.h @@ -189,6 +189,10 @@ bool isXFormMemOp(unsigned Opcode) const { return get(Opcode).TSFlags & PPCII::XFormMemOp; } + bool isPrefixed(unsigned Opcode) const { + return get(Opcode).TSFlags & PPCII::Prefixed; + } + static bool isSameClassPhysRegCopy(unsigned Opcode) { unsigned CopyOpcodes[] = { PPC::OR, PPC::OR8, PPC::FMR, PPC::VOR, PPC::XXLOR, PPC::XXLORf, diff --git a/llvm/test/MC/PowerPC/ppc64-prefix-align-labels.s b/llvm/test/MC/PowerPC/ppc64-prefix-align-labels.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/PowerPC/ppc64-prefix-align-labels.s @@ -0,0 +1,48 @@ +# RUN: llvm-mc -triple powerpc64-unknown-linux-gnu --filetype=obj -o - %s | \ +# RUN: llvm-objdump -D -r - | FileCheck -check-prefix=CHECK-BE %s +# RUN: llvm-mc -triple powerpc64le-unknown-linux-gnu --filetype=obj -o - %s | \ +# RUN: llvm-objdump -D -r - | FileCheck -check-prefix=CHECK-LE %s + +# The purpose of this test is to check that when an alignment nop is added +# it is added correctly with resepect to the labels in the .s file. +# The test contains 3 labels at the end (1:, 2:, 3:). The label 2: is on the +# same line as an unaligned 8 byte instruction. The desired behaviour is to have +# the alignment nop inserted after the 1: label but before the 2: label. The +# branch to 1: should jump to 3c: and the branch to 2: should jump to 40:. + + .text +_start: + b 1f; + b 2f; + b 3f; +# CHECK-BE: 0: 48 00 00 3c +# CHECK-BE-NEXT: 4: 48 00 00 3c +# CHECK-BE-NEXT: 8: 48 00 00 40 +# CHECK-LE: 0: 3c 00 00 48 +# CHECK-LE-NEXT: 4: 3c 00 00 48 +# CHECK-LE-NEXT: 8: 40 00 00 48 + trap + trap + trap + trap + trap + trap + trap + trap + trap + trap + trap + trap +1: +2: paddi 1, 2, 8589934576, 0 # 8 Byte Instruction +3: + blr +# CHECK-BE: 3c: 60 00 00 00 nop +# CHECK-BE-NEXT: 40: 06 01 ff ff +# CHECK-BE-NEXT: 44: 38 22 ff f0 +# CHECK-BE-NEXT: 48: 4e 80 00 20 +# CHECK-LE: 3c: 00 00 00 60 nop +# CHECK-LE-NEXT: 40: ff ff 01 06 +# CHECK-LE-NEXT: 44: f0 ff 22 38 +# CHECK-LE-NEXT: 48: 20 00 80 4e + diff --git a/llvm/test/MC/PowerPC/ppc64-prefix-align.s b/llvm/test/MC/PowerPC/ppc64-prefix-align.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/PowerPC/ppc64-prefix-align.s @@ -0,0 +1,86 @@ +# RUN: llvm-mc -triple powerpc64-unknown-linux-gnu --filetype=obj -o - %s | \ +# RUN: llvm-objdump -D -r - | FileCheck -check-prefix=CHECK-BE %s +# RUN: llvm-mc -triple powerpc64le-unknown-linux-gnu --filetype=obj -o - %s | \ +# RUN: llvm-objdump -D -r - | FileCheck -check-prefix=CHECK-LE %s + +# The purpose of this test is to make sure that 8 byte instructions do not +# cross 64 byte boundaries. If an 8 byte instruction is about to cross such +# a boundary then a nop should be added so that the 8 byte instruction starts +# 4 bytes later and does not cross the boundary. +# This instruction is 8 bytes: paddi 1, 2, 8589934576, 0 +# This instruction is 4 bytes: addi 2, 3, 15 +# The branches are also 4 bytes each: beq 0, LAB1 (or LAB2) + +beq 0, LAB1 # 4 +beq 1, LAB2 # 8 +# CHECK-BE: 0: 41 82 00 c0 bt 2, .+192 +# CHECK-BE-NEXT: 4: 41 86 00 f8 bt 6, .+248 +# CHECK-LE: 0: c0 00 82 41 bt 2, .+192 +# CHECK-LE-NEXT: 4: f8 00 86 41 bt 6, .+248 +paddi 1, 2, 8589934576, 0 # 16 +paddi 1, 2, 8589934576, 0 # 24 +paddi 1, 2, 8589934576, 0 # 32 +paddi 1, 2, 8589934576, 0 # 40 +paddi 1, 2, 8589934576, 0 # 48 +paddi 1, 2, 8589934576, 0 # 56 +addi 2, 3, 15 # 60 +# Below the lines 40: and 44: contain the 8 byte instruction. +# We check to make sure that the nop is added at 3c: so that the 8 byte +# instruction can start at 40: which is 64 bytes aligned. +# CHECK-BE: 38: 38 43 00 0f +# CHECK-BE-NEXT: 3c: 60 00 00 00 nop +# CHECK-BE-NEXT: 40: 06 01 ff ff +# CHECK-BE-NEXT: 44: 38 22 ff f0 +# CHECK-LE: 38: 0f 00 43 38 +# CHECK-LE-NEXT: 3c: 00 00 00 60 nop +# CHECK-LE-NEXT: 40: ff ff 01 06 +# CHECK-LE-NEXT: 44: f0 ff 22 38 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 # 64 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +addi 2, 3, 15 # 60 +# CHECK-BE: b8: 38 43 00 0f +# CHECK-BE-NEXT: bc: 60 00 00 00 nop +# CHECK-BE: LAB1: +# CHECK-BE-NEXT: c0: 06 01 ff ff +# CHECK-BE-NEXT: c4: 38 22 ff f0 +# CHECK-LE: b8: 0f 00 43 38 +# CHECK-LE-NEXT: bc: 00 00 00 60 nop +# CHECK-LE: LAB1: +# CHECK-LE-NEXT: c0: ff ff 01 06 +# CHECK-LE-NEXT: c4: f0 ff 22 38 +LAB1: paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +paddi 1, 2, 8589934576, 0 +addi 2, 3, 15 # 60 +# CHECK-BE: f8: 38 43 00 0f +# CHECK-BE: LAB2: +# CHECK-BE-NEXT: fc: 60 00 00 00 nop +# CHECK-BE-NEXT: 100: 06 01 ff ff +# CHECK-BE-NEXT: 104: 38 22 ff f0 +# CHECK-LE: f8: 0f 00 43 38 +# CHECK-LE: LAB2: +# CHECK-LE-NEXT: fc: 00 00 00 60 nop +# CHECK-LE-NEXT: 100: ff ff 01 06 +# CHECK-LE-NEXT: 104: f0 ff 22 38 +LAB2: + paddi 1, 2, 8589934576, 0 + + +