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