Coverage Report

Created: 2025-07-01 07:03

/src/capstonenext/arch/LoongArch/LoongArchMapping.c
Line
Count
Source (jump to first uncovered line)
1
/* Capstone Disassembly Engine */
2
/* By Jiajie Chen <c@jia.je>, 2024 */
3
/*    Yanglin Xun <1109673069@qq.com>, 2024 */
4
5
#ifdef CAPSTONE_HAS_LOONGARCH
6
7
#include <stdio.h>
8
#include <string.h>
9
10
#include <capstone/capstone.h>
11
#include <capstone/loongarch.h>
12
13
#include "../../Mapping.h"
14
#include "../../MCDisassembler.h"
15
#include "../../cs_priv.h"
16
#include "../../cs_simple_types.h"
17
18
#include "LoongArchMapping.h"
19
#include "LoongArchLinkage.h"
20
21
#define GET_REGINFO_ENUM
22
#define GET_REGINFO_MC_DESC
23
#include "LoongArchGenRegisterInfo.inc"
24
25
#define GET_INSTRINFO_ENUM
26
#include "LoongArchGenInstrInfo.inc"
27
28
void LoongArch_init_mri(MCRegisterInfo *MRI)
29
0
{
30
0
  MCRegisterInfo_InitMCRegisterInfo(MRI, LoongArchRegDesc,
31
0
            sizeof(LoongArchRegDesc), 0, 0,
32
0
            LoongArchMCRegisterClasses,
33
0
            ARR_SIZE(LoongArchMCRegisterClasses),
34
0
            0, 0, LoongArchRegDiffLists, 0,
35
0
            LoongArchSubRegIdxLists,
36
0
            ARR_SIZE(LoongArchSubRegIdxLists), 0);
37
0
}
38
39
const char *LoongArch_reg_name(csh handle, unsigned int reg)
40
0
{
41
0
  int syntax_opt = ((cs_struct *)(uintptr_t)handle)->syntax;
42
43
0
  if (syntax_opt & CS_OPT_SYNTAX_NOREGNAME) {
44
0
    return LoongArch_LLVM_getRegisterName(reg,
45
0
                  LoongArch_NoRegAltName);
46
0
  }
47
0
  return LoongArch_LLVM_getRegisterName(reg, LoongArch_RegAliasName);
48
0
}
49
50
void LoongArch_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
51
0
{
52
  // Not used by LoongArch. Information is set after disassembly.
53
0
}
54
55
static const char *const insn_name_maps[] = {
56
#include "LoongArchGenCSMappingInsnName.inc"
57
};
58
59
#ifndef CAPSTONE_DIET
60
static const name_map insn_alias_mnem_map[] = {
61
#include "LoongArchGenCSAliasMnemMap.inc"
62
  { LOONGARCH_INS_ALIAS_END, NULL },
63
};
64
#endif
65
66
const char *LoongArch_insn_name(csh handle, unsigned int id)
67
0
{
68
0
#ifndef CAPSTONE_DIET
69
0
  if (id < LOONGARCH_INS_ALIAS_END && id > LOONGARCH_INS_ALIAS_BEGIN) {
70
0
    if (id - LOONGARCH_INS_ALIAS_BEGIN >=
71
0
        ARR_SIZE(insn_alias_mnem_map))
72
0
      return NULL;
73
74
0
    return insn_alias_mnem_map[id - LOONGARCH_INS_ALIAS_BEGIN - 1]
75
0
      .name;
76
0
  }
77
0
  if (id >= LOONGARCH_INS_ENDING)
78
0
    return NULL;
79
80
0
  if (id < ARR_SIZE(insn_name_maps))
81
0
    return insn_name_maps[id];
82
  // not found
83
0
  return NULL;
84
#else
85
  return NULL;
86
#endif
87
0
}
88
89
#ifndef CAPSTONE_DIET
90
static const name_map group_name_maps[] = {
91
  { LOONGARCH_GRP_INVALID, NULL },
92
93
  { LOONGARCH_GRP_JUMP, "jump" },
94
  { LOONGARCH_GRP_CALL, "call" },
95
  { LOONGARCH_GRP_RET, "return" },
96
  { LOONGARCH_GRP_INT, "int" },
97
  { LOONGARCH_GRP_IRET, "iret" },
98
  { LOONGARCH_GRP_PRIVILEGE, "privilege" },
99
  { LOONGARCH_GRP_BRANCH_RELATIVE, "branch_relative" },
100
101
// architecture-specific groups
102
#include "LoongArchGenCSFeatureName.inc"
103
};
104
#endif
105
106
const char *LoongArch_group_name(csh handle, unsigned int id)
107
0
{
108
0
#ifndef CAPSTONE_DIET
109
0
  return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
110
#else
111
  return NULL;
112
#endif
113
0
}
114
115
void LoongArch_reg_access(const cs_insn *insn, cs_regs regs_read,
116
        uint8_t *regs_read_count, cs_regs regs_write,
117
        uint8_t *regs_write_count)
118
0
{
119
0
  uint8_t i;
120
0
  uint8_t read_count, write_count;
121
0
  cs_loongarch *loongarch = &(insn->detail->loongarch);
122
123
0
  read_count = insn->detail->regs_read_count;
124
0
  write_count = insn->detail->regs_write_count;
125
126
  // implicit registers
127
0
  memcpy(regs_read, insn->detail->regs_read,
128
0
         read_count * sizeof(insn->detail->regs_read[0]));
129
0
  memcpy(regs_write, insn->detail->regs_write,
130
0
         write_count * sizeof(insn->detail->regs_write[0]));
131
132
  // explicit registers
133
0
  for (i = 0; i < loongarch->op_count; i++) {
134
0
    cs_loongarch_op *op = &(loongarch->operands[i]);
135
0
    switch ((int)op->type) {
136
0
    case LOONGARCH_OP_REG:
137
0
      if ((op->access & CS_AC_READ) &&
138
0
          !arr_exist(regs_read, read_count, op->reg)) {
139
0
        regs_read[read_count] = (uint16_t)op->reg;
140
0
        read_count++;
141
0
      }
142
0
      if ((op->access & CS_AC_WRITE) &&
143
0
          !arr_exist(regs_write, write_count, op->reg)) {
144
0
        regs_write[write_count] = (uint16_t)op->reg;
145
0
        write_count++;
146
0
      }
147
0
      break;
148
0
    case LOONGARCH_OP_MEM:
149
      // registers appeared in memory references always being read
150
0
      if ((op->mem.base != LOONGARCH_REG_INVALID) &&
151
0
          !arr_exist(regs_read, read_count, op->mem.base)) {
152
0
        regs_read[read_count] = (uint16_t)op->mem.base;
153
0
        read_count++;
154
0
      }
155
0
      if ((insn->detail->writeback) &&
156
0
          (op->mem.base != LOONGARCH_REG_INVALID) &&
157
0
          !arr_exist(regs_write, write_count, op->mem.base)) {
158
0
        regs_write[write_count] =
159
0
          (uint16_t)op->mem.base;
160
0
        write_count++;
161
0
      }
162
0
    default:
163
0
      break;
164
0
    }
165
0
  }
166
167
0
  *regs_read_count = read_count;
168
0
  *regs_write_count = write_count;
169
0
}
170
171
const insn_map loongarch_insns[] = {
172
#include "LoongArchGenCSMappingInsn.inc"
173
};
174
175
void LoongArch_rewrite_memory_operand(MCInst *MI)
176
0
{
177
  // rewrite base + disp operands to memory operands in memory instructions
178
  // convert e.g.
179
  // ld.d   $t3, $t2, 0x410
180
  // op_count: 3
181
  //         operands[0].type: REG = t3
182
  //         operands[0].access: WRITE
183
  //         operands[1].type: REG = t2
184
  //         operands[1].access: READ
185
  //         operands[2].type: IMM = 0x410
186
  //         operands[2].access: READ
187
  // to:
188
  // op_count: 3
189
  //         operands[0].type: REG = t3
190
  //         operands[0].access: WRITE
191
  //         operands[1].type: MEM
192
  //                 operands[1].mem.base: REG = t2
193
  //                 operands[1].mem.disp: 0x410
194
  //        operands[1].access: READ
195
196
0
  if (!detail_is_set(MI))
197
0
    return;
198
199
0
  const loongarch_suppl_info *suppl_info =
200
0
    map_get_suppl_info(MI, loongarch_insns);
201
0
  if (!suppl_info)
202
0
    return;
203
204
0
  if (suppl_info->memory_access == CS_AC_INVALID) {
205
    // not memory instruction
206
0
    return;
207
0
  }
208
209
  // handle special cases
210
0
  unsigned int base;
211
0
  switch (MI->flat_insn->id) {
212
0
  case LOONGARCH_INS_SC_Q:
213
0
  case LOONGARCH_INS_LLACQ_W:
214
0
  case LOONGARCH_INS_LLACQ_D:
215
0
  case LOONGARCH_INS_SCREL_W:
216
0
  case LOONGARCH_INS_SCREL_D:
217
    // last register rj is memory operand
218
0
    LoongArch_get_detail_op(MI, -1)->type = LOONGARCH_OP_MEM;
219
0
    base = LoongArch_get_detail_op(MI, -1)->reg;
220
0
    LoongArch_get_detail_op(MI, -1)->mem.base = base;
221
0
    LoongArch_get_detail_op(MI, -1)->access =
222
0
      suppl_info->memory_access;
223
0
    return;
224
225
0
  case LOONGARCH_INS_LDGT_B:
226
0
  case LOONGARCH_INS_LDGT_H:
227
0
  case LOONGARCH_INS_LDGT_W:
228
0
  case LOONGARCH_INS_LDGT_D:
229
0
  case LOONGARCH_INS_LDLE_B:
230
0
  case LOONGARCH_INS_LDLE_H:
231
0
  case LOONGARCH_INS_LDLE_W:
232
0
  case LOONGARCH_INS_LDLE_D:
233
0
  case LOONGARCH_INS_STGT_B:
234
0
  case LOONGARCH_INS_STGT_H:
235
0
  case LOONGARCH_INS_STGT_W:
236
0
  case LOONGARCH_INS_STGT_D:
237
0
  case LOONGARCH_INS_STLE_B:
238
0
  case LOONGARCH_INS_STLE_H:
239
0
  case LOONGARCH_INS_STLE_W:
240
0
  case LOONGARCH_INS_STLE_D:
241
0
  case LOONGARCH_INS_FLDLE_S:
242
0
  case LOONGARCH_INS_FLDLE_D:
243
0
  case LOONGARCH_INS_FLDGT_S:
244
0
  case LOONGARCH_INS_FLDGT_D:
245
0
  case LOONGARCH_INS_FSTLE_S:
246
0
  case LOONGARCH_INS_FSTLE_D:
247
0
  case LOONGARCH_INS_FSTGT_S:
248
0
  case LOONGARCH_INS_FSTGT_D:
249
    // second register rj is memory operand
250
0
    LoongArch_get_detail_op(MI, -2)->type = LOONGARCH_OP_MEM;
251
0
    base = LoongArch_get_detail_op(MI, -2)->reg;
252
0
    LoongArch_get_detail_op(MI, -2)->mem.base = base;
253
0
    LoongArch_get_detail_op(MI, -2)->access =
254
0
      suppl_info->memory_access;
255
0
    return;
256
0
  default:
257
0
    break;
258
0
  }
259
260
0
  switch (suppl_info->form) {
261
0
  case LOONGARCH_INSN_FORM_FMT2RI12:   // ld, ldl, ldr, st, stl, str
262
0
  case LOONGARCH_INSN_FORM_FMT2RI14:   // ll, sc, ldptr, stptr
263
0
  case LOONGARCH_INSN_FORM_FMT2RI9_VRI:  // vldrepl.d
264
0
  case LOONGARCH_INSN_FORM_FMT2RI10_VRI:   // vldrepl.w
265
0
  case LOONGARCH_INSN_FORM_FMT2RI11_VRI:   // vldrepl.h
266
0
  case LOONGARCH_INSN_FORM_FMT2RI12_VRI:   // vld, vldrepl, vst
267
0
  case LOONGARCH_INSN_FORM_FMT2RI8I1_VRII: // vstelm.d
268
0
  case LOONGARCH_INSN_FORM_FMT2RI8I2_VRII: // vstelm.w
269
0
  case LOONGARCH_INSN_FORM_FMT2RI8I3_VRII: // vstelm.h
270
0
  case LOONGARCH_INSN_FORM_FMT2RI8I4_VRII: // vstelm.b
271
0
  case LOONGARCH_INSN_FORM_FMT2RI9_XRI:  // xvldrepl.d
272
0
  case LOONGARCH_INSN_FORM_FMT2RI10_XRI:   // xvldrepl.w
273
0
  case LOONGARCH_INSN_FORM_FMT2RI11_XRI:   // xvldrepl.h
274
0
  case LOONGARCH_INSN_FORM_FMT2RI12_XRI:   // xvld, xvldrepl, xvst
275
0
  case LOONGARCH_INSN_FORM_FMT2RI8I2_XRII: // xvstelm.d
276
0
  case LOONGARCH_INSN_FORM_FMT2RI8I3_XRII: // xvstelm.w
277
0
  case LOONGARCH_INSN_FORM_FMT2RI8I4_XRII: // xvstelm.h
278
0
  case LOONGARCH_INSN_FORM_FMT2RI8I5_XRII: // xvstelm.b
279
0
  case LOONGARCH_INSN_FORM_FMTPRELD:   // preld
280
0
  case LOONGARCH_INSN_FORM_FPFMT2RI12:   // fld, fst
281
    // immediate offset
282
0
    LoongArch_get_detail_op(MI, -2)->type = LOONGARCH_OP_MEM;
283
0
    base = LoongArch_get_detail_op(MI, -2)->reg;
284
0
    LoongArch_get_detail_op(MI, -2)->mem.base = base;
285
0
    LoongArch_get_detail_op(MI, -2)->mem.disp =
286
0
      LoongArch_get_detail_op(MI, -1)->imm;
287
0
    LoongArch_get_detail_op(MI, -2)->access =
288
0
      suppl_info->memory_access;
289
0
    LoongArch_dec_op_count(MI);
290
0
    break;
291
292
0
  case LOONGARCH_INSN_FORM_FMT3R: // ldx, stx, amo
293
0
    if (suppl_info->memory_access == CS_AC_READ_WRITE) {
294
      // amo: read + write
295
      // last register rj is memory operand
296
0
      LoongArch_get_detail_op(MI, -1)->type =
297
0
        LOONGARCH_OP_MEM;
298
0
      base = LoongArch_get_detail_op(MI, -1)->reg;
299
0
      LoongArch_get_detail_op(MI, -1)->mem.base = base;
300
0
      LoongArch_get_detail_op(MI, -1)->access =
301
0
        suppl_info->memory_access;
302
0
      break;
303
0
    }
304
    // fallthrough
305
306
0
  case LOONGARCH_INSN_FORM_FPFMTMEM:  // fldx, fstx
307
0
  case LOONGARCH_INSN_FORM_FMT3R_VRR: // vldx, vstx
308
0
  case LOONGARCH_INSN_FORM_FMT3R_XRR: // xvldx, xvstx
309
0
  case LOONGARCH_INSN_FORM_FMTPRELDX: // preldx
310
    // register offset
311
0
    LoongArch_get_detail_op(MI, -2)->type = LOONGARCH_OP_MEM;
312
0
    base = LoongArch_get_detail_op(MI, -2)->reg;
313
0
    LoongArch_get_detail_op(MI, -2)->mem.base = base;
314
0
    LoongArch_get_detail_op(MI, -2)->mem.index =
315
0
      LoongArch_get_detail_op(MI, -1)->reg;
316
0
    LoongArch_get_detail_op(MI, -2)->access =
317
0
      suppl_info->memory_access;
318
0
    LoongArch_dec_op_count(MI);
319
0
    break;
320
321
0
  default:
322
0
    CS_ASSERT_RET(0 && "Unknown LoongArch memory instruction");
323
0
    break;
324
0
  }
325
0
}
326
327
void LoongArch_rewrite_address_operand(MCInst *MI)
328
0
{
329
  // rewrite offset immediate operand to absolute address in direct branch instructions
330
  // convert e.g.
331
  // 0x1000: beqz $t0, 0x100c
332
  // op_count: 2
333
  //         operands[0].type: REG = t0
334
  //         operands[0].access: READ
335
  //         operands[1].type: IMM = 0xc
336
  //         operands[1].access: READ
337
  // to:
338
  // op_count: 2
339
  //         operands[0].type: REG = t0
340
  //         operands[0].access: READ
341
  //         operands[1].type: IMM = 0x100c
342
  //         operands[1].access: READ
343
344
0
  if (!detail_is_set(MI))
345
0
    return;
346
347
  // handle different types of branch instructions
348
0
  switch (MI->flat_insn->id) {
349
0
  case LOONGARCH_INS_B:
350
0
  case LOONGARCH_INS_BCEQZ:
351
0
  case LOONGARCH_INS_BCNEZ:
352
0
  case LOONGARCH_INS_BEQ:
353
0
  case LOONGARCH_INS_BEQZ:
354
0
  case LOONGARCH_INS_BGE:
355
0
  case LOONGARCH_INS_BGEU:
356
0
  case LOONGARCH_INS_BL:
357
0
  case LOONGARCH_INS_BLT:
358
0
  case LOONGARCH_INS_BLTU:
359
0
  case LOONGARCH_INS_BNE:
360
0
  case LOONGARCH_INS_BNEZ:
361
    // last operand is address operand
362
0
    LoongArch_get_detail_op(MI, -1)->imm += MI->address;
363
0
    return;
364
365
0
  default:
366
0
    break;
367
0
  }
368
0
}
369
370
void LoongArch_set_instr_map_data(MCInst *MI)
371
0
{
372
0
  map_cs_id(MI, loongarch_insns, ARR_SIZE(loongarch_insns));
373
0
  map_implicit_reads(MI, loongarch_insns);
374
0
  map_implicit_writes(MI, loongarch_insns);
375
0
  map_groups(MI, loongarch_insns);
376
0
  const loongarch_suppl_info *suppl_info =
377
0
    map_get_suppl_info(MI, loongarch_insns);
378
0
  if (suppl_info) {
379
0
    LoongArch_get_detail(MI)->format = suppl_info->form;
380
0
  }
381
0
}
382
383
bool LoongArch_getInstruction(csh handle, const uint8_t *code, size_t code_len,
384
            MCInst *instr, uint16_t *size, uint64_t address,
385
            void *info)
386
0
{
387
0
  uint64_t temp_size;
388
0
  LoongArch_init_cs_detail(instr);
389
0
  DecodeStatus Result = LoongArch_LLVM_getInstruction(instr, &temp_size, code,
390
0
                code_len, address, info);
391
0
  LoongArch_set_instr_map_data(instr);
392
0
  *size = temp_size;
393
0
  if (Result == MCDisassembler_SoftFail) {
394
0
    MCInst_setSoftFail(instr);
395
0
  }
396
0
  return Result != MCDisassembler_Fail;
397
0
}
398
399
/// Adds group to the instruction which are not defined in LLVM.
400
static void LoongArch_add_cs_groups(MCInst *MI)
401
0
{
402
0
  if (!MI->flat_insn->detail)
403
0
    return;
404
0
  unsigned Opcode = MI->flat_insn->id;
405
0
  cs_loongarch *loongarch = &(MI->flat_insn->detail->loongarch);
406
0
  switch (Opcode) {
407
0
  default:
408
0
    return;
409
0
  case LOONGARCH_INS_BL:
410
0
    add_group(MI, LOONGARCH_GRP_CALL);
411
0
    break;
412
0
  case LOONGARCH_INS_JIRL:
413
0
    if (loongarch->op_count == 3 &&
414
0
        loongarch->operands[0].reg == LOONGARCH_REG_RA) {
415
      // call: jirl ra, rj, offs16
416
0
      add_group(MI, LOONGARCH_GRP_CALL);
417
0
    } else if (loongarch->op_count == 0) {
418
      // ret
419
0
      add_group(MI, LOONGARCH_GRP_RET);
420
0
    } else if (loongarch->op_count == 1) {
421
      // jr rj
422
0
      add_group(MI, LOONGARCH_GRP_JUMP);
423
0
    } else if (loongarch->op_count == 3) {
424
      // none of the above, generic jirl
425
0
      add_group(MI, LOONGARCH_GRP_JUMP);
426
0
    }
427
0
    break;
428
0
  case LOONGARCH_INS_B:
429
0
  case LOONGARCH_INS_BCEQZ:
430
0
  case LOONGARCH_INS_BEQ:
431
0
  case LOONGARCH_INS_BEQZ:
432
0
  case LOONGARCH_INS_BGE:
433
0
  case LOONGARCH_INS_BGEU:
434
0
  case LOONGARCH_INS_BLT:
435
0
  case LOONGARCH_INS_BLTU:
436
0
  case LOONGARCH_INS_BNE:
437
0
  case LOONGARCH_INS_BNEZ:
438
0
    add_group(MI, LOONGARCH_GRP_JUMP);
439
0
    add_group(MI, LOONGARCH_GRP_BRANCH_RELATIVE);
440
0
    break;
441
0
  case LOONGARCH_INS_SYSCALL:
442
0
    add_group(MI, LOONGARCH_GRP_INT);
443
0
    break;
444
0
  case LOONGARCH_INS_ERTN:
445
0
    add_group(MI, LOONGARCH_GRP_IRET);
446
0
    add_group(MI, LOONGARCH_GRP_PRIVILEGE);
447
0
    break;
448
0
  case LOONGARCH_INS_CSRXCHG:
449
0
  case LOONGARCH_INS_CACOP:
450
0
  case LOONGARCH_INS_LDDIR:
451
0
  case LOONGARCH_INS_LDPTE:
452
0
  case LOONGARCH_INS_IOCSRRD_B:
453
0
  case LOONGARCH_INS_IOCSRRD_H:
454
0
  case LOONGARCH_INS_IOCSRRD_W:
455
0
  case LOONGARCH_INS_IOCSRRD_D:
456
0
  case LOONGARCH_INS_IOCSRWR_B:
457
0
  case LOONGARCH_INS_IOCSRWR_H:
458
0
  case LOONGARCH_INS_IOCSRWR_W:
459
0
  case LOONGARCH_INS_IOCSRWR_D:
460
0
  case LOONGARCH_INS_TLBCLR:
461
0
  case LOONGARCH_INS_TLBFLUSH:
462
0
  case LOONGARCH_INS_TLBSRCH:
463
0
  case LOONGARCH_INS_TLBRD:
464
0
  case LOONGARCH_INS_TLBWR:
465
0
  case LOONGARCH_INS_INVTLB:
466
0
    add_group(MI, LOONGARCH_GRP_PRIVILEGE);
467
0
    break;
468
0
  }
469
0
}
470
471
void LoongArch_printer(MCInst *MI, SStream *O,
472
           void * /* MCRegisterInfo* */ info)
473
0
{
474
0
  MCRegisterInfo *MRI = (MCRegisterInfo *)info;
475
0
  MI->MRI = MRI;
476
0
  MI->flat_insn->usesAliasDetails = map_use_alias_details(MI);
477
0
  LoongArch_LLVM_printInst(MI, MI->address, "", O);
478
479
0
  LoongArch_rewrite_memory_operand(MI);
480
0
  LoongArch_rewrite_address_operand(MI);
481
0
  LoongArch_add_cs_groups(MI);
482
0
#ifndef CAPSTONE_DIET
483
0
  map_set_alias_id(MI, O, insn_alias_mnem_map,
484
0
       ARR_SIZE(insn_alias_mnem_map));
485
0
#endif
486
0
}
487
488
void LoongArch_setup_op(cs_loongarch_op *op)
489
0
{
490
0
  memset(op, 0, sizeof(cs_loongarch_op));
491
0
  op->type = LOONGARCH_OP_INVALID;
492
0
}
493
494
void LoongArch_init_cs_detail(MCInst *MI)
495
0
{
496
0
  if (detail_is_set(MI)) {
497
0
    unsigned int i;
498
499
0
    memset(get_detail(MI), 0,
500
0
           offsetof(cs_detail, loongarch) + sizeof(cs_loongarch));
501
502
0
    for (i = 0; i < ARR_SIZE(LoongArch_get_detail(MI)->operands);
503
0
         i++)
504
0
      LoongArch_setup_op(
505
0
        &LoongArch_get_detail(MI)->operands[i]);
506
0
  }
507
0
}
508
509
static const map_insn_ops insn_operands[] = {
510
#include "LoongArchGenCSMappingInsnOp.inc"
511
};
512
513
void LoongArch_set_detail_op_imm(MCInst *MI, unsigned OpNum,
514
         loongarch_op_type ImmType, int64_t Imm)
515
0
{
516
0
  if (!detail_is_set(MI))
517
0
    return;
518
0
  CS_ASSERT_RET((map_get_op_type(MI, OpNum) & ~CS_OP_MEM) == CS_OP_IMM);
519
0
  CS_ASSERT_RET(ImmType == LOONGARCH_OP_IMM);
520
521
0
  LoongArch_get_detail_op(MI, 0)->type = ImmType;
522
0
  LoongArch_get_detail_op(MI, 0)->imm = Imm;
523
0
  LoongArch_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum);
524
0
  LoongArch_inc_op_count(MI);
525
0
}
526
527
void LoongArch_set_detail_op_reg(MCInst *MI, unsigned OpNum, loongarch_reg Reg)
528
0
{
529
0
  if (!detail_is_set(MI))
530
0
    return;
531
0
  CS_ASSERT_RET((map_get_op_type(MI, OpNum) & ~CS_OP_MEM) == CS_OP_REG);
532
533
0
  LoongArch_get_detail_op(MI, 0)->type = LOONGARCH_OP_REG;
534
0
  LoongArch_get_detail_op(MI, 0)->reg = Reg;
535
0
  LoongArch_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum);
536
0
  LoongArch_inc_op_count(MI);
537
0
}
538
539
void LoongArch_add_cs_detail(MCInst *MI, int /* loongarch_op_group */ op_group,
540
           va_list args)
541
0
{
542
0
  if (!detail_is_set(MI))
543
0
    return;
544
545
0
  unsigned OpNum = va_arg(args, unsigned);
546
  // Handle memory operands later
547
0
  cs_op_type op_type = map_get_op_type(MI, OpNum) & ~CS_OP_MEM;
548
549
  // Fill cs_detail
550
0
  switch (op_group) {
551
0
  default:
552
0
    printf("ERROR: Operand group %d not handled!\n", op_group);
553
0
    CS_ASSERT_RET(0);
554
0
  case LoongArch_OP_GROUP_Operand:
555
0
    if (op_type == CS_OP_IMM) {
556
0
      LoongArch_set_detail_op_imm(MI, OpNum, LOONGARCH_OP_IMM,
557
0
                MCInst_getOpVal(MI, OpNum));
558
0
    } else if (op_type == CS_OP_REG) {
559
0
      LoongArch_set_detail_op_reg(MI, OpNum,
560
0
                MCInst_getOpVal(MI, OpNum));
561
0
    } else
562
0
      CS_ASSERT_RET(0 && "Op type not handled.");
563
0
    break;
564
0
  case LoongArch_OP_GROUP_AtomicMemOp:
565
0
    CS_ASSERT_RET(op_type == CS_OP_REG);
566
    // converted to MEM operand later in LoongArch_rewrite_memory_operand
567
0
    LoongArch_set_detail_op_reg(MI, OpNum,
568
0
              MCInst_getOpVal(MI, OpNum));
569
0
    break;
570
0
  }
571
0
}
572
573
#endif