1 | //===- LoongArchAsmPrinter.cpp - LoongArch LLVM Assembly Printer -*- C++ -*--=// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file contains a printer that converts from our internal representation |
10 | // of machine-dependent LLVM code to GAS-format LoongArch assembly language. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "LoongArchAsmPrinter.h" |
15 | #include "LoongArch.h" |
16 | #include "LoongArchMachineFunctionInfo.h" |
17 | #include "MCTargetDesc/LoongArchInstPrinter.h" |
18 | #include "MCTargetDesc/LoongArchMCTargetDesc.h" |
19 | #include "TargetInfo/LoongArchTargetInfo.h" |
20 | #include "llvm/CodeGen/AsmPrinter.h" |
21 | #include "llvm/CodeGen/MachineJumpTableInfo.h" |
22 | #include "llvm/CodeGen/MachineModuleInfoImpls.h" |
23 | #include "llvm/MC/MCAsmInfo.h" |
24 | #include "llvm/MC/MCContext.h" |
25 | #include "llvm/MC/MCInstBuilder.h" |
26 | #include "llvm/MC/MCSectionELF.h" |
27 | #include "llvm/MC/TargetRegistry.h" |
28 | #include "llvm/Support/Compiler.h" |
29 | |
30 | using namespace llvm; |
31 | |
32 | #define DEBUG_TYPE "loongarch-asm-printer" |
33 | |
34 | cl::opt<bool> LArchAnnotateTableJump( |
35 | "loongarch-annotate-tablejump" , cl::Hidden, |
36 | cl::desc( |
37 | "Annotate table jump instruction to correlate it with the jump table." ), |
38 | cl::init(Val: false)); |
39 | |
40 | // Simple pseudo-instructions have their lowering (with expansion to real |
41 | // instructions) auto-generated. |
42 | #include "LoongArchGenMCPseudoLowering.inc" |
43 | |
44 | void LoongArchAsmPrinter::emitInstruction(const MachineInstr *MI) { |
45 | LoongArch_MC::verifyInstructionPredicates( |
46 | Opcode: MI->getOpcode(), Features: getSubtargetInfo().getFeatureBits()); |
47 | |
48 | // Do any auto-generated pseudo lowerings. |
49 | if (MCInst OutInst; lowerPseudoInstExpansion(MI, Inst&: OutInst)) { |
50 | EmitToStreamer(S&: *OutStreamer, Inst: OutInst); |
51 | return; |
52 | } |
53 | |
54 | switch (MI->getOpcode()) { |
55 | case TargetOpcode::STATEPOINT: |
56 | LowerSTATEPOINT(MI: *MI); |
57 | return; |
58 | case TargetOpcode::PATCHABLE_FUNCTION_ENTER: |
59 | LowerPATCHABLE_FUNCTION_ENTER(MI: *MI); |
60 | return; |
61 | case TargetOpcode::PATCHABLE_FUNCTION_EXIT: |
62 | LowerPATCHABLE_FUNCTION_EXIT(MI: *MI); |
63 | return; |
64 | case TargetOpcode::PATCHABLE_TAIL_CALL: |
65 | LowerPATCHABLE_TAIL_CALL(MI: *MI); |
66 | return; |
67 | } |
68 | |
69 | MCInst TmpInst; |
70 | if (!lowerLoongArchMachineInstrToMCInst(MI, OutMI&: TmpInst, AP&: *this)) |
71 | EmitToStreamer(S&: *OutStreamer, Inst: TmpInst); |
72 | } |
73 | |
74 | bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, |
75 | const char *, |
76 | raw_ostream &OS) { |
77 | // First try the generic code, which knows about modifiers like 'c' and 'n'. |
78 | if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) |
79 | return false; |
80 | |
81 | const MachineOperand &MO = MI->getOperand(i: OpNo); |
82 | if (ExtraCode && ExtraCode[0]) { |
83 | if (ExtraCode[1] != 0) |
84 | return true; // Unknown modifier. |
85 | |
86 | switch (ExtraCode[0]) { |
87 | default: |
88 | return true; // Unknown modifier. |
89 | case 'z': // Print $zero register if zero, regular printing otherwise. |
90 | if (MO.isImm() && MO.getImm() == 0) { |
91 | OS << '$' << LoongArchInstPrinter::getRegisterName(Reg: LoongArch::R0); |
92 | return false; |
93 | } |
94 | break; |
95 | case 'u': // Print LASX registers. |
96 | case 'w': // Print LSX registers. |
97 | { |
98 | // If the operand is an LASX, LSX or floating point register, print the |
99 | // name of LASX or LSX register with the same index in that register |
100 | // class. |
101 | unsigned RegID = MO.getReg().id(), FirstReg; |
102 | if (RegID >= LoongArch::XR0 && RegID <= LoongArch::XR31) |
103 | FirstReg = LoongArch::XR0; |
104 | else if (RegID >= LoongArch::VR0 && RegID <= LoongArch::VR31) |
105 | FirstReg = LoongArch::VR0; |
106 | else if (RegID >= LoongArch::F0_64 && RegID <= LoongArch::F31_64) |
107 | FirstReg = LoongArch::F0_64; |
108 | else if (RegID >= LoongArch::F0 && RegID <= LoongArch::F31) |
109 | FirstReg = LoongArch::F0; |
110 | else |
111 | return true; |
112 | OS << '$' |
113 | << LoongArchInstPrinter::getRegisterName( |
114 | Reg: RegID - FirstReg + |
115 | (ExtraCode[0] == 'u' ? LoongArch::XR0 : LoongArch::VR0)); |
116 | return false; |
117 | } |
118 | // TODO: handle other extra codes if any. |
119 | } |
120 | } |
121 | |
122 | switch (MO.getType()) { |
123 | case MachineOperand::MO_Immediate: |
124 | OS << MO.getImm(); |
125 | return false; |
126 | case MachineOperand::MO_Register: |
127 | OS << '$' << LoongArchInstPrinter::getRegisterName(Reg: MO.getReg()); |
128 | return false; |
129 | case MachineOperand::MO_GlobalAddress: |
130 | PrintSymbolOperand(MO, OS); |
131 | return false; |
132 | default: |
133 | llvm_unreachable("not implemented" ); |
134 | } |
135 | |
136 | return true; |
137 | } |
138 | |
139 | bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, |
140 | unsigned OpNo, |
141 | const char *, |
142 | raw_ostream &OS) { |
143 | // TODO: handle extra code. |
144 | if (ExtraCode) |
145 | return true; |
146 | |
147 | // We only support memory operands like "Base + Offset", where base must be a |
148 | // register, and offset can be a register or an immediate value. |
149 | const MachineOperand &BaseMO = MI->getOperand(i: OpNo); |
150 | // Base address must be a register. |
151 | if (!BaseMO.isReg()) |
152 | return true; |
153 | // Print the base address register. |
154 | OS << "$" << LoongArchInstPrinter::getRegisterName(Reg: BaseMO.getReg()); |
155 | // Print the offset operand. |
156 | const MachineOperand &OffsetMO = MI->getOperand(i: OpNo + 1); |
157 | MCOperand MCO; |
158 | if (!lowerOperand(MO: OffsetMO, MCOp&: MCO)) |
159 | return true; |
160 | if (OffsetMO.isReg()) |
161 | OS << ", $" << LoongArchInstPrinter::getRegisterName(Reg: OffsetMO.getReg()); |
162 | else if (OffsetMO.isImm()) |
163 | OS << ", " << OffsetMO.getImm(); |
164 | else if (OffsetMO.isGlobal() || OffsetMO.isBlockAddress() || |
165 | OffsetMO.isMCSymbol()) { |
166 | OS << ", " ; |
167 | MAI->printExpr(OS, *MCO.getExpr()); |
168 | } else |
169 | return true; |
170 | |
171 | return false; |
172 | } |
173 | |
174 | void LoongArchAsmPrinter::LowerSTATEPOINT(const MachineInstr &MI) { |
175 | StatepointOpers SOpers(&MI); |
176 | if (unsigned PatchBytes = SOpers.getNumPatchBytes()) { |
177 | assert(PatchBytes % 4 == 0 && "Invalid number of NOP bytes requested!" ); |
178 | emitNops(N: PatchBytes / 4); |
179 | } else { |
180 | // Lower call target and choose correct opcode. |
181 | const MachineOperand &CallTarget = SOpers.getCallTarget(); |
182 | MCOperand CallTargetMCOp; |
183 | switch (CallTarget.getType()) { |
184 | case MachineOperand::MO_GlobalAddress: |
185 | case MachineOperand::MO_ExternalSymbol: |
186 | lowerOperand(MO: CallTarget, MCOp&: CallTargetMCOp); |
187 | EmitToStreamer(S&: *OutStreamer, |
188 | Inst: MCInstBuilder(LoongArch::BL).addOperand(Op: CallTargetMCOp)); |
189 | break; |
190 | case MachineOperand::MO_Immediate: |
191 | CallTargetMCOp = MCOperand::createImm(Val: CallTarget.getImm()); |
192 | EmitToStreamer(S&: *OutStreamer, |
193 | Inst: MCInstBuilder(LoongArch::BL).addOperand(Op: CallTargetMCOp)); |
194 | break; |
195 | case MachineOperand::MO_Register: |
196 | CallTargetMCOp = MCOperand::createReg(Reg: CallTarget.getReg()); |
197 | EmitToStreamer(S&: *OutStreamer, Inst: MCInstBuilder(LoongArch::JIRL) |
198 | .addReg(Reg: LoongArch::R1) |
199 | .addOperand(Op: CallTargetMCOp) |
200 | .addImm(Val: 0)); |
201 | break; |
202 | default: |
203 | llvm_unreachable("Unsupported operand type in statepoint call target" ); |
204 | break; |
205 | } |
206 | } |
207 | |
208 | auto &Ctx = OutStreamer->getContext(); |
209 | MCSymbol *MILabel = Ctx.createTempSymbol(); |
210 | OutStreamer->emitLabel(Symbol: MILabel); |
211 | SM.recordStatepoint(L: *MILabel, MI); |
212 | } |
213 | |
214 | void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER( |
215 | const MachineInstr &MI) { |
216 | const Function &F = MF->getFunction(); |
217 | if (F.hasFnAttribute(Kind: "patchable-function-entry" )) { |
218 | unsigned Num; |
219 | if (F.getFnAttribute(Kind: "patchable-function-entry" ) |
220 | .getValueAsString() |
221 | .getAsInteger(Radix: 10, Result&: Num)) |
222 | return; |
223 | emitNops(N: Num); |
224 | return; |
225 | } |
226 | |
227 | emitSled(MI, Kind: SledKind::FUNCTION_ENTER); |
228 | } |
229 | |
230 | void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) { |
231 | emitSled(MI, Kind: SledKind::FUNCTION_EXIT); |
232 | } |
233 | |
234 | void LoongArchAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) { |
235 | emitSled(MI, Kind: SledKind::TAIL_CALL); |
236 | } |
237 | |
238 | void LoongArchAsmPrinter::emitSled(const MachineInstr &MI, SledKind Kind) { |
239 | // For loongarch64 we want to emit the following pattern: |
240 | // |
241 | // .Lxray_sled_beginN: |
242 | // B .Lxray_sled_endN |
243 | // 11 NOPs (44 bytes) |
244 | // .Lxray_sled_endN: |
245 | // |
246 | // We need the extra bytes because at runtime they may be used for the |
247 | // actual pattern defined at compiler-rt/lib/xray/xray_loongarch64.cpp. |
248 | // The count here should be adjusted accordingly if the implementation |
249 | // changes. |
250 | const int8_t NoopsInSledCount = 11; |
251 | OutStreamer->emitCodeAlignment(Alignment: Align(4), STI: &getSubtargetInfo()); |
252 | MCSymbol *BeginOfSled = OutContext.createTempSymbol(Name: "xray_sled_begin" ); |
253 | MCSymbol *EndOfSled = OutContext.createTempSymbol(Name: "xray_sled_end" ); |
254 | OutStreamer->emitLabel(Symbol: BeginOfSled); |
255 | EmitToStreamer(S&: *OutStreamer, |
256 | Inst: MCInstBuilder(LoongArch::B) |
257 | .addExpr(Val: MCSymbolRefExpr::create(Symbol: EndOfSled, Ctx&: OutContext))); |
258 | emitNops(N: NoopsInSledCount); |
259 | OutStreamer->emitLabel(Symbol: EndOfSled); |
260 | recordSled(Sled: BeginOfSled, MI, Kind, Version: 2); |
261 | } |
262 | |
263 | void LoongArchAsmPrinter::emitJumpTableInfo() { |
264 | AsmPrinter::emitJumpTableInfo(); |
265 | |
266 | if (!LArchAnnotateTableJump) |
267 | return; |
268 | |
269 | assert(TM.getTargetTriple().isOSBinFormatELF()); |
270 | |
271 | auto *LAFI = MF->getInfo<LoongArchMachineFunctionInfo>(); |
272 | unsigned EntrySize = LAFI->getJumpInfoSize(); |
273 | auto JTI = MF->getJumpTableInfo(); |
274 | |
275 | if (!JTI || 0 == EntrySize) |
276 | return; |
277 | |
278 | unsigned Size = getDataLayout().getPointerSize(); |
279 | auto JT = JTI->getJumpTables(); |
280 | |
281 | // Emit an additional section to store the correlation info as pairs of |
282 | // addresses, each pair contains the address of a jump instruction (jr) and |
283 | // the address of the jump table. |
284 | OutStreamer->switchSection(Section: MMI->getContext().getELFSection( |
285 | Section: ".discard.tablejump_annotate" , Type: ELF::SHT_PROGBITS, Flags: 0)); |
286 | |
287 | for (unsigned Idx = 0; Idx < EntrySize; ++Idx) { |
288 | int JTIIdx = LAFI->getJumpInfoJTIIndex(Idx); |
289 | if (JT[JTIIdx].MBBs.empty()) |
290 | continue; |
291 | OutStreamer->emitValue( |
292 | Value: MCSymbolRefExpr::create(Symbol: LAFI->getJumpInfoJrMI(Idx)->getPreInstrSymbol(), |
293 | Ctx&: OutContext), |
294 | Size); |
295 | OutStreamer->emitValue( |
296 | Value: MCSymbolRefExpr::create(Symbol: GetJTISymbol(JTID: JTIIdx), Ctx&: OutContext), Size); |
297 | } |
298 | } |
299 | |
300 | bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) { |
301 | AsmPrinter::runOnMachineFunction(MF); |
302 | // Emit the XRay table for this function. |
303 | emitXRayTable(); |
304 | return true; |
305 | } |
306 | |
307 | char LoongArchAsmPrinter::ID = 0; |
308 | |
309 | INITIALIZE_PASS(LoongArchAsmPrinter, "loongarch-asm-printer" , |
310 | "LoongArch Assembly Printer" , false, false) |
311 | |
312 | // Force static initialization. |
313 | extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void |
314 | LLVMInitializeLoongArchAsmPrinter() { |
315 | RegisterAsmPrinter<LoongArchAsmPrinter> X(getTheLoongArch32Target()); |
316 | RegisterAsmPrinter<LoongArchAsmPrinter> Y(getTheLoongArch64Target()); |
317 | } |
318 | |