X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=target-s390x%2Ftranslate.c;h=bc99a378a72ebe1df04b738794759652b62ddf88;hb=8d07d6c46597a885eb38d99cc6fff399ce69cd21;hp=2873f7d2e4b39fad50dc553907f3825a89c3292b;hpb=2db014b5a73f295f6edbdc2c8400a94ccfc90624;p=qemu.git diff --git a/target-s390x/translate.c b/target-s390x/translate.c index 2873f7d2e..bc99a378a 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -86,9 +86,11 @@ static uint64_t pc_to_link_info(DisasContext *s, uint64_t pc) return pc; } -void cpu_dump_state(CPUS390XState *env, FILE *f, fprintf_function cpu_fprintf, - int flags) +void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, + int flags) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; int i; if (env->cc_op > 3) { @@ -186,10 +188,6 @@ void s390x_translate_init(void) offsetof(CPUS390XState, fregs[i].d), cpu_reg_names[i + 16]); } - - /* register helpers */ -#define GEN_HELPER 2 -#include "helper.h" } static TCGv_i64 load_reg(int reg) @@ -331,61 +329,69 @@ static inline void check_privileged(DisasContext *s) static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) { - TCGv_i64 tmp; + TCGv_i64 tmp = tcg_temp_new_i64(); + bool need_31 = !(s->tb->flags & FLAG_MASK_64); - /* 31-bitify the immediate part; register contents are dealt with below */ - if (!(s->tb->flags & FLAG_MASK_64)) { - d2 &= 0x7fffffffUL; - } + /* Note that d2 is limited to 20 bits, signed. If we crop negative + displacements early we create larger immedate addends. */ - if (x2) { - if (d2) { - tmp = tcg_const_i64(d2); - tcg_gen_add_i64(tmp, tmp, regs[x2]); - } else { - tmp = load_reg(x2); - } - if (b2) { - tcg_gen_add_i64(tmp, tmp, regs[b2]); - } + /* Note that addi optimizes the imm==0 case. */ + if (b2 && x2) { + tcg_gen_add_i64(tmp, regs[b2], regs[x2]); + tcg_gen_addi_i64(tmp, tmp, d2); } else if (b2) { - if (d2) { - tmp = tcg_const_i64(d2); - tcg_gen_add_i64(tmp, tmp, regs[b2]); - } else { - tmp = load_reg(b2); - } + tcg_gen_addi_i64(tmp, regs[b2], d2); + } else if (x2) { + tcg_gen_addi_i64(tmp, regs[x2], d2); } else { - tmp = tcg_const_i64(d2); + if (need_31) { + d2 &= 0x7fffffff; + need_31 = false; + } + tcg_gen_movi_i64(tmp, d2); } - - /* 31-bit mode mask if there are values loaded from registers */ - if (!(s->tb->flags & FLAG_MASK_64) && (x2 || b2)) { - tcg_gen_andi_i64(tmp, tmp, 0x7fffffffUL); + if (need_31) { + tcg_gen_andi_i64(tmp, tmp, 0x7fffffff); } return tmp; } +static inline bool live_cc_data(DisasContext *s) +{ + return (s->cc_op != CC_OP_DYNAMIC + && s->cc_op != CC_OP_STATIC + && s->cc_op > 3); +} + static inline void gen_op_movi_cc(DisasContext *s, uint32_t val) { + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_src); + tcg_gen_discard_i64(cc_dst); + tcg_gen_discard_i64(cc_vr); + } s->cc_op = CC_OP_CONST0 + val; } static void gen_op_update1_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 dst) { - tcg_gen_discard_i64(cc_src); + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_src); + tcg_gen_discard_i64(cc_vr); + } tcg_gen_mov_i64(cc_dst, dst); - tcg_gen_discard_i64(cc_vr); s->cc_op = op; } static void gen_op_update2_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 src, TCGv_i64 dst) { + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_vr); + } tcg_gen_mov_i64(cc_src, src); tcg_gen_mov_i64(cc_dst, dst); - tcg_gen_discard_i64(cc_vr); s->cc_op = op; } @@ -421,9 +427,11 @@ static void gen_set_cc_nz_f128(DisasContext *s, TCGv_i64 vh, TCGv_i64 vl) /* CC value is in env->cc_op */ static void set_cc_static(DisasContext *s) { - tcg_gen_discard_i64(cc_src); - tcg_gen_discard_i64(cc_dst); - tcg_gen_discard_i64(cc_vr); + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_src); + tcg_gen_discard_i64(cc_dst); + tcg_gen_discard_i64(cc_vr); + } s->cc_op = CC_OP_STATIC; } @@ -559,30 +567,29 @@ static void account_inline_branch(DisasContext *s, int cc_op) } /* Table of mask values to comparison codes, given a comparison as input. - For a true comparison CC=3 will never be set, but we treat this - conservatively for possible use when CC=3 indicates overflow. */ + For such, CC=3 should not be possible. */ static const TCGCond ltgt_cond[16] = { TCG_COND_NEVER, TCG_COND_NEVER, /* | | | x */ - TCG_COND_GT, TCG_COND_NEVER, /* | | GT | x */ - TCG_COND_LT, TCG_COND_NEVER, /* | LT | | x */ - TCG_COND_NE, TCG_COND_NEVER, /* | LT | GT | x */ - TCG_COND_EQ, TCG_COND_NEVER, /* EQ | | | x */ - TCG_COND_GE, TCG_COND_NEVER, /* EQ | | GT | x */ - TCG_COND_LE, TCG_COND_NEVER, /* EQ | LT | | x */ + TCG_COND_GT, TCG_COND_GT, /* | | GT | x */ + TCG_COND_LT, TCG_COND_LT, /* | LT | | x */ + TCG_COND_NE, TCG_COND_NE, /* | LT | GT | x */ + TCG_COND_EQ, TCG_COND_EQ, /* EQ | | | x */ + TCG_COND_GE, TCG_COND_GE, /* EQ | | GT | x */ + TCG_COND_LE, TCG_COND_LE, /* EQ | LT | | x */ TCG_COND_ALWAYS, TCG_COND_ALWAYS, /* EQ | LT | GT | x */ }; /* Table of mask values to comparison codes, given a logic op as input. For such, only CC=0 and CC=1 should be possible. */ static const TCGCond nz_cond[16] = { - /* | | x | x */ - TCG_COND_NEVER, TCG_COND_NEVER, TCG_COND_NEVER, TCG_COND_NEVER, - /* | NE | x | x */ - TCG_COND_NE, TCG_COND_NE, TCG_COND_NE, TCG_COND_NE, - /* EQ | | x | x */ - TCG_COND_EQ, TCG_COND_EQ, TCG_COND_EQ, TCG_COND_EQ, - /* EQ | NE | x | x */ - TCG_COND_ALWAYS, TCG_COND_ALWAYS, TCG_COND_ALWAYS, TCG_COND_ALWAYS, + TCG_COND_NEVER, TCG_COND_NEVER, /* | | x | x */ + TCG_COND_NEVER, TCG_COND_NEVER, + TCG_COND_NE, TCG_COND_NE, /* | NE | x | x */ + TCG_COND_NE, TCG_COND_NE, + TCG_COND_EQ, TCG_COND_EQ, /* EQ | | x | x */ + TCG_COND_EQ, TCG_COND_EQ, + TCG_COND_ALWAYS, TCG_COND_ALWAYS, /* EQ | NE | x | x */ + TCG_COND_ALWAYS, TCG_COND_ALWAYS, }; /* Interpret MASK in terms of S->CC_OP, and fill in C with all the @@ -675,6 +682,49 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) account_inline_branch(s, old_cc_op); break; + case CC_OP_ADDU_32: + case CC_OP_ADDU_64: + switch (mask) { + case 8 | 2: /* vr == 0 */ + cond = TCG_COND_EQ; + break; + case 4 | 1: /* vr != 0 */ + cond = TCG_COND_NE; + break; + case 8 | 4: /* no carry -> vr >= src */ + cond = TCG_COND_GEU; + break; + case 2 | 1: /* carry -> vr < src */ + cond = TCG_COND_LTU; + break; + default: + goto do_dynamic; + } + account_inline_branch(s, old_cc_op); + break; + + case CC_OP_SUBU_32: + case CC_OP_SUBU_64: + /* Note that CC=0 is impossible; treat it as dont-care. */ + switch (mask & 7) { + case 2: /* zero -> op1 == op2 */ + cond = TCG_COND_EQ; + break; + case 4 | 1: /* !zero -> op1 != op2 */ + cond = TCG_COND_NE; + break; + case 4: /* borrow (!carry) -> op1 < op2 */ + cond = TCG_COND_LTU; + break; + case 2 | 1: /* !borrow (carry) -> op1 >= op2 */ + cond = TCG_COND_GEU; + break; + default: + goto do_dynamic; + } + account_inline_branch(s, old_cc_op); + break; + default: do_dynamic: /* Calculate cc value. */ @@ -702,6 +752,7 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) break; case CC_OP_LTGT_32: case CC_OP_LTUGTU_32: + case CC_OP_SUBU_32: c->is_64 = false; c->u.s32.a = tcg_temp_new_i32(); tcg_gen_trunc_i64_i32(c->u.s32.a, cc_src); @@ -718,6 +769,7 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) break; case CC_OP_LTGT_64: case CC_OP_LTUGTU_64: + case CC_OP_SUBU_64: c->u.s64.a = cc_src; c->u.s64.b = cc_dst; c->g1 = c->g2 = true; @@ -731,6 +783,29 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) tcg_gen_and_i64(c->u.s64.a, cc_src, cc_dst); break; + case CC_OP_ADDU_32: + c->is_64 = false; + c->u.s32.a = tcg_temp_new_i32(); + c->u.s32.b = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(c->u.s32.a, cc_vr); + if (cond == TCG_COND_EQ || cond == TCG_COND_NE) { + tcg_gen_movi_i32(c->u.s32.b, 0); + } else { + tcg_gen_trunc_i64_i32(c->u.s32.b, cc_src); + } + break; + + case CC_OP_ADDU_64: + c->u.s64.a = cc_vr; + c->g1 = true; + if (cond == TCG_COND_EQ || cond == TCG_COND_NE) { + c->u.s64.b = tcg_const_i64(0); + } else { + c->u.s64.b = cc_src; + c->g2 = true; + } + break; + case CC_OP_STATIC: c->is_64 = false; c->u.s32.a = cc_op; @@ -993,6 +1068,18 @@ typedef struct { TCGv_i64 addr1; } DisasOps; +/* Instructions can place constraints on their operands, raising specification + exceptions if they are violated. To make this easy to automate, each "in1", + "in2", "prep", "wout" helper will have a SPEC_ define that equals one + of the following, or 0. To make this easy to document, we'll put the + SPEC_ defines next to . */ + +#define SPEC_r1_even 1 +#define SPEC_r2_even 2 +#define SPEC_r3_even 4 +#define SPEC_r1_f128 8 +#define SPEC_r2_f128 16 + /* Return values from translate_one, indicating the state of the TB. */ typedef enum { /* Continue the TB. */ @@ -1036,8 +1123,9 @@ typedef enum DisasFacility { struct DisasInsn { unsigned opc:16; - DisasFormat fmt:6; - DisasFacility fac:6; + DisasFormat fmt:8; + DisasFacility fac:8; + unsigned spec:8; const char *name; @@ -1052,7 +1140,7 @@ struct DisasInsn { }; /* ====================================================================== */ -/* Miscelaneous helpers, used by several operations. */ +/* Miscellaneous helpers, used by several operations. */ static void help_l2_shift(DisasContext *s, DisasFields *f, DisasOps *o, int mask) @@ -1077,7 +1165,7 @@ static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest) update_cc_op(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((tcg_target_long)s->tb); + tcg_gen_exit_tb((uintptr_t)s->tb); return EXIT_GOTO_TB; } else { tcg_gen_movi_i64(psw_addr, dest); @@ -1135,13 +1223,13 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, /* Branch not taken. */ tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, s->next_pc); - tcg_gen_exit_tb((tcg_target_long)s->tb + 0); + tcg_gen_exit_tb((uintptr_t)s->tb + 0); /* Branch taken. */ gen_set_label(lab); tcg_gen_goto_tb(1); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((tcg_target_long)s->tb + 1); + tcg_gen_exit_tb((uintptr_t)s->tb + 1); ret = EXIT_GOTO_TB; } else { @@ -1164,7 +1252,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, update_cc_op(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, s->next_pc); - tcg_gen_exit_tb((tcg_target_long)s->tb + 0); + tcg_gen_exit_tb((uintptr_t)s->tb + 0); gen_set_label(lab); if (is_imm) { @@ -1247,18 +1335,28 @@ static ExitStatus op_add(DisasContext *s, DisasOps *o) static ExitStatus op_addc(DisasContext *s, DisasOps *o) { - TCGv_i64 cc; + DisasCompare cmp; + TCGv_i64 carry; tcg_gen_add_i64(o->out, o->in1, o->in2); - /* XXX possible optimization point */ - gen_op_calc_cc(s); - cc = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(cc, cc_op); - tcg_gen_shri_i64(cc, cc, 1); + /* The carry flag is the msb of CC, therefore the branch mask that would + create that comparison is 3. Feeding the generated comparison to + setcond produces the carry flag that we desire. */ + disas_jcc(s, &cmp, 3); + carry = tcg_temp_new_i64(); + if (cmp.is_64) { + tcg_gen_setcond_i64(cmp.cond, carry, cmp.u.s64.a, cmp.u.s64.b); + } else { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_setcond_i32(cmp.cond, t, cmp.u.s32.a, cmp.u.s32.b); + tcg_gen_extu_i32_i64(carry, t); + tcg_temp_free_i32(t); + } + free_compare(&cmp); - tcg_gen_add_i64(o->out, o->out, cc); - tcg_temp_free_i64(cc); + tcg_gen_add_i64(o->out, o->out, carry); + tcg_temp_free_i64(carry); return NO_EXIT; } @@ -1433,9 +1531,7 @@ static ExitStatus op_cj(DisasContext *s, DisasOps *o) bool is_imm; DisasCompare c; - /* Bit 3 of the m3 field is reserved and should be zero. - Choose to ignore it wrt the ltgt_cond table above. */ - c.cond = ltgt_cond[m3 & 14]; + c.cond = ltgt_cond[m3]; if (s->insn->data) { c.cond = tcg_unsigned_cond(c.cond); } @@ -1731,18 +1827,102 @@ static ExitStatus op_cps(DisasContext *s, DisasOps *o) static ExitStatus op_cs(DisasContext *s, DisasOps *o) { - int r3 = get_field(s->fields, r3); - potential_page_fault(s); - gen_helper_cs(o->out, cpu_env, o->in1, o->in2, regs[r3]); + /* FIXME: needs an atomic solution for CONFIG_USER_ONLY. */ + int d2 = get_field(s->fields, d2); + int b2 = get_field(s->fields, b2); + int is_64 = s->insn->data; + TCGv_i64 addr, mem, cc, z; + + /* Note that in1 = R3 (new value) and + in2 = (zero-extended) R1 (expected value). */ + + /* Load the memory into the (temporary) output. While the PoO only talks + about moving the memory to R1 on inequality, if we include equality it + means that R1 is equal to the memory in all conditions. */ + addr = get_address(s, 0, b2, d2); + if (is_64) { + tcg_gen_qemu_ld64(o->out, addr, get_mem_index(s)); + } else { + tcg_gen_qemu_ld32u(o->out, addr, get_mem_index(s)); + } + + /* Are the memory and expected values (un)equal? Note that this setcond + produces the output CC value, thus the NE sense of the test. */ + cc = tcg_temp_new_i64(); + tcg_gen_setcond_i64(TCG_COND_NE, cc, o->in2, o->out); + + /* If the memory and expected values are equal (CC==0), copy R3 to MEM. + Recall that we are allowed to unconditionally issue the store (and + thus any possible write trap), so (re-)store the original contents + of MEM in case of inequality. */ + z = tcg_const_i64(0); + mem = tcg_temp_new_i64(); + tcg_gen_movcond_i64(TCG_COND_EQ, mem, cc, z, o->in1, o->out); + if (is_64) { + tcg_gen_qemu_st64(mem, addr, get_mem_index(s)); + } else { + tcg_gen_qemu_st32(mem, addr, get_mem_index(s)); + } + tcg_temp_free_i64(z); + tcg_temp_free_i64(mem); + tcg_temp_free_i64(addr); + + /* Store CC back to cc_op. Wait until after the store so that any + exception gets the old cc_op value. */ + tcg_gen_trunc_i64_i32(cc_op, cc); + tcg_temp_free_i64(cc); set_cc_static(s); return NO_EXIT; } -static ExitStatus op_csg(DisasContext *s, DisasOps *o) +static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) { + /* FIXME: needs an atomic solution for CONFIG_USER_ONLY. */ + int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); - potential_page_fault(s); - gen_helper_csg(o->out, cpu_env, o->in1, o->in2, regs[r3]); + int d2 = get_field(s->fields, d2); + int b2 = get_field(s->fields, b2); + TCGv_i64 addrh, addrl, memh, meml, outh, outl, cc, z; + + /* Note that R1:R1+1 = expected value and R3:R3+1 = new value. */ + + addrh = get_address(s, 0, b2, d2); + addrl = get_address(s, 0, b2, d2 + 8); + outh = tcg_temp_new_i64(); + outl = tcg_temp_new_i64(); + + tcg_gen_qemu_ld64(outh, addrh, get_mem_index(s)); + tcg_gen_qemu_ld64(outl, addrl, get_mem_index(s)); + + /* Fold the double-word compare with arithmetic. */ + cc = tcg_temp_new_i64(); + z = tcg_temp_new_i64(); + tcg_gen_xor_i64(cc, outh, regs[r1]); + tcg_gen_xor_i64(z, outl, regs[r1 + 1]); + tcg_gen_or_i64(cc, cc, z); + tcg_gen_movi_i64(z, 0); + tcg_gen_setcond_i64(TCG_COND_NE, cc, cc, z); + + memh = tcg_temp_new_i64(); + meml = tcg_temp_new_i64(); + tcg_gen_movcond_i64(TCG_COND_EQ, memh, cc, z, regs[r3], outh); + tcg_gen_movcond_i64(TCG_COND_EQ, meml, cc, z, regs[r3 + 1], outl); + tcg_temp_free_i64(z); + + tcg_gen_qemu_st64(memh, addrh, get_mem_index(s)); + tcg_gen_qemu_st64(meml, addrl, get_mem_index(s)); + tcg_temp_free_i64(memh); + tcg_temp_free_i64(meml); + tcg_temp_free_i64(addrh); + tcg_temp_free_i64(addrl); + + /* Save back state now that we've passed all exceptions. */ + tcg_gen_mov_i64(regs[r1], outh); + tcg_gen_mov_i64(regs[r1 + 1], outl); + tcg_gen_trunc_i64_i32(cc_op, cc); + tcg_temp_free_i64(outh); + tcg_temp_free_i64(outl); + tcg_temp_free_i64(cc); set_cc_static(s); return NO_EXIT; } @@ -1759,29 +1939,6 @@ static ExitStatus op_csp(DisasContext *s, DisasOps *o) } #endif -static ExitStatus op_cds(DisasContext *s, DisasOps *o) -{ - int r3 = get_field(s->fields, r3); - TCGv_i64 in3 = tcg_temp_new_i64(); - tcg_gen_deposit_i64(in3, regs[r3 + 1], regs[r3], 32, 32); - potential_page_fault(s); - gen_helper_csg(o->out, cpu_env, o->in1, o->in2, in3); - tcg_temp_free_i64(in3); - set_cc_static(s); - return NO_EXIT; -} - -static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) -{ - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - potential_page_fault(s); - /* XXX rewrite in tcg */ - gen_helper_cdsg(cc_op, cpu_env, r1, o->in2, r3); - set_cc_static(s); - return NO_EXIT; -} - static ExitStatus op_cvd(DisasContext *s, DisasOps *o) { TCGv_i64 t1 = tcg_temp_new_i64(); @@ -1801,9 +1958,7 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o) TCGv_i32 t; TCGCond c; - /* Bit 3 of the m3 field is reserved and should be zero. - Choose to ignore it wrt the ltgt_cond table above. */ - c = tcg_invert_cond(ltgt_cond[m3 & 14]); + c = tcg_invert_cond(ltgt_cond[m3]); if (s->insn->data) { c = tcg_unsigned_cond(c); } @@ -2409,8 +2564,7 @@ static ExitStatus op_mul(DisasContext *s, DisasOps *o) static ExitStatus op_mul128(DisasContext *s, DisasOps *o) { - gen_helper_mul128(o->out, cpu_env, o->in1, o->in2); - return_low128(o->out2); + tcg_gen_mulu2_i64(o->out2, o->out, o->in1, o->in2); return NO_EXIT; } @@ -2909,6 +3063,52 @@ static ExitStatus op_sfpc(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_sfas(DisasContext *s, DisasOps *o) +{ + gen_helper_sfas(cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_srnm(DisasContext *s, DisasOps *o) +{ + int b2 = get_field(s->fields, b2); + int d2 = get_field(s->fields, d2); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + int mask, pos, len; + + switch (s->fields->op2) { + case 0x99: /* SRNM */ + pos = 0, len = 2; + break; + case 0xb8: /* SRNMB */ + pos = 0, len = 3; + break; + case 0xb9: /* SRNMT */ + pos = 4, len = 3; + break; + default: + tcg_abort(); + } + mask = (1 << len) - 1; + + /* Insert the value into the appropriate field of the FPC. */ + if (b2 == 0) { + tcg_gen_movi_i64(t1, d2 & mask); + } else { + tcg_gen_addi_i64(t1, regs[b2], d2); + tcg_gen_andi_i64(t1, t1, mask); + } + tcg_gen_ld32u_i64(t2, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_gen_deposit_i64(t2, t2, t1, pos, len); + tcg_temp_free_i64(t1); + + /* Then install the new FPC to set the rounding mode in fpu_status. */ + gen_helper_sfpc(cpu_env, t2); + tcg_temp_free_i64(t2); + return NO_EXIT; +} + #ifndef CONFIG_USER_ONLY static ExitStatus op_spka(DisasContext *s, DisasOps *o) { @@ -3259,19 +3459,27 @@ static ExitStatus op_sub(DisasContext *s, DisasOps *o) static ExitStatus op_subb(DisasContext *s, DisasOps *o) { - TCGv_i64 cc; + DisasCompare cmp; + TCGv_i64 borrow; - assert(!o->g_in2); - tcg_gen_not_i64(o->in2, o->in2); - tcg_gen_add_i64(o->out, o->in1, o->in2); + tcg_gen_sub_i64(o->out, o->in1, o->in2); - /* XXX possible optimization point */ - gen_op_calc_cc(s); - cc = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(cc, cc_op); - tcg_gen_shri_i64(cc, cc, 1); - tcg_gen_add_i64(o->out, o->out, cc); - tcg_temp_free_i64(cc); + /* The !borrow flag is the msb of CC. Since we want the inverse of + that, we ask for a comparison of CC=0 | CC=1 -> mask of 8 | 4. */ + disas_jcc(s, &cmp, 8 | 4); + borrow = tcg_temp_new_i64(); + if (cmp.is_64) { + tcg_gen_setcond_i64(cmp.cond, borrow, cmp.u.s64.a, cmp.u.s64.b); + } else { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_setcond_i32(cmp.cond, t, cmp.u.s32.a, cmp.u.s32.b); + tcg_gen_extu_i32_i64(borrow, t); + tcg_temp_free_i32(t); + } + free_compare(&cmp); + + tcg_gen_sub_i64(o->out, o->out, borrow); + tcg_temp_free_i64(borrow); return NO_EXIT; } @@ -3346,10 +3554,54 @@ static ExitStatus op_unpk(DisasContext *s, DisasOps *o) static ExitStatus op_xc(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + int d1 = get_field(s->fields, d1); + int d2 = get_field(s->fields, d2); + int b1 = get_field(s->fields, b1); + int b2 = get_field(s->fields, b2); + int l = get_field(s->fields, l1); + TCGv_i32 t32; + + o->addr1 = get_address(s, 0, b1, d1); + + /* If the addresses are identical, this is a store/memset of zero. */ + if (b1 == b2 && d1 == d2 && (l + 1) <= 32) { + o->in2 = tcg_const_i64(0); + + l++; + while (l >= 8) { + tcg_gen_qemu_st64(o->in2, o->addr1, get_mem_index(s)); + l -= 8; + if (l > 0) { + tcg_gen_addi_i64(o->addr1, o->addr1, 8); + } + } + if (l >= 4) { + tcg_gen_qemu_st32(o->in2, o->addr1, get_mem_index(s)); + l -= 4; + if (l > 0) { + tcg_gen_addi_i64(o->addr1, o->addr1, 4); + } + } + if (l >= 2) { + tcg_gen_qemu_st16(o->in2, o->addr1, get_mem_index(s)); + l -= 2; + if (l > 0) { + tcg_gen_addi_i64(o->addr1, o->addr1, 2); + } + } + if (l) { + tcg_gen_qemu_st8(o->in2, o->addr1, get_mem_index(s)); + } + gen_op_movi_cc(s, 0); + return NO_EXIT; + } + + /* But in general we'll defer to a helper. */ + o->in2 = get_address(s, 0, b2, d2); + t32 = tcg_const_i32(l); potential_page_fault(s); - gen_helper_xc(cc_op, cpu_env, l, o->addr1, o->in2); - tcg_temp_free_i32(l); + gen_helper_xc(cc_op, cpu_env, t32, o->addr1, o->in2); + tcg_temp_free_i32(t32); set_cc_static(s); return NO_EXIT; } @@ -3552,7 +3804,7 @@ static void cout_tm64(DisasContext *s, DisasOps *o) } /* ====================================================================== */ -/* The "PREPeration" generators. These initialize the DisasOps.OUT fields +/* The "PREParation" generators. These initialize the DisasOps.OUT fields with the TCG register to which we will write. Used in combination with the "wout" generators, in some cases we need a new temporary, and in some cases we can write to a TCG global. */ @@ -3561,42 +3813,46 @@ static void prep_new(DisasContext *s, DisasFields *f, DisasOps *o) { o->out = tcg_temp_new_i64(); } +#define SPEC_prep_new 0 static void prep_new_P(DisasContext *s, DisasFields *f, DisasOps *o) { o->out = tcg_temp_new_i64(); o->out2 = tcg_temp_new_i64(); } +#define SPEC_prep_new_P 0 static void prep_r1(DisasContext *s, DisasFields *f, DisasOps *o) { o->out = regs[get_field(f, r1)]; o->g_out = true; } +#define SPEC_prep_r1 0 static void prep_r1_P(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be even. */ int r1 = get_field(f, r1); o->out = regs[r1]; - o->out2 = regs[(r1 + 1) & 15]; + o->out2 = regs[r1 + 1]; o->g_out = o->g_out2 = true; } +#define SPEC_prep_r1_P SPEC_r1_even static void prep_f1(DisasContext *s, DisasFields *f, DisasOps *o) { o->out = fregs[get_field(f, r1)]; o->g_out = true; } +#define SPEC_prep_f1 0 static void prep_x1(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be < 14. */ int r1 = get_field(f, r1); o->out = fregs[r1]; - o->out2 = fregs[(r1 + 2) & 15]; + o->out2 = fregs[r1 + 2]; o->g_out = o->g_out2 = true; } +#define SPEC_prep_x1 SPEC_r1_f128 /* ====================================================================== */ /* The "Write OUTput" generators. These generally perform some non-trivial @@ -3608,58 +3864,64 @@ static void wout_r1(DisasContext *s, DisasFields *f, DisasOps *o) { store_reg(get_field(f, r1), o->out); } +#define SPEC_wout_r1 0 static void wout_r1_8(DisasContext *s, DisasFields *f, DisasOps *o) { int r1 = get_field(f, r1); tcg_gen_deposit_i64(regs[r1], regs[r1], o->out, 0, 8); } +#define SPEC_wout_r1_8 0 static void wout_r1_16(DisasContext *s, DisasFields *f, DisasOps *o) { int r1 = get_field(f, r1); tcg_gen_deposit_i64(regs[r1], regs[r1], o->out, 0, 16); } +#define SPEC_wout_r1_16 0 static void wout_r1_32(DisasContext *s, DisasFields *f, DisasOps *o) { store_reg32_i64(get_field(f, r1), o->out); } +#define SPEC_wout_r1_32 0 static void wout_r1_P32(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be even. */ int r1 = get_field(f, r1); store_reg32_i64(r1, o->out); - store_reg32_i64((r1 + 1) & 15, o->out2); + store_reg32_i64(r1 + 1, o->out2); } +#define SPEC_wout_r1_P32 SPEC_r1_even static void wout_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be even. */ int r1 = get_field(f, r1); - store_reg32_i64((r1 + 1) & 15, o->out); + store_reg32_i64(r1 + 1, o->out); tcg_gen_shri_i64(o->out, o->out, 32); store_reg32_i64(r1, o->out); } +#define SPEC_wout_r1_D32 SPEC_r1_even static void wout_e1(DisasContext *s, DisasFields *f, DisasOps *o) { store_freg32_i64(get_field(f, r1), o->out); } +#define SPEC_wout_e1 0 static void wout_f1(DisasContext *s, DisasFields *f, DisasOps *o) { store_freg(get_field(f, r1), o->out); } +#define SPEC_wout_f1 0 static void wout_x1(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be < 14. */ int f1 = get_field(s->fields, r1); store_freg(f1, o->out); - store_freg((f1 + 2) & 15, o->out2); + store_freg(f1 + 2, o->out2); } +#define SPEC_wout_x1 SPEC_r1_f128 static void wout_cond_r1r2_32(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3667,6 +3929,7 @@ static void wout_cond_r1r2_32(DisasContext *s, DisasFields *f, DisasOps *o) store_reg32_i64(get_field(f, r1), o->out); } } +#define SPEC_wout_cond_r1r2_32 0 static void wout_cond_e1e2(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3674,31 +3937,37 @@ static void wout_cond_e1e2(DisasContext *s, DisasFields *f, DisasOps *o) store_freg32_i64(get_field(f, r1), o->out); } } +#define SPEC_wout_cond_e1e2 0 static void wout_m1_8(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st8(o->out, o->addr1, get_mem_index(s)); } +#define SPEC_wout_m1_8 0 static void wout_m1_16(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st16(o->out, o->addr1, get_mem_index(s)); } +#define SPEC_wout_m1_16 0 static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s)); } +#define SPEC_wout_m1_32 0 static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s)); } +#define SPEC_wout_m1_64 0 static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s)); } +#define SPEC_wout_m2_32 0 /* ====================================================================== */ /* The "INput 1" generators. These load the first operand to an insn. */ @@ -3707,126 +3976,146 @@ static void in1_r1(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = load_reg(get_field(f, r1)); } +#define SPEC_in1_r1 0 static void in1_r1_o(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = regs[get_field(f, r1)]; o->g_in1 = true; } +#define SPEC_in1_r1_o 0 static void in1_r1_32s(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = tcg_temp_new_i64(); tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r1)]); } +#define SPEC_in1_r1_32s 0 static void in1_r1_32u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = tcg_temp_new_i64(); tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r1)]); } +#define SPEC_in1_r1_32u 0 static void in1_r1_sr32(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = tcg_temp_new_i64(); tcg_gen_shri_i64(o->in1, regs[get_field(f, r1)], 32); } +#define SPEC_in1_r1_sr32 0 static void in1_r1p1(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be even. */ - int r1 = get_field(f, r1); - o->in1 = load_reg((r1 + 1) & 15); + o->in1 = load_reg(get_field(f, r1) + 1); } +#define SPEC_in1_r1p1 SPEC_r1_even static void in1_r1p1_32s(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be even. */ - int r1 = get_field(f, r1); o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(o->in1, regs[(r1 + 1) & 15]); + tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r1) + 1]); } +#define SPEC_in1_r1p1_32s SPEC_r1_even static void in1_r1p1_32u(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be even. */ - int r1 = get_field(f, r1); o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(o->in1, regs[(r1 + 1) & 15]); + tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r1) + 1]); } +#define SPEC_in1_r1p1_32u SPEC_r1_even static void in1_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be even. */ int r1 = get_field(f, r1); o->in1 = tcg_temp_new_i64(); tcg_gen_concat32_i64(o->in1, regs[r1 + 1], regs[r1]); } +#define SPEC_in1_r1_D32 SPEC_r1_even static void in1_r2(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = load_reg(get_field(f, r2)); } +#define SPEC_in1_r2 0 static void in1_r3(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = load_reg(get_field(f, r3)); } +#define SPEC_in1_r3 0 static void in1_r3_o(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = regs[get_field(f, r3)]; o->g_in1 = true; } +#define SPEC_in1_r3_o 0 static void in1_r3_32s(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = tcg_temp_new_i64(); tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r3)]); } +#define SPEC_in1_r3_32s 0 static void in1_r3_32u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = tcg_temp_new_i64(); tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r3)]); } +#define SPEC_in1_r3_32u 0 + +static void in1_r3_D32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r3 = get_field(f, r3); + o->in1 = tcg_temp_new_i64(); + tcg_gen_concat32_i64(o->in1, regs[r3 + 1], regs[r3]); +} +#define SPEC_in1_r3_D32 SPEC_r3_even static void in1_e1(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = load_freg32_i64(get_field(f, r1)); } +#define SPEC_in1_e1 0 static void in1_f1_o(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = fregs[get_field(f, r1)]; o->g_in1 = true; } +#define SPEC_in1_f1_o 0 static void in1_x1_o(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be < 14. */ int r1 = get_field(f, r1); o->out = fregs[r1]; - o->out2 = fregs[(r1 + 2) & 15]; + o->out2 = fregs[r1 + 2]; o->g_out = o->g_out2 = true; } +#define SPEC_in1_x1_o SPEC_r1_f128 static void in1_f3_o(DisasContext *s, DisasFields *f, DisasOps *o) { o->in1 = fregs[get_field(f, r3)]; o->g_in1 = true; } +#define SPEC_in1_f3_o 0 static void in1_la1(DisasContext *s, DisasFields *f, DisasOps *o) { o->addr1 = get_address(s, 0, get_field(f, b1), get_field(f, d1)); } +#define SPEC_in1_la1 0 static void in1_la2(DisasContext *s, DisasFields *f, DisasOps *o) { int x2 = have_field(f, x2) ? get_field(f, x2) : 0; o->addr1 = get_address(s, x2, get_field(f, b2), get_field(f, d2)); } +#define SPEC_in1_la2 0 static void in1_m1_8u(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3834,6 +4123,7 @@ static void in1_m1_8u(DisasContext *s, DisasFields *f, DisasOps *o) o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld8u(o->in1, o->addr1, get_mem_index(s)); } +#define SPEC_in1_m1_8u 0 static void in1_m1_16s(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3841,6 +4131,7 @@ static void in1_m1_16s(DisasContext *s, DisasFields *f, DisasOps *o) o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld16s(o->in1, o->addr1, get_mem_index(s)); } +#define SPEC_in1_m1_16s 0 static void in1_m1_16u(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3848,6 +4139,7 @@ static void in1_m1_16u(DisasContext *s, DisasFields *f, DisasOps *o) o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld16u(o->in1, o->addr1, get_mem_index(s)); } +#define SPEC_in1_m1_16u 0 static void in1_m1_32s(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3855,6 +4147,7 @@ static void in1_m1_32s(DisasContext *s, DisasFields *f, DisasOps *o) o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld32s(o->in1, o->addr1, get_mem_index(s)); } +#define SPEC_in1_m1_32s 0 static void in1_m1_32u(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3862,6 +4155,7 @@ static void in1_m1_32u(DisasContext *s, DisasFields *f, DisasOps *o) o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld32u(o->in1, o->addr1, get_mem_index(s)); } +#define SPEC_in1_m1_32u 0 static void in1_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3869,6 +4163,7 @@ static void in1_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld64(o->in1, o->addr1, get_mem_index(s)); } +#define SPEC_in1_m1_64 0 /* ====================================================================== */ /* The "INput 2" generators. These load the second operand to an insn. */ @@ -3878,29 +4173,42 @@ static void in2_r1_o(DisasContext *s, DisasFields *f, DisasOps *o) o->in2 = regs[get_field(f, r1)]; o->g_in2 = true; } +#define SPEC_in2_r1_o 0 static void in2_r1_16u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext16u_i64(o->in2, regs[get_field(f, r1)]); } +#define SPEC_in2_r1_16u 0 static void in2_r1_32u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r1)]); } +#define SPEC_in2_r1_32u 0 + +static void in2_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + o->in2 = tcg_temp_new_i64(); + tcg_gen_concat32_i64(o->in2, regs[r1 + 1], regs[r1]); +} +#define SPEC_in2_r1_D32 SPEC_r1_even static void in2_r2(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = load_reg(get_field(f, r2)); } +#define SPEC_in2_r2 0 static void in2_r2_o(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = regs[get_field(f, r2)]; o->g_in2 = true; } +#define SPEC_in2_r2_o 0 static void in2_r2_nz(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -3909,185 +4217,216 @@ static void in2_r2_nz(DisasContext *s, DisasFields *f, DisasOps *o) o->in2 = load_reg(r2); } } +#define SPEC_in2_r2_nz 0 static void in2_r2_8s(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext8s_i64(o->in2, regs[get_field(f, r2)]); } +#define SPEC_in2_r2_8s 0 static void in2_r2_8u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext8u_i64(o->in2, regs[get_field(f, r2)]); } +#define SPEC_in2_r2_8u 0 static void in2_r2_16s(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext16s_i64(o->in2, regs[get_field(f, r2)]); } +#define SPEC_in2_r2_16s 0 static void in2_r2_16u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext16u_i64(o->in2, regs[get_field(f, r2)]); } +#define SPEC_in2_r2_16u 0 static void in2_r3(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = load_reg(get_field(f, r3)); } +#define SPEC_in2_r3 0 static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext32s_i64(o->in2, regs[get_field(f, r2)]); } +#define SPEC_in2_r2_32s 0 static void in2_r2_32u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r2)]); } +#define SPEC_in2_r2_32u 0 static void in2_e2(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = load_freg32_i64(get_field(f, r2)); } +#define SPEC_in2_e2 0 static void in2_f2_o(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = fregs[get_field(f, r2)]; o->g_in2 = true; } +#define SPEC_in2_f2_o 0 static void in2_x2_o(DisasContext *s, DisasFields *f, DisasOps *o) { - /* ??? Specification exception: r1 must be < 14. */ int r2 = get_field(f, r2); o->in1 = fregs[r2]; - o->in2 = fregs[(r2 + 2) & 15]; + o->in2 = fregs[r2 + 2]; o->g_in1 = o->g_in2 = true; } +#define SPEC_in2_x2_o SPEC_r2_f128 static void in2_ra2(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = get_address(s, 0, get_field(f, r2), 0); } +#define SPEC_in2_ra2 0 static void in2_a2(DisasContext *s, DisasFields *f, DisasOps *o) { int x2 = have_field(f, x2) ? get_field(f, x2) : 0; o->in2 = get_address(s, x2, get_field(f, b2), get_field(f, d2)); } +#define SPEC_in2_a2 0 static void in2_ri2(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_const_i64(s->pc + (int64_t)get_field(f, i2) * 2); } +#define SPEC_in2_ri2 0 static void in2_sh32(DisasContext *s, DisasFields *f, DisasOps *o) { help_l2_shift(s, f, o, 31); } +#define SPEC_in2_sh32 0 static void in2_sh64(DisasContext *s, DisasFields *f, DisasOps *o) { help_l2_shift(s, f, o, 63); } +#define SPEC_in2_sh64 0 static void in2_m2_8u(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); tcg_gen_qemu_ld8u(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_m2_8u 0 static void in2_m2_16s(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); tcg_gen_qemu_ld16s(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_m2_16s 0 static void in2_m2_16u(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_m2_16u 0 static void in2_m2_32s(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_m2_32s 0 static void in2_m2_32u(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_m2_32u 0 static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_m2_64 0 static void in2_mri2_16u(DisasContext *s, DisasFields *f, DisasOps *o) { in2_ri2(s, f, o); tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_mri2_16u 0 static void in2_mri2_32s(DisasContext *s, DisasFields *f, DisasOps *o) { in2_ri2(s, f, o); tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_mri2_32s 0 static void in2_mri2_32u(DisasContext *s, DisasFields *f, DisasOps *o) { in2_ri2(s, f, o); tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_mri2_32u 0 static void in2_mri2_64(DisasContext *s, DisasFields *f, DisasOps *o) { in2_ri2(s, f, o); tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); } +#define SPEC_in2_mri2_64 0 static void in2_i2(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_const_i64(get_field(f, i2)); } +#define SPEC_in2_i2 0 static void in2_i2_8u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_const_i64((uint8_t)get_field(f, i2)); } +#define SPEC_in2_i2_8u 0 static void in2_i2_16u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_const_i64((uint16_t)get_field(f, i2)); } +#define SPEC_in2_i2_16u 0 static void in2_i2_32u(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_const_i64((uint32_t)get_field(f, i2)); } +#define SPEC_in2_i2_32u 0 static void in2_i2_16u_shl(DisasContext *s, DisasFields *f, DisasOps *o) { uint64_t i2 = (uint16_t)get_field(f, i2); o->in2 = tcg_const_i64(i2 << s->insn->data); } +#define SPEC_in2_i2_16u_shl 0 static void in2_i2_32u_shl(DisasContext *s, DisasFields *f, DisasOps *o) { uint64_t i2 = (uint32_t)get_field(f, i2); o->in2 = tcg_const_i64(i2 << s->insn->data); } +#define SPEC_in2_i2_32u_shl 0 /* ====================================================================== */ @@ -4106,18 +4445,19 @@ enum DisasInsnEnum { }; #undef D -#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) { \ - .opc = OPC, \ - .fmt = FMT_##FT, \ - .fac = FAC_##FC, \ - .name = #NM, \ - .help_in1 = in1_##I1, \ - .help_in2 = in2_##I2, \ - .help_prep = prep_##P, \ - .help_wout = wout_##W, \ - .help_cout = cout_##CC, \ - .help_op = op_##OP, \ - .data = D \ +#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) { \ + .opc = OPC, \ + .fmt = FMT_##FT, \ + .fac = FAC_##FC, \ + .spec = SPEC_in1_##I1 | SPEC_in2_##I2 | SPEC_prep_##P | SPEC_wout_##W, \ + .name = #NM, \ + .help_in1 = in1_##I1, \ + .help_in2 = in2_##I2, \ + .help_prep = prep_##P, \ + .help_wout = wout_##W, \ + .help_cout = cout_##CC, \ + .help_op = op_##OP, \ + .data = D \ }, /* Allow 0 to be used for NULL in the table below. */ @@ -4128,6 +4468,11 @@ enum DisasInsnEnum { #define cout_0 NULL #define op_0 NULL +#define SPEC_in1_0 0 +#define SPEC_in2_0 0 +#define SPEC_prep_0 0 +#define SPEC_wout_0 0 + static const DisasInsn insn_info[] = { #include "insn-data.def" }; @@ -4295,6 +4640,46 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) return EXIT_NORETURN; } + /* Check for insn specification exceptions. */ + if (insn->spec) { + int spec = insn->spec, excp = 0, r; + + if (spec & SPEC_r1_even) { + r = get_field(&f, r1); + if (r & 1) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r2_even) { + r = get_field(&f, r2); + if (r & 1) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r3_even) { + r = get_field(&f, r3); + if (r & 1) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r1_f128) { + r = get_field(&f, r1); + if (r > 13) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r2_f128) { + r = get_field(&f, r2); + if (r > 13) { + excp = PGM_SPECIFICATION; + } + } + if (excp) { + gen_program_exception(s, excp); + return EXIT_NORETURN; + } + } + /* Set up the strutures we use to communicate with the helpers. */ s->insn = insn; s->fields = &f; @@ -4347,10 +4732,12 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) return ret; } -static inline void gen_intermediate_code_internal(CPUS390XState *env, +static inline void gen_intermediate_code_internal(S390CPU *cpu, TranslationBlock *tb, - int search_pc) + bool search_pc) { + CPUState *cs = CPU(cpu); + CPUS390XState *env = &cpu->env; DisasContext dc; target_ulong pc_start; uint64_t next_page_start; @@ -4371,7 +4758,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, dc.tb = tb; dc.pc = pc_start; dc.cc_op = CC_OP_DYNAMIC; - do_debug = dc.singlestep_enabled = env->singlestep_enabled; + do_debug = dc.singlestep_enabled = cs->singlestep_enabled; gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; @@ -4383,7 +4770,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, max_insns = CF_COUNT_MASK; } - gen_icount_start(); + gen_tb_start(); do { if (search_pc) { @@ -4428,7 +4815,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, || tcg_ctx.gen_opc_ptr >= gen_opc_end || num_insns >= max_insns || singlestep - || env->singlestep_enabled)) { + || cs->singlestep_enabled)) { status = EXIT_PC_STALE; } } while (status == NO_EXIT); @@ -4459,7 +4846,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, abort(); } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; @@ -4483,12 +4870,12 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, void gen_intermediate_code (CPUS390XState *env, struct TranslationBlock *tb) { - gen_intermediate_code_internal(env, tb, 0); + gen_intermediate_code_internal(s390_env_get_cpu(env), tb, false); } void gen_intermediate_code_pc (CPUS390XState *env, struct TranslationBlock *tb) { - gen_intermediate_code_internal(env, tb, 1); + gen_intermediate_code_internal(s390_env_get_cpu(env), tb, true); } void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, int pc_pos)