1//===-- RISCVInstPrinter.cpp - Convert RISC-V MCInst to asm syntax --------===//
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 class prints an RISC-V MCInst to a .s file.
10//
11//===----------------------------------------------------------------------===//
12
13#include "RISCVInstPrinter.h"
14#include "RISCVBaseInfo.h"
15#include "llvm/MC/MCAsmInfo.h"
16#include "llvm/MC/MCExpr.h"
17#include "llvm/MC/MCInst.h"
18#include "llvm/MC/MCInstPrinter.h"
19#include "llvm/MC/MCSubtargetInfo.h"
20#include "llvm/MC/MCSymbol.h"
21#include "llvm/Support/CommandLine.h"
22#include "llvm/Support/ErrorHandling.h"
23using namespace llvm;
24
25#define DEBUG_TYPE "asm-printer"
26
27// Include the auto-generated portion of the assembly writer.
28#define PRINT_ALIAS_INSTR
29#include "RISCVGenAsmWriter.inc"
30
31static cl::opt<bool>
32 NoAliases("riscv-no-aliases",
33 cl::desc("Disable the emission of assembler pseudo instructions"),
34 cl::init(Val: false), cl::Hidden);
35
36static cl::opt<bool> EmitX8AsFP("riscv-emit-x8-as-fp",
37 cl::desc("Emit x8 as fp instead of s0"),
38 cl::init(Val: false), cl::Hidden);
39
40// Print architectural register names rather than the ABI names (such as x2
41// instead of sp).
42// TODO: Make RISCVInstPrinter::getRegisterName non-static so that this can a
43// member.
44static bool ArchRegNames;
45
46// The command-line flags above are used by llvm-mc and llc. They can be used by
47// `llvm-objdump`, but we override their values here to handle options passed to
48// `llvm-objdump` with `-M` (which matches GNU objdump). There did not seem to
49// be an easier way to allow these options in all these tools, without doing it
50// this way.
51bool RISCVInstPrinter::applyTargetSpecificCLOption(StringRef Opt) {
52 if (Opt == "no-aliases") {
53 PrintAliases = false;
54 return true;
55 }
56 if (Opt == "numeric") {
57 ArchRegNames = true;
58 return true;
59 }
60 if (Opt == "emit-x8-as-fp") {
61 if (!ArchRegNames)
62 EmitX8AsFP = true;
63 return true;
64 }
65
66 return false;
67}
68
69void RISCVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
70 StringRef Annot, const MCSubtargetInfo &STI,
71 raw_ostream &O) {
72 bool Res = false;
73 const MCInst *NewMI = MI;
74 MCInst UncompressedMI;
75 if (PrintAliases && !NoAliases)
76 Res = RISCVRVC::uncompress(OutInst&: UncompressedMI, MI: *MI, STI);
77 if (Res)
78 NewMI = const_cast<MCInst *>(&UncompressedMI);
79 if (!PrintAliases || NoAliases || !printAliasInstr(MI: NewMI, Address, STI, OS&: O))
80 printInstruction(MI: NewMI, Address, STI, O);
81 printAnnotation(OS&: O, Annot);
82}
83
84void RISCVInstPrinter::printRegName(raw_ostream &O, MCRegister Reg) {
85 markup(OS&: O, M: Markup::Register) << getRegisterName(Reg);
86}
87
88void RISCVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
89 const MCSubtargetInfo &STI,
90 raw_ostream &O) {
91 const MCOperand &MO = MI->getOperand(i: OpNo);
92
93 if (MO.isReg()) {
94 printRegName(O, Reg: MO.getReg());
95 return;
96 }
97
98 if (MO.isImm()) {
99 markup(OS&: O, M: Markup::Immediate) << formatImm(Value: MO.getImm());
100 return;
101 }
102
103 assert(MO.isExpr() && "Unknown operand kind in printOperand");
104 MAI.printExpr(O, *MO.getExpr());
105}
106
107void RISCVInstPrinter::printBranchOperand(const MCInst *MI, uint64_t Address,
108 unsigned OpNo,
109 const MCSubtargetInfo &STI,
110 raw_ostream &O) {
111 const MCOperand &MO = MI->getOperand(i: OpNo);
112 if (!MO.isImm())
113 return printOperand(MI, OpNo, STI, O);
114
115 if (PrintBranchImmAsAddress) {
116 uint64_t Target = Address + MO.getImm();
117 if (!STI.hasFeature(Feature: RISCV::Feature64Bit))
118 Target &= 0xffffffff;
119 markup(OS&: O, M: Markup::Target) << formatHex(Value: Target);
120 } else {
121 markup(OS&: O, M: Markup::Target) << formatImm(Value: MO.getImm());
122 }
123}
124
125void RISCVInstPrinter::printCSRSystemRegister(const MCInst *MI, unsigned OpNo,
126 const MCSubtargetInfo &STI,
127 raw_ostream &O) {
128 unsigned Imm = MI->getOperand(i: OpNo).getImm();
129 auto Range = RISCVSysReg::lookupSysRegByEncoding(Encoding: Imm);
130 for (auto &Reg : Range) {
131 if (Reg.IsAltName || Reg.IsDeprecatedName)
132 continue;
133 if (Reg.haveRequiredFeatures(ActiveFeatures: STI.getFeatureBits())) {
134 markup(OS&: O, M: Markup::Register) << Reg.Name;
135 return;
136 }
137 }
138 markup(OS&: O, M: Markup::Register) << formatImm(Value: Imm);
139}
140
141void RISCVInstPrinter::printFenceArg(const MCInst *MI, unsigned OpNo,
142 const MCSubtargetInfo &STI,
143 raw_ostream &O) {
144 unsigned FenceArg = MI->getOperand(i: OpNo).getImm();
145 assert (((FenceArg >> 4) == 0) && "Invalid immediate in printFenceArg");
146
147 if ((FenceArg & RISCVFenceField::I) != 0)
148 O << 'i';
149 if ((FenceArg & RISCVFenceField::O) != 0)
150 O << 'o';
151 if ((FenceArg & RISCVFenceField::R) != 0)
152 O << 'r';
153 if ((FenceArg & RISCVFenceField::W) != 0)
154 O << 'w';
155 if (FenceArg == 0)
156 O << "0";
157}
158
159void RISCVInstPrinter::printFRMArg(const MCInst *MI, unsigned OpNo,
160 const MCSubtargetInfo &STI, raw_ostream &O) {
161 auto FRMArg =
162 static_cast<RISCVFPRndMode::RoundingMode>(MI->getOperand(i: OpNo).getImm());
163 if (PrintAliases && !NoAliases && FRMArg == RISCVFPRndMode::RoundingMode::DYN)
164 return;
165 O << ", " << RISCVFPRndMode::roundingModeToString(RndMode: FRMArg);
166}
167
168void RISCVInstPrinter::printFRMArgLegacy(const MCInst *MI, unsigned OpNo,
169 const MCSubtargetInfo &STI,
170 raw_ostream &O) {
171 auto FRMArg =
172 static_cast<RISCVFPRndMode::RoundingMode>(MI->getOperand(i: OpNo).getImm());
173 // Never print rounding mode if it's the default 'rne'. This ensures the
174 // output can still be parsed by older tools that erroneously failed to
175 // accept a rounding mode.
176 if (FRMArg == RISCVFPRndMode::RoundingMode::RNE)
177 return;
178 O << ", " << RISCVFPRndMode::roundingModeToString(RndMode: FRMArg);
179}
180
181void RISCVInstPrinter::printFPImmOperand(const MCInst *MI, unsigned OpNo,
182 const MCSubtargetInfo &STI,
183 raw_ostream &O) {
184 unsigned Imm = MI->getOperand(i: OpNo).getImm();
185 if (Imm == 1) {
186 markup(OS&: O, M: Markup::Immediate) << "min";
187 } else if (Imm == 30) {
188 markup(OS&: O, M: Markup::Immediate) << "inf";
189 } else if (Imm == 31) {
190 markup(OS&: O, M: Markup::Immediate) << "nan";
191 } else {
192 float FPVal = RISCVLoadFPImm::getFPImm(Imm);
193 // If the value is an integer, print a .0 fraction. Otherwise, use %g to
194 // which will not print trailing zeros and will use scientific notation
195 // if it is shorter than printing as a decimal. The smallest value requires
196 // 12 digits of precision including the decimal.
197 if (FPVal == (int)(FPVal))
198 markup(OS&: O, M: Markup::Immediate) << format(Fmt: "%.1f", Vals: FPVal);
199 else
200 markup(OS&: O, M: Markup::Immediate) << format(Fmt: "%.12g", Vals: FPVal);
201 }
202}
203
204void RISCVInstPrinter::printZeroOffsetMemOp(const MCInst *MI, unsigned OpNo,
205 const MCSubtargetInfo &STI,
206 raw_ostream &O) {
207 const MCOperand &MO = MI->getOperand(i: OpNo);
208
209 assert(MO.isReg() && "printZeroOffsetMemOp can only print register operands");
210 O << "(";
211 printRegName(O, Reg: MO.getReg());
212 O << ")";
213}
214
215void RISCVInstPrinter::printVTypeI(const MCInst *MI, unsigned OpNo,
216 const MCSubtargetInfo &STI, raw_ostream &O) {
217 unsigned Imm = MI->getOperand(i: OpNo).getImm();
218 // Print the raw immediate for reserved values: vlmul[2:0]=4, vsew[2:0]=0b1xx,
219 // or non-zero in bits 8 and above.
220 if (RISCVVType::getVLMUL(VType: Imm) == RISCVVType::VLMUL::LMUL_RESERVED ||
221 RISCVVType::getSEW(VType: Imm) > 64 || (Imm >> 8) != 0) {
222 O << formatImm(Value: Imm);
223 return;
224 }
225 // Print the text form.
226 RISCVVType::printVType(VType: Imm, OS&: O);
227}
228
229void RISCVInstPrinter::printXSfmmVType(const MCInst *MI, unsigned OpNo,
230 const MCSubtargetInfo &STI,
231 raw_ostream &O) {
232 unsigned Imm = MI->getOperand(i: OpNo).getImm();
233 assert(RISCVVType::isValidXSfmmVType(Imm));
234 unsigned SEW = RISCVVType::getSEW(VType: Imm);
235 O << "e" << SEW;
236 bool AltFmt = RISCVVType::isAltFmt(VType: Imm);
237 if (AltFmt)
238 O << "alt";
239 unsigned Widen = RISCVVType::getXSfmmWiden(VType: Imm);
240 O << ", w" << Widen;
241}
242
243// Print a Zcmp RList. If we are printing architectural register names rather
244// than ABI register names, we need to print "{x1, x8-x9, x18-x27}" for all
245// registers. Otherwise, we print "{ra, s0-s11}".
246void RISCVInstPrinter::printRegList(const MCInst *MI, unsigned OpNo,
247 const MCSubtargetInfo &STI, raw_ostream &O) {
248 unsigned Imm = MI->getOperand(i: OpNo).getImm();
249
250 assert(Imm >= RISCVZC::RLISTENCODE::RA &&
251 Imm <= RISCVZC::RLISTENCODE::RA_S0_S11 && "Invalid Rlist");
252
253 O << "{";
254 printRegName(O, Reg: RISCV::X1);
255
256 if (Imm >= RISCVZC::RLISTENCODE::RA_S0) {
257 O << ", ";
258 printRegName(O, Reg: RISCV::X8);
259 }
260
261 if (Imm >= RISCVZC::RLISTENCODE::RA_S0_S1) {
262 O << '-';
263 if (Imm == RISCVZC::RLISTENCODE::RA_S0_S1 || ArchRegNames)
264 printRegName(O, Reg: RISCV::X9);
265 }
266
267 if (Imm >= RISCVZC::RLISTENCODE::RA_S0_S2) {
268 if (ArchRegNames)
269 O << ", ";
270 if (Imm == RISCVZC::RLISTENCODE::RA_S0_S2 || ArchRegNames)
271 printRegName(O, Reg: RISCV::X18);
272 }
273
274 if (Imm >= RISCVZC::RLISTENCODE::RA_S0_S3) {
275 if (ArchRegNames)
276 O << '-';
277 unsigned Offset = (Imm - RISCVZC::RLISTENCODE::RA_S0_S3);
278 // Encodings for S3-S9 are contiguous. There is no encoding for S10, so we
279 // must skip to S11(X27).
280 if (Imm == RISCVZC::RLISTENCODE::RA_S0_S11)
281 ++Offset;
282 printRegName(O, Reg: RISCV::X19 + Offset);
283 }
284
285 O << "}";
286}
287
288void RISCVInstPrinter::printRegReg(const MCInst *MI, unsigned OpNo,
289 const MCSubtargetInfo &STI, raw_ostream &O) {
290 const MCOperand &OffsetMO = MI->getOperand(i: OpNo + 1);
291
292 assert(OffsetMO.isReg() && "printRegReg can only print register operands");
293 printRegName(O, Reg: OffsetMO.getReg());
294
295 O << "(";
296 const MCOperand &BaseMO = MI->getOperand(i: OpNo);
297 assert(BaseMO.isReg() && "printRegReg can only print register operands");
298 printRegName(O, Reg: BaseMO.getReg());
299 O << ")";
300}
301
302void RISCVInstPrinter::printStackAdj(const MCInst *MI, unsigned OpNo,
303 const MCSubtargetInfo &STI, raw_ostream &O,
304 bool Negate) {
305 int64_t Imm = MI->getOperand(i: OpNo).getImm();
306 bool IsRV64 = STI.hasFeature(Feature: RISCV::Feature64Bit);
307 int64_t StackAdj = 0;
308 auto RlistVal = MI->getOperand(i: 0).getImm();
309 auto Base = RISCVZC::getStackAdjBase(RlistVal, IsRV64);
310 StackAdj = Imm + Base;
311 assert((StackAdj >= Base && StackAdj <= Base + 48) &&
312 "Incorrect stack adjust");
313 if (Negate)
314 StackAdj = -StackAdj;
315
316 // RAII guard for ANSI color escape sequences
317 WithMarkup ScopedMarkup = markup(OS&: O, M: Markup::Immediate);
318 O << StackAdj;
319}
320
321void RISCVInstPrinter::printVMaskReg(const MCInst *MI, unsigned OpNo,
322 const MCSubtargetInfo &STI,
323 raw_ostream &O) {
324 const MCOperand &MO = MI->getOperand(i: OpNo);
325
326 assert(MO.isReg() && "printVMaskReg can only print register operands");
327 if (MO.getReg() == RISCV::NoRegister)
328 return;
329 O << ", ";
330 printRegName(O, Reg: MO.getReg());
331 O << ".t";
332}
333
334const char *RISCVInstPrinter::getRegisterName(MCRegister Reg) {
335 // When PrintAliases is enabled, and EmitX8AsFP is enabled, x8 will be printed
336 // as fp instead of s0. Note that these similar registers are not replaced:
337 // - X8_H: used for f16 register in zhinx
338 // - X8_W: used for f32 register in zfinx
339 // - X8_X9: used for GPR Pair
340 if (!ArchRegNames && EmitX8AsFP && Reg == RISCV::X8)
341 return "fp";
342 return getRegisterName(Reg, AltIdx: ArchRegNames ? RISCV::NoRegAltName
343 : RISCV::ABIRegAltName);
344}
345