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 "LoongArchTargetMachine.h"
17 #include "MCTargetDesc/LoongArchInstPrinter.h"
18 #include "TargetInfo/LoongArchTargetInfo.h"
19 #include "llvm/CodeGen/AsmPrinter.h"
20 #include "llvm/MC/MCContext.h"
21 #include "llvm/MC/MCInstBuilder.h"
22 #include "llvm/MC/TargetRegistry.h"
23 
24 using namespace llvm;
25 
26 #define DEBUG_TYPE "loongarch-asm-printer"
27 
28 // Simple pseudo-instructions have their lowering (with expansion to real
29 // instructions) auto-generated.
30 #include "LoongArchGenMCPseudoLowering.inc"
31 
32 void LoongArchAsmPrinter::emitInstruction(const MachineInstr *MI) {
33   LoongArch_MC::verifyInstructionPredicates(
34       MI->getOpcode(), getSubtargetInfo().getFeatureBits());
35 
36   // Do any auto-generated pseudo lowerings.
37   if (emitPseudoExpansionLowering(*OutStreamer, MI))
38     return;
39 
40   switch (MI->getOpcode()) {
41   case TargetOpcode::PATCHABLE_FUNCTION_ENTER:
42     LowerPATCHABLE_FUNCTION_ENTER(*MI);
43     return;
44   case TargetOpcode::PATCHABLE_FUNCTION_EXIT:
45     LowerPATCHABLE_FUNCTION_EXIT(*MI);
46     return;
47   case TargetOpcode::PATCHABLE_TAIL_CALL:
48     LowerPATCHABLE_TAIL_CALL(*MI);
49     return;
50   }
51 
52   MCInst TmpInst;
53   if (!lowerLoongArchMachineInstrToMCInst(MI, TmpInst, *this))
54     EmitToStreamer(*OutStreamer, TmpInst);
55 }
56 
57 bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
58                                           const char *ExtraCode,
59                                           raw_ostream &OS) {
60   // First try the generic code, which knows about modifiers like 'c' and 'n'.
61   if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS))
62     return false;
63 
64   const MachineOperand &MO = MI->getOperand(OpNo);
65   if (ExtraCode && ExtraCode[0]) {
66     if (ExtraCode[1] != 0)
67       return true; // Unknown modifier.
68 
69     switch (ExtraCode[0]) {
70     default:
71       return true; // Unknown modifier.
72     case 'z':      // Print $zero register if zero, regular printing otherwise.
73       if (MO.isImm() && MO.getImm() == 0) {
74         OS << '$' << LoongArchInstPrinter::getRegisterName(LoongArch::R0);
75         return false;
76       }
77       break;
78     case 'w': // Print LSX registers.
79       if (MO.getReg().id() >= LoongArch::VR0 &&
80           MO.getReg().id() <= LoongArch::VR31)
81         break;
82       // The modifier is 'w' but the operand is not an LSX register; Report an
83       // unknown operand error.
84       return true;
85     case 'u': // Print LASX registers.
86       if (MO.getReg().id() >= LoongArch::XR0 &&
87           MO.getReg().id() <= LoongArch::XR31)
88         break;
89       // The modifier is 'u' but the operand is not an LASX register; Report an
90       // unknown operand error.
91       return true;
92       // TODO: handle other extra codes if any.
93     }
94   }
95 
96   switch (MO.getType()) {
97   case MachineOperand::MO_Immediate:
98     OS << MO.getImm();
99     return false;
100   case MachineOperand::MO_Register:
101     OS << '$' << LoongArchInstPrinter::getRegisterName(MO.getReg());
102     return false;
103   case MachineOperand::MO_GlobalAddress:
104     PrintSymbolOperand(MO, OS);
105     return false;
106   default:
107     llvm_unreachable("not implemented");
108   }
109 
110   return true;
111 }
112 
113 bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
114                                                 unsigned OpNo,
115                                                 const char *ExtraCode,
116                                                 raw_ostream &OS) {
117   // TODO: handle extra code.
118   if (ExtraCode)
119     return true;
120 
121   // We only support memory operands like "Base + Offset", where base must be a
122   // register, and offset can be a register or an immediate value.
123   const MachineOperand &BaseMO = MI->getOperand(OpNo);
124   // Base address must be a register.
125   if (!BaseMO.isReg())
126     return true;
127   // Print the base address register.
128   OS << "$" << LoongArchInstPrinter::getRegisterName(BaseMO.getReg());
129   // Print the offset operand.
130   const MachineOperand &OffsetMO = MI->getOperand(OpNo + 1);
131   if (OffsetMO.isReg())
132     OS << ", $" << LoongArchInstPrinter::getRegisterName(OffsetMO.getReg());
133   else if (OffsetMO.isImm())
134     OS << ", " << OffsetMO.getImm();
135   else
136     return true;
137 
138   return false;
139 }
140 
141 void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(
142     const MachineInstr &MI) {
143   const Function &F = MF->getFunction();
144   if (F.hasFnAttribute("patchable-function-entry")) {
145     unsigned Num;
146     if (F.getFnAttribute("patchable-function-entry")
147             .getValueAsString()
148             .getAsInteger(10, Num))
149       return;
150     emitNops(Num);
151     return;
152   }
153 
154   emitSled(MI, SledKind::FUNCTION_ENTER);
155 }
156 
157 void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) {
158   emitSled(MI, SledKind::FUNCTION_EXIT);
159 }
160 
161 void LoongArchAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) {
162   emitSled(MI, SledKind::TAIL_CALL);
163 }
164 
165 void LoongArchAsmPrinter::emitSled(const MachineInstr &MI, SledKind Kind) {
166   // For loongarch64 we want to emit the following pattern:
167   //
168   // .Lxray_sled_beginN:
169   //   B .Lxray_sled_endN
170   //   11 NOPs (44 bytes)
171   // .Lxray_sled_endN:
172   //
173   // We need the extra bytes because at runtime they may be used for the
174   // actual pattern defined at compiler-rt/lib/xray/xray_loongarch64.cpp.
175   // The count here should be adjusted accordingly if the implementation
176   // changes.
177   const int8_t NoopsInSledCount = 11;
178   OutStreamer->emitCodeAlignment(Align(4), &getSubtargetInfo());
179   MCSymbol *BeginOfSled = OutContext.createTempSymbol("xray_sled_begin");
180   MCSymbol *EndOfSled = OutContext.createTempSymbol("xray_sled_end");
181   OutStreamer->emitLabel(BeginOfSled);
182   EmitToStreamer(*OutStreamer,
183                  MCInstBuilder(LoongArch::B)
184                      .addExpr(MCSymbolRefExpr::create(EndOfSled, OutContext)));
185   emitNops(NoopsInSledCount);
186   OutStreamer->emitLabel(EndOfSled);
187   recordSled(BeginOfSled, MI, Kind, 2);
188 }
189 
190 bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
191   AsmPrinter::runOnMachineFunction(MF);
192   // Emit the XRay table for this function.
193   emitXRayTable();
194   return true;
195 }
196 
197 // Force static initialization.
198 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmPrinter() {
199   RegisterAsmPrinter<LoongArchAsmPrinter> X(getTheLoongArch32Target());
200   RegisterAsmPrinter<LoongArchAsmPrinter> Y(getTheLoongArch64Target());
201 }
202