1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

[XRay] support conditional return on PPC.

Summary: Conditional returns were not taken into consideration at all. Implement them by turning them into jumps and normal returns. This means there is a slightly higher performance penalty for conditional returns, but this is the best we can do, and it still disturbs little of the rest.

Reviewers: dberris, echristo

Subscribers: sanjoy, nemanjai, hiraditya, kbarton, llvm-commits

Differential Revision: https://reviews.llvm.org/D38102

llvm-svn: 314005
This commit is contained in:
Tim Shen 2017-09-22 18:30:02 +00:00
parent 940990d757
commit 7c634910d0
7 changed files with 276 additions and 72 deletions

View File

@ -1049,6 +1049,7 @@ def PATCHABLE_RET : Instruction {
let AsmString = "# XRay Function Patchable RET.";
let usesCustomInserter = 1;
let hasSideEffects = 1;
let isTerminator = 1;
let isReturn = 1;
}
def PATCHABLE_FUNCTION_EXIT : Instruction {

View File

@ -34,6 +34,15 @@ using namespace llvm;
namespace {
struct InstrumentationOptions {
// Whether to emit PATCHABLE_TAIL_CALL.
bool HandleTailcall;
// Whether to emit PATCHABLE_RET/PATCHABLE_FUNCTION_EXIT for all forms of
// return, e.g. conditional return.
bool HandleAllReturns;
};
struct XRayInstrumentation : public MachineFunctionPass {
static char ID;
@ -59,7 +68,8 @@ private:
// This is the approach to go on CPUs which have a single RET instruction,
// like x86/x86_64.
void replaceRetWithPatchableRet(MachineFunction &MF,
const TargetInstrInfo *TII);
const TargetInstrInfo *TII,
InstrumentationOptions);
// Prepend the original return instruction with the exit sled code ("patchable
// function exit" pseudo-instruction), preserving the original return
@ -70,25 +80,28 @@ private:
// have to call the trampoline and return from it to the original return
// instruction of the function being instrumented.
void prependRetWithPatchableExit(MachineFunction &MF,
const TargetInstrInfo *TII);
const TargetInstrInfo *TII,
InstrumentationOptions);
};
} // end anonymous namespace
void XRayInstrumentation::replaceRetWithPatchableRet(
MachineFunction &MF, const TargetInstrInfo *TII) {
MachineFunction &MF, const TargetInstrInfo *TII,
InstrumentationOptions op) {
// We look for *all* terminators and returns, then replace those with
// PATCHABLE_RET instructions.
SmallVector<MachineInstr *, 4> Terminators;
for (auto &MBB : MF) {
for (auto &T : MBB.terminators()) {
unsigned Opc = 0;
if (T.isReturn() && T.getOpcode() == TII->getReturnOpcode()) {
if (T.isReturn() &&
(op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
// Replace return instructions with:
// PATCHABLE_RET <Opcode>, <Operand>...
Opc = TargetOpcode::PATCHABLE_RET;
}
if (TII->isTailCall(T)) {
if (TII->isTailCall(T) && op.HandleTailcall) {
// Treat the tail call as a return instruction, which has a
// different-looking sled than the normal return case.
Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
@ -108,14 +121,24 @@ void XRayInstrumentation::replaceRetWithPatchableRet(
}
void XRayInstrumentation::prependRetWithPatchableExit(
MachineFunction &MF, const TargetInstrInfo *TII) {
MachineFunction &MF, const TargetInstrInfo *TII,
InstrumentationOptions op) {
for (auto &MBB : MF)
for (auto &T : MBB.terminators())
if (T.isReturn()) {
// Prepend the return instruction with PATCHABLE_FUNCTION_EXIT.
BuildMI(MBB, T, T.getDebugLoc(),
TII->get(TargetOpcode::PATCHABLE_FUNCTION_EXIT));
for (auto &T : MBB.terminators()) {
unsigned Opc = 0;
if (T.isReturn() &&
(op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT;
}
if (TII->isTailCall(T) && op.HandleTailcall) {
Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
}
if (Opc != 0) {
// Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or
// PATCHABLE_TAIL_CALL .
BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc));
}
}
}
bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
@ -171,20 +194,35 @@ bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
case Triple::ArchType::arm:
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:
case Triple::ArchType::mips64el: {
// For the architectures which don't have a single return instruction
prependRetWithPatchableExit(MF, TII);
InstrumentationOptions op;
op.HandleTailcall = false;
op.HandleAllReturns = true;
prependRetWithPatchableExit(MF, TII, op);
break;
default:
}
case Triple::ArchType::ppc64le: {
// PPC has conditional returns. Turn them into branch and plain returns.
InstrumentationOptions op;
op.HandleTailcall = false;
op.HandleAllReturns = true;
replaceRetWithPatchableRet(MF, TII, op);
break;
}
default: {
// For the architectures that have a single return instruction (such as
// RETQ on x86_64).
replaceRetWithPatchableRet(MF, TII);
InstrumentationOptions op;
op.HandleTailcall = true;
op.HandleAllReturns = false;
replaceRetWithPatchableRet(MF, TII, op);
break;
}
}
return true;
}

View File

@ -26,8 +26,10 @@ namespace llvm {
class PassRegistry;
class FunctionPass;
class MachineInstr;
class MachineOperand;
class AsmPrinter;
class MCInst;
class MCOperand;
FunctionPass *createPPCCTRLoops();
#ifndef NDEBUG
@ -49,6 +51,9 @@ namespace llvm {
FunctionPass *createPPCExpandISELPass();
void LowerPPCMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
AsmPrinter &AP, bool isDarwin);
bool LowerPPCMachineOperandToMCOperand(const MachineOperand &MO,
MCOperand &OutMO, AsmPrinter &AP,
bool isDarwin);
void initializePPCVSXFMAMutatePass(PassRegistry&);
void initializePPCBoolRetToIntPass(PassRegistry&);

View File

@ -19,6 +19,7 @@
#include "InstPrinter/PPCInstPrinter.h"
#include "MCTargetDesc/PPCMCExpr.h"
#include "MCTargetDesc/PPCMCTargetDesc.h"
#include "MCTargetDesc/PPCPredicates.h"
#include "PPC.h"
#include "PPCInstrInfo.h"
#include "PPCMachineFunctionInfo.h"
@ -1089,7 +1090,61 @@ void PPCLinuxAsmPrinter::EmitInstruction(const MachineInstr *MI) {
recordSled(BeginOfSled, *MI, SledKind::FUNCTION_ENTER);
break;
}
case TargetOpcode::PATCHABLE_FUNCTION_EXIT: {
case TargetOpcode::PATCHABLE_RET: {
unsigned RetOpcode = MI->getOperand(0).getImm();
MCInst RetInst;
RetInst.setOpcode(RetOpcode);
for (const auto &MO :
make_range(std::next(MI->operands_begin()), MI->operands_end())) {
MCOperand MCOp;
if (LowerPPCMachineOperandToMCOperand(MO, MCOp, *this, false))
RetInst.addOperand(MCOp);
}
bool IsConditional;
if (RetOpcode == PPC::BCCLR) {
IsConditional = true;
} else if (RetOpcode == PPC::TCRETURNdi8 || RetOpcode == PPC::TCRETURNri8 ||
RetOpcode == PPC::TCRETURNai8) {
break;
} else if (RetOpcode == PPC::BLR8 || RetOpcode == PPC::TAILB8) {
IsConditional = false;
} else {
EmitToStreamer(*OutStreamer, RetInst);
break;
}
MCSymbol *FallthroughLabel;
if (IsConditional) {
// Before:
// bgtlr cr0
//
// After:
// ble cr0, .end
// .p2align 3
// .begin:
// blr # lis 0, FuncId[16..32]
// nop # li 0, FuncId[0..15]
// std 0, -8(1)
// mflr 0
// bl __xray_FunctionExit
// mtlr 0
// blr
// .end:
//
// Update compiler-rt/lib/xray/xray_powerpc64.cc accordingly when number
// of instructions change.
FallthroughLabel = OutContext.createTempSymbol();
EmitToStreamer(
*OutStreamer,
MCInstBuilder(PPC::BCC)
.addImm(PPC::InvertPredicate(
static_cast<PPC::Predicate>(MI->getOperand(1).getImm())))
.addReg(MI->getOperand(2).getReg())
.addExpr(MCSymbolRefExpr::create(FallthroughLabel, OutContext)));
RetInst = MCInst();
RetInst.setOpcode(PPC::BLR8);
}
// .p2align 3
// .begin:
// b(lr)? # lis 0, FuncId[16..32]
@ -1098,24 +1153,14 @@ void PPCLinuxAsmPrinter::EmitInstruction(const MachineInstr *MI) {
// mflr 0
// bl __xray_FunctionExit
// mtlr 0
// .end:
// b(lr)?
//
// Update compiler-rt/lib/xray/xray_powerpc64.cc accordingly when number
// of instructions change.
const MachineInstr *Next = [&] {
MachineBasicBlock::const_iterator It(MI);
assert(It != MI->getParent()->end());
++It;
assert(It->isReturn());
return &*It;
}();
OutStreamer->EmitCodeAlignment(8);
MCSymbol *BeginOfSled = OutContext.createTempSymbol();
OutStreamer->EmitLabel(BeginOfSled);
MCInst TmpInst;
LowerPPCMachineInstrToMCInst(Next, TmpInst, *this, false);
EmitToStreamer(*OutStreamer, TmpInst);
EmitToStreamer(*OutStreamer, RetInst);
EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::NOP));
EmitToStreamer(
*OutStreamer,
@ -1127,17 +1172,18 @@ void PPCLinuxAsmPrinter::EmitInstruction(const MachineInstr *MI) {
OutContext.getOrCreateSymbol("__xray_FunctionExit"),
OutContext)));
EmitToStreamer(*OutStreamer, MCInstBuilder(PPC::MTLR8).addReg(PPC::X0));
EmitToStreamer(*OutStreamer, RetInst);
if (IsConditional)
OutStreamer->EmitLabel(FallthroughLabel);
recordSled(BeginOfSled, *MI, SledKind::FUNCTION_EXIT);
break;
}
case TargetOpcode::PATCHABLE_FUNCTION_EXIT:
llvm_unreachable("PATCHABLE_FUNCTION_EXIT should never be emitted");
case TargetOpcode::PATCHABLE_TAIL_CALL:
// TODO: Define a trampoline `__xray_FunctionTailExit` and differentiate a
// normal function exit from a tail exit.
case TargetOpcode::PATCHABLE_RET:
// PPC's tail call instruction, e.g. PPC::TCRETURNdi8, doesn't really
// lower to a PPC::B instruction. The PPC::B instruction is generated
// before it, and handled by the normal case.
llvm_unreachable("Tail call is handled in the normal case. See comments"
llvm_unreachable("Tail call is handled in the normal case. See comments "
"around this assert.");
}
}

View File

@ -143,45 +143,48 @@ void llvm::LowerPPCMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
OutMI.setOpcode(MI->getOpcode());
for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
const MachineOperand &MO = MI->getOperand(i);
MCOperand MCOp;
switch (MO.getType()) {
default:
MI->print(errs());
llvm_unreachable("unknown operand type");
case MachineOperand::MO_Register:
assert(!MO.getSubReg() && "Subregs should be eliminated!");
assert(MO.getReg() > PPC::NoRegister &&
MO.getReg() < PPC::NUM_TARGET_REGS &&
"Invalid register for this target!");
MCOp = MCOperand::createReg(MO.getReg());
break;
case MachineOperand::MO_Immediate:
MCOp = MCOperand::createImm(MO.getImm());
break;
case MachineOperand::MO_MachineBasicBlock:
MCOp = MCOperand::createExpr(MCSymbolRefExpr::create(
MO.getMBB()->getSymbol(), AP.OutContext));
break;
case MachineOperand::MO_GlobalAddress:
case MachineOperand::MO_ExternalSymbol:
MCOp = GetSymbolRef(MO, GetSymbolFromOperand(MO, AP), AP, isDarwin);
break;
case MachineOperand::MO_JumpTableIndex:
MCOp = GetSymbolRef(MO, AP.GetJTISymbol(MO.getIndex()), AP, isDarwin);
break;
case MachineOperand::MO_ConstantPoolIndex:
MCOp = GetSymbolRef(MO, AP.GetCPISymbol(MO.getIndex()), AP, isDarwin);
break;
case MachineOperand::MO_BlockAddress:
MCOp = GetSymbolRef(MO,AP.GetBlockAddressSymbol(MO.getBlockAddress()),AP,
isDarwin);
break;
case MachineOperand::MO_RegisterMask:
continue;
}
OutMI.addOperand(MCOp);
if (LowerPPCMachineOperandToMCOperand(MI->getOperand(i), MCOp, AP,
isDarwin))
OutMI.addOperand(MCOp);
}
}
bool llvm::LowerPPCMachineOperandToMCOperand(const MachineOperand &MO,
MCOperand &OutMO, AsmPrinter &AP,
bool isDarwin) {
switch (MO.getType()) {
default:
llvm_unreachable("unknown operand type");
case MachineOperand::MO_Register:
assert(!MO.getSubReg() && "Subregs should be eliminated!");
assert(MO.getReg() > PPC::NoRegister &&
MO.getReg() < PPC::NUM_TARGET_REGS &&
"Invalid register for this target!");
OutMO = MCOperand::createReg(MO.getReg());
return true;
case MachineOperand::MO_Immediate:
OutMO = MCOperand::createImm(MO.getImm());
return true;
case MachineOperand::MO_MachineBasicBlock:
OutMO = MCOperand::createExpr(
MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), AP.OutContext));
return true;
case MachineOperand::MO_GlobalAddress:
case MachineOperand::MO_ExternalSymbol:
OutMO = GetSymbolRef(MO, GetSymbolFromOperand(MO, AP), AP, isDarwin);
return true;
case MachineOperand::MO_JumpTableIndex:
OutMO = GetSymbolRef(MO, AP.GetJTISymbol(MO.getIndex()), AP, isDarwin);
return true;
case MachineOperand::MO_ConstantPoolIndex:
OutMO = GetSymbolRef(MO, AP.GetCPISymbol(MO.getIndex()), AP, isDarwin);
return true;
case MachineOperand::MO_BlockAddress:
OutMO = GetSymbolRef(MO, AP.GetBlockAddressSymbol(MO.getBlockAddress()), AP,
isDarwin);
return true;
case MachineOperand::MO_RegisterMask:
return false;
}
}

View File

@ -0,0 +1,79 @@
; RUN: llc -filetype=asm -o - -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s
define void @Foo(i32 signext %a, i32 signext %b) #0 {
; CHECK-LABEL: @Foo
; CHECK: cmpw [[CR:[0-9]+]]
; CHECK-NEXT: ble [[CR]], [[LABEL:\.[a-zA-Z0-9]+]]
; CHECK-NEXT: .p2align 3
; CHECK-NEXT: {{\.[a-zA-Z0-9]+}}:
; CHECK-NEXT: blr
; CHECK-NEXT: nop
; CHECK-NEXT: std 0
; CHECK-NEXT: mflr 0
; CHECK-NEXT: bl __xray_FunctionExit
; CHECK-NEXT: nop
; CHECK-NEXT: mtlr 0
; CHECK-NEXT: blr
; CHECK-NEXT: [[LABEL]]:
entry:
%cmp = icmp sgt i32 %a, %b
br i1 %cmp, label %return, label %if.end
; CHECK: .p2align 3
; CHECK-NEXT: {{\.[a-zA-Z0-9]+}}:
; CHECK-NEXT: blr
; CHECK-NEXT: nop
; CHECK-NEXT: std 0
; CHECK-NEXT: mflr 0
; CHECK-NEXT: bl __xray_FunctionExit
; CHECK-NEXT: nop
; CHECK-NEXT: mtlr 0
; CHECK-NEXT: blr
if.end:
tail call void @Bar()
br label %return
return:
ret void
}
define void @Foo2(i32 signext %a, i32 signext %b) #0 {
; CHECK-LABEL: @Foo2
; CHECK: cmpw [[CR:[0-9]+]]
; CHECK-NEXT: bge [[CR]], [[LABEL:\.[a-zA-Z0-9]+]]
; CHECK-NEXT: .p2align 3
; CHECK-NEXT: {{\.[a-zA-Z0-9]+}}:
; CHECK-NEXT: blr
; CHECK-NEXT: nop
; CHECK-NEXT: std 0
; CHECK-NEXT: mflr 0
; CHECK-NEXT: bl __xray_FunctionExit
; CHECK-NEXT: nop
; CHECK-NEXT: mtlr 0
; CHECK-NEXT: blr
; CHECK-NEXT: [[LABEL]]:
entry:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %return, label %if.end
; CHECK: .p2align 3
; CHECK-NEXT: {{\.[a-zA-Z0-9]+}}:
; CHECK-NEXT: blr
; CHECK-NEXT: nop
; CHECK-NEXT: std 0
; CHECK-NEXT: mflr 0
; CHECK-NEXT: bl __xray_FunctionExit
; CHECK-NEXT: nop
; CHECK-NEXT: mtlr 0
; CHECK-NEXT: blr
if.end:
tail call void @Bar()
br label %return
return:
ret void
}
declare void @Bar()
attributes #0 = { "function-instrument"="xray-always" }

View File

@ -0,0 +1,32 @@
; RUN: llc -o - -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s
define void @ILLBeBack() #0 {
; CHECK-LABEL @ILLBeBack
; CHECK: beq {{[0-9]+}}, [[LABEL:\.[a-zA-Z0-9_]+]]
; CHECK: bl __xray_FunctionExit
; CHECK: [[LABEL]]:
bb:
br i1 undef, label %bb1, label %bb8
bb1:
%tmp = tail call i64 asm sideeffect "", "=&r,=*m,b,r,*m,~{cc}"(i64* nonnull undef, i64* nonnull undef, i64 1, i64* nonnull undef)
%tmp2 = icmp eq i64 %tmp, 0
br i1 %tmp2, label %bb3, label %bb8
bb3:
%tmp4 = tail call i64 asm sideeffect "", "=&r,=*m,b,r,r,*m,~{cc}"(i64* undef, i64* undef, i64 0, i64 undef, i64* undef)
%tmp5 = icmp eq i64 0, %tmp4
br i1 %tmp5, label %bb6, label %bb3
bb6:
br i1 undef, label %bb7, label %bb8
bb7:
tail call void () undef()
ret void
bb8:
ret void
}
attributes #0 = { "function-instrument"="xray-always" }