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