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
emitInstruction(const MachineInstr * MI)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
PrintAsmOperand(const MachineInstr * MI,unsigned OpNo,const char * ExtraCode,raw_ostream & OS)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
PrintAsmMemoryOperand(const MachineInstr * MI,unsigned OpNo,const char * ExtraCode,raw_ostream & OS)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
LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr & MI)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
LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr & MI)157 void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) {
158 emitSled(MI, SledKind::FUNCTION_EXIT);
159 }
160
LowerPATCHABLE_TAIL_CALL(const MachineInstr & MI)161 void LoongArchAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) {
162 emitSled(MI, SledKind::TAIL_CALL);
163 }
164
emitSled(const MachineInstr & MI,SledKind Kind)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
runOnMachineFunction(MachineFunction & MF)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.
LLVMInitializeLoongArchAsmPrinter()198 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmPrinter() {
199 RegisterAsmPrinter<LoongArchAsmPrinter> X(getTheLoongArch32Target());
200 RegisterAsmPrinter<LoongArchAsmPrinter> Y(getTheLoongArch64Target());
201 }
202