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