llvm.org GIT mirror llvm / 339ade7
[XRay] ARM 32-bit no-Thumb support in LLVM This is a port of XRay to ARM 32-bit, without Thumb support yet. The XRay instrumentation support is moving up to AsmPrinter. This is one of 3 commits to different repositories of XRay ARM port. The other 2 are: 1. https://reviews.llvm.org/D23932 (Clang test) 2. https://reviews.llvm.org/D23933 (compiler-rt) Differential Revision: https://reviews.llvm.org/D23931 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280888 91177308-0d34-0410-b5e6-96231b3b80d8 Dean Michael Berris 4 years ago
17 changed file(s) with 318 addition(s) and 63 deletion(s). Raw diff Collapse all Expand all
187187 MCSymbol *getSymbol(const GlobalValue *GV) const;
188188
189189 //===------------------------------------------------------------------===//
190 // XRay instrumentation implementation.
191 //===------------------------------------------------------------------===//
192 public:
193 // This describes the kind of sled we're storing in the XRay table.
194 enum class SledKind : uint8_t {
195 FUNCTION_ENTER = 0,
196 FUNCTION_EXIT = 1,
197 TAIL_CALL = 2,
198 };
199
200 // The table will contain these structs that point to the sled, the function
201 // containing the sled, and what kind of sled (and whether they should always
202 // be instrumented).
203 struct XRayFunctionEntry {
204 const MCSymbol *Sled;
205 const MCSymbol *Function;
206 SledKind Kind;
207 bool AlwaysInstrument;
208 const class Function *Fn;
209 };
210
211 // All the sleds to be emitted.
212 std::vector Sleds;
213
214 // Helper function to record a given XRay sled.
215 void recordSled(MCSymbol *Sled, const MachineInstr &MI, SledKind Kind);
216
217 //===------------------------------------------------------------------===//
190218 // MachineFunctionPass Implementation.
191219 //===------------------------------------------------------------------===//
192220
955955 def PATCHABLE_RET : Instruction {
956956 let OutOperandList = (outs unknown:$dst);
957957 let InOperandList = (ins variable_ops);
958 let AsmString = "# XRay Function Exit.";
958 let AsmString = "# XRay Function Patchable RET.";
959959 let usesCustomInserter = 1;
960960 let hasSideEffects = 1;
961961 let isReturn = 1;
962 }
963 def PATCHABLE_FUNCTION_EXIT : Instruction {
964 let OutOperandList = (outs);
965 let InOperandList = (ins);
966 let AsmString = "# XRay Function Exit.";
967 let usesCustomInserter = 1;
968 let hasSideEffects = 0; // FIXME: is this correct?
969 let isReturn = 0; // Original return instruction will follow
962970 }
963971 def PATCHABLE_TAIL_CALL : Instruction {
964972 let OutOperandList = (outs unknown:$dst);
152152 /// Wraps a return instruction and its operands to enable adding nop sleds
153153 /// either before or after the return. The nop sleds are useful for inserting
154154 /// instrumentation instructions at runtime.
155 /// The patch here replaces the return instruction.
155156 HANDLE_TARGET_OPCODE(PATCHABLE_RET)
157
158 /// This is a marker instruction which gets translated into a nop sled, useful
159 /// for inserting instrumentation instructions at runtime.
160 /// The patch here prepends the return instruction.
161 /// The same thing as in x86_64 is not possible for ARM because it has multiple
162 /// return instructions. Furthermore, CPU allows parametrized and even
163 /// conditional return instructions. In the current ARM implementation we are
164 /// making use of the fact that currently LLVM doesn't seem to generate
165 /// conditional return instructions.
166 /// On ARM, the same instruction can be used for popping multiple registers
167 /// from the stack and returning (it just pops pc register too), and LLVM
168 /// generates it sometimes. So we can't insert the sled between this stack
169 /// adjustment and the return without splitting the original instruction into 2
170 /// instructions. So on ARM, rather than jumping into the exit trampoline, we
171 /// call it, it does the tracing, preserves the stack and returns.
172 HANDLE_TARGET_OPCODE(PATCHABLE_FUNCTION_EXIT)
156173
157174 /// Wraps a tail call instruction and its operands to enable adding nop sleds
158175 /// either before or after the tail exit. We use this as a disambiguation from
7070
7171 virtual ~TargetSubtargetInfo();
7272
73 virtual bool isXRaySupported() const { return false; }
74
7375 // Interfaces to the major aspects of target machine information:
7476 //
7577 // -- Instruction opcode and operand information
26052605 AsmPrinterHandler::~AsmPrinterHandler() {}
26062606
26072607 void AsmPrinterHandler::markFunctionEnd() {}
2608
2609 void AsmPrinter::recordSled(MCSymbol *Sled, const MachineInstr &MI,
2610 SledKind Kind) {
2611 auto Fn = MI.getParent()->getParent()->getFunction();
2612 auto Attr = Fn->getFnAttribute("function-instrument");
2613 bool AlwaysInstrument =
2614 Attr.isStringAttribute() && Attr.getValueAsString() == "xray-always";
2615 Sleds.emplace_back(
2616 XRayFunctionEntry{ Sled, CurrentFnSym, Kind, AlwaysInstrument, Fn });
2617 }
3333 }
3434
3535 bool runOnMachineFunction(MachineFunction &MF) override;
36
37 private:
38 // Replace the original RET instruction with the exit sled code ("patchable
39 // ret" pseudo-instruction), so that at runtime XRay can replace the sled
40 // with a code jumping to XRay trampoline, which calls the tracing handler
41 // and, in the end, issues the RET instruction.
42 // This is the approach to go on CPUs which have a single RET instruction,
43 // like x86/x86_64.
44 void replaceRetWithPatchableRet(MachineFunction &MF,
45 const TargetInstrInfo *TII);
46 // Prepend the original return instruction with the exit sled code ("patchable
47 // function exit" pseudo-instruction), preserving the original return
48 // instruction just after the exit sled code.
49 // This is the approach to go on CPUs which have multiple options for the
50 // return instruction, like ARM. For such CPUs we can't just jump into the
51 // XRay trampoline and issue a single return instruction there. We rather
52 // have to call the trampoline and return from it to the original return
53 // instruction of the function being instrumented.
54 void prependRetWithPatchableExit(MachineFunction &MF,
55 const TargetInstrInfo *TII);
3656 };
37 }
57 } // anonymous namespace
3858
39 bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
40 auto &F = *MF.getFunction();
41 auto InstrAttr = F.getFnAttribute("function-instrument");
42 bool AlwaysInstrument = !InstrAttr.hasAttribute(Attribute::None) &&
43 InstrAttr.isStringAttribute() &&
44 InstrAttr.getValueAsString() == "xray-always";
45 Attribute Attr = F.getFnAttribute("xray-instruction-threshold");
46 unsigned XRayThreshold = 0;
47 if (!AlwaysInstrument) {
48 if (Attr.hasAttribute(Attribute::None) || !Attr.isStringAttribute())
49 return false; // XRay threshold attribute not found.
50 if (Attr.getValueAsString().getAsInteger(10, XRayThreshold))
51 return false; // Invalid value for threshold.
52 if (F.size() < XRayThreshold)
53 return false; // Function is too small.
54 }
55
56 // FIXME: Do the loop triviality analysis here or in an earlier pass.
57
58 // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
59 // MachineFunction.
60 auto &FirstMBB = *MF.begin();
61 auto &FirstMI = *FirstMBB.begin();
62 auto *TII = MF.getSubtarget().getInstrInfo();
63 BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(),
64 TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
65
66 // Then we look for *all* terminators and returns, then replace those with
59 void XRayInstrumentation::replaceRetWithPatchableRet(MachineFunction &MF,
60 const TargetInstrInfo *TII)
61 {
62 // We look for *all* terminators and returns, then replace those with
6763 // PATCHABLE_RET instructions.
6864 SmallVector Terminators;
6965 for (auto &MBB : MF) {
9187
9288 for (auto &I : Terminators)
9389 I->eraseFromParent();
90 }
9491
92 void XRayInstrumentation::prependRetWithPatchableExit(MachineFunction &MF,
93 const TargetInstrInfo *TII)
94 {
95 for (auto &MBB : MF) {
96 for (auto &T : MBB.terminators()) {
97 if (T.isReturn()) {
98 // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT
99 auto MIB = BuildMI(MBB, T, T.getDebugLoc(),
100 TII->get(TargetOpcode::PATCHABLE_FUNCTION_EXIT));
101 }
102 }
103 }
104 }
105
106 bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) {
107 auto &F = *MF.getFunction();
108 auto InstrAttr = F.getFnAttribute("function-instrument");
109 bool AlwaysInstrument = !InstrAttr.hasAttribute(Attribute::None) &&
110 InstrAttr.isStringAttribute() &&
111 InstrAttr.getValueAsString() == "xray-always";
112 Attribute Attr = F.getFnAttribute("xray-instruction-threshold");
113 unsigned XRayThreshold = 0;
114 if (!AlwaysInstrument) {
115 if (Attr.hasAttribute(Attribute::None) || !Attr.isStringAttribute())
116 return false; // XRay threshold attribute not found.
117 if (Attr.getValueAsString().getAsInteger(10, XRayThreshold))
118 return false; // Invalid value for threshold.
119 if (F.size() < XRayThreshold)
120 return false; // Function is too small.
121 }
122
123 if (!MF.getSubtarget().isXRaySupported()) {
124 //FIXME: can this be reported somehow?
125 return false;
126 }
127
128 // FIXME: Do the loop triviality analysis here or in an earlier pass.
129
130 // First, insert an PATCHABLE_FUNCTION_ENTER as the first instruction of the
131 // MachineFunction.
132 auto &FirstMBB = *MF.begin();
133 auto &FirstMI = *FirstMBB.begin();
134 auto *TII = MF.getSubtarget().getInstrInfo();
135 BuildMI(FirstMBB, FirstMI, FirstMI.getDebugLoc(),
136 TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
137
138 switch (MF.getTarget().getTargetTriple().getArch()) {
139 case Triple::ArchType::arm:
140 // For the architectures which don't have a single return instruction
141 prependRetWithPatchableExit(MF, TII);
142 break;
143 default:
144 // For the architectures that have a single return instruction (such as
145 // RETQ on x86_64).
146 replaceRetWithPatchableRet(MF, TII);
147 break;
148 }
95149 return true;
96150 }
97151
148148
149149 // Emit the rest of the function body.
150150 EmitFunctionBody();
151
152 // Emit the XRay table for this function.
153 EmitXRayTable();
151154
152155 // If we need V4T thumb mode Register Indirect Jump pads, emit them.
153156 // These are created per function, rather than per TU, since it's
20042007 .addReg(0));
20052008 return;
20062009 }
2010 case ARM::PATCHABLE_FUNCTION_ENTER:
2011 LowerPATCHABLE_FUNCTION_ENTER(*MI);
2012 return;
2013 case ARM::PATCHABLE_FUNCTION_EXIT:
2014 LowerPATCHABLE_FUNCTION_EXIT(*MI);
2015 return;
20072016 }
20082017
20092018 MCInst TmpInst;
9393 // lowerOperand - Convert a MachineOperand into the equivalent MCOperand.
9494 bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp);
9595
96 //===------------------------------------------------------------------===//
97 // XRay implementation
98 //===------------------------------------------------------------------===//
99 public:
100 // XRay-specific lowering for ARM.
101 void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI);
102 void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI);
103 // Helper function that emits the XRay sleds we've collected for a particular
104 // function.
105 void EmitXRayTable();
106
96107 private:
108 void EmitSled(const MachineInstr &MI, SledKind Kind);
97109
98110 // Helpers for EmitStartOfAsmFile() and EmitEndOfAsmFile()
99111 void emitAttributes();
9898 public:
9999 // Return whether the target has an explicit NOP encoding.
100100 bool hasNOP() const;
101
102 virtual void getNoopForElfTarget(MCInst &NopInst) const {
103 getNoopForMachoTarget(NopInst);
104 }
101105
102106 // Return the non-pre/post incrementing version of 'Opc'. Return 0
103107 // if there is not such an opcode.
2020 #include "llvm/IR/Mangler.h"
2121 #include "llvm/MC/MCExpr.h"
2222 #include "llvm/MC/MCInst.h"
23 #include "llvm/MC/MCContext.h"
24 #include "llvm/MC/MCSymbolELF.h"
25 #include "llvm/MC/MCSectionELF.h"
26 #include "llvm/MC/MCInstBuilder.h"
27 #include "llvm/MC/MCStreamer.h"
2328 using namespace llvm;
2429
2530
149154 }
150155 }
151156 }
157
158 void ARMAsmPrinter::EmitSled(const MachineInstr &MI, SledKind Kind)
159 {
160 static const int8_t NoopsInSledCount = 6;
161 // We want to emit the following pattern:
162 //
163 // .Lxray_sled_N:
164 // ALIGN
165 // B #20
166 // ; 6 NOP instructions (24 bytes)
167 // .tmpN
168 //
169 // We need the 24 bytes (6 instructions) because at runtime, we'd be patching
170 // over the full 28 bytes (7 instructions) with the following pattern:
171 //
172 // PUSH{ r0, lr }
173 // MOVW r0, #
174 // MOVT r0, #
175 // MOVW ip, #
176 // MOVT ip, #
177 // BLX ip
178 // POP{ r0, lr }
179 //
180 OutStreamer->EmitCodeAlignment(4);
181 auto CurSled = OutContext.createTempSymbol("xray_sled_", true);
182 OutStreamer->EmitLabel(CurSled);
183 auto Target = OutContext.createTempSymbol();
184
185 // Emit "B #20" instruction, which jumps over the next 24 bytes (because
186 // register pc is 8 bytes ahead of the jump instruction by the moment CPU
187 // is executing it).
188 // By analogy to ARMAsmPrinter::emitPseudoExpansionLowering() |case ARM::B|.
189 // It is not clear why |addReg(0)| is needed (the last operand).
190 EmitToStreamer(*OutStreamer, MCInstBuilder(ARM::Bcc).addImm(20)
191 .addImm(ARMCC::AL).addReg(0));
192
193 MCInst Noop;
194 Subtarget->getInstrInfo()->getNoopForElfTarget(Noop);
195 for (int8_t I = 0; I < NoopsInSledCount; I++)
196 {
197 OutStreamer->EmitInstruction(Noop, getSubtargetInfo());
198 }
199
200 OutStreamer->EmitLabel(Target);
201 recordSled(CurSled, MI, Kind);
202 }
203
204 void ARMAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI)
205 {
206 EmitSled(MI, SledKind::FUNCTION_ENTER);
207 }
208
209 void ARMAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI)
210 {
211 EmitSled(MI, SledKind::FUNCTION_EXIT);
212 }
213
214 void ARMAsmPrinter::EmitXRayTable()
215 {
216 if (Sleds.empty())
217 return;
218 if (Subtarget->isTargetELF()) {
219 auto *Section = OutContext.getELFSection(
220 "xray_instr_map", ELF::SHT_PROGBITS,
221 ELF::SHF_ALLOC | ELF::SHF_GROUP | ELF::SHF_MERGE, 0,
222 CurrentFnSym->getName());
223 auto PrevSection = OutStreamer->getCurrentSectionOnly();
224 OutStreamer->SwitchSection(Section);
225 for (const auto &Sled : Sleds) {
226 OutStreamer->EmitSymbolValue(Sled.Sled, 4);
227 OutStreamer->EmitSymbolValue(CurrentFnSym, 4);
228 auto Kind = static_cast(Sled.Kind);
229 OutStreamer->EmitBytes(
230 StringRef(reinterpret_cast(&Kind), 1));
231 OutStreamer->EmitBytes(
232 StringRef(reinterpret_cast(&Sled.AlwaysInstrument), 1));
233 OutStreamer->EmitZeros(6);
234 }
235 OutStreamer->SwitchSection(PrevSection);
236 }
237 Sleds.clear();
238 }
100100 : (ARMBaseInstrInfo *)new Thumb2InstrInfo(*this)),
101101 TLInfo(TM, *this) {}
102102
103 bool ARMSubtarget::isXRaySupported() const {
104 // We don't currently suppport Thumb, but Windows requires Thumb.
105 return hasV6Ops() && !isTargetWindows();
106 }
107
103108 void ARMSubtarget::initializeEnvironment() {
104109 // MCAsmInfo isn't always present (e.g. in opt) so we can't initialize this
105110 // directly from it, but we can try to make sure they're consistent when both
539539 }
540540 bool isTargetAndroid() const { return TargetTriple.isAndroid(); }
541541
542 virtual bool isXRaySupported() const override;
543
542544 bool isAPCS_ABI() const;
543545 bool isAAPCS_ABI() const;
544546 bool isAAPCS16_ABI() const;
7070
7171 StackMapShadowTracker SMShadowTracker;
7272
73 // This describes the kind of sled we're storing in the XRay table.
74 enum class SledKind : uint8_t {
75 FUNCTION_ENTER = 0,
76 FUNCTION_EXIT = 1,
77 TAIL_CALL = 2,
78 };
79
80 // The table will contain these structs that point to the sled, the function
81 // containing the sled, and what kind of sled (and whether they should always
82 // be instrumented).
83 struct XRayFunctionEntry {
84 const MCSymbol *Sled;
85 const MCSymbol *Function;
86 SledKind Kind;
87 bool AlwaysInstrument;
88 const class Function *Fn;
89 };
90
91 // All the sleds to be emitted.
92 std::vector Sleds;
93
9473 // All instructions emitted by the X86AsmPrinter should use this helper
9574 // method.
9675 //
11695 // function.
11796 void EmitXRayTable();
11897
119 // Helper function to record a given XRay sled.
120 void recordSled(MCSymbol *Sled, const MachineInstr &MI, SledKind Kind);
12198 public:
12299 explicit X86AsmPrinter(TargetMachine &TM,
123100 std::unique_ptr Streamer)
10191019
10201020 EmitNops(*OutStreamer, NumBytes - EncodedBytes, Subtarget->is64Bit(),
10211021 getSubtargetInfo());
1022 }
1023
1024 void X86AsmPrinter::recordSled(MCSymbol *Sled, const MachineInstr &MI,
1025 SledKind Kind) {
1026 auto Fn = MI.getParent()->getParent()->getFunction();
1027 auto Attr = Fn->getFnAttribute("function-instrument");
1028 bool AlwaysInstrument =
1029 Attr.isStringAttribute() && Attr.getValueAsString() == "xray-always";
1030 Sleds.emplace_back(
1031 XRayFunctionEntry{Sled, CurrentFnSym, Kind, AlwaysInstrument, Fn});
10321022 }
10331023
10341024 void X86AsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI,
459459 bool hasPKU() const { return HasPKU; }
460460 bool hasMPX() const { return HasMPX; }
461461
462 virtual bool isXRaySupported() const override { return is64Bit(); }
463
462464 bool isAtom() const { return X86ProcFamily == IntelAtom; }
463465 bool isSLM() const { return X86ProcFamily == IntelSLM; }
464466 bool useSoftFloat() const { return UseSoftFloat; }
0 ; RUN: llc -filetype=asm -o - -mtriple=armv6-unknown-linux-gnu < %s | FileCheck %s
1
2 define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" {
3 ; CHECK-LABEL: Lxray_sled_0:
4 ; CHECK-NEXT: b #20
5 ; CHECK-NEXT: mov r0, r0
6 ; CHECK-NEXT: mov r0, r0
7 ; CHECK-NEXT: mov r0, r0
8 ; CHECK-NEXT: mov r0, r0
9 ; CHECK-NEXT: mov r0, r0
10 ; CHECK-NEXT: mov r0, r0
11 ; CHECK-LABEL: Ltmp0:
12 ret i32 0
13 ; CHECK-LABEL: Lxray_sled_1:
14 ; CHECK-NEXT: b #20
15 ; CHECK-NEXT: mov r0, r0
16 ; CHECK-NEXT: mov r0, r0
17 ; CHECK-NEXT: mov r0, r0
18 ; CHECK-NEXT: mov r0, r0
19 ; CHECK-NEXT: mov r0, r0
20 ; CHECK-NEXT: mov r0, r0
21 ; CHECK-LABEL: Ltmp1:
22 ; CHECK-NEXT: bx lr
23 }
0 ; RUN: llc -filetype=asm -o - -mtriple=armv7-unknown-linux-gnu < %s | FileCheck %s
1
2 define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" {
3 ; CHECK-LABEL: Lxray_sled_0:
4 ; CHECK-NEXT: b #20
5 ; CHECK-NEXT: nop
6 ; CHECK-NEXT: nop
7 ; CHECK-NEXT: nop
8 ; CHECK-NEXT: nop
9 ; CHECK-NEXT: nop
10 ; CHECK-NEXT: nop
11 ; CHECK-LABEL: Ltmp0:
12 ret i32 0
13 ; CHECK-LABEL: Lxray_sled_1:
14 ; CHECK-NEXT: b #20
15 ; CHECK-NEXT: nop
16 ; CHECK-NEXT: nop
17 ; CHECK-NEXT: nop
18 ; CHECK-NEXT: nop
19 ; CHECK-NEXT: nop
20 ; CHECK-NEXT: nop
21 ; CHECK-LABEL: Ltmp1:
22 ; CHECK-NEXT: bx lr
23 }