XRayInstrumentation.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. //===-- XRayInstrumentation.cpp - Adds XRay instrumentation to functions. -===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // This file implements a MachineFunctionPass that inserts the appropriate
  11. // XRay instrumentation instructions. We look for XRay-specific attributes
  12. // on the function to determine whether we should insert the replacement
  13. // operations.
  14. //
  15. //===---------------------------------------------------------------------===//
  16. #include "llvm/CodeGen/Analysis.h"
  17. #include "llvm/CodeGen/MachineFunction.h"
  18. #include "llvm/CodeGen/MachineFunctionPass.h"
  19. #include "llvm/CodeGen/MachineInstrBuilder.h"
  20. #include "llvm/CodeGen/Passes.h"
  21. #include "llvm/Support/TargetRegistry.h"
  22. #include "llvm/Target/TargetInstrInfo.h"
  23. #include "llvm/Target/TargetSubtargetInfo.h"
  24. using namespace llvm;
  25. namespace {
  26. struct XRayInstrumentation : public MachineFunctionPass {
  27. static char ID;
  28. XRayInstrumentation() : MachineFunctionPass(ID) {
  29. initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry());
  30. }
  31. bool runOnMachineFunction(MachineFunction &MF) override;
  32. private:
  33. // Replace the original RET instruction with the exit sled code ("patchable
  34. // ret" pseudo-instruction), so that at runtime XRay can replace the sled
  35. // with a code jumping to XRay trampoline, which calls the tracing handler
  36. // and, in the end, issues the RET instruction.
  37. // This is the approach to go on CPUs which have a single RET instruction,
  38. // like x86/x86_64.
  39. void replaceRetWithPatchableRet(MachineFunction &MF,
  40. const TargetInstrInfo *TII);
  41. // Prepend the original return instruction with the exit sled code ("patchable
  42. // function exit" pseudo-instruction), preserving the original return
  43. // instruction just after the exit sled code.
  44. // This is the approach to go on CPUs which have multiple options for the
  45. // return instruction, like ARM. For such CPUs we can't just jump into the
  46. // XRay trampoline and issue a single return instruction there. We rather
  47. // have to call the trampoline and return from it to the original return
  48. // instruction of the function being instrumented.
  49. void prependRetWithPatchableExit(MachineFunction &MF,
  50. const TargetInstrInfo *TII);
  51. };
  52. } // anonymous namespace
  53. void XRayInstrumentation::replaceRetWithPatchableRet(MachineFunction &MF,
  54. const TargetInstrInfo *TII)
  55. {
  56. // We look for *all* terminators and returns, then replace those with
  57. // PATCHABLE_RET instructions.
  58. SmallVector<MachineInstr *, 4> Terminators;
  59. for (auto &MBB : MF) {
  60. for (auto &T : MBB.terminators()) {
  61. unsigned Opc = 0;
  62. if (T.isReturn() && T.getOpcode() == TII->getReturnOpcode()) {
  63. // Replace return instructions with:
  64. // PATCHABLE_RET <Opcode>, <Operand>...
  65. Opc = TargetOpcode::PATCHABLE_RET;
  66. }
  67. if (TII->isTailCall(T)) {
  68. // Treat the tail call as a return instruction, which has a
  69. // different-looking sled than the normal return case.
  70. Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
  71. }
  72. if (Opc != 0) {
  73. auto MIB = BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc))
  74. .addImm(T.getOpcode());
  75. for (auto &MO : T.operands())
  76. MIB.add(MO);
  77. Terminators.push_back(&T);
  78. }
  79. }
  80. }
  81. for (auto &I : Terminators)
  82. I->eraseFromParent();
  83. }
  84. void XRayInstrumentation::prependRetWithPatchableExit(MachineFunction &MF,
  85. const TargetInstrInfo *TII)
  86. {
  87. for (auto &MBB : MF) {
  88. for (auto &T : MBB.terminators()) {
  89. unsigned Opc = 0;
  90. if (T.isReturn()) {
  91. Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT;
  92. }
  93. if (TII->isTailCall(T)) {
  94. Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
  95. }
  96. if (Opc != 0) {
  97. // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or
  98. // PATCHABLE_TAIL_CALL .
  99. BuildMI(MBB, T, T.getDebugLoc(),TII->get(Opc));
  100. }
  101. }
  102. }
  103. }
  104. bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
  105. auto &F = *MF.getFunction();
  106. auto InstrAttr = F.getFnAttribute("function-instrument");
  107. bool AlwaysInstrument = !InstrAttr.hasAttribute(Attribute::None) &&
  108. InstrAttr.isStringAttribute() &&
  109. InstrAttr.getValueAsString() == "xray-always";
  110. Attribute Attr = F.getFnAttribute("xray-instruction-threshold");
  111. unsigned XRayThreshold = 0;
  112. if (!AlwaysInstrument) {
  113. if (Attr.hasAttribute(Attribute::None) || !Attr.isStringAttribute())
  114. return false; // XRay threshold attribute not found.
  115. if (Attr.getValueAsString().getAsInteger(10, XRayThreshold))
  116. return false; // Invalid value for threshold.
  117. if (F.size() < XRayThreshold)
  118. return false; // Function is too small.
  119. }
  120. // We look for the first non-empty MachineBasicBlock, so that we can insert
  121. // the function instrumentation in the appropriate place.
  122. auto MBI =
  123. find_if(MF, [&](const MachineBasicBlock &MBB) { return !MBB.empty(); });
  124. if (MBI == MF.end())
  125. return false; // The function is empty.
  126. auto *TII = MF.getSubtarget().getInstrInfo();
  127. auto &FirstMBB = *MBI;
  128. auto &FirstMI = *FirstMBB.begin();
  129. if (!MF.getSubtarget().isXRaySupported()) {
  130. FirstMI.emitError("An attempt to perform XRay instrumentation for an"
  131. " unsupported target.");
  132. return false;
  133. }
  134. // FIXME: Do the loop triviality analysis here or in an earlier pass.
  135. // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
  136. // MachineFunction.
  137. BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(),
  138. TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
  139. switch (MF.getTarget().getTargetTriple().getArch()) {
  140. case Triple::ArchType::arm:
  141. case Triple::ArchType::thumb:
  142. case Triple::ArchType::aarch64:
  143. case Triple::ArchType::ppc64le:
  144. // For the architectures which don't have a single return instruction
  145. prependRetWithPatchableExit(MF, TII);
  146. break;
  147. default:
  148. // For the architectures that have a single return instruction (such as
  149. // RETQ on x86_64).
  150. replaceRetWithPatchableRet(MF, TII);
  151. break;
  152. }
  153. return true;
  154. }
  155. char XRayInstrumentation::ID = 0;
  156. char &llvm::XRayInstrumentationID = XRayInstrumentation::ID;
  157. INITIALIZE_PASS(XRayInstrumentation, "xray-instrumentation", "Insert XRay ops",
  158. false, false)