1*0b57cec5SDimitry Andric //===- XRayInstrumentation.cpp - Adds XRay instrumentation to functions. --===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file implements a MachineFunctionPass that inserts the appropriate 10*0b57cec5SDimitry Andric // XRay instrumentation instructions. We look for XRay-specific attributes 11*0b57cec5SDimitry Andric // on the function to determine whether we should insert the replacement 12*0b57cec5SDimitry Andric // operations. 13*0b57cec5SDimitry Andric // 14*0b57cec5SDimitry Andric //===---------------------------------------------------------------------===// 15*0b57cec5SDimitry Andric 16*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 17*0b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 18*0b57cec5SDimitry Andric #include "llvm/ADT/Triple.h" 19*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h" 20*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineDominators.h" 21*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 22*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 23*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 24*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h" 25*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h" 26*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h" 27*0b57cec5SDimitry Andric #include "llvm/IR/Attributes.h" 28*0b57cec5SDimitry Andric #include "llvm/IR/Function.h" 29*0b57cec5SDimitry Andric #include "llvm/Pass.h" 30*0b57cec5SDimitry Andric #include "llvm/Target/TargetMachine.h" 31*0b57cec5SDimitry Andric 32*0b57cec5SDimitry Andric using namespace llvm; 33*0b57cec5SDimitry Andric 34*0b57cec5SDimitry Andric namespace { 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric struct InstrumentationOptions { 37*0b57cec5SDimitry Andric // Whether to emit PATCHABLE_TAIL_CALL. 38*0b57cec5SDimitry Andric bool HandleTailcall; 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric // Whether to emit PATCHABLE_RET/PATCHABLE_FUNCTION_EXIT for all forms of 41*0b57cec5SDimitry Andric // return, e.g. conditional return. 42*0b57cec5SDimitry Andric bool HandleAllReturns; 43*0b57cec5SDimitry Andric }; 44*0b57cec5SDimitry Andric 45*0b57cec5SDimitry Andric struct XRayInstrumentation : public MachineFunctionPass { 46*0b57cec5SDimitry Andric static char ID; 47*0b57cec5SDimitry Andric 48*0b57cec5SDimitry Andric XRayInstrumentation() : MachineFunctionPass(ID) { 49*0b57cec5SDimitry Andric initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry()); 50*0b57cec5SDimitry Andric } 51*0b57cec5SDimitry Andric 52*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 53*0b57cec5SDimitry Andric AU.setPreservesCFG(); 54*0b57cec5SDimitry Andric AU.addPreserved<MachineLoopInfo>(); 55*0b57cec5SDimitry Andric AU.addPreserved<MachineDominatorTree>(); 56*0b57cec5SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 57*0b57cec5SDimitry Andric } 58*0b57cec5SDimitry Andric 59*0b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 60*0b57cec5SDimitry Andric 61*0b57cec5SDimitry Andric private: 62*0b57cec5SDimitry Andric // Replace the original RET instruction with the exit sled code ("patchable 63*0b57cec5SDimitry Andric // ret" pseudo-instruction), so that at runtime XRay can replace the sled 64*0b57cec5SDimitry Andric // with a code jumping to XRay trampoline, which calls the tracing handler 65*0b57cec5SDimitry Andric // and, in the end, issues the RET instruction. 66*0b57cec5SDimitry Andric // This is the approach to go on CPUs which have a single RET instruction, 67*0b57cec5SDimitry Andric // like x86/x86_64. 68*0b57cec5SDimitry Andric void replaceRetWithPatchableRet(MachineFunction &MF, 69*0b57cec5SDimitry Andric const TargetInstrInfo *TII, 70*0b57cec5SDimitry Andric InstrumentationOptions); 71*0b57cec5SDimitry Andric 72*0b57cec5SDimitry Andric // Prepend the original return instruction with the exit sled code ("patchable 73*0b57cec5SDimitry Andric // function exit" pseudo-instruction), preserving the original return 74*0b57cec5SDimitry Andric // instruction just after the exit sled code. 75*0b57cec5SDimitry Andric // This is the approach to go on CPUs which have multiple options for the 76*0b57cec5SDimitry Andric // return instruction, like ARM. For such CPUs we can't just jump into the 77*0b57cec5SDimitry Andric // XRay trampoline and issue a single return instruction there. We rather 78*0b57cec5SDimitry Andric // have to call the trampoline and return from it to the original return 79*0b57cec5SDimitry Andric // instruction of the function being instrumented. 80*0b57cec5SDimitry Andric void prependRetWithPatchableExit(MachineFunction &MF, 81*0b57cec5SDimitry Andric const TargetInstrInfo *TII, 82*0b57cec5SDimitry Andric InstrumentationOptions); 83*0b57cec5SDimitry Andric }; 84*0b57cec5SDimitry Andric 85*0b57cec5SDimitry Andric } // end anonymous namespace 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric void XRayInstrumentation::replaceRetWithPatchableRet( 88*0b57cec5SDimitry Andric MachineFunction &MF, const TargetInstrInfo *TII, 89*0b57cec5SDimitry Andric InstrumentationOptions op) { 90*0b57cec5SDimitry Andric // We look for *all* terminators and returns, then replace those with 91*0b57cec5SDimitry Andric // PATCHABLE_RET instructions. 92*0b57cec5SDimitry Andric SmallVector<MachineInstr *, 4> Terminators; 93*0b57cec5SDimitry Andric for (auto &MBB : MF) { 94*0b57cec5SDimitry Andric for (auto &T : MBB.terminators()) { 95*0b57cec5SDimitry Andric unsigned Opc = 0; 96*0b57cec5SDimitry Andric if (T.isReturn() && 97*0b57cec5SDimitry Andric (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) { 98*0b57cec5SDimitry Andric // Replace return instructions with: 99*0b57cec5SDimitry Andric // PATCHABLE_RET <Opcode>, <Operand>... 100*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_RET; 101*0b57cec5SDimitry Andric } 102*0b57cec5SDimitry Andric if (TII->isTailCall(T) && op.HandleTailcall) { 103*0b57cec5SDimitry Andric // Treat the tail call as a return instruction, which has a 104*0b57cec5SDimitry Andric // different-looking sled than the normal return case. 105*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_TAIL_CALL; 106*0b57cec5SDimitry Andric } 107*0b57cec5SDimitry Andric if (Opc != 0) { 108*0b57cec5SDimitry Andric auto MIB = BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc)) 109*0b57cec5SDimitry Andric .addImm(T.getOpcode()); 110*0b57cec5SDimitry Andric for (auto &MO : T.operands()) 111*0b57cec5SDimitry Andric MIB.add(MO); 112*0b57cec5SDimitry Andric Terminators.push_back(&T); 113*0b57cec5SDimitry Andric if (T.isCall()) 114*0b57cec5SDimitry Andric MF.updateCallSiteInfo(&T); 115*0b57cec5SDimitry Andric } 116*0b57cec5SDimitry Andric } 117*0b57cec5SDimitry Andric } 118*0b57cec5SDimitry Andric 119*0b57cec5SDimitry Andric for (auto &I : Terminators) 120*0b57cec5SDimitry Andric I->eraseFromParent(); 121*0b57cec5SDimitry Andric } 122*0b57cec5SDimitry Andric 123*0b57cec5SDimitry Andric void XRayInstrumentation::prependRetWithPatchableExit( 124*0b57cec5SDimitry Andric MachineFunction &MF, const TargetInstrInfo *TII, 125*0b57cec5SDimitry Andric InstrumentationOptions op) { 126*0b57cec5SDimitry Andric for (auto &MBB : MF) 127*0b57cec5SDimitry Andric for (auto &T : MBB.terminators()) { 128*0b57cec5SDimitry Andric unsigned Opc = 0; 129*0b57cec5SDimitry Andric if (T.isReturn() && 130*0b57cec5SDimitry Andric (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) { 131*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT; 132*0b57cec5SDimitry Andric } 133*0b57cec5SDimitry Andric if (TII->isTailCall(T) && op.HandleTailcall) { 134*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_TAIL_CALL; 135*0b57cec5SDimitry Andric } 136*0b57cec5SDimitry Andric if (Opc != 0) { 137*0b57cec5SDimitry Andric // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or 138*0b57cec5SDimitry Andric // PATCHABLE_TAIL_CALL . 139*0b57cec5SDimitry Andric BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc)); 140*0b57cec5SDimitry Andric } 141*0b57cec5SDimitry Andric } 142*0b57cec5SDimitry Andric } 143*0b57cec5SDimitry Andric 144*0b57cec5SDimitry Andric bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) { 145*0b57cec5SDimitry Andric auto &F = MF.getFunction(); 146*0b57cec5SDimitry Andric auto InstrAttr = F.getFnAttribute("function-instrument"); 147*0b57cec5SDimitry Andric bool AlwaysInstrument = !InstrAttr.hasAttribute(Attribute::None) && 148*0b57cec5SDimitry Andric InstrAttr.isStringAttribute() && 149*0b57cec5SDimitry Andric InstrAttr.getValueAsString() == "xray-always"; 150*0b57cec5SDimitry Andric Attribute Attr = F.getFnAttribute("xray-instruction-threshold"); 151*0b57cec5SDimitry Andric unsigned XRayThreshold = 0; 152*0b57cec5SDimitry Andric if (!AlwaysInstrument) { 153*0b57cec5SDimitry Andric if (Attr.hasAttribute(Attribute::None) || !Attr.isStringAttribute()) 154*0b57cec5SDimitry Andric return false; // XRay threshold attribute not found. 155*0b57cec5SDimitry Andric if (Attr.getValueAsString().getAsInteger(10, XRayThreshold)) 156*0b57cec5SDimitry Andric return false; // Invalid value for threshold. 157*0b57cec5SDimitry Andric 158*0b57cec5SDimitry Andric // Count the number of MachineInstr`s in MachineFunction 159*0b57cec5SDimitry Andric int64_t MICount = 0; 160*0b57cec5SDimitry Andric for (const auto &MBB : MF) 161*0b57cec5SDimitry Andric MICount += MBB.size(); 162*0b57cec5SDimitry Andric 163*0b57cec5SDimitry Andric // Get MachineDominatorTree or compute it on the fly if it's unavailable 164*0b57cec5SDimitry Andric auto *MDT = getAnalysisIfAvailable<MachineDominatorTree>(); 165*0b57cec5SDimitry Andric MachineDominatorTree ComputedMDT; 166*0b57cec5SDimitry Andric if (!MDT) { 167*0b57cec5SDimitry Andric ComputedMDT.getBase().recalculate(MF); 168*0b57cec5SDimitry Andric MDT = &ComputedMDT; 169*0b57cec5SDimitry Andric } 170*0b57cec5SDimitry Andric 171*0b57cec5SDimitry Andric // Get MachineLoopInfo or compute it on the fly if it's unavailable 172*0b57cec5SDimitry Andric auto *MLI = getAnalysisIfAvailable<MachineLoopInfo>(); 173*0b57cec5SDimitry Andric MachineLoopInfo ComputedMLI; 174*0b57cec5SDimitry Andric if (!MLI) { 175*0b57cec5SDimitry Andric ComputedMLI.getBase().analyze(MDT->getBase()); 176*0b57cec5SDimitry Andric MLI = &ComputedMLI; 177*0b57cec5SDimitry Andric } 178*0b57cec5SDimitry Andric 179*0b57cec5SDimitry Andric // Check if we have a loop. 180*0b57cec5SDimitry Andric // FIXME: Maybe make this smarter, and see whether the loops are dependent 181*0b57cec5SDimitry Andric // on inputs or side-effects? 182*0b57cec5SDimitry Andric if (MLI->empty() && MICount < XRayThreshold) 183*0b57cec5SDimitry Andric return false; // Function is too small and has no loops. 184*0b57cec5SDimitry Andric } 185*0b57cec5SDimitry Andric 186*0b57cec5SDimitry Andric // We look for the first non-empty MachineBasicBlock, so that we can insert 187*0b57cec5SDimitry Andric // the function instrumentation in the appropriate place. 188*0b57cec5SDimitry Andric auto MBI = llvm::find_if( 189*0b57cec5SDimitry Andric MF, [&](const MachineBasicBlock &MBB) { return !MBB.empty(); }); 190*0b57cec5SDimitry Andric if (MBI == MF.end()) 191*0b57cec5SDimitry Andric return false; // The function is empty. 192*0b57cec5SDimitry Andric 193*0b57cec5SDimitry Andric auto *TII = MF.getSubtarget().getInstrInfo(); 194*0b57cec5SDimitry Andric auto &FirstMBB = *MBI; 195*0b57cec5SDimitry Andric auto &FirstMI = *FirstMBB.begin(); 196*0b57cec5SDimitry Andric 197*0b57cec5SDimitry Andric if (!MF.getSubtarget().isXRaySupported()) { 198*0b57cec5SDimitry Andric FirstMI.emitError("An attempt to perform XRay instrumentation for an" 199*0b57cec5SDimitry Andric " unsupported target."); 200*0b57cec5SDimitry Andric return false; 201*0b57cec5SDimitry Andric } 202*0b57cec5SDimitry Andric 203*0b57cec5SDimitry Andric // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the 204*0b57cec5SDimitry Andric // MachineFunction. 205*0b57cec5SDimitry Andric BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(), 206*0b57cec5SDimitry Andric TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER)); 207*0b57cec5SDimitry Andric 208*0b57cec5SDimitry Andric switch (MF.getTarget().getTargetTriple().getArch()) { 209*0b57cec5SDimitry Andric case Triple::ArchType::arm: 210*0b57cec5SDimitry Andric case Triple::ArchType::thumb: 211*0b57cec5SDimitry Andric case Triple::ArchType::aarch64: 212*0b57cec5SDimitry Andric case Triple::ArchType::mips: 213*0b57cec5SDimitry Andric case Triple::ArchType::mipsel: 214*0b57cec5SDimitry Andric case Triple::ArchType::mips64: 215*0b57cec5SDimitry Andric case Triple::ArchType::mips64el: { 216*0b57cec5SDimitry Andric // For the architectures which don't have a single return instruction 217*0b57cec5SDimitry Andric InstrumentationOptions op; 218*0b57cec5SDimitry Andric op.HandleTailcall = false; 219*0b57cec5SDimitry Andric op.HandleAllReturns = true; 220*0b57cec5SDimitry Andric prependRetWithPatchableExit(MF, TII, op); 221*0b57cec5SDimitry Andric break; 222*0b57cec5SDimitry Andric } 223*0b57cec5SDimitry Andric case Triple::ArchType::ppc64le: { 224*0b57cec5SDimitry Andric // PPC has conditional returns. Turn them into branch and plain returns. 225*0b57cec5SDimitry Andric InstrumentationOptions op; 226*0b57cec5SDimitry Andric op.HandleTailcall = false; 227*0b57cec5SDimitry Andric op.HandleAllReturns = true; 228*0b57cec5SDimitry Andric replaceRetWithPatchableRet(MF, TII, op); 229*0b57cec5SDimitry Andric break; 230*0b57cec5SDimitry Andric } 231*0b57cec5SDimitry Andric default: { 232*0b57cec5SDimitry Andric // For the architectures that have a single return instruction (such as 233*0b57cec5SDimitry Andric // RETQ on x86_64). 234*0b57cec5SDimitry Andric InstrumentationOptions op; 235*0b57cec5SDimitry Andric op.HandleTailcall = true; 236*0b57cec5SDimitry Andric op.HandleAllReturns = false; 237*0b57cec5SDimitry Andric replaceRetWithPatchableRet(MF, TII, op); 238*0b57cec5SDimitry Andric break; 239*0b57cec5SDimitry Andric } 240*0b57cec5SDimitry Andric } 241*0b57cec5SDimitry Andric return true; 242*0b57cec5SDimitry Andric } 243*0b57cec5SDimitry Andric 244*0b57cec5SDimitry Andric char XRayInstrumentation::ID = 0; 245*0b57cec5SDimitry Andric char &llvm::XRayInstrumentationID = XRayInstrumentation::ID; 246*0b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(XRayInstrumentation, "xray-instrumentation", 247*0b57cec5SDimitry Andric "Insert XRay ops", false, false) 248*0b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo) 249*0b57cec5SDimitry Andric INITIALIZE_PASS_END(XRayInstrumentation, "xray-instrumentation", 250*0b57cec5SDimitry Andric "Insert XRay ops", false, false) 251