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