1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[LLVM][XRAY][MIPS] Support xray on mips/mipsel/mips64/mips64el

Summary: Adds support for xray instrumentation on mips for both 32-bit and 64-bit.

Reviewed by sdardis, dberris
Differential: D27697

llvm-svn: 295164
This commit is contained in:
Sagar Thakur 2017-02-15 10:48:11 +00:00
parent e27f967c5e
commit 5602c70c9c
6 changed files with 362 additions and 4 deletions

View File

@ -158,6 +158,10 @@ bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
case Triple::ArchType::thumb:
case Triple::ArchType::aarch64:
case Triple::ArchType::ppc64le:
case Triple::ArchType::mips:
case Triple::ArchType::mipsel:
case Triple::ArchType::mips64:
case Triple::ArchType::mips64el:
// For the architectures which don't have a single return instruction
prependRetWithPatchableExit(MF, TII);
break;

View File

@ -39,6 +39,7 @@
#include "llvm/MC/MCELFStreamer.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCSymbolELF.h"
@ -79,6 +80,9 @@ bool MipsAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
NaClAlignIndirectJumpTargets(MF);
AsmPrinter::runOnMachineFunction(MF);
EmitXRayTable();
return true;
}
@ -132,6 +136,7 @@ void MipsAsmPrinter::emitPseudoIndirectBranch(MCStreamer &OutStreamer,
void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) {
MipsTargetStreamer &TS = getTargetStreamer();
unsigned Opc = MI->getOpcode();
TS.forbidModuleDirective();
if (MI->isDebugValue()) {
@ -143,20 +148,20 @@ void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) {
}
// If we just ended a constant pool, mark it as such.
if (InConstantPool && MI->getOpcode() != Mips::CONSTPOOL_ENTRY) {
if (InConstantPool && Opc != Mips::CONSTPOOL_ENTRY) {
OutStreamer->EmitDataRegion(MCDR_DataRegionEnd);
InConstantPool = false;
}
if (MI->getOpcode() == Mips::CONSTPOOL_ENTRY) {
if (Opc == Mips::CONSTPOOL_ENTRY) {
// CONSTPOOL_ENTRY - This instruction represents a floating
//constant pool in the function. The first operand is the ID#
// constant pool in the function. The first operand is the ID#
// for this instruction, the second is the index into the
// MachineConstantPool that this is, the third is the size in
// bytes of this constant pool entry.
// The required alignment is specified on the basic block holding this MI.
//
unsigned LabelId = (unsigned)MI->getOperand(0).getImm();
unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex();
unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex();
// If this is the first entry of the pool, mark it.
if (!InConstantPool) {
@ -174,6 +179,17 @@ void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) {
return;
}
switch (Opc) {
case Mips::PATCHABLE_FUNCTION_ENTER:
LowerPATCHABLE_FUNCTION_ENTER(*MI);
return;
case Mips::PATCHABLE_FUNCTION_EXIT:
LowerPATCHABLE_FUNCTION_EXIT(*MI);
return;
case Mips::PATCHABLE_TAIL_CALL:
LowerPATCHABLE_TAIL_CALL(*MI);
return;
}
MachineBasicBlock::const_instr_iterator I = MI->getIterator();
MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end();
@ -1034,6 +1050,149 @@ void MipsAsmPrinter::EmitEndOfAsmFile(Module &M) {
OutStreamer->SwitchSection(OutContext.getObjectFileInfo()->getTextSection());
}
void MipsAsmPrinter::EmitSled(const MachineInstr &MI, SledKind Kind) {
const uint8_t NoopsInSledCount = Subtarget->isGP64bit() ? 15 : 11;
// For mips32 we want to emit the following pattern:
//
// .Lxray_sled_N:
// ALIGN
// B .tmpN
// 11 NOP instructions (44 bytes)
// ADDIU T9, T9, 52
// .tmpN
//
// We need the 44 bytes (11 instructions) because at runtime, we'd
// be patching over the full 48 bytes (12 instructions) with the following
// pattern:
//
// ADDIU SP, SP, -8
// NOP
// SW RA, 4(SP)
// SW T9, 0(SP)
// LUI T9, %hi(__xray_FunctionEntry/Exit)
// ORI T9, T9, %lo(__xray_FunctionEntry/Exit)
// LUI T0, %hi(function_id)
// JALR T9
// ORI T0, T0, %lo(function_id)
// LW T9, 0(SP)
// LW RA, 4(SP)
// ADDIU SP, SP, 8
//
// We add 52 bytes to t9 because we want to adjust the function pointer to
// the actual start of function i.e. the address just after the noop sled.
// We do this because gp displacement relocation is emitted at the start of
// of the function i.e after the nop sled and to correctly calculate the
// global offset table address, t9 must hold the address of the instruction
// containing the gp displacement relocation.
// FIXME: Is this correct for the static relocation model?
//
// For mips64 we want to emit the following pattern:
//
// .Lxray_sled_N:
// ALIGN
// B .tmpN
// 15 NOP instructions (60 bytes)
// .tmpN
//
// We need the 60 bytes (15 instructions) because at runtime, we'd
// be patching over the full 64 bytes (16 instructions) with the following
// pattern:
//
// DADDIU SP, SP, -16
// NOP
// SD RA, 8(SP)
// SD T9, 0(SP)
// LUI T9, %highest(__xray_FunctionEntry/Exit)
// ORI T9, T9, %higher(__xray_FunctionEntry/Exit)
// DSLL T9, T9, 16
// ORI T9, T9, %hi(__xray_FunctionEntry/Exit)
// DSLL T9, T9, 16
// ORI T9, T9, %lo(__xray_FunctionEntry/Exit)
// LUI T0, %hi(function_id)
// JALR T9
// ADDIU T0, T0, %lo(function_id)
// LD T9, 0(SP)
// LD RA, 8(SP)
// DADDIU SP, SP, 16
//
OutStreamer->EmitCodeAlignment(4);
auto CurSled = OutContext.createTempSymbol("xray_sled_", true);
OutStreamer->EmitLabel(CurSled);
auto Target = OutContext.createTempSymbol();
// Emit "B .tmpN" instruction, which jumps over the nop sled to the actual
// start of function
const MCExpr *TargetExpr = MCSymbolRefExpr::create(
Target, MCSymbolRefExpr::VariantKind::VK_None, OutContext);
EmitToStreamer(*OutStreamer, MCInstBuilder(Mips::BEQ)
.addReg(Mips::ZERO)
.addReg(Mips::ZERO)
.addExpr(TargetExpr));
for (int8_t I = 0; I < NoopsInSledCount; I++)
EmitToStreamer(*OutStreamer, MCInstBuilder(Mips::SLL)
.addReg(Mips::ZERO)
.addReg(Mips::ZERO)
.addImm(0));
OutStreamer->EmitLabel(Target);
if (!Subtarget->isGP64bit()) {
EmitToStreamer(*OutStreamer,
MCInstBuilder(Mips::ADDiu)
.addReg(Mips::T9)
.addReg(Mips::T9)
.addImm(0x34));
}
recordSled(CurSled, MI, Kind);
}
void MipsAsmPrinter::EmitXRayTable() {
if (Sleds.empty())
return;
if (Subtarget->isTargetELF()) {
auto PrevSection = OutStreamer->getCurrentSectionOnly();
auto Fn = MF->getFunction();
MCSection *Section;
if (Fn->hasComdat())
Section = OutContext.getELFSection("xray_instr_map", ELF::SHT_PROGBITS,
ELF::SHF_ALLOC | ELF::SHF_GROUP, 0,
Fn->getComdat()->getName());
else
Section =
OutContext.getELFSection("xray_instr_map", ELF::SHT_PROGBITS,
ELF::SHF_ALLOC, 0, CurrentFnSym->getName());
OutStreamer->SwitchSection(Section);
for (const auto &Sled : Sleds) {
OutStreamer->EmitSymbolValue(Sled.Sled, Subtarget->isGP64bit() ? 8 : 4);
OutStreamer->EmitSymbolValue(CurrentFnSym, Subtarget->isGP64bit() ? 8 : 4);
auto Kind = static_cast<uint8_t>(Sled.Kind);
OutStreamer->EmitBytes(
StringRef(reinterpret_cast<const char *>(&Kind), 1));
OutStreamer->EmitBytes(
StringRef(reinterpret_cast<const char *>(&Sled.AlwaysInstrument), 1));
OutStreamer->EmitZeros(Subtarget->isGP64bit() ? 14 : 6);
}
OutStreamer->SwitchSection(PrevSection);
}
Sleds.clear();
}
void MipsAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI) {
EmitSled(MI, SledKind::FUNCTION_ENTER);
}
void MipsAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) {
EmitSled(MI, SledKind::FUNCTION_EXIT);
}
void MipsAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) {
EmitSled(MI, SledKind::TAIL_CALL);
}
void MipsAsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
raw_ostream &OS) {
// TODO: implement

View File

@ -35,7 +35,21 @@ class LLVM_LIBRARY_VISIBILITY MipsAsmPrinter : public AsmPrinter {
void EmitInstrWithMacroNoAT(const MachineInstr *MI);
//===------------------------------------------------------------------===//
// XRay implementation
//===------------------------------------------------------------------===//
public:
// XRay-specific lowering for Mips.
void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI);
void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI);
void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI);
// Helper function that emits the XRay sleds we've collected for a particular
// function.
void EmitXRayTable();
private:
void EmitSled(const MachineInstr &MI, SledKind Kind);
// tblgen'erated function.
bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
const MachineInstr *MI);

View File

@ -236,6 +236,7 @@ public:
return (HasSym32 && isABI_N64()) || isABI_N32() || isABI_O32();
}
bool isSingleFloat() const { return IsSingleFloat; }
bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); }
bool hasVFPU() const { return HasVFPU; }
bool inMips16Mode() const { return InMips16Mode; }
bool inMips16ModeDefault() const {
@ -277,6 +278,8 @@ public:
bool isTargetNaCl() const { return TargetTriple.isOSNaCl(); }
bool isXRaySupported() const override { return true; }
// for now constant islands are on for the whole compilation unit but we only
// really use them if in addition we are in mips16 mode
static bool useConstantIslands();

View File

@ -0,0 +1,147 @@
; RUN: llc -filetype=asm -o - -mtriple=mips-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS32 %s
; RUN: llc -filetype=asm -o - -mtriple=mipsel-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS32 %s
; RUN: llc -filetype=asm -o - -mtriple=mips64-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS64 %s
; RUN: llc -filetype=asm -o - -mtriple=mips64el-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS64 %s
define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" {
; CHECK: .p2align 2
; CHECK-MIPS64-LABEL: .Lxray_sled_0:
; CHECK-MIPS32-LABEL: $xray_sled_0:
; CHECK-MIPS64: b .Ltmp0
; CHECK-MIPS32: b $tmp0
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64-LABEL: .Ltmp0:
; CHECK-MIPS32-LABEL: $tmp0:
; CHECK-MIPS32: addiu $25, $25, 52
ret i32 0
; CHECK: .p2align 2
; CHECK-MIPS64-LABEL: .Lxray_sled_1:
; CHECK-MIPS32-LABEL: $xray_sled_1:
; CHECK-MIPS64: b .Ltmp1
; CHECK-MIPS32: b $tmp1
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64-LABEL: .Ltmp1:
; CHECK-MIPS32-LABEL: $tmp1:
; CHECK-MIPS32: addiu $25, $25, 52
}
; CHECK: .section xray_instr_map,{{.*}}
; CHECK-MIPS64: .8byte .Lxray_sled_0
; CHECK-MIPS64: .8byte .Lxray_sled_1
; CHECK-MIPS32: .4byte ($xray_sled_0)
; CHECK-MIPS32: .4byte ($xray_sled_1)
; We test multiple returns in a single function to make sure we're getting all
; of them with XRay instrumentation.
define i32 @bar(i32 %i) nounwind noinline uwtable "function-instrument"="xray-always" {
; CHECK: .p2align 2
; CHECK-MIPS64-LABEL: .Lxray_sled_2:
; CHECK-MIPS32-LABEL: $xray_sled_2:
; CHECK-MIPS64: b .Ltmp2
; CHECK-MIPS32: b $tmp2
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64-LABEL: .Ltmp2:
; CHECK-MIPS32-LABEL: $tmp2:
; CHECK-MIPS32: addiu $25, $25, 52
Test:
%cond = icmp eq i32 %i, 0
br i1 %cond, label %IsEqual, label %NotEqual
IsEqual:
ret i32 0
; CHECK: .p2align 2
; CHECK-MIPS64-LABEL: .Lxray_sled_3:
; CHECK-MIPS32-LABEL: $xray_sled_3:
; CHECK-MIPS64: b .Ltmp3
; CHECK-MIPS32: b $tmp3
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64-LABEL: .Ltmp3:
; CHECK-MIPS32-LABEL: $tmp3:
; CHECK-MIPS32: addiu $25, $25, 52
NotEqual:
ret i32 1
; CHECK: .p2align 2
; CHECK-MIPS64-LABEL: .Lxray_sled_4:
; CHECK-MIPS32-LABEL: $xray_sled_4:
; CHECK-MIPS64: b .Ltmp4
; CHECK-MIPS32: b $tmp4
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64: nop
; CHECK-MIPS64-LABEL: .Ltmp4:
; CHECK-MIPS32-LABEL: $tmp4:
; CHECK-MIPS32: addiu $25, $25, 52
}
; CHECK: .section xray_instr_map,{{.*}}
; CHECK-MIPS64: .8byte .Lxray_sled_2
; CHECK-MIPS64: .8byte .Lxray_sled_3
; CHECK-MIPS64: .8byte .Lxray_sled_4
; CHECK-MIPS32: .4byte ($xray_sled_2)
; CHECK-MIPS32: .4byte ($xray_sled_3)
; CHECK-MIPS32: .4byte ($xray_sled_4)

View File

@ -0,0 +1,31 @@
; RUN: llc -filetype=asm -o - -mtriple=mips-unknown-linux-gnu -function-sections < %s | FileCheck %s
; RUN: llc -filetype=asm -o - -mtriple=mipsel-unknown-linux-gnu -function-sections < %s | FileCheck %s
; RUN: llc -filetype=obj -o %t -mtriple=mips-unknown-linux-gnu -function-sections < %s
; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ
; RUN: llc -filetype=obj -o %t -mtriple=mipsel-unknown-linux-gnu -function-sections < %s
; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ
; RUN: llc -filetype=asm -o - -mtriple=mips64-unknown-linux-gnu -function-sections < %s | FileCheck %s
; RUN: llc -filetype=asm -o - -mtriple=mips64el-unknown-linux-gnu -function-sections < %s | FileCheck %s
; RUN: llc -filetype=obj -o %t -mtriple=mips64-unknown-linux-gnu -function-sections < %s
; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ
; RUN: llc -filetype=obj -o %t -mtriple=mips64el-unknown-linux-gnu -function-sections < %s
; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ
define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" {
; CHECK: .section .text.foo,"ax",@progbits
ret i32 0
; CHECK: .section xray_instr_map,"a",@progbits
}
; CHECK-OBJ: Section {
; CHECK-OBJ: Name: xray_instr_map
$bar = comdat any
define i32 @bar() nounwind noinline uwtable "function-instrument"="xray-always" comdat($bar) {
; CHECK: .section .text.bar,"axG",@progbits,bar,comdat
ret i32 1
; CHECK: .section xray_instr_map,"aG",@progbits,bar,comdat
}
; CHECK-OBJ: Section {
; CHECK-OBJ: Name: xray_instr_map