1fb69e66cSEugene Zelenko //===- XRayInstrumentation.cpp - Adds XRay instrumentation to functions. --===//
252735fc4SDean Michael Berris //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
652735fc4SDean Michael Berris //
752735fc4SDean Michael Berris //===----------------------------------------------------------------------===//
852735fc4SDean Michael Berris //
952735fc4SDean Michael Berris // This file implements a MachineFunctionPass that inserts the appropriate
1052735fc4SDean Michael Berris // XRay instrumentation instructions. We look for XRay-specific attributes
1152735fc4SDean Michael Berris // on the function to determine whether we should insert the replacement
1252735fc4SDean Michael Berris // operations.
1352735fc4SDean Michael Berris //
1452735fc4SDean Michael Berris //===---------------------------------------------------------------------===//
1552735fc4SDean Michael Berris 
16fb69e66cSEugene Zelenko #include "llvm/ADT/STLExtras.h"
17711dec26SDean Michael Berris #include "llvm/ADT/SmallVector.h"
18fb69e66cSEugene Zelenko #include "llvm/ADT/Triple.h"
19fb69e66cSEugene Zelenko #include "llvm/CodeGen/MachineBasicBlock.h"
206bda14b3SChandler Carruth #include "llvm/CodeGen/MachineDominators.h"
2152735fc4SDean Michael Berris #include "llvm/CodeGen/MachineFunction.h"
2252735fc4SDean Michael Berris #include "llvm/CodeGen/MachineFunctionPass.h"
2352735fc4SDean Michael Berris #include "llvm/CodeGen/MachineInstrBuilder.h"
2422f2bcf4SDean Michael Berris #include "llvm/CodeGen/MachineLoopInfo.h"
253f833edcSDavid Blaikie #include "llvm/CodeGen/TargetInstrInfo.h"
26b3bde2eaSDavid Blaikie #include "llvm/CodeGen/TargetSubtargetInfo.h"
27fb69e66cSEugene Zelenko #include "llvm/IR/Attributes.h"
28fb69e66cSEugene Zelenko #include "llvm/IR/Function.h"
2905da2fe5SReid Kleckner #include "llvm/InitializePasses.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 
XRayInstrumentation__anond1c8e69c0111::XRayInstrumentation4952735fc4SDean Michael Berris   XRayInstrumentation() : MachineFunctionPass(ID) {
5052735fc4SDean Michael Berris     initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry());
5152735fc4SDean Michael Berris   }
5252735fc4SDean Michael Berris 
getAnalysisUsage__anond1c8e69c0111::XRayInstrumentation5322f2bcf4SDean Michael Berris   void getAnalysisUsage(AnalysisUsage &AU) const override {
5422f2bcf4SDean Michael Berris     AU.setPreservesCFG();
5522f2bcf4SDean Michael Berris     AU.addPreserved<MachineLoopInfo>();
5622f2bcf4SDean Michael Berris     AU.addPreserved<MachineDominatorTree>();
5722f2bcf4SDean Michael Berris     MachineFunctionPass::getAnalysisUsage(AU);
5822f2bcf4SDean Michael Berris   }
5922f2bcf4SDean Michael Berris 
6052735fc4SDean Michael Berris   bool runOnMachineFunction(MachineFunction &MF) override;
6146401544SDean Michael Berris 
6246401544SDean Michael Berris private:
6346401544SDean Michael Berris   // Replace the original RET instruction with the exit sled code ("patchable
6446401544SDean Michael Berris   //   ret" pseudo-instruction), so that at runtime XRay can replace the sled
6546401544SDean Michael Berris   //   with a code jumping to XRay trampoline, which calls the tracing handler
6646401544SDean Michael Berris   //   and, in the end, issues the RET instruction.
6746401544SDean Michael Berris   // This is the approach to go on CPUs which have a single RET instruction,
6846401544SDean Michael Berris   //   like x86/x86_64.
6946401544SDean Michael Berris   void replaceRetWithPatchableRet(MachineFunction &MF,
70cee75361STim Shen                                   const TargetInstrInfo *TII,
71cee75361STim Shen                                   InstrumentationOptions);
72a331133eSSerge Rogatch 
7346401544SDean Michael Berris   // Prepend the original return instruction with the exit sled code ("patchable
7446401544SDean Michael Berris   //   function exit" pseudo-instruction), preserving the original return
7546401544SDean Michael Berris   //   instruction just after the exit sled code.
7646401544SDean Michael Berris   // This is the approach to go on CPUs which have multiple options for the
7746401544SDean Michael Berris   //   return instruction, like ARM. For such CPUs we can't just jump into the
7846401544SDean Michael Berris   //   XRay trampoline and issue a single return instruction there. We rather
7946401544SDean Michael Berris   //   have to call the trampoline and return from it to the original return
8046401544SDean Michael Berris   //   instruction of the function being instrumented.
8146401544SDean Michael Berris   void prependRetWithPatchableExit(MachineFunction &MF,
82cee75361STim Shen                                    const TargetInstrInfo *TII,
83cee75361STim Shen                                    InstrumentationOptions);
8452735fc4SDean Michael Berris };
85fb69e66cSEugene Zelenko 
86fb69e66cSEugene Zelenko } // end anonymous namespace
8752735fc4SDean Michael Berris 
replaceRetWithPatchableRet(MachineFunction & MF,const TargetInstrInfo * TII,InstrumentationOptions op)8822f2bcf4SDean Michael Berris void XRayInstrumentation::replaceRetWithPatchableRet(
89cee75361STim Shen     MachineFunction &MF, const TargetInstrInfo *TII,
90cee75361STim Shen     InstrumentationOptions op) {
9146401544SDean Michael Berris   // We look for *all* terminators and returns, then replace those with
9252735fc4SDean Michael Berris   // PATCHABLE_RET instructions.
9352735fc4SDean Michael Berris   SmallVector<MachineInstr *, 4> Terminators;
9452735fc4SDean Michael Berris   for (auto &MBB : MF) {
9552735fc4SDean Michael Berris     for (auto &T : MBB.terminators()) {
96e8ae5baaSDean Michael Berris       unsigned Opc = 0;
97cee75361STim Shen       if (T.isReturn() &&
98cee75361STim Shen           (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
9952735fc4SDean Michael Berris         // Replace return instructions with:
10052735fc4SDean Michael Berris         //   PATCHABLE_RET <Opcode>, <Operand>...
101e8ae5baaSDean Michael Berris         Opc = TargetOpcode::PATCHABLE_RET;
102e8ae5baaSDean Michael Berris       }
103cee75361STim Shen       if (TII->isTailCall(T) && op.HandleTailcall) {
104e8ae5baaSDean Michael Berris         // Treat the tail call as a return instruction, which has a
105e8ae5baaSDean Michael Berris         // different-looking sled than the normal return case.
106e8ae5baaSDean Michael Berris         Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
107e8ae5baaSDean Michael Berris       }
108e8ae5baaSDean Michael Berris       if (Opc != 0) {
109e8ae5baaSDean Michael Berris         auto MIB = BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc))
11052735fc4SDean Michael Berris                        .addImm(T.getOpcode());
11152735fc4SDean Michael Berris         for (auto &MO : T.operands())
112116bbab4SDiana Picus           MIB.add(MO);
11352735fc4SDean Michael Berris         Terminators.push_back(&T);
114016d91ccSDjordje Todorovic         if (T.shouldUpdateCallSiteInfo())
11598603a81SNikola Prica           MF.eraseCallSiteInfo(&T);
11652735fc4SDean Michael Berris       }
11752735fc4SDean Michael Berris     }
11852735fc4SDean Michael Berris   }
11952735fc4SDean Michael Berris 
12052735fc4SDean Michael Berris   for (auto &I : Terminators)
12152735fc4SDean Michael Berris     I->eraseFromParent();
12246401544SDean Michael Berris }
12352735fc4SDean Michael Berris 
prependRetWithPatchableExit(MachineFunction & MF,const TargetInstrInfo * TII,InstrumentationOptions op)12422f2bcf4SDean Michael Berris void XRayInstrumentation::prependRetWithPatchableExit(
125cee75361STim Shen     MachineFunction &MF, const TargetInstrInfo *TII,
126cee75361STim Shen     InstrumentationOptions op) {
127711dec26SDean Michael Berris   for (auto &MBB : MF)
128cee75361STim Shen     for (auto &T : MBB.terminators()) {
129cee75361STim Shen       unsigned Opc = 0;
130cee75361STim Shen       if (T.isReturn() &&
131cee75361STim Shen           (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
132cee75361STim Shen         Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT;
133cee75361STim Shen       }
134cee75361STim Shen       if (TII->isTailCall(T) && op.HandleTailcall) {
135cee75361STim Shen         Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
136cee75361STim Shen       }
137cee75361STim Shen       if (Opc != 0) {
138cee75361STim Shen         // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or
139cee75361STim Shen         //   PATCHABLE_TAIL_CALL .
140cee75361STim Shen         BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc));
141cee75361STim Shen       }
14246401544SDean Michael Berris     }
14346401544SDean Michael Berris }
14446401544SDean Michael Berris 
runOnMachineFunction(MachineFunction & MF)14546401544SDean Michael Berris bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
146f1caa283SMatthias Braun   auto &F = MF.getFunction();
14746401544SDean Michael Berris   auto InstrAttr = F.getFnAttribute("function-instrument");
148aab90384SCraig Topper   bool AlwaysInstrument = InstrAttr.isStringAttribute() &&
14946401544SDean Michael Berris                           InstrAttr.getValueAsString() == "xray-always";
15068a1f091SIan Levesque   bool NeverInstrument = InstrAttr.isStringAttribute() &&
15168a1f091SIan Levesque                          InstrAttr.getValueAsString() == "xray-never";
15268a1f091SIan Levesque   if (NeverInstrument && !AlwaysInstrument)
15368a1f091SIan Levesque     return false;
1547628e474SIan Levesque   auto ThresholdAttr = F.getFnAttribute("xray-instruction-threshold");
1557628e474SIan Levesque   auto IgnoreLoopsAttr = F.getFnAttribute("xray-ignore-loops");
1567628e474SIan Levesque   unsigned int XRayThreshold = 0;
15746401544SDean Michael Berris   if (!AlwaysInstrument) {
158aab90384SCraig Topper     if (!ThresholdAttr.isStringAttribute())
15946401544SDean Michael Berris       return false; // XRay threshold attribute not found.
1607628e474SIan Levesque     if (ThresholdAttr.getValueAsString().getAsInteger(10, XRayThreshold))
16146401544SDean Michael Berris       return false; // Invalid value for threshold.
16222f2bcf4SDean Michael Berris 
163aab90384SCraig Topper     bool IgnoreLoops = IgnoreLoopsAttr.isValid();
1647628e474SIan Levesque 
16585427c0dSSerge Rogatch     // Count the number of MachineInstr`s in MachineFunction
16685427c0dSSerge Rogatch     int64_t MICount = 0;
16785427c0dSSerge Rogatch     for (const auto &MBB : MF)
16885427c0dSSerge Rogatch       MICount += MBB.size();
16985427c0dSSerge Rogatch 
1707628e474SIan Levesque     bool TooFewInstrs = MICount < XRayThreshold;
1717628e474SIan Levesque 
1727628e474SIan Levesque     if (!IgnoreLoops) {
173fb3f509eSMichael Zolotukhin       // Get MachineDominatorTree or compute it on the fly if it's unavailable
174fb3f509eSMichael Zolotukhin       auto *MDT = getAnalysisIfAvailable<MachineDominatorTree>();
175fb3f509eSMichael Zolotukhin       MachineDominatorTree ComputedMDT;
176fb3f509eSMichael Zolotukhin       if (!MDT) {
177fb3f509eSMichael Zolotukhin         ComputedMDT.getBase().recalculate(MF);
178fb3f509eSMichael Zolotukhin         MDT = &ComputedMDT;
179fb3f509eSMichael Zolotukhin       }
180fb3f509eSMichael Zolotukhin 
181fb3f509eSMichael Zolotukhin       // Get MachineLoopInfo or compute it on the fly if it's unavailable
182fb3f509eSMichael Zolotukhin       auto *MLI = getAnalysisIfAvailable<MachineLoopInfo>();
183fb3f509eSMichael Zolotukhin       MachineLoopInfo ComputedMLI;
184fb3f509eSMichael Zolotukhin       if (!MLI) {
185fb3f509eSMichael Zolotukhin         ComputedMLI.getBase().analyze(MDT->getBase());
186fb3f509eSMichael Zolotukhin         MLI = &ComputedMLI;
187fb3f509eSMichael Zolotukhin       }
188fb3f509eSMichael Zolotukhin 
18922f2bcf4SDean Michael Berris       // Check if we have a loop.
19022f2bcf4SDean Michael Berris       // FIXME: Maybe make this smarter, and see whether the loops are dependent
19122f2bcf4SDean Michael Berris       // on inputs or side-effects?
1927628e474SIan Levesque       if (MLI->empty() && TooFewInstrs)
19322f2bcf4SDean Michael Berris         return false; // Function is too small and has no loops.
1947628e474SIan Levesque     } else if (TooFewInstrs) {
1957628e474SIan Levesque       // Function is too small
1967628e474SIan Levesque       return false;
1977628e474SIan Levesque     }
19846401544SDean Michael Berris   }
19946401544SDean Michael Berris 
20003b8be57SDean Michael Berris   // We look for the first non-empty MachineBasicBlock, so that we can insert
20103b8be57SDean Michael Berris   // the function instrumentation in the appropriate place.
202fb69e66cSEugene Zelenko   auto MBI = llvm::find_if(
203fb69e66cSEugene Zelenko       MF, [&](const MachineBasicBlock &MBB) { return !MBB.empty(); });
20403b8be57SDean Michael Berris   if (MBI == MF.end())
20503b8be57SDean Michael Berris     return false; // The function is empty.
20603b8be57SDean Michael Berris 
20703b8be57SDean Michael Berris   auto *TII = MF.getSubtarget().getInstrInfo();
20803b8be57SDean Michael Berris   auto &FirstMBB = *MBI;
20946401544SDean Michael Berris   auto &FirstMI = *FirstMBB.begin();
21046401544SDean Michael Berris 
21146401544SDean Michael Berris   if (!MF.getSubtarget().isXRaySupported()) {
21246401544SDean Michael Berris     FirstMI.emitError("An attempt to perform XRay instrumentation for an"
21346401544SDean Michael Berris                       " unsupported target.");
21446401544SDean Michael Berris     return false;
21546401544SDean Michael Berris   }
21646401544SDean Michael Berris 
21797ba4830SIan Levesque   if (!F.hasFnAttribute("xray-skip-entry")) {
21846401544SDean Michael Berris     // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
21946401544SDean Michael Berris     // MachineFunction.
22046401544SDean Michael Berris     BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(),
22146401544SDean Michael Berris             TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
22297ba4830SIan Levesque   }
22346401544SDean Michael Berris 
22497ba4830SIan Levesque   if (!F.hasFnAttribute("xray-skip-exit")) {
22546401544SDean Michael Berris     switch (MF.getTarget().getTargetTriple().getArch()) {
22646401544SDean Michael Berris     case Triple::ArchType::arm:
22746401544SDean Michael Berris     case Triple::ArchType::thumb:
2283234d3a4SDean Michael Berris     case Triple::ArchType::aarch64:
229*1e68c799SBrian Cain     case Triple::ArchType::hexagon:
230ec657929SSagar Thakur     case Triple::ArchType::mips:
231ec657929SSagar Thakur     case Triple::ArchType::mipsel:
232ec657929SSagar Thakur     case Triple::ArchType::mips64:
233cee75361STim Shen     case Triple::ArchType::mips64el: {
23446401544SDean Michael Berris       // For the architectures which don't have a single return instruction
235cee75361STim Shen       InstrumentationOptions op;
236cee75361STim Shen       op.HandleTailcall = false;
237cee75361STim Shen       op.HandleAllReturns = true;
238cee75361STim Shen       prependRetWithPatchableExit(MF, TII, op);
23946401544SDean Michael Berris       break;
240cee75361STim Shen     }
241cee75361STim Shen     case Triple::ArchType::ppc64le: {
242cee75361STim Shen       // PPC has conditional returns. Turn them into branch and plain returns.
243cee75361STim Shen       InstrumentationOptions op;
244cee75361STim Shen       op.HandleTailcall = false;
245cee75361STim Shen       op.HandleAllReturns = true;
246cee75361STim Shen       replaceRetWithPatchableRet(MF, TII, op);
247cee75361STim Shen       break;
248cee75361STim Shen     }
249cee75361STim Shen     default: {
25046401544SDean Michael Berris       // For the architectures that have a single return instruction (such as
25146401544SDean Michael Berris       //   RETQ on x86_64).
252cee75361STim Shen       InstrumentationOptions op;
253cee75361STim Shen       op.HandleTailcall = true;
254cee75361STim Shen       op.HandleAllReturns = false;
255cee75361STim Shen       replaceRetWithPatchableRet(MF, TII, op);
25646401544SDean Michael Berris       break;
25746401544SDean Michael Berris     }
258cee75361STim Shen     }
25997ba4830SIan Levesque   }
26052735fc4SDean Michael Berris   return true;
26152735fc4SDean Michael Berris }
26252735fc4SDean Michael Berris 
26352735fc4SDean Michael Berris char XRayInstrumentation::ID = 0;
26452735fc4SDean Michael Berris char &llvm::XRayInstrumentationID = XRayInstrumentation::ID;
26522f2bcf4SDean Michael Berris INITIALIZE_PASS_BEGIN(XRayInstrumentation, "xray-instrumentation",
26622f2bcf4SDean Michael Berris                       "Insert XRay ops", false, false)
26722f2bcf4SDean Michael Berris INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
26822f2bcf4SDean Michael Berris INITIALIZE_PASS_END(XRayInstrumentation, "xray-instrumentation",
26922f2bcf4SDean Michael Berris                     "Insert XRay ops", false, false)
270