/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 |