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" 29480093f4SDimitry Andric #include "llvm/InitializePasses.h" 30*0b57cec5SDimitry Andric #include "llvm/Pass.h" 31*0b57cec5SDimitry Andric #include "llvm/Target/TargetMachine.h" 32*0b57cec5SDimitry Andric 33*0b57cec5SDimitry Andric using namespace llvm; 34*0b57cec5SDimitry Andric 35*0b57cec5SDimitry Andric namespace { 36*0b57cec5SDimitry Andric 37*0b57cec5SDimitry Andric struct InstrumentationOptions { 38*0b57cec5SDimitry Andric // Whether to emit PATCHABLE_TAIL_CALL. 39*0b57cec5SDimitry Andric bool HandleTailcall; 40*0b57cec5SDimitry Andric 41*0b57cec5SDimitry Andric // Whether to emit PATCHABLE_RET/PATCHABLE_FUNCTION_EXIT for all forms of 42*0b57cec5SDimitry Andric // return, e.g. conditional return. 43*0b57cec5SDimitry Andric bool HandleAllReturns; 44*0b57cec5SDimitry Andric }; 45*0b57cec5SDimitry Andric 46*0b57cec5SDimitry Andric struct XRayInstrumentation : public MachineFunctionPass { 47*0b57cec5SDimitry Andric static char ID; 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric XRayInstrumentation() : MachineFunctionPass(ID) { 50*0b57cec5SDimitry Andric initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry()); 51*0b57cec5SDimitry Andric } 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 54*0b57cec5SDimitry Andric AU.setPreservesCFG(); 55*0b57cec5SDimitry Andric AU.addPreserved<MachineLoopInfo>(); 56*0b57cec5SDimitry Andric AU.addPreserved<MachineDominatorTree>(); 57*0b57cec5SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 58*0b57cec5SDimitry Andric } 59*0b57cec5SDimitry Andric 60*0b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 61*0b57cec5SDimitry Andric 62*0b57cec5SDimitry Andric private: 63*0b57cec5SDimitry Andric // Replace the original RET instruction with the exit sled code ("patchable 64*0b57cec5SDimitry Andric // ret" pseudo-instruction), so that at runtime XRay can replace the sled 65*0b57cec5SDimitry Andric // with a code jumping to XRay trampoline, which calls the tracing handler 66*0b57cec5SDimitry Andric // and, in the end, issues the RET instruction. 67*0b57cec5SDimitry Andric // This is the approach to go on CPUs which have a single RET instruction, 68*0b57cec5SDimitry Andric // like x86/x86_64. 69*0b57cec5SDimitry Andric void replaceRetWithPatchableRet(MachineFunction &MF, 70*0b57cec5SDimitry Andric const TargetInstrInfo *TII, 71*0b57cec5SDimitry Andric InstrumentationOptions); 72*0b57cec5SDimitry Andric 73*0b57cec5SDimitry Andric // Prepend the original return instruction with the exit sled code ("patchable 74*0b57cec5SDimitry Andric // function exit" pseudo-instruction), preserving the original return 75*0b57cec5SDimitry Andric // instruction just after the exit sled code. 76*0b57cec5SDimitry Andric // This is the approach to go on CPUs which have multiple options for the 77*0b57cec5SDimitry Andric // return instruction, like ARM. For such CPUs we can't just jump into the 78*0b57cec5SDimitry Andric // XRay trampoline and issue a single return instruction there. We rather 79*0b57cec5SDimitry Andric // have to call the trampoline and return from it to the original return 80*0b57cec5SDimitry Andric // instruction of the function being instrumented. 81*0b57cec5SDimitry Andric void prependRetWithPatchableExit(MachineFunction &MF, 82*0b57cec5SDimitry Andric const TargetInstrInfo *TII, 83*0b57cec5SDimitry Andric InstrumentationOptions); 84*0b57cec5SDimitry Andric }; 85*0b57cec5SDimitry Andric 86*0b57cec5SDimitry Andric } // end anonymous namespace 87*0b57cec5SDimitry Andric 88*0b57cec5SDimitry Andric void XRayInstrumentation::replaceRetWithPatchableRet( 89*0b57cec5SDimitry Andric MachineFunction &MF, const TargetInstrInfo *TII, 90*0b57cec5SDimitry Andric InstrumentationOptions op) { 91*0b57cec5SDimitry Andric // We look for *all* terminators and returns, then replace those with 92*0b57cec5SDimitry Andric // PATCHABLE_RET instructions. 93*0b57cec5SDimitry Andric SmallVector<MachineInstr *, 4> Terminators; 94*0b57cec5SDimitry Andric for (auto &MBB : MF) { 95*0b57cec5SDimitry Andric for (auto &T : MBB.terminators()) { 96*0b57cec5SDimitry Andric unsigned Opc = 0; 97*0b57cec5SDimitry Andric if (T.isReturn() && 98*0b57cec5SDimitry Andric (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) { 99*0b57cec5SDimitry Andric // Replace return instructions with: 100*0b57cec5SDimitry Andric // PATCHABLE_RET <Opcode>, <Operand>... 101*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_RET; 102*0b57cec5SDimitry Andric } 103*0b57cec5SDimitry Andric if (TII->isTailCall(T) && op.HandleTailcall) { 104*0b57cec5SDimitry Andric // Treat the tail call as a return instruction, which has a 105*0b57cec5SDimitry Andric // different-looking sled than the normal return case. 106*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_TAIL_CALL; 107*0b57cec5SDimitry Andric } 108*0b57cec5SDimitry Andric if (Opc != 0) { 109*0b57cec5SDimitry Andric auto MIB = BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc)) 110*0b57cec5SDimitry Andric .addImm(T.getOpcode()); 111*0b57cec5SDimitry Andric for (auto &MO : T.operands()) 112*0b57cec5SDimitry Andric MIB.add(MO); 113*0b57cec5SDimitry Andric Terminators.push_back(&T); 1145ffd83dbSDimitry Andric if (T.shouldUpdateCallSiteInfo()) 1158bcb0991SDimitry Andric MF.eraseCallSiteInfo(&T); 116*0b57cec5SDimitry Andric } 117*0b57cec5SDimitry Andric } 118*0b57cec5SDimitry Andric } 119*0b57cec5SDimitry Andric 120*0b57cec5SDimitry Andric for (auto &I : Terminators) 121*0b57cec5SDimitry Andric I->eraseFromParent(); 122*0b57cec5SDimitry Andric } 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric void XRayInstrumentation::prependRetWithPatchableExit( 125*0b57cec5SDimitry Andric MachineFunction &MF, const TargetInstrInfo *TII, 126*0b57cec5SDimitry Andric InstrumentationOptions op) { 127*0b57cec5SDimitry Andric for (auto &MBB : MF) 128*0b57cec5SDimitry Andric for (auto &T : MBB.terminators()) { 129*0b57cec5SDimitry Andric unsigned Opc = 0; 130*0b57cec5SDimitry Andric if (T.isReturn() && 131*0b57cec5SDimitry Andric (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) { 132*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT; 133*0b57cec5SDimitry Andric } 134*0b57cec5SDimitry Andric if (TII->isTailCall(T) && op.HandleTailcall) { 135*0b57cec5SDimitry Andric Opc = TargetOpcode::PATCHABLE_TAIL_CALL; 136*0b57cec5SDimitry Andric } 137*0b57cec5SDimitry Andric if (Opc != 0) { 138*0b57cec5SDimitry Andric // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or 139*0b57cec5SDimitry Andric // PATCHABLE_TAIL_CALL . 140*0b57cec5SDimitry Andric BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc)); 141*0b57cec5SDimitry Andric } 142*0b57cec5SDimitry Andric } 143*0b57cec5SDimitry Andric } 144*0b57cec5SDimitry Andric 145*0b57cec5SDimitry Andric bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) { 146*0b57cec5SDimitry Andric auto &F = MF.getFunction(); 147*0b57cec5SDimitry Andric auto InstrAttr = F.getFnAttribute("function-instrument"); 148e8d8bef9SDimitry Andric bool AlwaysInstrument = InstrAttr.isStringAttribute() && 149*0b57cec5SDimitry Andric InstrAttr.getValueAsString() == "xray-always"; 150e8d8bef9SDimitry Andric bool NeverInstrument = InstrAttr.isStringAttribute() && 151e8d8bef9SDimitry Andric InstrAttr.getValueAsString() == "xray-never"; 152e8d8bef9SDimitry Andric if (NeverInstrument && !AlwaysInstrument) 153e8d8bef9SDimitry Andric return false; 1545ffd83dbSDimitry Andric auto ThresholdAttr = F.getFnAttribute("xray-instruction-threshold"); 1555ffd83dbSDimitry Andric auto IgnoreLoopsAttr = F.getFnAttribute("xray-ignore-loops"); 1565ffd83dbSDimitry Andric unsigned int XRayThreshold = 0; 157*0b57cec5SDimitry Andric if (!AlwaysInstrument) { 158e8d8bef9SDimitry Andric if (!ThresholdAttr.isStringAttribute()) 159*0b57cec5SDimitry Andric return false; // XRay threshold attribute not found. 1605ffd83dbSDimitry Andric if (ThresholdAttr.getValueAsString().getAsInteger(10, XRayThreshold)) 161*0b57cec5SDimitry Andric return false; // Invalid value for threshold. 162*0b57cec5SDimitry Andric 163e8d8bef9SDimitry Andric bool IgnoreLoops = IgnoreLoopsAttr.isValid(); 1645ffd83dbSDimitry Andric 165*0b57cec5SDimitry Andric // Count the number of MachineInstr`s in MachineFunction 166*0b57cec5SDimitry Andric int64_t MICount = 0; 167*0b57cec5SDimitry Andric for (const auto &MBB : MF) 168*0b57cec5SDimitry Andric MICount += MBB.size(); 169*0b57cec5SDimitry Andric 1705ffd83dbSDimitry Andric bool TooFewInstrs = MICount < XRayThreshold; 1715ffd83dbSDimitry Andric 1725ffd83dbSDimitry Andric if (!IgnoreLoops) { 173*0b57cec5SDimitry Andric // Get MachineDominatorTree or compute it on the fly if it's unavailable 174*0b57cec5SDimitry Andric auto *MDT = getAnalysisIfAvailable<MachineDominatorTree>(); 175*0b57cec5SDimitry Andric MachineDominatorTree ComputedMDT; 176*0b57cec5SDimitry Andric if (!MDT) { 177*0b57cec5SDimitry Andric ComputedMDT.getBase().recalculate(MF); 178*0b57cec5SDimitry Andric MDT = &ComputedMDT; 179*0b57cec5SDimitry Andric } 180*0b57cec5SDimitry Andric 181*0b57cec5SDimitry Andric // Get MachineLoopInfo or compute it on the fly if it's unavailable 182*0b57cec5SDimitry Andric auto *MLI = getAnalysisIfAvailable<MachineLoopInfo>(); 183*0b57cec5SDimitry Andric MachineLoopInfo ComputedMLI; 184*0b57cec5SDimitry Andric if (!MLI) { 185*0b57cec5SDimitry Andric ComputedMLI.getBase().analyze(MDT->getBase()); 186*0b57cec5SDimitry Andric MLI = &ComputedMLI; 187*0b57cec5SDimitry Andric } 188*0b57cec5SDimitry Andric 189*0b57cec5SDimitry Andric // Check if we have a loop. 190*0b57cec5SDimitry Andric // FIXME: Maybe make this smarter, and see whether the loops are dependent 191*0b57cec5SDimitry Andric // on inputs or side-effects? 1925ffd83dbSDimitry Andric if (MLI->empty() && TooFewInstrs) 193*0b57cec5SDimitry Andric return false; // Function is too small and has no loops. 1945ffd83dbSDimitry Andric } else if (TooFewInstrs) { 1955ffd83dbSDimitry Andric // Function is too small 1965ffd83dbSDimitry Andric return false; 1975ffd83dbSDimitry Andric } 198*0b57cec5SDimitry Andric } 199*0b57cec5SDimitry Andric 200*0b57cec5SDimitry Andric // We look for the first non-empty MachineBasicBlock, so that we can insert 201*0b57cec5SDimitry Andric // the function instrumentation in the appropriate place. 202*0b57cec5SDimitry Andric auto MBI = llvm::find_if( 203*0b57cec5SDimitry Andric MF, [&](const MachineBasicBlock &MBB) { return !MBB.empty(); }); 204*0b57cec5SDimitry Andric if (MBI == MF.end()) 205*0b57cec5SDimitry Andric return false; // The function is empty. 206*0b57cec5SDimitry Andric 207*0b57cec5SDimitry Andric auto *TII = MF.getSubtarget().getInstrInfo(); 208*0b57cec5SDimitry Andric auto &FirstMBB = *MBI; 209*0b57cec5SDimitry Andric auto &FirstMI = *FirstMBB.begin(); 210*0b57cec5SDimitry Andric 211*0b57cec5SDimitry Andric if (!MF.getSubtarget().isXRaySupported()) { 212*0b57cec5SDimitry Andric FirstMI.emitError("An attempt to perform XRay instrumentation for an" 213*0b57cec5SDimitry Andric " unsupported target."); 214*0b57cec5SDimitry Andric return false; 215*0b57cec5SDimitry Andric } 216*0b57cec5SDimitry Andric 2175ffd83dbSDimitry Andric if (!F.hasFnAttribute("xray-skip-entry")) { 218*0b57cec5SDimitry Andric // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the 219*0b57cec5SDimitry Andric // MachineFunction. 220*0b57cec5SDimitry Andric BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(), 221*0b57cec5SDimitry Andric TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER)); 2225ffd83dbSDimitry Andric } 223*0b57cec5SDimitry Andric 2245ffd83dbSDimitry Andric if (!F.hasFnAttribute("xray-skip-exit")) { 225*0b57cec5SDimitry Andric switch (MF.getTarget().getTargetTriple().getArch()) { 226*0b57cec5SDimitry Andric case Triple::ArchType::arm: 227*0b57cec5SDimitry Andric case Triple::ArchType::thumb: 228*0b57cec5SDimitry Andric case Triple::ArchType::aarch64: 229*0b57cec5SDimitry Andric case Triple::ArchType::mips: 230*0b57cec5SDimitry Andric case Triple::ArchType::mipsel: 231*0b57cec5SDimitry Andric case Triple::ArchType::mips64: 232*0b57cec5SDimitry Andric case Triple::ArchType::mips64el: { 233*0b57cec5SDimitry Andric // For the architectures which don't have a single return instruction 234*0b57cec5SDimitry Andric InstrumentationOptions op; 235*0b57cec5SDimitry Andric op.HandleTailcall = false; 236*0b57cec5SDimitry Andric op.HandleAllReturns = true; 237*0b57cec5SDimitry Andric prependRetWithPatchableExit(MF, TII, op); 238*0b57cec5SDimitry Andric break; 239*0b57cec5SDimitry Andric } 240*0b57cec5SDimitry Andric case Triple::ArchType::ppc64le: { 241*0b57cec5SDimitry Andric // PPC has conditional returns. Turn them into branch and plain returns. 242*0b57cec5SDimitry Andric InstrumentationOptions op; 243*0b57cec5SDimitry Andric op.HandleTailcall = false; 244*0b57cec5SDimitry Andric op.HandleAllReturns = true; 245*0b57cec5SDimitry Andric replaceRetWithPatchableRet(MF, TII, op); 246*0b57cec5SDimitry Andric break; 247*0b57cec5SDimitry Andric } 248*0b57cec5SDimitry Andric default: { 249*0b57cec5SDimitry Andric // For the architectures that have a single return instruction (such as 250*0b57cec5SDimitry Andric // RETQ on x86_64). 251*0b57cec5SDimitry Andric InstrumentationOptions op; 252*0b57cec5SDimitry Andric op.HandleTailcall = true; 253*0b57cec5SDimitry Andric op.HandleAllReturns = false; 254*0b57cec5SDimitry Andric replaceRetWithPatchableRet(MF, TII, op); 255*0b57cec5SDimitry Andric break; 256*0b57cec5SDimitry Andric } 257*0b57cec5SDimitry Andric } 2585ffd83dbSDimitry Andric } 259*0b57cec5SDimitry Andric return true; 260*0b57cec5SDimitry Andric } 261*0b57cec5SDimitry Andric 262*0b57cec5SDimitry Andric char XRayInstrumentation::ID = 0; 263*0b57cec5SDimitry Andric char &llvm::XRayInstrumentationID = XRayInstrumentation::ID; 264*0b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(XRayInstrumentation, "xray-instrumentation", 265*0b57cec5SDimitry Andric "Insert XRay ops", false, false) 266*0b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo) 267*0b57cec5SDimitry Andric INITIALIZE_PASS_END(XRayInstrumentation, "xray-instrumentation", 268*0b57cec5SDimitry Andric "Insert XRay ops", false, false) 269