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
30using namespace llvm;
31
32#define DEBUG_TYPE "loongarch-asm-printer"
33
34cl::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
44void 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
74bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
75 const char *ExtraCode,
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
139bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
140 unsigned OpNo,
141 const char *ExtraCode,
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
174void 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
214void 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
230void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) {
231 emitSled(MI, Kind: SledKind::FUNCTION_EXIT);
232}
233
234void LoongArchAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) {
235 emitSled(MI, Kind: SledKind::TAIL_CALL);
236}
237
238void 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
263void 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
300bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
301 AsmPrinter::runOnMachineFunction(MF);
302 // Emit the XRay table for this function.
303 emitXRayTable();
304 return true;
305}
306
307char LoongArchAsmPrinter::ID = 0;
308
309INITIALIZE_PASS(LoongArchAsmPrinter, "loongarch-asm-printer",
310 "LoongArch Assembly Printer", false, false)
311
312// Force static initialization.
313extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
314LLVMInitializeLoongArchAsmPrinter() {
315 RegisterAsmPrinter<LoongArchAsmPrinter> X(getTheLoongArch32Target());
316 RegisterAsmPrinter<LoongArchAsmPrinter> Y(getTheLoongArch64Target());
317}
318