@@ -34,7 +34,74 @@ struct XRayInstrumentation : public MachineFunctionPass {
34
34
}
35
35
36
36
bool runOnMachineFunction (MachineFunction &MF) override ;
37
+
38
+ private:
39
+ // Replace the original RET instruction with the exit sled code ("patchable
40
+ // ret" pseudo-instruction), so that at runtime XRay can replace the sled
41
+ // with a code jumping to XRay trampoline, which calls the tracing handler
42
+ // and, in the end, issues the RET instruction.
43
+ // This is the approach to go on CPUs which have a single RET instruction,
44
+ // like x86/x86_64.
45
+ void replaceRetWithPatchableRet (MachineFunction &MF,
46
+ const TargetInstrInfo *TII);
47
+ // Prepend the original return instruction with the exit sled code ("patchable
48
+ // function exit" pseudo-instruction), preserving the original return
49
+ // instruction just after the exit sled code.
50
+ // This is the approach to go on CPUs which have multiple options for the
51
+ // return instruction, like ARM. For such CPUs we can't just jump into the
52
+ // XRay trampoline and issue a single return instruction there. We rather
53
+ // have to call the trampoline and return from it to the original return
54
+ // instruction of the function being instrumented.
55
+ void prependRetWithPatchableExit (MachineFunction &MF,
56
+ const TargetInstrInfo *TII);
37
57
};
58
+ } // anonymous namespace
59
+
60
+ void XRayInstrumentation::replaceRetWithPatchableRet (MachineFunction &MF,
61
+ const TargetInstrInfo *TII)
62
+ {
63
+ // We look for *all* terminators and returns, then replace those with
64
+ // PATCHABLE_RET instructions.
65
+ SmallVector<MachineInstr *, 4 > Terminators;
66
+ for (auto &MBB : MF) {
67
+ for (auto &T : MBB.terminators ()) {
68
+ unsigned Opc = 0 ;
69
+ if (T.isReturn () && T.getOpcode () == TII->getReturnOpcode ()) {
70
+ // Replace return instructions with:
71
+ // PATCHABLE_RET <Opcode>, <Operand>...
72
+ Opc = TargetOpcode::PATCHABLE_RET;
73
+ }
74
+ if (TII->isTailCall (T)) {
75
+ // Treat the tail call as a return instruction, which has a
76
+ // different-looking sled than the normal return case.
77
+ Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
78
+ }
79
+ if (Opc != 0 ) {
80
+ auto MIB = BuildMI (MBB, T, T.getDebugLoc (), TII->get (Opc))
81
+ .addImm (T.getOpcode ());
82
+ for (auto &MO : T.operands ())
83
+ MIB.addOperand (MO);
84
+ Terminators.push_back (&T);
85
+ }
86
+ }
87
+ }
88
+
89
+ for (auto &I : Terminators)
90
+ I->eraseFromParent ();
91
+ }
92
+
93
+ void XRayInstrumentation::prependRetWithPatchableExit (MachineFunction &MF,
94
+ const TargetInstrInfo *TII)
95
+ {
96
+ for (auto &MBB : MF) {
97
+ for (auto &T : MBB.terminators ()) {
98
+ if (T.isReturn ()) {
99
+ // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT
100
+ auto MIB = BuildMI (MBB, T, T.getDebugLoc (),
101
+ TII->get (TargetOpcode::PATCHABLE_FUNCTION_EXIT));
102
+ }
103
+ }
104
+ }
38
105
}
39
106
40
107
bool XRayInstrumentation::runOnMachineFunction (MachineFunction &MF) {
@@ -54,6 +121,11 @@ bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
54
121
return false ; // Function is too small.
55
122
}
56
123
124
+ if (!MF.getSubtarget ().isXRaySupported ()) {
125
+ // FIXME: can this be reported somehow?
126
+ return false ;
127
+ }
128
+
57
129
// FIXME: Do the loop triviality analysis here or in an earlier pass.
58
130
59
131
// First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
@@ -64,35 +136,17 @@ bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
64
136
BuildMI (FirstMBB, FirstMI, FirstMI.getDebugLoc (),
65
137
TII->get (TargetOpcode::PATCHABLE_FUNCTION_ENTER));
66
138
67
- // Then we look for *all* terminators and returns, then replace those with
68
- // PATCHABLE_RET instructions.
69
- SmallVector<MachineInstr *, 4 > Terminators;
70
- for (auto &MBB : MF) {
71
- for (auto &T : MBB.terminators ()) {
72
- unsigned Opc = 0 ;
73
- if (T.isReturn () && T.getOpcode () == TII->getReturnOpcode ()) {
74
- // Replace return instructions with:
75
- // PATCHABLE_RET <Opcode>, <Operand>...
76
- Opc = TargetOpcode::PATCHABLE_RET;
77
- }
78
- if (TII->isTailCall (T)) {
79
- // Treat the tail call as a return instruction, which has a
80
- // different-looking sled than the normal return case.
81
- Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
82
- }
83
- if (Opc != 0 ) {
84
- auto MIB = BuildMI (MBB, T, T.getDebugLoc (), TII->get (Opc))
85
- .addImm (T.getOpcode ());
86
- for (auto &MO : T.operands ())
87
- MIB.addOperand (MO);
88
- Terminators.push_back (&T);
89
- }
90
- }
139
+ switch (MF.getTarget ().getTargetTriple ().getArch ()) {
140
+ case Triple::ArchType::arm:
141
+ // For the architectures which don't have a single return instruction
142
+ prependRetWithPatchableExit (MF, TII);
143
+ break ;
144
+ default :
145
+ // For the architectures that have a single return instruction (such as
146
+ // RETQ on x86_64).
147
+ replaceRetWithPatchableRet (MF, TII);
148
+ break ;
91
149
}
92
-
93
- for (auto &I : Terminators)
94
- I->eraseFromParent ();
95
-
96
150
return true ;
97
151
}
98
152
0 commit comments