Coverage Report

Created: 2025-11-11 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/capstonev5/arch/Mips/MipsInstPrinter.c
Line
Count
Source
1
//===-- MipsInstPrinter.cpp - Convert Mips MCInst to assembly syntax ------===//
2
//
3
//                     The LLVM Compiler Infrastructure
4
//
5
// This file is distributed under the University of Illinois Open Source
6
// License. See LICENSE.TXT for details.
7
//
8
//===----------------------------------------------------------------------===//
9
//
10
// This class prints an Mips MCInst to a .s file.
11
//
12
//===----------------------------------------------------------------------===//
13
14
/* Capstone Disassembly Engine */
15
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2015 */
16
17
#ifdef CAPSTONE_HAS_MIPS
18
19
#include <capstone/platform.h>
20
#include <stdlib.h>
21
#include <stdio.h>  // debug
22
#include <string.h>
23
24
#include "MipsInstPrinter.h"
25
#include "../../MCInst.h"
26
#include "../../utils.h"
27
#include "../../SStream.h"
28
#include "../../MCRegisterInfo.h"
29
#include "MipsMapping.h"
30
31
#include "MipsInstPrinter.h"
32
33
static void printUnsignedImm(MCInst *MI, int opNum, SStream *O);
34
static char *printAliasInstr(MCInst *MI, SStream *O, void *info);
35
static char *printAlias(MCInst *MI, SStream *OS);
36
37
// These enumeration declarations were originally in MipsInstrInfo.h but
38
// had to be moved here to avoid circular dependencies between
39
// LLVMMipsCodeGen and LLVMMipsAsmPrinter.
40
41
// Mips Condition Codes
42
typedef enum Mips_CondCode {
43
  // To be used with float branch True
44
  Mips_FCOND_F,
45
  Mips_FCOND_UN,
46
  Mips_FCOND_OEQ,
47
  Mips_FCOND_UEQ,
48
  Mips_FCOND_OLT,
49
  Mips_FCOND_ULT,
50
  Mips_FCOND_OLE,
51
  Mips_FCOND_ULE,
52
  Mips_FCOND_SF,
53
  Mips_FCOND_NGLE,
54
  Mips_FCOND_SEQ,
55
  Mips_FCOND_NGL,
56
  Mips_FCOND_LT,
57
  Mips_FCOND_NGE,
58
  Mips_FCOND_LE,
59
  Mips_FCOND_NGT,
60
61
  // To be used with float branch False
62
  // This conditions have the same mnemonic as the
63
  // above ones, but are used with a branch False;
64
  Mips_FCOND_T,
65
  Mips_FCOND_OR,
66
  Mips_FCOND_UNE,
67
  Mips_FCOND_ONE,
68
  Mips_FCOND_UGE,
69
  Mips_FCOND_OGE,
70
  Mips_FCOND_UGT,
71
  Mips_FCOND_OGT,
72
  Mips_FCOND_ST,
73
  Mips_FCOND_GLE,
74
  Mips_FCOND_SNE,
75
  Mips_FCOND_GL,
76
  Mips_FCOND_NLT,
77
  Mips_FCOND_GE,
78
  Mips_FCOND_NLE,
79
  Mips_FCOND_GT
80
} Mips_CondCode;
81
82
#define GET_INSTRINFO_ENUM
83
#include "MipsGenInstrInfo.inc"
84
85
static const char *getRegisterName(unsigned RegNo);
86
static void printInstruction(MCInst *MI, SStream *O, const MCRegisterInfo *MRI);
87
88
static void set_mem_access(MCInst *MI, bool status)
89
32.0k
{
90
32.0k
  MI->csh->doing_mem = status;
91
92
32.0k
  if (MI->csh->detail != CS_OPT_ON)
93
0
    return;
94
95
32.0k
  if (status) {
96
16.0k
    MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_MEM;
97
16.0k
    MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = MIPS_REG_INVALID;
98
16.0k
    MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = 0;
99
16.0k
  } else {
100
    // done, create the next operand slot
101
16.0k
    MI->flat_insn->detail->mips.op_count++;
102
16.0k
  }
103
32.0k
}
104
105
static bool isReg(MCInst *MI, unsigned OpNo, unsigned R)
106
7.19k
{
107
7.19k
  return (MCOperand_isReg(MCInst_getOperand(MI, OpNo)) &&
108
7.19k
      MCOperand_getReg(MCInst_getOperand(MI, OpNo)) == R);
109
7.19k
}
110
111
static const char* MipsFCCToString(Mips_CondCode CC)
112
0
{
113
0
  switch (CC) {
114
0
    default: return 0; // never reach
115
0
    case Mips_FCOND_F:
116
0
    case Mips_FCOND_T:   return "f";
117
0
    case Mips_FCOND_UN:
118
0
    case Mips_FCOND_OR:  return "un";
119
0
    case Mips_FCOND_OEQ:
120
0
    case Mips_FCOND_UNE: return "eq";
121
0
    case Mips_FCOND_UEQ:
122
0
    case Mips_FCOND_ONE: return "ueq";
123
0
    case Mips_FCOND_OLT:
124
0
    case Mips_FCOND_UGE: return "olt";
125
0
    case Mips_FCOND_ULT:
126
0
    case Mips_FCOND_OGE: return "ult";
127
0
    case Mips_FCOND_OLE:
128
0
    case Mips_FCOND_UGT: return "ole";
129
0
    case Mips_FCOND_ULE:
130
0
    case Mips_FCOND_OGT: return "ule";
131
0
    case Mips_FCOND_SF:
132
0
    case Mips_FCOND_ST:  return "sf";
133
0
    case Mips_FCOND_NGLE:
134
0
    case Mips_FCOND_GLE: return "ngle";
135
0
    case Mips_FCOND_SEQ:
136
0
    case Mips_FCOND_SNE: return "seq";
137
0
    case Mips_FCOND_NGL:
138
0
    case Mips_FCOND_GL:  return "ngl";
139
0
    case Mips_FCOND_LT:
140
0
    case Mips_FCOND_NLT: return "lt";
141
0
    case Mips_FCOND_NGE:
142
0
    case Mips_FCOND_GE:  return "nge";
143
0
    case Mips_FCOND_LE:
144
0
    case Mips_FCOND_NLE: return "le";
145
0
    case Mips_FCOND_NGT:
146
0
    case Mips_FCOND_GT:  return "ngt";
147
0
  }
148
0
}
149
150
static void printRegName(SStream *OS, unsigned RegNo)
151
157k
{
152
157k
  SStream_concat(OS, "$%s", getRegisterName(RegNo));
153
157k
}
154
155
void Mips_printInst(MCInst *MI, SStream *O, void *info)
156
92.1k
{
157
92.1k
  char *mnem;
158
159
92.1k
  switch (MCInst_getOpcode(MI)) {
160
92.1k
    default: break;
161
92.1k
    case Mips_Save16:
162
0
    case Mips_SaveX16:
163
0
    case Mips_Restore16:
164
0
    case Mips_RestoreX16:
165
0
      return;
166
92.1k
  }
167
168
  // Try to print any aliases first.
169
92.1k
  mnem = printAliasInstr(MI, O, info);
170
92.1k
  if (!mnem) {
171
85.4k
    mnem = printAlias(MI, O);
172
85.4k
    if (!mnem) {
173
83.7k
      printInstruction(MI, O, NULL);
174
83.7k
    }
175
85.4k
  }
176
177
92.1k
  if (mnem) {
178
    // fixup instruction id due to the change in alias instruction
179
8.48k
    MCInst_setOpcodePub(MI, Mips_map_insn(mnem));
180
8.48k
    cs_mem_free(mnem);
181
8.48k
  }
182
92.1k
}
183
184
static void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
185
202k
{
186
202k
  MCOperand *Op;
187
188
202k
  if (OpNo >= MI->size)
189
0
    return;
190
191
202k
  Op = MCInst_getOperand(MI, OpNo);
192
202k
  if (MCOperand_isReg(Op)) {
193
153k
    unsigned int reg = MCOperand_getReg(Op);
194
153k
    printRegName(O, reg);
195
153k
    reg = Mips_map_register(reg);
196
153k
    if (MI->csh->detail) {
197
153k
      if (MI->csh->doing_mem) {
198
16.0k
        MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.base = reg;
199
137k
      } else {
200
137k
        MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG;
201
137k
        MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg;
202
137k
        MI->flat_insn->detail->mips.op_count++;
203
137k
      }
204
153k
    }
205
153k
  } else if (MCOperand_isImm(Op)) {
206
49.7k
    int64_t imm = MCOperand_getImm(Op);
207
49.7k
    if (MI->csh->doing_mem) {
208
16.0k
      if (imm) { // only print Imm offset if it is not 0
209
15.3k
        printInt64(O, imm);
210
15.3k
      }
211
16.0k
      if (MI->csh->detail)
212
16.0k
        MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].mem.disp = imm;
213
33.6k
    } else {
214
33.6k
      printInt64(O, imm);
215
216
33.6k
      if (MI->csh->detail) {
217
33.6k
        MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
218
33.6k
        MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
219
33.6k
        MI->flat_insn->detail->mips.op_count++;
220
33.6k
      }
221
33.6k
    }
222
49.7k
  }
223
202k
}
224
225
static void printUnsignedImm(MCInst *MI, int opNum, SStream *O)
226
15.2k
{
227
15.2k
  MCOperand *MO = MCInst_getOperand(MI, opNum);
228
15.2k
  if (MCOperand_isImm(MO)) {
229
15.2k
    int64_t imm = MCOperand_getImm(MO);
230
15.2k
    printInt64(O, imm);
231
232
15.2k
    if (MI->csh->detail) {
233
15.2k
      MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
234
15.2k
      MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = (unsigned short int)imm;
235
15.2k
      MI->flat_insn->detail->mips.op_count++;
236
15.2k
    }
237
15.2k
  } else
238
0
    printOperand(MI, opNum, O);
239
15.2k
}
240
241
static void printUnsignedImm8(MCInst *MI, int opNum, SStream *O)
242
3.95k
{
243
3.95k
  MCOperand *MO = MCInst_getOperand(MI, opNum);
244
3.95k
  if (MCOperand_isImm(MO)) {
245
3.95k
    uint8_t imm = (uint8_t)MCOperand_getImm(MO);
246
3.95k
    if (imm > HEX_THRESHOLD)
247
1.46k
      SStream_concat(O, "0x%x", imm);
248
2.48k
    else
249
2.48k
      SStream_concat(O, "%u", imm);
250
3.95k
    if (MI->csh->detail) {
251
3.95k
      MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_IMM;
252
3.95k
      MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].imm = imm;
253
3.95k
      MI->flat_insn->detail->mips.op_count++;
254
3.95k
    }
255
3.95k
  } else
256
0
    printOperand(MI, opNum, O);
257
3.95k
}
258
259
static void printMemOperand(MCInst *MI, int opNum, SStream *O)
260
16.0k
{
261
  // Load/Store memory operands -- imm($reg)
262
  // If PIC target the target is loaded as the
263
  // pattern lw $25,%call16($28)
264
265
  // opNum can be invalid if instruction had reglist as operand.
266
  // MemOperand is always last operand of instruction (base + offset).
267
16.0k
  switch (MCInst_getOpcode(MI)) {
268
15.0k
    default:
269
15.0k
      break;
270
15.0k
    case Mips_SWM32_MM:
271
320
    case Mips_LWM32_MM:
272
719
    case Mips_SWM16_MM:
273
995
    case Mips_LWM16_MM:
274
995
      opNum = MCInst_getNumOperands(MI) - 2;
275
995
      break;
276
16.0k
  }
277
278
16.0k
  set_mem_access(MI, true);
279
16.0k
  printOperand(MI, opNum + 1, O);
280
16.0k
  SStream_concat0(O, "(");
281
16.0k
  printOperand(MI, opNum, O);
282
16.0k
  SStream_concat0(O, ")");
283
16.0k
  set_mem_access(MI, false);
284
16.0k
}
285
286
// TODO???
287
static void printMemOperandEA(MCInst *MI, int opNum, SStream *O)
288
0
{
289
  // when using stack locations for not load/store instructions
290
  // print the same way as all normal 3 operand instructions.
291
0
  printOperand(MI, opNum, O);
292
0
  SStream_concat0(O, ", ");
293
0
  printOperand(MI, opNum + 1, O);
294
0
  return;
295
0
}
296
297
static void printFCCOperand(MCInst *MI, int opNum, SStream *O)
298
0
{
299
0
  MCOperand *MO = MCInst_getOperand(MI, opNum);
300
0
  SStream_concat0(O, MipsFCCToString((Mips_CondCode)MCOperand_getImm(MO)));
301
0
}
302
303
static void printRegisterPair(MCInst *MI, int opNum, SStream *O)
304
266
{
305
266
  printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, opNum)));
306
266
}
307
308
static char *printAlias1(const char *Str, MCInst *MI, unsigned OpNo, SStream *OS)
309
1.70k
{
310
1.70k
  SStream_concat(OS, "%s\t", Str);
311
1.70k
  printOperand(MI, OpNo, OS);
312
1.70k
  return cs_strdup(Str);
313
1.70k
}
314
315
static char *printAlias2(const char *Str, MCInst *MI,
316
    unsigned OpNo0, unsigned OpNo1, SStream *OS)
317
1.16k
{
318
1.16k
  char *tmp;
319
320
1.16k
  tmp = printAlias1(Str, MI, OpNo0, OS);
321
1.16k
  SStream_concat0(OS, ", ");
322
1.16k
  printOperand(MI, OpNo1, OS);
323
324
1.16k
  return tmp;
325
1.16k
}
326
327
#define GET_REGINFO_ENUM
328
#include "MipsGenRegisterInfo.inc"
329
330
static char *printAlias(MCInst *MI, SStream *OS)
331
85.4k
{
332
85.4k
  switch (MCInst_getOpcode(MI)) {
333
1.35k
    case Mips_BEQ:
334
1.67k
    case Mips_BEQ_MM:
335
      // beq $zero, $zero, $L2 => b $L2
336
      // beq $r0, $zero, $L2 => beqz $r0, $L2
337
1.67k
      if (isReg(MI, 0, Mips_ZERO) && isReg(MI, 1, Mips_ZERO))
338
276
        return printAlias1("b", MI, 2, OS);
339
1.39k
      if (isReg(MI, 1, Mips_ZERO))
340
214
        return printAlias2("beqz", MI, 0, 2, OS);
341
1.18k
      return NULL;
342
0
    case Mips_BEQ64:
343
      // beq $r0, $zero, $L2 => beqz $r0, $L2
344
0
      if (isReg(MI, 1, Mips_ZERO_64))
345
0
        return printAlias2("beqz", MI, 0, 2, OS);
346
0
      return NULL;
347
1.36k
    case Mips_BNE:
348
      // bne $r0, $zero, $L2 => bnez $r0, $L2
349
1.36k
      if (isReg(MI, 1, Mips_ZERO))
350
319
        return printAlias2("bnez", MI, 0, 2, OS);
351
1.05k
      return NULL;
352
0
    case Mips_BNE64:
353
      // bne $r0, $zero, $L2 => bnez $r0, $L2
354
0
      if (isReg(MI, 1, Mips_ZERO_64))
355
0
        return printAlias2("bnez", MI, 0, 2, OS);
356
0
      return NULL;
357
329
    case Mips_BGEZAL:
358
      // bgezal $zero, $L1 => bal $L1
359
329
      if (isReg(MI, 0, Mips_ZERO))
360
67
        return printAlias1("bal", MI, 1, OS);
361
262
      return NULL;
362
215
    case Mips_BC1T:
363
      // bc1t $fcc0, $L1 => bc1t $L1
364
215
      if (isReg(MI, 0, Mips_FCC0))
365
0
        return printAlias1("bc1t", MI, 1, OS);
366
215
      return NULL;
367
200
    case Mips_BC1F:
368
      // bc1f $fcc0, $L1 => bc1f $L1
369
200
      if (isReg(MI, 0, Mips_FCC0))
370
0
        return printAlias1("bc1f", MI, 1, OS);
371
200
      return NULL;
372
260
    case Mips_JALR:
373
      // jalr $ra, $r1 => jalr $r1
374
260
      if (isReg(MI, 0, Mips_RA))
375
194
        return printAlias1("jalr", MI, 1, OS);
376
66
      return NULL;
377
0
    case Mips_JALR64:
378
      // jalr $ra, $r1 => jalr $r1
379
0
      if (isReg(MI, 0, Mips_RA_64))
380
0
        return printAlias1("jalr", MI, 1, OS);
381
0
      return NULL;
382
73
    case Mips_NOR:
383
847
    case Mips_NOR_MM:
384
      // nor $r0, $r1, $zero => not $r0, $r1
385
847
      if (isReg(MI, 2, Mips_ZERO))
386
438
        return printAlias2("not", MI, 0, 1, OS);
387
409
      return NULL;
388
0
    case Mips_NOR64:
389
      // nor $r0, $r1, $zero => not $r0, $r1
390
0
      if (isReg(MI, 2, Mips_ZERO_64))
391
0
        return printAlias2("not", MI, 0, 1, OS);
392
0
      return NULL;
393
401
    case Mips_OR:
394
      // or $r0, $r1, $zero => move $r0, $r1
395
401
      if (isReg(MI, 2, Mips_ZERO))
396
196
        return printAlias2("move", MI, 0, 1, OS);
397
205
      return NULL;
398
80.1k
    default: return NULL;
399
85.4k
  }
400
85.4k
}
401
402
static void printRegisterList(MCInst *MI, int opNum, SStream *O)
403
1.25k
{
404
1.25k
  int i, e, reg;
405
406
  // - 2 because register List is always first operand of instruction and it is
407
  // always followed by memory operand (base + offset).
408
4.87k
  for (i = opNum, e = MCInst_getNumOperands(MI) - 2; i != e; ++i) {
409
3.61k
    if (i != opNum)
410
2.36k
      SStream_concat0(O, ", ");
411
3.61k
    reg = MCOperand_getReg(MCInst_getOperand(MI, i));
412
3.61k
    printRegName(O, reg);
413
3.61k
    if (MI->csh->detail) {
414
3.61k
      MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].type = MIPS_OP_REG;
415
3.61k
      MI->flat_insn->detail->mips.operands[MI->flat_insn->detail->mips.op_count].reg = reg;
416
3.61k
      MI->flat_insn->detail->mips.op_count++;
417
3.61k
    }
418
3.61k
  }
419
1.25k
}
420
421
#define PRINT_ALIAS_INSTR
422
#include "MipsGenAsmWriter.inc"
423
424
#endif