Coverage Report

Created: 2026-04-29 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/capstonenext/arch/RISCV/RISCVInstPrinter.c
Line
Count
Source
1
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
2
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2022, */
3
/*    Rot127 <unisono@quyllur.org> 2022-2023 */
4
/* Automatically translated source file from LLVM. */
5
6
/* LLVM-commit: <commit> */
7
/* LLVM-tag: <tag> */
8
9
/* Only small edits allowed. */
10
/* For multiple similar edits, please create a Patch for the translator. */
11
12
/* Capstone's C++ file translator: */
13
/* https://github.com/capstone-engine/capstone/tree/next/suite/auto-sync */
14
15
//===-- RISCVInstPrinter.cpp - Convert RISC-V MCInst to asm syntax --------===//
16
//
17
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
18
// See https://llvm.org/LICENSE.txt for license information.
19
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
20
//
21
//===----------------------------------------------------------------------===//
22
//
23
// This class prints an RISC-V MCInst to a .s file.
24
//
25
//===----------------------------------------------------------------------===//
26
27
#include <capstone/platform.h>
28
#include "../../MathExtras.h"
29
30
#include "RISCVMapping.h"
31
#include "RISCVInstPrinter.h"
32
33
#define GET_SUBTARGETINFO_ENUM
34
#include "RISCVGenSubtargetInfo.inc"
35
36
#define GET_INSTRINFO_ENUM
37
#include "RISCVGenInstrInfo.inc"
38
39
#define GET_REGINFO_ENUM
40
#include "RISCVGenRegisterInfo.inc"
41
42
#define GET_SysRegsList_IMPL
43
#include "RISCVGenSystemOperands.inc"
44
45
#define GEN_UNCOMPRESS_INSTR
46
#include "RISCVGenCompressedInstructionsInfo.inc"
47
48
#include "RISCVMapping.h"
49
#include "../../Mapping.h"
50
51
#define CONCAT(a, b) CONCAT_(a, b)
52
#define CONCAT_(a, b) a##_##b
53
54
#define DEBUG_TYPE "asm-printer"
55
56
static void printCustomAliasOperand(MCInst *MI, uint64_t Address,
57
            unsigned OpIdx, unsigned PrintMethodIdx,
58
            SStream *OS);
59
static inline void printRegName(SStream *O, MCRegister Reg);
60
static inline void printOperand(MCInst *MI, unsigned OpNo, SStream *O);
61
// Include the auto-generated portion of the assembly writer.
62
#define PRINT_ALIAS_INSTR
63
#include "RISCVGenAsmWriter.inc"
64
65
// Print architectural register names rather than the ABI names (such as x2
66
// instead of sp).
67
// TODO: Make RISCVInstPrinter_doGetRegisterName non-static so that this can a
68
// member.
69
static bool ArchRegNames;
70
71
const char *doGetRegisterName(MCRegister Reg)
72
110k
{
73
110k
  return getRegisterName(Reg, ArchRegNames ? RISCV_NoRegAltName :
74
110k
               RISCV_ABIRegAltName);
75
110k
}
76
77
static inline void printRegName(SStream *O, MCRegister Reg)
78
223k
{
79
223k
  SStream_concat0(markup_OS(O, Markup_Register), doGetRegisterName(Reg));
80
223k
}
81
82
bool haveRequiredFeatures(const RISCV_SysReg *Reg, MCInst *MI)
83
646
{
84
  // Not in 32-bit mode.
85
646
  if (Reg->isRV32Only &&
86
487
      RISCV_getFeatureBits(MI->csh->mode, RISCV_Feature64Bit))
87
230
    return false;
88
89
416
  return true;
90
646
}
91
92
static inline void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
93
138k
{
94
138k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_Operand, OpNo);
95
96
138k
  MCOperand *MO = MCInst_getOperand(MI, (OpNo));
97
98
138k
  if (MCOperand_isReg(MO)) {
99
105k
    printRegName(O, MCOperand_getReg(MO));
100
105k
    return;
101
105k
  }
102
103
32.9k
  if (MCOperand_isImm(MO)) {
104
32.9k
    printInt64(markup_OS(O, Markup_Immediate),
105
32.9k
         MCOperand_getImm(MO));
106
32.9k
    return;
107
32.9k
  }
108
109
0
  CS_ASSERT(MCOperand_isExpr(MO) &&
110
0
      "Unknown operand kind in printOperand");
111
0
  printExpr(O, MCOperand_getExpr(MO));
112
0
}
113
114
void printBranchOperand(MCInst *MI, uint64_t Address, unsigned OpNo, SStream *O)
115
5.43k
{
116
5.43k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_BranchOperand, OpNo);
117
5.43k
  MCOperand *MO = MCInst_getOperand(MI, (OpNo));
118
5.43k
  if (!MCOperand_isImm(MO))
119
0
    return printOperand(MI, OpNo, O);
120
121
5.43k
  if (MI->csh->PrintBranchImmAsAddress) {
122
5.43k
    uint64_t Target = Address + MCOperand_getImm(MO);
123
5.43k
    if (!RISCV_getFeatureBits(MI->csh->mode, RISCV_Feature64Bit))
124
2.86k
      Target &= 0xffffffff;
125
5.43k
    printUInt64(markup_OS(O, Markup_Target), Target);
126
5.43k
  } else {
127
0
    printInt64(markup_OS(O, Markup_Target), MCOperand_getImm(MO));
128
0
  }
129
5.43k
}
130
131
void printCSRSystemRegister(MCInst *MI, unsigned OpNo, SStream *O)
132
1.81k
{
133
1.81k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_CSRSystemRegister, OpNo);
134
1.81k
  unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
135
1.81k
  const RISCV_SysReg *SysReg = RISCV_lookupSysRegByEncoding(Imm);
136
1.81k
  if (SysReg && haveRequiredFeatures(SysReg, MI))
137
416
    SStream_concat0(markup_OS(O, Markup_Register), SysReg->Name);
138
1.39k
  else
139
1.39k
    printUInt64(markup_OS(O, Markup_Register), Imm);
140
1.81k
}
141
142
void printFenceArg(MCInst *MI, unsigned OpNo, SStream *O)
143
1.08k
{
144
1.08k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FenceArg, OpNo);
145
1.08k
  unsigned FenceArg = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
146
1.08k
  CS_ASSERT(((FenceArg >> 4) == 0) &&
147
1.08k
      "Invalid immediate in printFenceArg");
148
149
1.08k
  if ((FenceArg & RISCVFenceField_I) != 0)
150
475
    SStream_concat0(O, "i");
151
152
1.08k
  if ((FenceArg & RISCVFenceField_O) != 0)
153
258
    SStream_concat0(O, "o");
154
155
1.08k
  if ((FenceArg & RISCVFenceField_R) != 0)
156
483
    SStream_concat0(O, "r");
157
158
1.08k
  if ((FenceArg & RISCVFenceField_W) != 0)
159
443
    SStream_concat0(O, "w");
160
161
1.08k
  if (FenceArg == 0)
162
384
    SStream_concat0(O, "0");
163
1.08k
}
164
165
void printFRMArg(MCInst *MI, unsigned OpNo, SStream *O)
166
3.52k
{
167
3.52k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FRMArg, OpNo);
168
3.52k
  unsigned FRMArg = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
169
3.52k
  if (!(MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT) &&
170
3.52k
      FRMArg == RISCVFPRndMode_DYN)
171
427
    return;
172
3.09k
  SStream_concat(O, "%s", ", ");
173
3.09k
  SStream_concat0(O, RISCVFPRndMode_roundingModeToString(FRMArg));
174
3.09k
}
175
176
void printFRMArgLegacy(MCInst *MI, unsigned OpNo, SStream *O)
177
466
{
178
466
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FRMArgLegacy, OpNo);
179
466
  unsigned FRMArg = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
180
  // Never print rounding mode if it's the default 'rne'. This ensures the
181
  // output can still be parsed by older tools that erroneously failed to
182
  // accept a rounding mode.
183
466
  if (FRMArg == RISCVFPRndMode_RNE)
184
42
    return;
185
424
  SStream_concat(O, "%s", ", ");
186
424
  SStream_concat0(O, RISCVFPRndMode_roundingModeToString(FRMArg));
187
424
}
188
189
void printFPImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
190
1.04k
{
191
1.04k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_FPImmOperand, OpNo);
192
1.04k
  unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
193
1.04k
  if (Imm == 1) {
194
68
    SStream_concat0(markup_OS(O, Markup_Immediate), "min");
195
977
  } else if (Imm == 30) {
196
533
    SStream_concat0(markup_OS(O, Markup_Immediate), "inf");
197
533
  } else if (Imm == 31) {
198
20
    SStream_concat0(markup_OS(O, Markup_Immediate), "nan");
199
424
  } else {
200
424
    float FPVal = getFPImm(Imm);
201
    // If the value is an integer, print a .0 fraction. Otherwise, use %g to
202
    // which will not print trailing zeros and will use scientific notation
203
    // if it is shorter than printing as a decimal. The smallest value requires
204
    // 12 digits of precision including the decimal.
205
424
    if (FPVal == (int)(FPVal))
206
351
      printfFloat(markup_OS(O, Markup_Immediate), "%.1f",
207
351
            FPVal);
208
73
    else
209
73
      printfFloat(markup_OS(O, Markup_Immediate), "%.12g",
210
73
            FPVal);
211
424
  }
212
1.04k
}
213
214
void printZeroOffsetMemOp(MCInst *MI, unsigned OpNo, SStream *O)
215
1.76k
{
216
1.76k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_ZeroOffsetMemOp, OpNo);
217
1.76k
  MCOperand *MO = MCInst_getOperand(MI, (OpNo));
218
219
1.76k
  CS_ASSERT(MCOperand_isReg(MO) &&
220
1.76k
      "printZeroOffsetMemOp can only print register operands");
221
1.76k
  SStream_concat0(O, "(");
222
1.76k
  printRegName(O, MCOperand_getReg(MO));
223
1.76k
  SStream_concat0(O, ")");
224
1.76k
}
225
226
void printVTypeI(MCInst *MI, unsigned OpNo, SStream *O)
227
2.09k
{
228
2.09k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_VTypeI, OpNo);
229
2.09k
  unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
230
  // Print the raw immediate for reserved values: vlmul[2:0]=4, vsew[2:0]=0b1xx,
231
  // or non-zero in bits 8 and above.
232
2.09k
  if (RISCVVType_getVLMUL(Imm) == RISCVII_LMUL_RESERVED ||
233
2.07k
      RISCVVType_getSEW(Imm) > 64 || (Imm >> 8) != 0) {
234
1.13k
    printUInt64(O, Imm);
235
1.13k
    return;
236
1.13k
  }
237
  // Print the text form.
238
962
  printVType(Imm, O);
239
962
}
240
241
void printRlist(MCInst *MI, unsigned OpNo, SStream *O)
242
0
{
243
0
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_Rlist, OpNo);
244
0
  unsigned Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
245
0
  SStream_concat0(O, "{");
246
0
  switch (Imm) {
247
0
  case RISCVZC_RLISTENCODE_RA:
248
0
    SStream_concat0(markup_OS(O, Markup_Register),
249
0
        (ArchRegNames ? "x1" : "ra"));
250
0
    break;
251
0
  case RISCVZC_RLISTENCODE_RA_S0:
252
0
    SStream_concat0(markup_OS(O, Markup_Register),
253
0
        (ArchRegNames ? "x1" : "ra"));
254
0
    SStream_concat0(O, ", ");
255
0
    SStream_concat0(markup_OS(O, Markup_Register),
256
0
        (ArchRegNames ? "x8" : "s0"));
257
0
    break;
258
0
  case RISCVZC_RLISTENCODE_RA_S0_S1:
259
0
    SStream_concat0(markup_OS(O, Markup_Register),
260
0
        (ArchRegNames ? "x1" : "ra"));
261
0
    SStream_concat0(O, ", ");
262
0
    SStream_concat0(markup_OS(O, Markup_Register),
263
0
        (ArchRegNames ? "x8" : "s0"));
264
0
    SStream_concat0(O, "-");
265
266
0
    SStream_concat0(markup_OS(O, Markup_Register),
267
0
        (ArchRegNames ? "x9" : "s1"));
268
0
    break;
269
0
  case RISCVZC_RLISTENCODE_RA_S0_S2:
270
0
    SStream_concat0(markup_OS(O, Markup_Register),
271
0
        (ArchRegNames ? "x1" : "ra"));
272
0
    SStream_concat0(O, ", ");
273
0
    SStream_concat0(markup_OS(O, Markup_Register),
274
0
        (ArchRegNames ? "x8" : "s0"));
275
0
    SStream_concat0(O, "-");
276
277
0
    SStream_concat0(markup_OS(O, Markup_Register),
278
0
        (ArchRegNames ? "x9" : "s2"));
279
0
    if (ArchRegNames) {
280
0
      SStream_concat0(O, ", ");
281
0
      SStream_concat0(markup_OS(O, Markup_Register), "x18");
282
0
    }
283
0
    break;
284
0
  case RISCVZC_RLISTENCODE_RA_S0_S3:
285
0
  case RISCVZC_RLISTENCODE_RA_S0_S4:
286
0
  case RISCVZC_RLISTENCODE_RA_S0_S5:
287
0
  case RISCVZC_RLISTENCODE_RA_S0_S6:
288
0
  case RISCVZC_RLISTENCODE_RA_S0_S7:
289
0
  case RISCVZC_RLISTENCODE_RA_S0_S8:
290
0
  case RISCVZC_RLISTENCODE_RA_S0_S9:
291
0
  case RISCVZC_RLISTENCODE_RA_S0_S11:
292
0
    SStream_concat0(markup_OS(O, Markup_Register),
293
0
        (ArchRegNames ? "x1" : "ra"));
294
0
    SStream_concat0(O, ", ");
295
0
    SStream_concat0(markup_OS(O, Markup_Register),
296
0
        (ArchRegNames ? "x8" : "s0"));
297
0
    SStream_concat0(O, "-");
298
299
0
    if (ArchRegNames) {
300
0
      SStream_concat0(markup_OS(O, Markup_Register), "x9");
301
0
      SStream_concat0(O, ", ");
302
0
      SStream_concat0(markup_OS(O, Markup_Register), "x18");
303
0
      SStream_concat0(O, "-");
304
0
    }
305
0
    SStream_concat0(
306
0
      markup_OS(O, Markup_Register),
307
0
      doGetRegisterName(
308
0
        RISCV_X19 +
309
0
        (Imm == RISCVZC_RLISTENCODE_RA_S0_S11 ?
310
0
           8 :
311
0
           Imm - RISCVZC_RLISTENCODE_RA_S0_S3)));
312
0
    break;
313
0
  default:
314
0
    CS_ASSERT(0 && "invalid register list");
315
0
  }
316
0
  SStream_concat0(O, "}");
317
0
}
318
319
void printRegReg(MCInst *MI, unsigned OpNo, SStream *O)
320
766
{
321
766
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_RegReg, OpNo);
322
766
  MCOperand *MO = MCInst_getOperand(MI, (OpNo));
323
324
766
  CS_ASSERT(MCOperand_isReg(MO) &&
325
766
      "printRegReg can only print register operands");
326
766
  if (MCOperand_getReg(MO) == RISCV_NoRegister)
327
0
    return;
328
766
  printRegName(O, MCOperand_getReg(MO));
329
330
766
  SStream_concat0(O, "(");
331
766
  MCOperand *MO1 = MCInst_getOperand(MI, (OpNo + 1));
332
766
  CS_ASSERT(MCOperand_isReg(MO1) &&
333
766
      "printRegReg can only print register operands");
334
766
  printRegName(O, MCOperand_getReg(MO1));
335
766
  SStream_concat0(O, ")");
336
766
}
337
338
void printSpimm(MCInst *MI, unsigned OpNo, SStream *O)
339
0
{
340
0
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_Spimm, OpNo);
341
0
  int64_t Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
342
0
  unsigned Opcode = MCInst_getOpcode(MI);
343
0
  bool IsRV64 = RISCV_getFeatureBits(MI->csh->mode, RISCV_Feature64Bit);
344
0
  bool IsEABI = RISCV_getFeatureBits(MI->csh->mode, RISCV_FeatureRVE);
345
0
  int64_t Spimm = 0;
346
0
  int64_t RlistVal = MCOperand_getImm(MCInst_getOperand(MI, (0)));
347
0
  CS_ASSERT(RlistVal != 16 && "Incorrect rlist.");
348
0
  unsigned Base = RISCVZC_getStackAdjBase(RlistVal, IsRV64, IsEABI);
349
0
  Spimm = Imm + Base;
350
0
  CS_ASSERT((Spimm >= Base && Spimm <= Base + 48) && "Incorrect spimm");
351
0
  if (Opcode == RISCV_CM_PUSH)
352
0
    Spimm = -Spimm;
353
354
0
  RISCVZC_printSpimm(Spimm, markup_OS(O, Markup_Immediate));
355
0
}
356
357
void printVMaskReg(MCInst *MI, unsigned OpNo, SStream *O)
358
4.43k
{
359
4.43k
  RISCV_add_cs_detail_0(MI, RISCV_OP_GROUP_VMaskReg, OpNo);
360
4.43k
  MCOperand *MO = MCInst_getOperand(MI, (OpNo));
361
362
4.43k
  CS_ASSERT(MCOperand_isReg(MO) &&
363
4.43k
      "printVMaskReg can only print register operands");
364
4.43k
  if (MCOperand_getReg(MO) == RISCV_NoRegister)
365
2.60k
    return;
366
1.83k
  SStream_concat0(O, ", ");
367
1.83k
  printRegName(O, MCOperand_getReg(MO));
368
1.83k
  SStream_concat0(O, ".t");
369
1.83k
}
370
371
void RISCV_LLVM_printInstruction(MCInst *MI, SStream *O,
372
         void * /* MCRegisterInfo* */ info)
373
62.0k
{
374
62.0k
  MI->MRI = (MCRegisterInfo *)info;
375
376
62.0k
  MCInst_setIsAlias(MI, false);
377
62.0k
  bool usesAliasDetails = map_use_alias_details(MI);
378
62.0k
  MI->flat_insn->usesAliasDetails = usesAliasDetails;
379
380
  /* check for a non-compressed instruction */
381
62.0k
  MCInst Uncompressed;
382
62.0k
  MCInst_Init(&Uncompressed, MI->csh->arch);
383
384
62.0k
  MCInst *McInstr = MI;
385
62.0k
  bool is_uncompressed = false;
386
  // side-effectful check for compressed instructions that creates the equivalent uncompressed instruction in case of true
387
  // (LLVM doesn't generate an API for doing a pure check)
388
62.0k
  if (uncompressInst(&Uncompressed, MI)) {
389
32.3k
    McInstr = &Uncompressed;
390
32.3k
    Uncompressed.address = MI->address;
391
32.3k
    Uncompressed.MRI = MI->MRI;
392
32.3k
    Uncompressed.csh = MI->csh;
393
32.3k
    Uncompressed.flat_insn = MI->flat_insn;
394
32.3k
    is_uncompressed = true;
395
32.3k
  }
396
397
  // print the exact instruction text and done
398
62.0k
  bool print_exact_text =
399
62.0k
    (MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT) ||
400
62.0k
    (is_uncompressed &&
401
32.3k
     MI->csh->syntax & CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED);
402
62.0k
  if (print_exact_text) {
403
0
    printInstruction(MI, MI->address, O);
404
62.0k
  } else {
405
    // side-effectful check for alias instructions that prints to the SStream if true
406
62.0k
    if (printAliasInstr(McInstr, MI->address, O)) {
407
8.31k
      MCInst_setIsAlias(MI, true);
408
      // do we still want the exact details even if the text is alias ?
409
8.31k
      if (!usesAliasDetails && detail_is_set(MI)) {
410
        // disable actual printing
411
0
        SStream_Close(O);
412
        // discard the alias operands
413
0
        memset(MI->flat_insn->detail->riscv.operands, 0,
414
0
               sizeof(MI->flat_insn->detail->riscv
415
0
                  .operands));
416
0
        MI->flat_insn->detail->riscv.op_count = 0;
417
        // re-disassemble again with no printing in order to obtain the full details
418
        // including the whole operands array
419
0
        printInstruction(MI, MI->address, O);
420
        // re-open the stream to restore the usual state
421
0
        SStream_Open(O);
422
0
      }
423
8.31k
    } else // the instruction is not an alias
424
53.7k
      printInstruction(McInstr, MI->address, O);
425
62.0k
  }
426
62.0k
  RISCV_add_groups(MI);
427
62.0k
  RISCV_add_missing_write_access(MI);
428
62.0k
  RISCV_compact_operands(MI);
429
62.0k
  RISCV_set_alias_id(MI, O);
430
62.0k
}
431
432
const char *getSysRegName(unsigned reg)
433
0
{
434
0
  const RISCV_SysReg *SysReg = RISCV_lookupSysRegByEncoding(reg);
435
0
  return SysReg->Name;
436
0
}
437
438
const char *RISCV_LLVM_getRegisterName(unsigned RegNo, unsigned AltIdx)
439
19.1k
{
440
19.1k
  return getRegisterName(RegNo, AltIdx);
441
19.1k
}
442
443
bool isCompressed(MCInst *MI)
444
62.0k
{
445
62.0k
  MCInst unused;
446
62.0k
  MCInst_Init(&unused, MI->csh->arch);
447
62.0k
  return uncompressInst(&unused, MI);
448
62.0k
}