diff options
author | Alex Bennée <alex.bennee@linaro.org> | 2013-12-23 23:27:29 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2013-12-23 23:27:29 +0000 |
commit | b0ff21b4f96fa8223ec252ec3e99a8a9af86cf0c (patch) | |
tree | f93a28975de5f8b151e6d2d80c0280028badd345 /target-arm | |
parent | a5e94a9d767b2111608fe2013492392c7117cef5 (diff) |
target-arm: A64: add support for add, addi, sub, subi
Implement the non-carry forms of addition and subtraction
(immediate, extended register and shifted register).
This includes the code to calculate NZCV if the instruction
calls for setting the flags.
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'target-arm')
-rw-r--r-- | target-arm/translate-a64.c | 292 |
1 files changed, 286 insertions, 6 deletions
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index a2cc9f0d7a..c0057a2fb7 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -297,6 +297,102 @@ static inline void gen_logic_CC(int sf, TCGv_i64 result) tcg_gen_movi_i32(cpu_VF, 0); } +/* dest = T0 + T1; compute C, N, V and Z flags */ +static void gen_add_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + if (sf) { + TCGv_i64 result, flag, tmp; + result = tcg_temp_new_i64(); + flag = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); + + tcg_gen_movi_i64(tmp, 0); + tcg_gen_add2_i64(result, flag, t0, tmp, t1, tmp); + + tcg_gen_trunc_i64_i32(cpu_CF, flag); + + gen_set_NZ64(result); + + tcg_gen_xor_i64(flag, result, t0); + tcg_gen_xor_i64(tmp, t0, t1); + tcg_gen_andc_i64(flag, flag, tmp); + tcg_temp_free_i64(tmp); + tcg_gen_shri_i64(flag, flag, 32); + tcg_gen_trunc_i64_i32(cpu_VF, flag); + + tcg_gen_mov_i64(dest, result); + tcg_temp_free_i64(result); + tcg_temp_free_i64(flag); + } else { + /* 32 bit arithmetic */ + TCGv_i32 t0_32 = tcg_temp_new_i32(); + TCGv_i32 t1_32 = tcg_temp_new_i32(); + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_movi_i32(tmp, 0); + tcg_gen_trunc_i64_i32(t0_32, t0); + tcg_gen_trunc_i64_i32(t1_32, t1); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, t1_32, tmp); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); + tcg_gen_xor_i32(tmp, t0_32, t1_32); + tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); + tcg_gen_extu_i32_i64(dest, cpu_NF); + + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(t0_32); + tcg_temp_free_i32(t1_32); + } +} + +/* dest = T0 - T1; compute C, N, V and Z flags */ +static void gen_sub_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + if (sf) { + /* 64 bit arithmetic */ + TCGv_i64 result, flag, tmp; + + result = tcg_temp_new_i64(); + flag = tcg_temp_new_i64(); + tcg_gen_sub_i64(result, t0, t1); + + gen_set_NZ64(result); + + tcg_gen_setcond_i64(TCG_COND_GEU, flag, t0, t1); + tcg_gen_trunc_i64_i32(cpu_CF, flag); + + tcg_gen_xor_i64(flag, result, t0); + tmp = tcg_temp_new_i64(); + tcg_gen_xor_i64(tmp, t0, t1); + tcg_gen_and_i64(flag, flag, tmp); + tcg_temp_free_i64(tmp); + tcg_gen_shri_i64(flag, flag, 32); + tcg_gen_trunc_i64_i32(cpu_VF, flag); + tcg_gen_mov_i64(dest, result); + tcg_temp_free_i64(flag); + tcg_temp_free_i64(result); + } else { + /* 32 bit arithmetic */ + TCGv_i32 t0_32 = tcg_temp_new_i32(); + TCGv_i32 t1_32 = tcg_temp_new_i32(); + TCGv_i32 tmp; + + tcg_gen_trunc_i64_i32(t0_32, t0); + tcg_gen_trunc_i64_i32(t1_32, t1); + tcg_gen_sub_i32(cpu_NF, t0_32, t1_32); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0_32, t1_32); + tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); + tmp = tcg_temp_new_i32(); + tcg_gen_xor_i32(tmp, t0_32, t1_32); + tcg_temp_free_i32(t0_32); + tcg_temp_free_i32(t1_32); + tcg_gen_and_i32(cpu_VF, cpu_VF, tmp); + tcg_temp_free_i32(tmp); + tcg_gen_extu_i32_i64(dest, cpu_NF); + } +} + /* * Load/Store generators */ @@ -1328,10 +1424,68 @@ static void disas_pc_rel_adr(DisasContext *s, uint32_t insn) tcg_gen_movi_i64(cpu_reg(s, rd), base + offset); } -/* Add/subtract (immediate) */ +/* + * C3.4.1 Add/subtract (immediate) + * + * 31 30 29 28 24 23 22 21 10 9 5 4 0 + * +--+--+--+-----------+-----+-------------+-----+-----+ + * |sf|op| S| 1 0 0 0 1 |shift| imm12 | Rn | Rd | + * +--+--+--+-----------+-----+-------------+-----+-----+ + * + * sf: 0 -> 32bit, 1 -> 64bit + * op: 0 -> add , 1 -> sub + * S: 1 -> set flags + * shift: 00 -> LSL imm by 0, 01 -> LSL imm by 12 + */ static void disas_add_sub_imm(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + uint64_t imm = extract32(insn, 10, 12); + int shift = extract32(insn, 22, 2); + bool setflags = extract32(insn, 29, 1); + bool sub_op = extract32(insn, 30, 1); + bool is_64bit = extract32(insn, 31, 1); + + TCGv_i64 tcg_rn = cpu_reg_sp(s, rn); + TCGv_i64 tcg_rd = setflags ? cpu_reg(s, rd) : cpu_reg_sp(s, rd); + TCGv_i64 tcg_result; + + switch (shift) { + case 0x0: + break; + case 0x1: + imm <<= 12; + break; + default: + unallocated_encoding(s); + return; + } + + tcg_result = tcg_temp_new_i64(); + if (!setflags) { + if (sub_op) { + tcg_gen_subi_i64(tcg_result, tcg_rn, imm); + } else { + tcg_gen_addi_i64(tcg_result, tcg_rn, imm); + } + } else { + TCGv_i64 tcg_imm = tcg_const_i64(imm); + if (sub_op) { + gen_sub_CC(is_64bit, tcg_result, tcg_rn, tcg_imm); + } else { + gen_add_CC(is_64bit, tcg_result, tcg_rn, tcg_imm); + } + tcg_temp_free_i64(tcg_imm); + } + + if (is_64bit) { + tcg_gen_mov_i64(tcg_rd, tcg_result); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_result); + } + + tcg_temp_free_i64(tcg_result); } /* The input should be a value in the bottom e bits (with higher @@ -1789,16 +1943,142 @@ static void disas_logic_reg(DisasContext *s, uint32_t insn) } } -/* Add/subtract (extended register) */ +/* + * C3.5.1 Add/subtract (extended register) + * + * 31|30|29|28 24|23 22|21|20 16|15 13|12 10|9 5|4 0| + * +--+--+--+-----------+-----+--+-------+------+------+----+----+ + * |sf|op| S| 0 1 0 1 1 | opt | 1| Rm |option| imm3 | Rn | Rd | + * +--+--+--+-----------+-----+--+-------+------+------+----+----+ + * + * sf: 0 -> 32bit, 1 -> 64bit + * op: 0 -> add , 1 -> sub + * S: 1 -> set flags + * opt: 00 + * option: extension type (see DecodeRegExtend) + * imm3: optional shift to Rm + * + * Rd = Rn + LSL(extend(Rm), amount) + */ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int imm3 = extract32(insn, 10, 3); + int option = extract32(insn, 13, 3); + int rm = extract32(insn, 16, 5); + bool setflags = extract32(insn, 29, 1); + bool sub_op = extract32(insn, 30, 1); + bool sf = extract32(insn, 31, 1); + + TCGv_i64 tcg_rm, tcg_rn; /* temps */ + TCGv_i64 tcg_rd; + TCGv_i64 tcg_result; + + if (imm3 > 4) { + unallocated_encoding(s); + return; + } + + /* non-flag setting ops may use SP */ + if (!setflags) { + tcg_rn = read_cpu_reg_sp(s, rn, sf); + tcg_rd = cpu_reg_sp(s, rd); + } else { + tcg_rn = read_cpu_reg(s, rn, sf); + tcg_rd = cpu_reg(s, rd); + } + + tcg_rm = read_cpu_reg(s, rm, sf); + ext_and_shift_reg(tcg_rm, tcg_rm, option, imm3); + + tcg_result = tcg_temp_new_i64(); + + if (!setflags) { + if (sub_op) { + tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); + } else { + tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); + } + } else { + if (sub_op) { + gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); + } else { + gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); + } + } + + if (sf) { + tcg_gen_mov_i64(tcg_rd, tcg_result); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_result); + } + + tcg_temp_free_i64(tcg_result); } -/* Add/subtract (shifted register) */ +/* + * C3.5.2 Add/subtract (shifted register) + * + * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 + * +--+--+--+-----------+-----+--+-------+---------+------+------+ + * |sf|op| S| 0 1 0 1 1 |shift| 0| Rm | imm6 | Rn | Rd | + * +--+--+--+-----------+-----+--+-------+---------+------+------+ + * + * sf: 0 -> 32bit, 1 -> 64bit + * op: 0 -> add , 1 -> sub + * S: 1 -> set flags + * shift: 00 -> LSL, 01 -> LSR, 10 -> ASR, 11 -> RESERVED + * imm6: Shift amount to apply to Rm before the add/sub + */ static void disas_add_sub_reg(DisasContext *s, uint32_t insn) { - unsupported_encoding(s, insn); + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int imm6 = extract32(insn, 10, 6); + int rm = extract32(insn, 16, 5); + int shift_type = extract32(insn, 22, 2); + bool setflags = extract32(insn, 29, 1); + bool sub_op = extract32(insn, 30, 1); + bool sf = extract32(insn, 31, 1); + + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_rn, tcg_rm; + TCGv_i64 tcg_result; + + if ((shift_type == 3) || (!sf && (imm6 > 31))) { + unallocated_encoding(s); + return; + } + + tcg_rn = read_cpu_reg(s, rn, sf); + tcg_rm = read_cpu_reg(s, rm, sf); + + shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, imm6); + + tcg_result = tcg_temp_new_i64(); + + if (!setflags) { + if (sub_op) { + tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); + } else { + tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); + } + } else { + if (sub_op) { + gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); + } else { + gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); + } + } + + if (sf) { + tcg_gen_mov_i64(tcg_rd, tcg_result); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_result); + } + + tcg_temp_free_i64(tcg_result); } /* Data-processing (3 source) */ |