1fb69e66cSEugene Zelenko //===- XRayInstrumentation.cpp - Adds XRay instrumentation to functions. --===//
252735fc4SDean Michael Berris //
352735fc4SDean Michael Berris //                     The LLVM Compiler Infrastructure
452735fc4SDean Michael Berris //
552735fc4SDean Michael Berris // This file is distributed under the University of Illinois Open Source
652735fc4SDean Michael Berris // License. See LICENSE.TXT for details.
752735fc4SDean Michael Berris //
852735fc4SDean Michael Berris //===----------------------------------------------------------------------===//
952735fc4SDean Michael Berris //
1052735fc4SDean Michael Berris // This file implements a MachineFunctionPass that inserts the appropriate
1152735fc4SDean Michael Berris // XRay instrumentation instructions. We look for XRay-specific attributes
1252735fc4SDean Michael Berris // on the function to determine whether we should insert the replacement
1352735fc4SDean Michael Berris // operations.
1452735fc4SDean Michael Berris //
1552735fc4SDean Michael Berris //===---------------------------------------------------------------------===//
1652735fc4SDean Michael Berris 
17fb69e66cSEugene Zelenko #include "llvm/ADT/STLExtras.h"
18711dec26SDean Michael Berris #include "llvm/ADT/SmallVector.h"
19fb69e66cSEugene Zelenko #include "llvm/ADT/Triple.h"
20fb69e66cSEugene Zelenko #include "llvm/CodeGen/MachineBasicBlock.h"
216bda14b3SChandler Carruth #include "llvm/CodeGen/MachineDominators.h"
2252735fc4SDean Michael Berris #include "llvm/CodeGen/MachineFunction.h"
2352735fc4SDean Michael Berris #include "llvm/CodeGen/MachineFunctionPass.h"
2452735fc4SDean Michael Berris #include "llvm/CodeGen/MachineInstrBuilder.h"
2522f2bcf4SDean Michael Berris #include "llvm/CodeGen/MachineLoopInfo.h"
263f833edcSDavid Blaikie #include "llvm/CodeGen/TargetInstrInfo.h"
27*b3bde2eaSDavid Blaikie #include "llvm/CodeGen/TargetSubtargetInfo.h"
28fb69e66cSEugene Zelenko #include "llvm/IR/Attributes.h"
29fb69e66cSEugene Zelenko #include "llvm/IR/Function.h"
30fb69e66cSEugene Zelenko #include "llvm/Pass.h"
31fb69e66cSEugene Zelenko #include "llvm/Target/TargetMachine.h"
3252735fc4SDean Michael Berris 
3352735fc4SDean Michael Berris using namespace llvm;
3452735fc4SDean Michael Berris 
3552735fc4SDean Michael Berris namespace {
36fb69e66cSEugene Zelenko 
37cee75361STim Shen struct InstrumentationOptions {
38cee75361STim Shen   // Whether to emit PATCHABLE_TAIL_CALL.
39cee75361STim Shen   bool HandleTailcall;
40cee75361STim Shen 
41cee75361STim Shen   // Whether to emit PATCHABLE_RET/PATCHABLE_FUNCTION_EXIT for all forms of
42cee75361STim Shen   // return, e.g. conditional return.
43cee75361STim Shen   bool HandleAllReturns;
44cee75361STim Shen };
45cee75361STim Shen 
4652735fc4SDean Michael Berris struct XRayInstrumentation : public MachineFunctionPass {
4752735fc4SDean Michael Berris   static char ID;
4852735fc4SDean Michael Berris 
4952735fc4SDean Michael Berris   XRayInstrumentation() : MachineFunctionPass(ID) {
5052735fc4SDean Michael Berris     initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry());
5152735fc4SDean Michael Berris   }
5252735fc4SDean Michael Berris 
5322f2bcf4SDean Michael Berris   void getAnalysisUsage(AnalysisUsage &AU) const override {
5422f2bcf4SDean Michael Berris     AU.setPreservesCFG();
5522f2bcf4SDean Michael Berris     AU.addRequired<MachineLoopInfo>();
5622f2bcf4SDean Michael Berris     AU.addPreserved<MachineLoopInfo>();
5722f2bcf4SDean Michael Berris     AU.addPreserved<MachineDominatorTree>();
5822f2bcf4SDean Michael Berris     MachineFunctionPass::getAnalysisUsage(AU);
5922f2bcf4SDean Michael Berris   }
6022f2bcf4SDean Michael Berris 
6152735fc4SDean Michael Berris   bool runOnMachineFunction(MachineFunction &MF) override;
6246401544SDean Michael Berris 
6346401544SDean Michael Berris private:
6446401544SDean Michael Berris   // Replace the original RET instruction with the exit sled code ("patchable
6546401544SDean Michael Berris   //   ret" pseudo-instruction), so that at runtime XRay can replace the sled
6646401544SDean Michael Berris   //   with a code jumping to XRay trampoline, which calls the tracing handler
6746401544SDean Michael Berris   //   and, in the end, issues the RET instruction.
6846401544SDean Michael Berris   // This is the approach to go on CPUs which have a single RET instruction,
6946401544SDean Michael Berris   //   like x86/x86_64.
7046401544SDean Michael Berris   void replaceRetWithPatchableRet(MachineFunction &MF,
71cee75361STim Shen                                   const TargetInstrInfo *TII,
72cee75361STim Shen                                   InstrumentationOptions);
73a331133eSSerge Rogatch 
7446401544SDean Michael Berris   // Prepend the original return instruction with the exit sled code ("patchable
7546401544SDean Michael Berris   //   function exit" pseudo-instruction), preserving the original return
7646401544SDean Michael Berris   //   instruction just after the exit sled code.
7746401544SDean Michael Berris   // This is the approach to go on CPUs which have multiple options for the
7846401544SDean Michael Berris   //   return instruction, like ARM. For such CPUs we can't just jump into the
7946401544SDean Michael Berris   //   XRay trampoline and issue a single return instruction there. We rather
8046401544SDean Michael Berris   //   have to call the trampoline and return from it to the original return
8146401544SDean Michael Berris   //   instruction of the function being instrumented.
8246401544SDean Michael Berris   void prependRetWithPatchableExit(MachineFunction &MF,
83cee75361STim Shen                                    const TargetInstrInfo *TII,
84cee75361STim Shen                                    InstrumentationOptions);
8552735fc4SDean Michael Berris };
86fb69e66cSEugene Zelenko 
87fb69e66cSEugene Zelenko } // end anonymous namespace
8852735fc4SDean Michael Berris 
8922f2bcf4SDean Michael Berris void XRayInstrumentation::replaceRetWithPatchableRet(
90cee75361STim Shen     MachineFunction &MF, const TargetInstrInfo *TII,
91cee75361STim Shen     InstrumentationOptions op) {
9246401544SDean Michael Berris   // We look for *all* terminators and returns, then replace those with
9352735fc4SDean Michael Berris   // PATCHABLE_RET instructions.
9452735fc4SDean Michael Berris   SmallVector<MachineInstr *, 4> Terminators;
9552735fc4SDean Michael Berris   for (auto &MBB : MF) {
9652735fc4SDean Michael Berris     for (auto &T : MBB.terminators()) {
97e8ae5baaSDean Michael Berris       unsigned Opc = 0;
98cee75361STim Shen       if (T.isReturn() &&
99cee75361STim Shen           (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
10052735fc4SDean Michael Berris         // Replace return instructions with:
10152735fc4SDean Michael Berris         //   PATCHABLE_RET <Opcode>, <Operand>...
102e8ae5baaSDean Michael Berris         Opc = TargetOpcode::PATCHABLE_RET;
103e8ae5baaSDean Michael Berris       }
104cee75361STim Shen       if (TII->isTailCall(T) && op.HandleTailcall) {
105e8ae5baaSDean Michael Berris         // Treat the tail call as a return instruction, which has a
106e8ae5baaSDean Michael Berris         // different-looking sled than the normal return case.
107e8ae5baaSDean Michael Berris         Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
108e8ae5baaSDean Michael Berris       }
109e8ae5baaSDean Michael Berris       if (Opc != 0) {
110e8ae5baaSDean Michael Berris         auto MIB = BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc))
11152735fc4SDean Michael Berris                        .addImm(T.getOpcode());
11252735fc4SDean Michael Berris         for (auto &MO : T.operands())
113116bbab4SDiana Picus           MIB.add(MO);
11452735fc4SDean Michael Berris         Terminators.push_back(&T);
11552735fc4SDean Michael Berris       }
11652735fc4SDean Michael Berris     }
11752735fc4SDean Michael Berris   }
11852735fc4SDean Michael Berris 
11952735fc4SDean Michael Berris   for (auto &I : Terminators)
12052735fc4SDean Michael Berris     I->eraseFromParent();
12146401544SDean Michael Berris }
12252735fc4SDean Michael Berris 
12322f2bcf4SDean Michael Berris void XRayInstrumentation::prependRetWithPatchableExit(
124cee75361STim Shen     MachineFunction &MF, const TargetInstrInfo *TII,
125cee75361STim Shen     InstrumentationOptions op) {
126711dec26SDean Michael Berris   for (auto &MBB : MF)
127cee75361STim Shen     for (auto &T : MBB.terminators()) {
128cee75361STim Shen       unsigned Opc = 0;
129cee75361STim Shen       if (T.isReturn() &&
130cee75361STim Shen           (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
131cee75361STim Shen         Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT;
132cee75361STim Shen       }
133cee75361STim Shen       if (TII->isTailCall(T) && op.HandleTailcall) {
134cee75361STim Shen         Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
135cee75361STim Shen       }
136cee75361STim Shen       if (Opc != 0) {
137cee75361STim Shen         // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or
138cee75361STim Shen         //   PATCHABLE_TAIL_CALL .
139cee75361STim Shen         BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc));
140cee75361STim Shen       }
14146401544SDean Michael Berris     }
14246401544SDean Michael Berris }
14346401544SDean Michael Berris 
14446401544SDean Michael Berris bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
14546401544SDean Michael Berris   auto &F = *MF.getFunction();
14646401544SDean Michael Berris   auto InstrAttr = F.getFnAttribute("function-instrument");
14746401544SDean Michael Berris   bool AlwaysInstrument = !InstrAttr.hasAttribute(Attribute::None) &&
14846401544SDean Michael Berris                           InstrAttr.isStringAttribute() &&
14946401544SDean Michael Berris                           InstrAttr.getValueAsString() == "xray-always";
15046401544SDean Michael Berris   Attribute Attr = F.getFnAttribute("xray-instruction-threshold");
15146401544SDean Michael Berris   unsigned XRayThreshold = 0;
15246401544SDean Michael Berris   if (!AlwaysInstrument) {
15346401544SDean Michael Berris     if (Attr.hasAttribute(Attribute::None) || !Attr.isStringAttribute())
15446401544SDean Michael Berris       return false; // XRay threshold attribute not found.
15546401544SDean Michael Berris     if (Attr.getValueAsString().getAsInteger(10, XRayThreshold))
15646401544SDean Michael Berris       return false; // Invalid value for threshold.
15722f2bcf4SDean Michael Berris 
15885427c0dSSerge Rogatch     // Count the number of MachineInstr`s in MachineFunction
15985427c0dSSerge Rogatch     int64_t MICount = 0;
16085427c0dSSerge Rogatch     for (const auto &MBB : MF)
16185427c0dSSerge Rogatch       MICount += MBB.size();
16285427c0dSSerge Rogatch 
16322f2bcf4SDean Michael Berris     // Check if we have a loop.
16422f2bcf4SDean Michael Berris     // FIXME: Maybe make this smarter, and see whether the loops are dependent
16522f2bcf4SDean Michael Berris     // on inputs or side-effects?
16622f2bcf4SDean Michael Berris     MachineLoopInfo &MLI = getAnalysis<MachineLoopInfo>();
16785427c0dSSerge Rogatch     if (MLI.empty() && MICount < XRayThreshold)
16822f2bcf4SDean Michael Berris       return false; // Function is too small and has no loops.
16946401544SDean Michael Berris   }
17046401544SDean Michael Berris 
17103b8be57SDean Michael Berris   // We look for the first non-empty MachineBasicBlock, so that we can insert
17203b8be57SDean Michael Berris   // the function instrumentation in the appropriate place.
173fb69e66cSEugene Zelenko   auto MBI = llvm::find_if(
174fb69e66cSEugene Zelenko       MF, [&](const MachineBasicBlock &MBB) { return !MBB.empty(); });
17503b8be57SDean Michael Berris   if (MBI == MF.end())
17603b8be57SDean Michael Berris     return false; // The function is empty.
17703b8be57SDean Michael Berris 
17803b8be57SDean Michael Berris   auto *TII = MF.getSubtarget().getInstrInfo();
17903b8be57SDean Michael Berris   auto &FirstMBB = *MBI;
18046401544SDean Michael Berris   auto &FirstMI = *FirstMBB.begin();
18146401544SDean Michael Berris 
18246401544SDean Michael Berris   if (!MF.getSubtarget().isXRaySupported()) {
18346401544SDean Michael Berris     FirstMI.emitError("An attempt to perform XRay instrumentation for an"
18446401544SDean Michael Berris                       " unsupported target.");
18546401544SDean Michael Berris     return false;
18646401544SDean Michael Berris   }
18746401544SDean Michael Berris 
18846401544SDean Michael Berris   // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
18946401544SDean Michael Berris   // MachineFunction.
19046401544SDean Michael Berris   BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(),
19146401544SDean Michael Berris           TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
19246401544SDean Michael Berris 
19346401544SDean Michael Berris   switch (MF.getTarget().getTargetTriple().getArch()) {
19446401544SDean Michael Berris   case Triple::ArchType::arm:
19546401544SDean Michael Berris   case Triple::ArchType::thumb:
1963234d3a4SDean Michael Berris   case Triple::ArchType::aarch64:
197ec657929SSagar Thakur   case Triple::ArchType::mips:
198ec657929SSagar Thakur   case Triple::ArchType::mipsel:
199ec657929SSagar Thakur   case Triple::ArchType::mips64:
200cee75361STim Shen   case Triple::ArchType::mips64el: {
20146401544SDean Michael Berris     // For the architectures which don't have a single return instruction
202cee75361STim Shen     InstrumentationOptions op;
203cee75361STim Shen     op.HandleTailcall = false;
204cee75361STim Shen     op.HandleAllReturns = true;
205cee75361STim Shen     prependRetWithPatchableExit(MF, TII, op);
20646401544SDean Michael Berris     break;
207cee75361STim Shen   }
208cee75361STim Shen   case Triple::ArchType::ppc64le: {
209cee75361STim Shen     // PPC has conditional returns. Turn them into branch and plain returns.
210cee75361STim Shen     InstrumentationOptions op;
211cee75361STim Shen     op.HandleTailcall = false;
212cee75361STim Shen     op.HandleAllReturns = true;
213cee75361STim Shen     replaceRetWithPatchableRet(MF, TII, op);
214cee75361STim Shen     break;
215cee75361STim Shen   }
216cee75361STim Shen   default: {
21746401544SDean Michael Berris     // For the architectures that have a single return instruction (such as
21846401544SDean Michael Berris     //   RETQ on x86_64).
219cee75361STim Shen     InstrumentationOptions op;
220cee75361STim Shen     op.HandleTailcall = true;
221cee75361STim Shen     op.HandleAllReturns = false;
222cee75361STim Shen     replaceRetWithPatchableRet(MF, TII, op);
22346401544SDean Michael Berris     break;
22446401544SDean Michael Berris   }
225cee75361STim Shen   }
22652735fc4SDean Michael Berris   return true;
22752735fc4SDean Michael Berris }
22852735fc4SDean Michael Berris 
22952735fc4SDean Michael Berris char XRayInstrumentation::ID = 0;
23052735fc4SDean Michael Berris char &llvm::XRayInstrumentationID = XRayInstrumentation::ID;
23122f2bcf4SDean Michael Berris INITIALIZE_PASS_BEGIN(XRayInstrumentation, "xray-instrumentation",
23222f2bcf4SDean Michael Berris                       "Insert XRay ops", false, false)
23322f2bcf4SDean Michael Berris INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
23422f2bcf4SDean Michael Berris INITIALIZE_PASS_END(XRayInstrumentation, "xray-instrumentation",
23522f2bcf4SDean Michael Berris                     "Insert XRay ops", false, false)
236