1db17bf38SDimitry Andric //===- XRayInstrumentation.cpp - Adds XRay instrumentation to functions. --===//
23ca95b02SDimitry Andric //
33ca95b02SDimitry Andric //                     The LLVM Compiler Infrastructure
43ca95b02SDimitry Andric //
53ca95b02SDimitry Andric // This file is distributed under the University of Illinois Open Source
63ca95b02SDimitry Andric // License. See LICENSE.TXT for details.
73ca95b02SDimitry Andric //
83ca95b02SDimitry Andric //===----------------------------------------------------------------------===//
93ca95b02SDimitry Andric //
103ca95b02SDimitry Andric // This file implements a MachineFunctionPass that inserts the appropriate
113ca95b02SDimitry Andric // XRay instrumentation instructions. We look for XRay-specific attributes
123ca95b02SDimitry Andric // on the function to determine whether we should insert the replacement
133ca95b02SDimitry Andric // operations.
143ca95b02SDimitry Andric //
153ca95b02SDimitry Andric //===---------------------------------------------------------------------===//
163ca95b02SDimitry Andric 
17db17bf38SDimitry Andric #include "llvm/ADT/STLExtras.h"
182cab237bSDimitry Andric #include "llvm/ADT/SmallVector.h"
19db17bf38SDimitry Andric #include "llvm/ADT/Triple.h"
20db17bf38SDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
21db17bf38SDimitry Andric #include "llvm/CodeGen/MachineDominators.h"
223ca95b02SDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
233ca95b02SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
243ca95b02SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
250f5676f4SDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h"
262cab237bSDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
272cab237bSDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
28db17bf38SDimitry Andric #include "llvm/IR/Attributes.h"
29db17bf38SDimitry Andric #include "llvm/IR/Function.h"
30db17bf38SDimitry Andric #include "llvm/Pass.h"
31db17bf38SDimitry Andric #include "llvm/Target/TargetMachine.h"
323ca95b02SDimitry Andric 
333ca95b02SDimitry Andric using namespace llvm;
343ca95b02SDimitry Andric 
353ca95b02SDimitry Andric namespace {
36db17bf38SDimitry Andric 
372cab237bSDimitry Andric struct InstrumentationOptions {
382cab237bSDimitry Andric   // Whether to emit PATCHABLE_TAIL_CALL.
392cab237bSDimitry Andric   bool HandleTailcall;
402cab237bSDimitry Andric 
412cab237bSDimitry Andric   // Whether to emit PATCHABLE_RET/PATCHABLE_FUNCTION_EXIT for all forms of
422cab237bSDimitry Andric   // return, e.g. conditional return.
432cab237bSDimitry Andric   bool HandleAllReturns;
442cab237bSDimitry Andric };
452cab237bSDimitry Andric 
463ca95b02SDimitry Andric struct XRayInstrumentation : public MachineFunctionPass {
473ca95b02SDimitry Andric   static char ID;
483ca95b02SDimitry Andric 
XRayInstrumentation__anona0cd56710111::XRayInstrumentation493ca95b02SDimitry Andric   XRayInstrumentation() : MachineFunctionPass(ID) {
503ca95b02SDimitry Andric     initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry());
513ca95b02SDimitry Andric   }
523ca95b02SDimitry Andric 
getAnalysisUsage__anona0cd56710111::XRayInstrumentation530f5676f4SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
540f5676f4SDimitry Andric     AU.setPreservesCFG();
550f5676f4SDimitry Andric     AU.addPreserved<MachineLoopInfo>();
560f5676f4SDimitry Andric     AU.addPreserved<MachineDominatorTree>();
570f5676f4SDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
580f5676f4SDimitry Andric   }
590f5676f4SDimitry Andric 
603ca95b02SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
61d88c1a5aSDimitry Andric 
62d88c1a5aSDimitry Andric private:
63d88c1a5aSDimitry Andric   // Replace the original RET instruction with the exit sled code ("patchable
64d88c1a5aSDimitry Andric   //   ret" pseudo-instruction), so that at runtime XRay can replace the sled
65d88c1a5aSDimitry Andric   //   with a code jumping to XRay trampoline, which calls the tracing handler
66d88c1a5aSDimitry Andric   //   and, in the end, issues the RET instruction.
67d88c1a5aSDimitry Andric   // This is the approach to go on CPUs which have a single RET instruction,
68d88c1a5aSDimitry Andric   //   like x86/x86_64.
69d88c1a5aSDimitry Andric   void replaceRetWithPatchableRet(MachineFunction &MF,
702cab237bSDimitry Andric                                   const TargetInstrInfo *TII,
712cab237bSDimitry Andric                                   InstrumentationOptions);
72d88c1a5aSDimitry Andric 
73d88c1a5aSDimitry Andric   // Prepend the original return instruction with the exit sled code ("patchable
74d88c1a5aSDimitry Andric   //   function exit" pseudo-instruction), preserving the original return
75d88c1a5aSDimitry Andric   //   instruction just after the exit sled code.
76d88c1a5aSDimitry Andric   // This is the approach to go on CPUs which have multiple options for the
77d88c1a5aSDimitry Andric   //   return instruction, like ARM. For such CPUs we can't just jump into the
78d88c1a5aSDimitry Andric   //   XRay trampoline and issue a single return instruction there. We rather
79d88c1a5aSDimitry Andric   //   have to call the trampoline and return from it to the original return
80d88c1a5aSDimitry Andric   //   instruction of the function being instrumented.
81d88c1a5aSDimitry Andric   void prependRetWithPatchableExit(MachineFunction &MF,
822cab237bSDimitry Andric                                    const TargetInstrInfo *TII,
832cab237bSDimitry Andric                                    InstrumentationOptions);
843ca95b02SDimitry Andric };
85db17bf38SDimitry Andric 
86db17bf38SDimitry Andric } // end anonymous namespace
87d88c1a5aSDimitry Andric 
replaceRetWithPatchableRet(MachineFunction & MF,const TargetInstrInfo * TII,InstrumentationOptions op)880f5676f4SDimitry Andric void XRayInstrumentation::replaceRetWithPatchableRet(
892cab237bSDimitry Andric     MachineFunction &MF, const TargetInstrInfo *TII,
902cab237bSDimitry Andric     InstrumentationOptions op) {
91d88c1a5aSDimitry Andric   // We look for *all* terminators and returns, then replace those with
92d88c1a5aSDimitry Andric   // PATCHABLE_RET instructions.
93d88c1a5aSDimitry Andric   SmallVector<MachineInstr *, 4> Terminators;
94d88c1a5aSDimitry Andric   for (auto &MBB : MF) {
95d88c1a5aSDimitry Andric     for (auto &T : MBB.terminators()) {
96d88c1a5aSDimitry Andric       unsigned Opc = 0;
972cab237bSDimitry Andric       if (T.isReturn() &&
982cab237bSDimitry Andric           (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
99d88c1a5aSDimitry Andric         // Replace return instructions with:
100d88c1a5aSDimitry Andric         //   PATCHABLE_RET <Opcode>, <Operand>...
101d88c1a5aSDimitry Andric         Opc = TargetOpcode::PATCHABLE_RET;
102d88c1a5aSDimitry Andric       }
1032cab237bSDimitry Andric       if (TII->isTailCall(T) && op.HandleTailcall) {
104d88c1a5aSDimitry Andric         // Treat the tail call as a return instruction, which has a
105d88c1a5aSDimitry Andric         // different-looking sled than the normal return case.
106d88c1a5aSDimitry Andric         Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
107d88c1a5aSDimitry Andric       }
108d88c1a5aSDimitry Andric       if (Opc != 0) {
109d88c1a5aSDimitry Andric         auto MIB = BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc))
110d88c1a5aSDimitry Andric                        .addImm(T.getOpcode());
111d88c1a5aSDimitry Andric         for (auto &MO : T.operands())
1127a7e6055SDimitry Andric           MIB.add(MO);
113d88c1a5aSDimitry Andric         Terminators.push_back(&T);
114d88c1a5aSDimitry Andric       }
115d88c1a5aSDimitry Andric     }
116d88c1a5aSDimitry Andric   }
117d88c1a5aSDimitry Andric 
118d88c1a5aSDimitry Andric   for (auto &I : Terminators)
119d88c1a5aSDimitry Andric     I->eraseFromParent();
120d88c1a5aSDimitry Andric }
121d88c1a5aSDimitry Andric 
prependRetWithPatchableExit(MachineFunction & MF,const TargetInstrInfo * TII,InstrumentationOptions op)1220f5676f4SDimitry Andric void XRayInstrumentation::prependRetWithPatchableExit(
1232cab237bSDimitry Andric     MachineFunction &MF, const TargetInstrInfo *TII,
1242cab237bSDimitry Andric     InstrumentationOptions op) {
1252cab237bSDimitry Andric   for (auto &MBB : MF)
126d88c1a5aSDimitry Andric     for (auto &T : MBB.terminators()) {
127d88c1a5aSDimitry Andric       unsigned Opc = 0;
1282cab237bSDimitry Andric       if (T.isReturn() &&
1292cab237bSDimitry Andric           (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
130d88c1a5aSDimitry Andric         Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT;
131d88c1a5aSDimitry Andric       }
1322cab237bSDimitry Andric       if (TII->isTailCall(T) && op.HandleTailcall) {
133d88c1a5aSDimitry Andric         Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
134d88c1a5aSDimitry Andric       }
135d88c1a5aSDimitry Andric       if (Opc != 0) {
136d88c1a5aSDimitry Andric         // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or
137d88c1a5aSDimitry Andric         //   PATCHABLE_TAIL_CALL .
138d88c1a5aSDimitry Andric         BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc));
139d88c1a5aSDimitry Andric       }
140d88c1a5aSDimitry Andric     }
141d88c1a5aSDimitry Andric }
1423ca95b02SDimitry Andric 
runOnMachineFunction(MachineFunction & MF)1433ca95b02SDimitry Andric bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
1442cab237bSDimitry Andric   auto &F = MF.getFunction();
1453ca95b02SDimitry Andric   auto InstrAttr = F.getFnAttribute("function-instrument");
1463ca95b02SDimitry Andric   bool AlwaysInstrument = !InstrAttr.hasAttribute(Attribute::None) &&
1473ca95b02SDimitry Andric                           InstrAttr.isStringAttribute() &&
1483ca95b02SDimitry Andric                           InstrAttr.getValueAsString() == "xray-always";
1493ca95b02SDimitry Andric   Attribute Attr = F.getFnAttribute("xray-instruction-threshold");
1503ca95b02SDimitry Andric   unsigned XRayThreshold = 0;
1513ca95b02SDimitry Andric   if (!AlwaysInstrument) {
1523ca95b02SDimitry Andric     if (Attr.hasAttribute(Attribute::None) || !Attr.isStringAttribute())
1533ca95b02SDimitry Andric       return false; // XRay threshold attribute not found.
1543ca95b02SDimitry Andric     if (Attr.getValueAsString().getAsInteger(10, XRayThreshold))
1553ca95b02SDimitry Andric       return false; // Invalid value for threshold.
1560f5676f4SDimitry Andric 
157db17bf38SDimitry Andric     // Count the number of MachineInstr`s in MachineFunction
158db17bf38SDimitry Andric     int64_t MICount = 0;
159db17bf38SDimitry Andric     for (const auto &MBB : MF)
160db17bf38SDimitry Andric       MICount += MBB.size();
161db17bf38SDimitry Andric 
162*4ba319b5SDimitry Andric     // Get MachineDominatorTree or compute it on the fly if it's unavailable
163*4ba319b5SDimitry Andric     auto *MDT = getAnalysisIfAvailable<MachineDominatorTree>();
164*4ba319b5SDimitry Andric     MachineDominatorTree ComputedMDT;
165*4ba319b5SDimitry Andric     if (!MDT) {
166*4ba319b5SDimitry Andric       ComputedMDT.getBase().recalculate(MF);
167*4ba319b5SDimitry Andric       MDT = &ComputedMDT;
168*4ba319b5SDimitry Andric     }
169*4ba319b5SDimitry Andric 
170*4ba319b5SDimitry Andric     // Get MachineLoopInfo or compute it on the fly if it's unavailable
171*4ba319b5SDimitry Andric     auto *MLI = getAnalysisIfAvailable<MachineLoopInfo>();
172*4ba319b5SDimitry Andric     MachineLoopInfo ComputedMLI;
173*4ba319b5SDimitry Andric     if (!MLI) {
174*4ba319b5SDimitry Andric       ComputedMLI.getBase().analyze(MDT->getBase());
175*4ba319b5SDimitry Andric       MLI = &ComputedMLI;
176*4ba319b5SDimitry Andric     }
177*4ba319b5SDimitry Andric 
1780f5676f4SDimitry Andric     // Check if we have a loop.
1790f5676f4SDimitry Andric     // FIXME: Maybe make this smarter, and see whether the loops are dependent
1800f5676f4SDimitry Andric     // on inputs or side-effects?
181*4ba319b5SDimitry Andric     if (MLI->empty() && MICount < XRayThreshold)
1820f5676f4SDimitry Andric       return false; // Function is too small and has no loops.
1833ca95b02SDimitry Andric   }
1843ca95b02SDimitry Andric 
185d88c1a5aSDimitry Andric   // We look for the first non-empty MachineBasicBlock, so that we can insert
186d88c1a5aSDimitry Andric   // the function instrumentation in the appropriate place.
187db17bf38SDimitry Andric   auto MBI = llvm::find_if(
188db17bf38SDimitry Andric       MF, [&](const MachineBasicBlock &MBB) { return !MBB.empty(); });
189d88c1a5aSDimitry Andric   if (MBI == MF.end())
190d88c1a5aSDimitry Andric     return false; // The function is empty.
191d88c1a5aSDimitry Andric 
192d88c1a5aSDimitry Andric   auto *TII = MF.getSubtarget().getInstrInfo();
193d88c1a5aSDimitry Andric   auto &FirstMBB = *MBI;
194d88c1a5aSDimitry Andric   auto &FirstMI = *FirstMBB.begin();
195d88c1a5aSDimitry Andric 
196d88c1a5aSDimitry Andric   if (!MF.getSubtarget().isXRaySupported()) {
197d88c1a5aSDimitry Andric     FirstMI.emitError("An attempt to perform XRay instrumentation for an"
198d88c1a5aSDimitry Andric                       " unsupported target.");
199d88c1a5aSDimitry Andric     return false;
200d88c1a5aSDimitry Andric   }
201d88c1a5aSDimitry Andric 
2023ca95b02SDimitry Andric   // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
2033ca95b02SDimitry Andric   // MachineFunction.
2043ca95b02SDimitry Andric   BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(),
2053ca95b02SDimitry Andric           TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
2063ca95b02SDimitry Andric 
207d88c1a5aSDimitry Andric   switch (MF.getTarget().getTargetTriple().getArch()) {
208d88c1a5aSDimitry Andric   case Triple::ArchType::arm:
209d88c1a5aSDimitry Andric   case Triple::ArchType::thumb:
210d88c1a5aSDimitry Andric   case Triple::ArchType::aarch64:
2117a7e6055SDimitry Andric   case Triple::ArchType::mips:
2127a7e6055SDimitry Andric   case Triple::ArchType::mipsel:
2137a7e6055SDimitry Andric   case Triple::ArchType::mips64:
2142cab237bSDimitry Andric   case Triple::ArchType::mips64el: {
215d88c1a5aSDimitry Andric     // For the architectures which don't have a single return instruction
2162cab237bSDimitry Andric     InstrumentationOptions op;
2172cab237bSDimitry Andric     op.HandleTailcall = false;
2182cab237bSDimitry Andric     op.HandleAllReturns = true;
2192cab237bSDimitry Andric     prependRetWithPatchableExit(MF, TII, op);
220d88c1a5aSDimitry Andric     break;
2212cab237bSDimitry Andric   }
2222cab237bSDimitry Andric   case Triple::ArchType::ppc64le: {
2232cab237bSDimitry Andric     // PPC has conditional returns. Turn them into branch and plain returns.
2242cab237bSDimitry Andric     InstrumentationOptions op;
2252cab237bSDimitry Andric     op.HandleTailcall = false;
2262cab237bSDimitry Andric     op.HandleAllReturns = true;
2272cab237bSDimitry Andric     replaceRetWithPatchableRet(MF, TII, op);
2282cab237bSDimitry Andric     break;
2292cab237bSDimitry Andric   }
2302cab237bSDimitry Andric   default: {
231d88c1a5aSDimitry Andric     // For the architectures that have a single return instruction (such as
232d88c1a5aSDimitry Andric     //   RETQ on x86_64).
2332cab237bSDimitry Andric     InstrumentationOptions op;
2342cab237bSDimitry Andric     op.HandleTailcall = true;
2352cab237bSDimitry Andric     op.HandleAllReturns = false;
2362cab237bSDimitry Andric     replaceRetWithPatchableRet(MF, TII, op);
2373ca95b02SDimitry Andric     break;
2383ca95b02SDimitry Andric   }
2392cab237bSDimitry Andric   }
2403ca95b02SDimitry Andric   return true;
2413ca95b02SDimitry Andric }
2423ca95b02SDimitry Andric 
2433ca95b02SDimitry Andric char XRayInstrumentation::ID = 0;
2443ca95b02SDimitry Andric char &llvm::XRayInstrumentationID = XRayInstrumentation::ID;
2450f5676f4SDimitry Andric INITIALIZE_PASS_BEGIN(XRayInstrumentation, "xray-instrumentation",
2460f5676f4SDimitry Andric                       "Insert XRay ops", false, false)
2470f5676f4SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
2480f5676f4SDimitry Andric INITIALIZE_PASS_END(XRayInstrumentation, "xray-instrumentation",
2490f5676f4SDimitry Andric                     "Insert XRay ops", false, false)
250