[PowerPC][Future] Prefixed Instructions 64 Byte Boundary Support

A known limitation for Future CPU is that the new prefixed instructions may
not cross 64 Byte boundaries.

All instructions are already 4 byte aligned so the only situation where this
can occur is when the prefix is in one 64 byte block and the instruction that
is prefixed is at the top of the next 64 byte block. To fix this case
PPCELFStreamer was added to intercept EmitInstruction. When a prefixed
instruction is emitted we try to align it to 64 Bytes by adding a maximum of
4 bytes. If the prefixed instruction crosses the 64 Byte boundary then the
alignment would trigger and a 4 byte nop would be added to push the
instruction into the next 64 byte block.

Differential Revision: https://reviews.llvm.org/D72570
This commit is contained in:
Stefan Pintilie 2020-01-27 19:20:53 -06:00
parent 258d8dd76a
commit f00be8da62
9 changed files with 324 additions and 0 deletions

View File

@ -9,4 +9,5 @@ add_llvm_component_library(LLVMPowerPCDesc
PPCMachObjectWriter.cpp
PPCELFObjectWriter.cpp
PPCXCOFFObjectWriter.cpp
PPCELFStreamer.cpp
)

View File

@ -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<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> OW,
std::unique_ptr<MCCodeEmitter> 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<PPCMCCodeEmitter*>(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<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> OW,
std::unique_ptr<MCCodeEmitter> Emitter) {
return new PPCELFStreamer(Context, std::move(MAB), std::move(OW),
std::move(Emitter));
}

View File

@ -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 <memory>
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<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> OW,
std::unique_ptr<MCCodeEmitter> 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<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> OW,
std::unique_ptr<MCCodeEmitter> Emitter);
} // end namespace llvm
#endif // LLVM_LIB_TARGET_PPC_MCELFSTREAMER_PPCELFSTREAMER_H

View File

@ -347,5 +347,11 @@ unsigned PPCMCCodeEmitter::getInstSizeInBytes(const MCInst &MI) const {
return Desc.getSize();
}
bool PPCMCCodeEmitter::isPrefixedInstruction(const MCInst &MI) const {
unsigned Opcode = MI.getOpcode();
const PPCInstrInfo *InstrInfo = static_cast<const PPCInstrInfo*>(&MCII);
return InstrInfo->isPrefixed(Opcode);
}
#define ENABLE_INSTR_PREDICATE_VERIFIER
#include "PPCGenMCCodeEmitter.inc"

View File

@ -103,6 +103,9 @@ public:
// 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

View File

@ -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 @@ static MCAsmInfo *createPPCMCAsmInfo(const MCRegisterInfo &MRI,
return MAI;
}
static MCStreamer *createPPCMCStreamer(const Triple &T, MCContext &Context,
std::unique_ptr<MCAsmBackend> &&MAB,
std::unique_ptr<MCObjectWriter> &&OW,
std::unique_ptr<MCCodeEmitter> &&Emitter,
bool RelaxAll) {
return createPPCELFStreamer(Context, std::move(MAB), std::move(OW),
std::move(Emitter));
}
namespace {
class PPCTargetAsmStreamer : public PPCTargetStreamer {
@ -313,6 +326,9 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializePowerPCTargetMC() {
// 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);

View File

@ -189,6 +189,10 @@ public:
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,

View File

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

View File

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