17ab83427SDimitry Andric //===- XRayInstrumentation.cpp - Adds XRay instrumentation to functions. --===//
201095a5dSDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
601095a5dSDimitry Andric //
701095a5dSDimitry Andric //===----------------------------------------------------------------------===//
801095a5dSDimitry Andric //
901095a5dSDimitry Andric // This file implements a MachineFunctionPass that inserts the appropriate
1001095a5dSDimitry Andric // XRay instrumentation instructions. We look for XRay-specific attributes
1101095a5dSDimitry Andric // on the function to determine whether we should insert the replacement
1201095a5dSDimitry Andric // operations.
1301095a5dSDimitry Andric //
1401095a5dSDimitry Andric //===---------------------------------------------------------------------===//
1501095a5dSDimitry Andric
167ab83427SDimitry Andric #include "llvm/ADT/STLExtras.h"
17044eb2f6SDimitry Andric #include "llvm/ADT/SmallVector.h"
187ab83427SDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
197ab83427SDimitry Andric #include "llvm/CodeGen/MachineDominators.h"
2001095a5dSDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
2101095a5dSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
2201095a5dSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
23c46e6a59SDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h"
24044eb2f6SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
25044eb2f6SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
267ab83427SDimitry Andric #include "llvm/IR/Attributes.h"
277ab83427SDimitry Andric #include "llvm/IR/Function.h"
28706b4fc4SDimitry Andric #include "llvm/InitializePasses.h"
297ab83427SDimitry Andric #include "llvm/Pass.h"
307ab83427SDimitry Andric #include "llvm/Target/TargetMachine.h"
317fa27ce4SDimitry Andric #include "llvm/TargetParser/Triple.h"
3201095a5dSDimitry Andric
3301095a5dSDimitry Andric using namespace llvm;
3401095a5dSDimitry Andric
3501095a5dSDimitry Andric namespace {
367ab83427SDimitry Andric
37044eb2f6SDimitry Andric struct InstrumentationOptions {
38044eb2f6SDimitry Andric // Whether to emit PATCHABLE_TAIL_CALL.
39044eb2f6SDimitry Andric bool HandleTailcall;
40044eb2f6SDimitry Andric
41044eb2f6SDimitry Andric // Whether to emit PATCHABLE_RET/PATCHABLE_FUNCTION_EXIT for all forms of
42044eb2f6SDimitry Andric // return, e.g. conditional return.
43044eb2f6SDimitry Andric bool HandleAllReturns;
44044eb2f6SDimitry Andric };
45044eb2f6SDimitry Andric
4601095a5dSDimitry Andric struct XRayInstrumentation : public MachineFunctionPass {
4701095a5dSDimitry Andric static char ID;
4801095a5dSDimitry Andric
XRayInstrumentation__anon49f1e4e10111::XRayInstrumentation4901095a5dSDimitry Andric XRayInstrumentation() : MachineFunctionPass(ID) {
5001095a5dSDimitry Andric initializeXRayInstrumentationPass(*PassRegistry::getPassRegistry());
5101095a5dSDimitry Andric }
5201095a5dSDimitry Andric
getAnalysisUsage__anon49f1e4e10111::XRayInstrumentation53c46e6a59SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
54c46e6a59SDimitry Andric AU.setPreservesCFG();
55ac9a064cSDimitry Andric AU.addPreserved<MachineLoopInfoWrapperPass>();
56ac9a064cSDimitry Andric AU.addPreserved<MachineDominatorTreeWrapperPass>();
57c46e6a59SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU);
58c46e6a59SDimitry Andric }
59c46e6a59SDimitry Andric
6001095a5dSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override;
61b915e9e0SDimitry Andric
62b915e9e0SDimitry Andric private:
63b915e9e0SDimitry Andric // Replace the original RET instruction with the exit sled code ("patchable
64b915e9e0SDimitry Andric // ret" pseudo-instruction), so that at runtime XRay can replace the sled
65b915e9e0SDimitry Andric // with a code jumping to XRay trampoline, which calls the tracing handler
66b915e9e0SDimitry Andric // and, in the end, issues the RET instruction.
67b915e9e0SDimitry Andric // This is the approach to go on CPUs which have a single RET instruction,
68b915e9e0SDimitry Andric // like x86/x86_64.
69b915e9e0SDimitry Andric void replaceRetWithPatchableRet(MachineFunction &MF,
70044eb2f6SDimitry Andric const TargetInstrInfo *TII,
71044eb2f6SDimitry Andric InstrumentationOptions);
72b915e9e0SDimitry Andric
73b915e9e0SDimitry Andric // Prepend the original return instruction with the exit sled code ("patchable
74b915e9e0SDimitry Andric // function exit" pseudo-instruction), preserving the original return
75b915e9e0SDimitry Andric // instruction just after the exit sled code.
76b915e9e0SDimitry Andric // This is the approach to go on CPUs which have multiple options for the
77b915e9e0SDimitry Andric // return instruction, like ARM. For such CPUs we can't just jump into the
78b915e9e0SDimitry Andric // XRay trampoline and issue a single return instruction there. We rather
79b915e9e0SDimitry Andric // have to call the trampoline and return from it to the original return
80b915e9e0SDimitry Andric // instruction of the function being instrumented.
81b915e9e0SDimitry Andric void prependRetWithPatchableExit(MachineFunction &MF,
82044eb2f6SDimitry Andric const TargetInstrInfo *TII,
83044eb2f6SDimitry Andric InstrumentationOptions);
8401095a5dSDimitry Andric };
857ab83427SDimitry Andric
867ab83427SDimitry Andric } // end anonymous namespace
87b915e9e0SDimitry Andric
replaceRetWithPatchableRet(MachineFunction & MF,const TargetInstrInfo * TII,InstrumentationOptions op)88c46e6a59SDimitry Andric void XRayInstrumentation::replaceRetWithPatchableRet(
89044eb2f6SDimitry Andric MachineFunction &MF, const TargetInstrInfo *TII,
90044eb2f6SDimitry Andric InstrumentationOptions op) {
91b915e9e0SDimitry Andric // We look for *all* terminators and returns, then replace those with
92b915e9e0SDimitry Andric // PATCHABLE_RET instructions.
93b915e9e0SDimitry Andric SmallVector<MachineInstr *, 4> Terminators;
94b915e9e0SDimitry Andric for (auto &MBB : MF) {
95b915e9e0SDimitry Andric for (auto &T : MBB.terminators()) {
96b915e9e0SDimitry Andric unsigned Opc = 0;
97044eb2f6SDimitry Andric if (T.isReturn() &&
98044eb2f6SDimitry Andric (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
99b915e9e0SDimitry Andric // Replace return instructions with:
100b915e9e0SDimitry Andric // PATCHABLE_RET <Opcode>, <Operand>...
101b915e9e0SDimitry Andric Opc = TargetOpcode::PATCHABLE_RET;
102b915e9e0SDimitry Andric }
103044eb2f6SDimitry Andric if (TII->isTailCall(T) && op.HandleTailcall) {
104b915e9e0SDimitry Andric // Treat the tail call as a return instruction, which has a
105b915e9e0SDimitry Andric // different-looking sled than the normal return case.
106b915e9e0SDimitry Andric Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
107b915e9e0SDimitry Andric }
108b915e9e0SDimitry Andric if (Opc != 0) {
109b915e9e0SDimitry Andric auto MIB = BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc))
110b915e9e0SDimitry Andric .addImm(T.getOpcode());
111b915e9e0SDimitry Andric for (auto &MO : T.operands())
11271d5a254SDimitry Andric MIB.add(MO);
113b915e9e0SDimitry Andric Terminators.push_back(&T);
114cfca06d7SDimitry Andric if (T.shouldUpdateCallSiteInfo())
1151d5ae102SDimitry Andric MF.eraseCallSiteInfo(&T);
116b915e9e0SDimitry Andric }
117b915e9e0SDimitry Andric }
118b915e9e0SDimitry Andric }
119b915e9e0SDimitry Andric
120b915e9e0SDimitry Andric for (auto &I : Terminators)
121b915e9e0SDimitry Andric I->eraseFromParent();
122b915e9e0SDimitry Andric }
123b915e9e0SDimitry Andric
prependRetWithPatchableExit(MachineFunction & MF,const TargetInstrInfo * TII,InstrumentationOptions op)124c46e6a59SDimitry Andric void XRayInstrumentation::prependRetWithPatchableExit(
125044eb2f6SDimitry Andric MachineFunction &MF, const TargetInstrInfo *TII,
126044eb2f6SDimitry Andric InstrumentationOptions op) {
127044eb2f6SDimitry Andric for (auto &MBB : MF)
128b915e9e0SDimitry Andric for (auto &T : MBB.terminators()) {
129b915e9e0SDimitry Andric unsigned Opc = 0;
130044eb2f6SDimitry Andric if (T.isReturn() &&
131044eb2f6SDimitry Andric (op.HandleAllReturns || T.getOpcode() == TII->getReturnOpcode())) {
132b915e9e0SDimitry Andric Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT;
133b915e9e0SDimitry Andric }
134044eb2f6SDimitry Andric if (TII->isTailCall(T) && op.HandleTailcall) {
135b915e9e0SDimitry Andric Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
136b915e9e0SDimitry Andric }
137b915e9e0SDimitry Andric if (Opc != 0) {
138b915e9e0SDimitry Andric // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or
139b915e9e0SDimitry Andric // PATCHABLE_TAIL_CALL .
140b915e9e0SDimitry Andric BuildMI(MBB, T, T.getDebugLoc(), TII->get(Opc));
141b915e9e0SDimitry Andric }
142b915e9e0SDimitry Andric }
143b915e9e0SDimitry Andric }
14401095a5dSDimitry Andric
runOnMachineFunction(MachineFunction & MF)14501095a5dSDimitry Andric bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
146044eb2f6SDimitry Andric auto &F = MF.getFunction();
14701095a5dSDimitry Andric auto InstrAttr = F.getFnAttribute("function-instrument");
148b60736ecSDimitry Andric bool AlwaysInstrument = InstrAttr.isStringAttribute() &&
14901095a5dSDimitry Andric InstrAttr.getValueAsString() == "xray-always";
150b60736ecSDimitry Andric bool NeverInstrument = InstrAttr.isStringAttribute() &&
151b60736ecSDimitry Andric InstrAttr.getValueAsString() == "xray-never";
152b60736ecSDimitry Andric if (NeverInstrument && !AlwaysInstrument)
153b60736ecSDimitry Andric return false;
154cfca06d7SDimitry Andric auto IgnoreLoopsAttr = F.getFnAttribute("xray-ignore-loops");
155c46e6a59SDimitry Andric
156e3b55780SDimitry Andric uint64_t XRayThreshold = 0;
157e3b55780SDimitry Andric if (!AlwaysInstrument) {
158b60736ecSDimitry Andric bool IgnoreLoops = IgnoreLoopsAttr.isValid();
159e3b55780SDimitry Andric XRayThreshold = F.getFnAttributeAsParsedInteger(
160e3b55780SDimitry Andric "xray-instruction-threshold", std::numeric_limits<uint64_t>::max());
161e3b55780SDimitry Andric if (XRayThreshold == std::numeric_limits<uint64_t>::max())
162e3b55780SDimitry Andric return false;
163cfca06d7SDimitry Andric
1647ab83427SDimitry Andric // Count the number of MachineInstr`s in MachineFunction
165e3b55780SDimitry Andric uint64_t MICount = 0;
1667ab83427SDimitry Andric for (const auto &MBB : MF)
1677ab83427SDimitry Andric MICount += MBB.size();
1687ab83427SDimitry Andric
169cfca06d7SDimitry Andric bool TooFewInstrs = MICount < XRayThreshold;
170cfca06d7SDimitry Andric
171cfca06d7SDimitry Andric if (!IgnoreLoops) {
172eb11fae6SDimitry Andric // Get MachineDominatorTree or compute it on the fly if it's unavailable
173ac9a064cSDimitry Andric auto *MDTWrapper =
174ac9a064cSDimitry Andric getAnalysisIfAvailable<MachineDominatorTreeWrapperPass>();
175ac9a064cSDimitry Andric auto *MDT = MDTWrapper ? &MDTWrapper->getDomTree() : nullptr;
176eb11fae6SDimitry Andric MachineDominatorTree ComputedMDT;
177eb11fae6SDimitry Andric if (!MDT) {
178eb11fae6SDimitry Andric ComputedMDT.getBase().recalculate(MF);
179eb11fae6SDimitry Andric MDT = &ComputedMDT;
180eb11fae6SDimitry Andric }
181eb11fae6SDimitry Andric
182eb11fae6SDimitry Andric // Get MachineLoopInfo or compute it on the fly if it's unavailable
183ac9a064cSDimitry Andric auto *MLIWrapper = getAnalysisIfAvailable<MachineLoopInfoWrapperPass>();
184ac9a064cSDimitry Andric auto *MLI = MLIWrapper ? &MLIWrapper->getLI() : nullptr;
185eb11fae6SDimitry Andric MachineLoopInfo ComputedMLI;
186eb11fae6SDimitry Andric if (!MLI) {
187ac9a064cSDimitry Andric ComputedMLI.analyze(MDT->getBase());
188eb11fae6SDimitry Andric MLI = &ComputedMLI;
189eb11fae6SDimitry Andric }
190eb11fae6SDimitry Andric
191c46e6a59SDimitry Andric // Check if we have a loop.
192c46e6a59SDimitry Andric // FIXME: Maybe make this smarter, and see whether the loops are dependent
193c46e6a59SDimitry Andric // on inputs or side-effects?
194cfca06d7SDimitry Andric if (MLI->empty() && TooFewInstrs)
195c46e6a59SDimitry Andric return false; // Function is too small and has no loops.
196cfca06d7SDimitry Andric } else if (TooFewInstrs) {
197cfca06d7SDimitry Andric // Function is too small
198cfca06d7SDimitry Andric return false;
199cfca06d7SDimitry Andric }
20001095a5dSDimitry Andric }
20101095a5dSDimitry Andric
202b915e9e0SDimitry Andric // We look for the first non-empty MachineBasicBlock, so that we can insert
203b915e9e0SDimitry Andric // the function instrumentation in the appropriate place.
2047ab83427SDimitry Andric auto MBI = llvm::find_if(
2057ab83427SDimitry Andric MF, [&](const MachineBasicBlock &MBB) { return !MBB.empty(); });
206b915e9e0SDimitry Andric if (MBI == MF.end())
207b915e9e0SDimitry Andric return false; // The function is empty.
208b915e9e0SDimitry Andric
209b915e9e0SDimitry Andric auto *TII = MF.getSubtarget().getInstrInfo();
210b915e9e0SDimitry Andric auto &FirstMBB = *MBI;
211b915e9e0SDimitry Andric auto &FirstMI = *FirstMBB.begin();
212b915e9e0SDimitry Andric
213b915e9e0SDimitry Andric if (!MF.getSubtarget().isXRaySupported()) {
214b915e9e0SDimitry Andric FirstMI.emitError("An attempt to perform XRay instrumentation for an"
215b915e9e0SDimitry Andric " unsupported target.");
216b915e9e0SDimitry Andric return false;
217b915e9e0SDimitry Andric }
218b915e9e0SDimitry Andric
219cfca06d7SDimitry Andric if (!F.hasFnAttribute("xray-skip-entry")) {
22001095a5dSDimitry Andric // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
22101095a5dSDimitry Andric // MachineFunction.
22201095a5dSDimitry Andric BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(),
22301095a5dSDimitry Andric TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
224cfca06d7SDimitry Andric }
22501095a5dSDimitry Andric
226cfca06d7SDimitry Andric if (!F.hasFnAttribute("xray-skip-exit")) {
227b915e9e0SDimitry Andric switch (MF.getTarget().getTargetTriple().getArch()) {
228b915e9e0SDimitry Andric case Triple::ArchType::arm:
229b915e9e0SDimitry Andric case Triple::ArchType::thumb:
230b915e9e0SDimitry Andric case Triple::ArchType::aarch64:
23177fc4c14SDimitry Andric case Triple::ArchType::hexagon:
2327fa27ce4SDimitry Andric case Triple::ArchType::loongarch64:
23371d5a254SDimitry Andric case Triple::ArchType::mips:
23471d5a254SDimitry Andric case Triple::ArchType::mipsel:
23571d5a254SDimitry Andric case Triple::ArchType::mips64:
236044eb2f6SDimitry Andric case Triple::ArchType::mips64el: {
237b915e9e0SDimitry Andric // For the architectures which don't have a single return instruction
238044eb2f6SDimitry Andric InstrumentationOptions op;
239044eb2f6SDimitry Andric op.HandleTailcall = false;
240044eb2f6SDimitry Andric op.HandleAllReturns = true;
241044eb2f6SDimitry Andric prependRetWithPatchableExit(MF, TII, op);
242b915e9e0SDimitry Andric break;
243044eb2f6SDimitry Andric }
244044eb2f6SDimitry Andric case Triple::ArchType::ppc64le: {
245044eb2f6SDimitry Andric // PPC has conditional returns. Turn them into branch and plain returns.
246044eb2f6SDimitry Andric InstrumentationOptions op;
247044eb2f6SDimitry Andric op.HandleTailcall = false;
248044eb2f6SDimitry Andric op.HandleAllReturns = true;
249044eb2f6SDimitry Andric replaceRetWithPatchableRet(MF, TII, op);
250044eb2f6SDimitry Andric break;
251044eb2f6SDimitry Andric }
252044eb2f6SDimitry Andric default: {
253b915e9e0SDimitry Andric // For the architectures that have a single return instruction (such as
254b915e9e0SDimitry Andric // RETQ on x86_64).
255044eb2f6SDimitry Andric InstrumentationOptions op;
256044eb2f6SDimitry Andric op.HandleTailcall = true;
257044eb2f6SDimitry Andric op.HandleAllReturns = false;
258044eb2f6SDimitry Andric replaceRetWithPatchableRet(MF, TII, op);
25901095a5dSDimitry Andric break;
26001095a5dSDimitry Andric }
261044eb2f6SDimitry Andric }
262cfca06d7SDimitry Andric }
26301095a5dSDimitry Andric return true;
26401095a5dSDimitry Andric }
26501095a5dSDimitry Andric
26601095a5dSDimitry Andric char XRayInstrumentation::ID = 0;
26701095a5dSDimitry Andric char &llvm::XRayInstrumentationID = XRayInstrumentation::ID;
268c46e6a59SDimitry Andric INITIALIZE_PASS_BEGIN(XRayInstrumentation, "xray-instrumentation",
269c46e6a59SDimitry Andric "Insert XRay ops", false, false)
270ac9a064cSDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineLoopInfoWrapperPass)
271c46e6a59SDimitry Andric INITIALIZE_PASS_END(XRayInstrumentation, "xray-instrumentation",
272c46e6a59SDimitry Andric "Insert XRay ops", false, false)
273