XRayInstrumentation.cpp 9.1 KB

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