Revision 4a08d475 target-arm/translate-a64.c
b/target-arm/translate-a64.c | ||
---|---|---|
99 | 99 |
cpu_fprintf(f, "\n"); |
100 | 100 |
} |
101 | 101 |
|
102 |
static int get_mem_index(DisasContext *s) |
|
103 |
{ |
|
104 |
#ifdef CONFIG_USER_ONLY |
|
105 |
return 1; |
|
106 |
#else |
|
107 |
return s->user; |
|
108 |
#endif |
|
109 |
} |
|
110 |
|
|
102 | 111 |
void gen_a64_set_pc_im(uint64_t val) |
103 | 112 |
{ |
104 | 113 |
tcg_gen_movi_i64(cpu_pc, val); |
... | ... | |
250 | 259 |
return v; |
251 | 260 |
} |
252 | 261 |
|
262 |
static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) |
|
263 |
{ |
|
264 |
TCGv_i64 v = new_tmp_a64(s); |
|
265 |
if (sf) { |
|
266 |
tcg_gen_mov_i64(v, cpu_X[reg]); |
|
267 |
} else { |
|
268 |
tcg_gen_ext32u_i64(v, cpu_X[reg]); |
|
269 |
} |
|
270 |
return v; |
|
271 |
} |
|
272 |
|
|
253 | 273 |
/* Set ZF and NF based on a 64 bit result. This is alas fiddlier |
254 | 274 |
* than the 32 bit equivalent. |
255 | 275 |
*/ |
... | ... | |
278 | 298 |
} |
279 | 299 |
|
280 | 300 |
/* |
301 |
* Load/Store generators |
|
302 |
*/ |
|
303 |
|
|
304 |
/* |
|
305 |
* Store from GPR register to memory |
|
306 |
*/ |
|
307 |
static void do_gpr_st(DisasContext *s, TCGv_i64 source, |
|
308 |
TCGv_i64 tcg_addr, int size) |
|
309 |
{ |
|
310 |
g_assert(size <= 3); |
|
311 |
tcg_gen_qemu_st_i64(source, tcg_addr, get_mem_index(s), MO_TE + size); |
|
312 |
} |
|
313 |
|
|
314 |
/* |
|
315 |
* Load from memory to GPR register |
|
316 |
*/ |
|
317 |
static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, |
|
318 |
int size, bool is_signed, bool extend) |
|
319 |
{ |
|
320 |
TCGMemOp memop = MO_TE + size; |
|
321 |
|
|
322 |
g_assert(size <= 3); |
|
323 |
|
|
324 |
if (is_signed) { |
|
325 |
memop += MO_SIGN; |
|
326 |
} |
|
327 |
|
|
328 |
tcg_gen_qemu_ld_i64(dest, tcg_addr, get_mem_index(s), memop); |
|
329 |
|
|
330 |
if (extend && is_signed) { |
|
331 |
g_assert(size < 3); |
|
332 |
tcg_gen_ext32u_i64(dest, dest); |
|
333 |
} |
|
334 |
} |
|
335 |
|
|
336 |
/* |
|
337 |
* Store from FP register to memory |
|
338 |
*/ |
|
339 |
static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) |
|
340 |
{ |
|
341 |
/* This writes the bottom N bits of a 128 bit wide vector to memory */ |
|
342 |
int freg_offs = offsetof(CPUARMState, vfp.regs[srcidx * 2]); |
|
343 |
TCGv_i64 tmp = tcg_temp_new_i64(); |
|
344 |
|
|
345 |
if (size < 4) { |
|
346 |
switch (size) { |
|
347 |
case 0: |
|
348 |
tcg_gen_ld8u_i64(tmp, cpu_env, freg_offs); |
|
349 |
break; |
|
350 |
case 1: |
|
351 |
tcg_gen_ld16u_i64(tmp, cpu_env, freg_offs); |
|
352 |
break; |
|
353 |
case 2: |
|
354 |
tcg_gen_ld32u_i64(tmp, cpu_env, freg_offs); |
|
355 |
break; |
|
356 |
case 3: |
|
357 |
tcg_gen_ld_i64(tmp, cpu_env, freg_offs); |
|
358 |
break; |
|
359 |
} |
|
360 |
tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TE + size); |
|
361 |
} else { |
|
362 |
TCGv_i64 tcg_hiaddr = tcg_temp_new_i64(); |
|
363 |
tcg_gen_ld_i64(tmp, cpu_env, freg_offs); |
|
364 |
tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TEQ); |
|
365 |
tcg_gen_qemu_st64(tmp, tcg_addr, get_mem_index(s)); |
|
366 |
tcg_gen_ld_i64(tmp, cpu_env, freg_offs + sizeof(float64)); |
|
367 |
tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); |
|
368 |
tcg_gen_qemu_st_i64(tmp, tcg_hiaddr, get_mem_index(s), MO_TEQ); |
|
369 |
tcg_temp_free_i64(tcg_hiaddr); |
|
370 |
} |
|
371 |
|
|
372 |
tcg_temp_free_i64(tmp); |
|
373 |
} |
|
374 |
|
|
375 |
/* |
|
376 |
* Load from memory to FP register |
|
377 |
*/ |
|
378 |
static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) |
|
379 |
{ |
|
380 |
/* This always zero-extends and writes to a full 128 bit wide vector */ |
|
381 |
int freg_offs = offsetof(CPUARMState, vfp.regs[destidx * 2]); |
|
382 |
TCGv_i64 tmplo = tcg_temp_new_i64(); |
|
383 |
TCGv_i64 tmphi; |
|
384 |
|
|
385 |
if (size < 4) { |
|
386 |
TCGMemOp memop = MO_TE + size; |
|
387 |
tmphi = tcg_const_i64(0); |
|
388 |
tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), memop); |
|
389 |
} else { |
|
390 |
TCGv_i64 tcg_hiaddr; |
|
391 |
tmphi = tcg_temp_new_i64(); |
|
392 |
tcg_hiaddr = tcg_temp_new_i64(); |
|
393 |
|
|
394 |
tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), MO_TEQ); |
|
395 |
tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); |
|
396 |
tcg_gen_qemu_ld_i64(tmphi, tcg_hiaddr, get_mem_index(s), MO_TEQ); |
|
397 |
tcg_temp_free_i64(tcg_hiaddr); |
|
398 |
} |
|
399 |
|
|
400 |
tcg_gen_st_i64(tmplo, cpu_env, freg_offs); |
|
401 |
tcg_gen_st_i64(tmphi, cpu_env, freg_offs + sizeof(float64)); |
|
402 |
|
|
403 |
tcg_temp_free_i64(tmplo); |
|
404 |
tcg_temp_free_i64(tmphi); |
|
405 |
} |
|
406 |
|
|
407 |
static inline void gen_check_sp_alignment(DisasContext *s) |
|
408 |
{ |
|
409 |
/* The AArch64 architecture mandates that (if enabled via PSTATE |
|
410 |
* or SCTLR bits) there is a check that SP is 16-aligned on every |
|
411 |
* SP-relative load or store (with an exception generated if it is not). |
|
412 |
* In line with general QEMU practice regarding misaligned accesses, |
|
413 |
* we omit these checks for the sake of guest program performance. |
|
414 |
* This function is provided as a hook so we can more easily add these |
|
415 |
* checks in future (possibly as a "favour catching guest program bugs |
|
416 |
* over speed" user selectable option). |
|
417 |
*/ |
|
418 |
} |
|
419 |
|
|
420 |
/* |
|
281 | 421 |
* the instruction disassembly implemented here matches |
282 | 422 |
* the instruction encoding classifications in chapter 3 (C3) |
283 | 423 |
* of the ARM Architecture Reference Manual (DDI0487A_a) |
... | ... | |
620 | 760 |
unsupported_encoding(s, insn); |
621 | 761 |
} |
622 | 762 |
|
623 |
/* Load/store pair (all forms) */ |
|
763 |
/* |
|
764 |
* C5.6.80 LDNP (Load Pair - non-temporal hint) |
|
765 |
* C5.6.81 LDP (Load Pair - non vector) |
|
766 |
* C5.6.82 LDPSW (Load Pair Signed Word - non vector) |
|
767 |
* C5.6.176 STNP (Store Pair - non-temporal hint) |
|
768 |
* C5.6.177 STP (Store Pair - non vector) |
|
769 |
* C6.3.165 LDNP (Load Pair of SIMD&FP - non-temporal hint) |
|
770 |
* C6.3.165 LDP (Load Pair of SIMD&FP) |
|
771 |
* C6.3.284 STNP (Store Pair of SIMD&FP - non-temporal hint) |
|
772 |
* C6.3.284 STP (Store Pair of SIMD&FP) |
|
773 |
* |
|
774 |
* 31 30 29 27 26 25 24 23 22 21 15 14 10 9 5 4 0 |
|
775 |
* +-----+-------+---+---+-------+---+-----------------------------+ |
|
776 |
* | opc | 1 0 1 | V | 0 | index | L | imm7 | Rt2 | Rn | Rt | |
|
777 |
* +-----+-------+---+---+-------+---+-------+-------+------+------+ |
|
778 |
* |
|
779 |
* opc: LDP/STP/LDNP/STNP 00 -> 32 bit, 10 -> 64 bit |
|
780 |
* LDPSW 01 |
|
781 |
* LDP/STP/LDNP/STNP (SIMD) 00 -> 32 bit, 01 -> 64 bit, 10 -> 128 bit |
|
782 |
* V: 0 -> GPR, 1 -> Vector |
|
783 |
* idx: 00 -> signed offset with non-temporal hint, 01 -> post-index, |
|
784 |
* 10 -> signed offset, 11 -> pre-index |
|
785 |
* L: 0 -> Store 1 -> Load |
|
786 |
* |
|
787 |
* Rt, Rt2 = GPR or SIMD registers to be stored |
|
788 |
* Rn = general purpose register containing address |
|
789 |
* imm7 = signed offset (multiple of 4 or 8 depending on size) |
|
790 |
*/ |
|
624 | 791 |
static void disas_ldst_pair(DisasContext *s, uint32_t insn) |
625 | 792 |
{ |
626 |
unsupported_encoding(s, insn); |
|
793 |
int rt = extract32(insn, 0, 5); |
|
794 |
int rn = extract32(insn, 5, 5); |
|
795 |
int rt2 = extract32(insn, 10, 5); |
|
796 |
int64_t offset = sextract32(insn, 15, 7); |
|
797 |
int index = extract32(insn, 23, 2); |
|
798 |
bool is_vector = extract32(insn, 26, 1); |
|
799 |
bool is_load = extract32(insn, 22, 1); |
|
800 |
int opc = extract32(insn, 30, 2); |
|
801 |
|
|
802 |
bool is_signed = false; |
|
803 |
bool postindex = false; |
|
804 |
bool wback = false; |
|
805 |
|
|
806 |
TCGv_i64 tcg_addr; /* calculated address */ |
|
807 |
int size; |
|
808 |
|
|
809 |
if (opc == 3) { |
|
810 |
unallocated_encoding(s); |
|
811 |
return; |
|
812 |
} |
|
813 |
|
|
814 |
if (is_vector) { |
|
815 |
size = 2 + opc; |
|
816 |
} else { |
|
817 |
size = 2 + extract32(opc, 1, 1); |
|
818 |
is_signed = extract32(opc, 0, 1); |
|
819 |
if (!is_load && is_signed) { |
|
820 |
unallocated_encoding(s); |
|
821 |
return; |
|
822 |
} |
|
823 |
} |
|
824 |
|
|
825 |
switch (index) { |
|
826 |
case 1: /* post-index */ |
|
827 |
postindex = true; |
|
828 |
wback = true; |
|
829 |
break; |
|
830 |
case 0: |
|
831 |
/* signed offset with "non-temporal" hint. Since we don't emulate |
|
832 |
* caches we don't care about hints to the cache system about |
|
833 |
* data access patterns, and handle this identically to plain |
|
834 |
* signed offset. |
|
835 |
*/ |
|
836 |
if (is_signed) { |
|
837 |
/* There is no non-temporal-hint version of LDPSW */ |
|
838 |
unallocated_encoding(s); |
|
839 |
return; |
|
840 |
} |
|
841 |
postindex = false; |
|
842 |
break; |
|
843 |
case 2: /* signed offset, rn not updated */ |
|
844 |
postindex = false; |
|
845 |
break; |
|
846 |
case 3: /* pre-index */ |
|
847 |
postindex = false; |
|
848 |
wback = true; |
|
849 |
break; |
|
850 |
} |
|
851 |
|
|
852 |
offset <<= size; |
|
853 |
|
|
854 |
if (rn == 31) { |
|
855 |
gen_check_sp_alignment(s); |
|
856 |
} |
|
857 |
|
|
858 |
tcg_addr = read_cpu_reg_sp(s, rn, 1); |
|
859 |
|
|
860 |
if (!postindex) { |
|
861 |
tcg_gen_addi_i64(tcg_addr, tcg_addr, offset); |
|
862 |
} |
|
863 |
|
|
864 |
if (is_vector) { |
|
865 |
if (is_load) { |
|
866 |
do_fp_ld(s, rt, tcg_addr, size); |
|
867 |
} else { |
|
868 |
do_fp_st(s, rt, tcg_addr, size); |
|
869 |
} |
|
870 |
} else { |
|
871 |
TCGv_i64 tcg_rt = cpu_reg(s, rt); |
|
872 |
if (is_load) { |
|
873 |
do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false); |
|
874 |
} else { |
|
875 |
do_gpr_st(s, tcg_rt, tcg_addr, size); |
|
876 |
} |
|
877 |
} |
|
878 |
tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size); |
|
879 |
if (is_vector) { |
|
880 |
if (is_load) { |
|
881 |
do_fp_ld(s, rt2, tcg_addr, size); |
|
882 |
} else { |
|
883 |
do_fp_st(s, rt2, tcg_addr, size); |
|
884 |
} |
|
885 |
} else { |
|
886 |
TCGv_i64 tcg_rt2 = cpu_reg(s, rt2); |
|
887 |
if (is_load) { |
|
888 |
do_gpr_ld(s, tcg_rt2, tcg_addr, size, is_signed, false); |
|
889 |
} else { |
|
890 |
do_gpr_st(s, tcg_rt2, tcg_addr, size); |
|
891 |
} |
|
892 |
} |
|
893 |
|
|
894 |
if (wback) { |
|
895 |
if (postindex) { |
|
896 |
tcg_gen_addi_i64(tcg_addr, tcg_addr, offset - (1 << size)); |
|
897 |
} else { |
|
898 |
tcg_gen_subi_i64(tcg_addr, tcg_addr, 1 << size); |
|
899 |
} |
|
900 |
tcg_gen_mov_i64(cpu_reg_sp(s, rn), tcg_addr); |
|
901 |
} |
|
627 | 902 |
} |
628 | 903 |
|
629 | 904 |
/* Load/store register (all forms) */ |
Also available in: Unified diff