diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2024-04-09 22:29:46 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2024-04-09 22:29:46 +0100 |
commit | fee571c7afced9bf4b01b864ea6e85f00fb50e30 (patch) | |
tree | 9b15e156bd4bc15b12ffafe0a50e8dfc888c6395 | |
parent | 927284d65bce63ab1495d3febe7c7b5b6d563874 (diff) | |
parent | 143bcc1d59f174b6c6743bd4ca8f99415ed1aba2 (diff) |
Merge tag 'pull-misc-20240409' of https://gitlab.com/rth7680/qemu into staging
target/m68k: Fix fp accrued exception reporting
target/hppa: Fix IIAOQ, IIASQ for pa2.0
target/sh4: Fixes to mac.l and mac.w saturation
target/sh4: Fixes to illegal delay slot reporting
linux-user: Fix waitid return of siginfo_t and rusage
linux-user: Preserve unswapped siginfo_t for strace
tcg/optimize: Do not attempt to constant fold neg_vec
accel/tcg: Improve can_do_io management, mmio bug fix
# -----BEGIN PGP SIGNATURE-----
#
# iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmYVl/kdHHJpY2hhcmQu
# aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV/MXgf/bMzLStvB+DvcpKCR
# hxewlDvNaDHntpXc0+3KzFPOeP9ELGlRDWSUcsdfR0v6BjUQHoUx9t+wC7R/Qe1B
# K9EWQUW9ayU++ELF9dXqtNDLGZaaSAx73PuCd+sDykBdj4/iuX0yc6htWQ+AbP0L
# x1j8CCKuCy/qDjQXyaAtCltlUurHgnswBgnZBxa2Bm0OSszDEBe49IXRIuFW5CcH
# PkVT250zZXU1lblOhpSnOBApZgxbSotk3Wdz7ARbzWisrCEW5x91ClWrP88odjX4
# wiRAe+LvFeLBjlFo+TWbdsvU6Zu2TNxSbv/Tr0HQSFoDkiXKU+5IM4L9Rx9x9EMo
# x1lmkg==
# =FYg/
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 09 Apr 2024 20:33:13 BST
# gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg: issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full]
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F
* tag 'pull-misc-20240409' of https://gitlab.com/rth7680/qemu:
linux-user: Preserve unswapped siginfo_t for strace
accel/tcg: Improve can_do_io management
target/s390x: Use insn_start from DisasContextBase
target/riscv: Use insn_start from DisasContextBase
target/microblaze: Use insn_start from DisasContextBase
target/i386: Preserve DisasContextBase.insn_start across rewind
target/hppa: Use insn_start from DisasContextBase
target/arm: Use insn_start from DisasContextBase
accel/tcg: Add insn_start to DisasContextBase
tcg: Add TCGContext.emit_before_op
target/m68k: Map FPU exceptions to FPSR register
target/sh4: add missing CHECK_NOT_DELAY_SLOT
target/sh4: Fix mac.w with saturation enabled
target/sh4: Fix mac.l with saturation enabled
target/sh4: Merge mach and macl into a union
target/sh4: mac.w: memory accesses are 16-bit words
target/hppa: Fix IIAOQ, IIASQ for pa2.0
linux-user: replace calloc() with g_new0()
linux-user: Fix waitid return of siginfo_t and rusage
tcg/optimize: Do not attempt to constant fold neg_vec
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
33 files changed, 412 insertions, 129 deletions
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 38c34009a5..9de0bc34c8 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -18,20 +18,14 @@ static void set_can_do_io(DisasContextBase *db, bool val) { - if (db->saved_can_do_io != val) { - db->saved_can_do_io = val; - - QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1); - tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env, - offsetof(ArchCPU, parent_obj.neg.can_do_io) - - offsetof(ArchCPU, env)); - } + QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1); + tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env, + offsetof(ArchCPU, parent_obj.neg.can_do_io) - + offsetof(ArchCPU, env)); } bool translator_io_start(DisasContextBase *db) { - set_can_do_io(db, true); - /* * Ensure that this instruction will be the last in the TB. * The target may override this to something more forceful. @@ -84,13 +78,6 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags) - offsetof(ArchCPU, env)); } - /* - * cpu->neg.can_do_io is set automatically here at the beginning of - * each translation block. The cost is minimal, plus it would be - * very easy to forget doing it in the translator. - */ - set_can_do_io(db, db->max_insns == 1); - return icount_start_insn; } @@ -129,6 +116,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, { uint32_t cflags = tb_cflags(tb); TCGOp *icount_start_insn; + TCGOp *first_insn_start = NULL; bool plugin_enabled; /* Initialize DisasContext */ @@ -139,7 +127,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, db->num_insns = 0; db->max_insns = *max_insns; db->singlestep_enabled = cflags & CF_SINGLE_STEP; - db->saved_can_do_io = -1; + db->insn_start = NULL; db->host_addr[0] = host_pc; db->host_addr[1] = NULL; @@ -157,6 +145,10 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, while (true) { *max_insns = ++db->num_insns; ops->insn_start(db, cpu); + db->insn_start = tcg_last_op(); + if (first_insn_start == NULL) { + first_insn_start = db->insn_start; + } tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ if (plugin_enabled) { @@ -169,10 +161,6 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, * done next -- either exiting this loop or locate the start of * the next instruction. */ - if (db->num_insns == db->max_insns) { - /* Accept I/O on the last instruction. */ - set_can_do_io(db, true); - } ops->translate_insn(db, cpu); /* @@ -205,6 +193,21 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, ops->tb_stop(db, cpu); gen_tb_end(tb, cflags, icount_start_insn, db->num_insns); + /* + * Manage can_do_io for the translation block: set to false before + * the first insn and set to true before the last insn. + */ + if (db->num_insns == 1) { + tcg_debug_assert(first_insn_start == db->insn_start); + } else { + tcg_debug_assert(first_insn_start != db->insn_start); + tcg_ctx->emit_before_op = first_insn_start; + set_can_do_io(db, false); + } + tcg_ctx->emit_before_op = db->insn_start; + set_can_do_io(db, true); + tcg_ctx->emit_before_op = NULL; + if (plugin_enabled) { plugin_gen_tb_end(cpu, db->num_insns); } diff --git a/include/exec/translator.h b/include/exec/translator.h index 51624feb10..2c4fb818e7 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -74,6 +74,8 @@ typedef enum DisasJumpType { * @singlestep_enabled: "Hardware" single stepping enabled. * @saved_can_do_io: Known value of cpu->neg.can_do_io, or -1 for unknown. * @plugin_enabled: TCG plugin enabled in this TB. + * @insn_start: The last op emitted by the insn_start hook, + * which is expected to be INDEX_op_insn_start. * * Architecture-agnostic disassembly context. */ @@ -85,8 +87,8 @@ typedef struct DisasContextBase { int num_insns; int max_insns; bool singlestep_enabled; - int8_t saved_can_do_io; bool plugin_enabled; + struct TCGOp *insn_start; void *host_addr[2]; } DisasContextBase; diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 451f3fec41..05a1912f8a 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -553,6 +553,12 @@ struct TCGContext { QTAILQ_HEAD(, TCGOp) ops, free_ops; QSIMPLEQ_HEAD(, TCGLabel) labels; + /* + * When clear, new ops are added to the tail of @ops. + * When set, new ops are added in front of @emit_before_op. + */ + TCGOp *emit_before_op; + /* Tells which temporary holds a given register. It does not take into account fixed registers */ TCGTemp *reg_to_temp[TCG_TARGET_NB_REGS]; diff --git a/linux-user/main.c b/linux-user/main.c index 9277df2e9d..149e35432e 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -928,11 +928,7 @@ int main(int argc, char **argv, char **envp) * Prepare copy of argv vector for target. */ target_argc = argc - optind; - target_argv = calloc(target_argc + 1, sizeof (char *)); - if (target_argv == NULL) { - (void) fprintf(stderr, "Unable to allocate memory for target_argv\n"); - exit(EXIT_FAILURE); - } + target_argv = g_new0(char *, target_argc + 1); /* * If argv0 is specified (using '-0' switch) we replace diff --git a/linux-user/signal.c b/linux-user/signal.c index a93148a4cb..05dc4afb52 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1173,6 +1173,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, CPUState *cpu = env_cpu(cpu_env); abi_ulong handler; sigset_t set; + target_siginfo_t unswapped; target_sigset_t target_old_set; struct target_sigaction *sa; TaskState *ts = get_task_state(cpu); @@ -1182,9 +1183,14 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, k->pending = 0; /* - * Writes out siginfo values byteswapped, accordingly to the target. It also - * cleans the si_type from si_code making it correct for the target. + * Writes out siginfo values byteswapped, accordingly to the target. + * It also cleans the si_type from si_code making it correct for + * the target. We must hold on to the original unswapped copy for + * strace below, because si_type is still required there. */ + if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { + unswapped = k->info; + } tswap_siginfo(&k->info, &k->info); sig = gdb_handlesig(cpu, sig, NULL, &k->info, sizeof(k->info)); @@ -1197,7 +1203,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, } if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { - print_taken_signal(sig, &k->info); + print_taken_signal(sig, &unswapped); } if (handler == TARGET_SIG_DFL) { diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e12d969c2e..3df2b94d9a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9272,14 +9272,24 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_waitid case TARGET_NR_waitid: { + struct rusage ru; siginfo_t info; - info.si_pid = 0; - ret = get_errno(safe_waitid(arg1, arg2, &info, arg4, NULL)); - if (!is_error(ret) && arg3 && info.si_pid != 0) { - if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_siginfo_t), 0))) + + ret = get_errno(safe_waitid(arg1, arg2, (arg3 ? &info : NULL), + arg4, (arg5 ? &ru : NULL))); + if (!is_error(ret)) { + if (arg3) { + p = lock_user(VERIFY_WRITE, arg3, + sizeof(target_siginfo_t), 0); + if (!p) { + return -TARGET_EFAULT; + } + host_to_target_siginfo(p, &info); + unlock_user(p, arg3, sizeof(target_siginfo_t)); + } + if (arg5 && host_to_target_rusage(arg5, &ru)) { return -TARGET_EFAULT; - host_to_target_siginfo(p, &info); - unlock_user(p, arg3, sizeof(target_siginfo_t)); + } } } return ret; diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 340265beb0..2666d52711 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -14179,7 +14179,7 @@ static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) pc_arg &= ~TARGET_PAGE_MASK; } tcg_gen_insn_start(pc_arg, 0, 0); - dc->insn_start = tcg_last_op(); + dc->insn_start_updated = false; } static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 69585e6003..dc49a8d806 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -9273,7 +9273,7 @@ static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) condexec_bits = (dc->condexec_cond << 4) | (dc->condexec_mask >> 1); } tcg_gen_insn_start(pc_arg, condexec_bits, 0); - dc->insn_start = tcg_last_op(); + dc->insn_start_updated = false; } static bool arm_check_kernelpage(DisasContext *dc) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 93be745cf3..dc66ff2190 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -165,10 +165,10 @@ typedef struct DisasContext { uint8_t gm_blocksize; /* True if this page is guarded. */ bool guarded_page; + /* True if the current insn_start has been updated. */ + bool insn_start_updated; /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ int c15_cpar; - /* TCG op of the current insn_start. */ - TCGOp *insn_start; /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */ uint32_t nv2_redirect_offset; } DisasContext; @@ -276,10 +276,10 @@ static inline void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) syn &= ARM_INSN_START_WORD2_MASK; syn >>= ARM_INSN_START_WORD2_SHIFT; - /* We check and clear insn_start_idx to catch multiple updates. */ - assert(s->insn_start != NULL); - tcg_set_insn_start_param(s->insn_start, 2, syn); - s->insn_start = NULL; + /* Check for multiple updates. */ + assert(!s->insn_start_updated); + s->insn_start_updated = true; + tcg_set_insn_start_param(s->base.insn_start, 2, syn); } static inline int curr_insn_len(DisasContext *s) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 90437a92cd..a667ee380d 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -107,14 +107,10 @@ void hppa_cpu_do_interrupt(CPUState *cs) /* step 3 */ /* - * For pa1.x, IIASQ is simply a copy of IASQ. - * For pa2.0, IIASQ is the top bits of the virtual address, - * or zero if translation is disabled. + * IIASQ is the top bits of the virtual address, or zero if translation + * is disabled -- with PSW_W == 0, this will reduce to the space. */ - if (!hppa_is_pa20(env)) { - env->cr[CR_IIASQ] = env->iasq_f >> 32; - env->cr_back[0] = env->iasq_b >> 32; - } else if (old_psw & PSW_C) { + if (old_psw & PSW_C) { env->cr[CR_IIASQ] = hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32; env->cr_back[0] = @@ -123,8 +119,14 @@ void hppa_cpu_do_interrupt(CPUState *cs) env->cr[CR_IIASQ] = 0; env->cr_back[0] = 0; } - env->cr[CR_IIAOQ] = env->iaoq_f; - env->cr_back[1] = env->iaoq_b; + /* IIAOQ is the full offset for wide mode, or 32 bits for narrow mode. */ + if (old_psw & PSW_W) { + env->cr[CR_IIAOQ] = env->iaoq_f; + env->cr_back[1] = env->iaoq_b; + } else { + env->cr[CR_IIAOQ] = (uint32_t)env->iaoq_f; + env->cr_back[1] = (uint32_t)env->iaoq_b; + } if (old_psw & PSW_Q) { /* step 5 */ diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 208e51c086..22d6c89964 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -78,21 +78,21 @@ target_ulong HELPER(swap_system_mask)(CPUHPPAState *env, target_ulong nsm) void HELPER(rfi)(CPUHPPAState *env) { - env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32; - env->iasq_b = (uint64_t)env->cr_back[0] << 32; - env->iaoq_f = env->cr[CR_IIAOQ]; - env->iaoq_b = env->cr_back[1]; + uint64_t mask; + + cpu_hppa_put_psw(env, env->cr[CR_IPSW]); /* * For pa2.0, IIASQ is the top bits of the virtual address. * To recreate the space identifier, remove the offset bits. + * For pa1.x, the mask reduces to no change to space. */ - if (hppa_is_pa20(env)) { - env->iasq_f &= ~env->iaoq_f; - env->iasq_b &= ~env->iaoq_b; - } + mask = gva_offset_mask(env->psw); - cpu_hppa_put_psw(env, env->cr[CR_IPSW]); + env->iaoq_f = env->cr[CR_IIAOQ]; + env->iaoq_b = env->cr_back[1]; + env->iasq_f = (env->cr[CR_IIASQ] << 32) & ~(env->iaoq_f & mask); + env->iasq_b = (env->cr_back[0] << 32) & ~(env->iaoq_b & mask); } static void getshadowregs(CPUHPPAState *env) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 8a1a8bc3aa..42fa480950 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -44,7 +44,6 @@ typedef struct DisasCond { typedef struct DisasContext { DisasContextBase base; CPUState *cs; - TCGOp *insn_start; uint64_t iaoq_f; uint64_t iaoq_b; @@ -62,6 +61,7 @@ typedef struct DisasContext { int privilege; bool psw_n_nonzero; bool is_pa20; + bool insn_start_updated; #ifdef CONFIG_USER_ONLY MemOp unalign; @@ -300,9 +300,9 @@ void hppa_translate_init(void) static void set_insn_breg(DisasContext *ctx, int breg) { - assert(ctx->insn_start != NULL); - tcg_set_insn_start_param(ctx->insn_start, 2, breg); - ctx->insn_start = NULL; + assert(!ctx->insn_start_updated); + ctx->insn_start_updated = true; + tcg_set_insn_start_param(ctx->base.insn_start, 2, breg); } static DisasCond cond_make_f(void) @@ -4694,7 +4694,7 @@ static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b, 0); - ctx->insn_start = tcg_last_op(); + ctx->insn_start_updated = false; } static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 07f642dc9e..76a42c679c 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -139,6 +139,7 @@ typedef struct DisasContext { TCGv_i64 tmp1_i64; sigjmp_buf jmpbuf; + TCGOp *prev_insn_start; TCGOp *prev_insn_end; } DisasContext; @@ -3123,6 +3124,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) /* END TODO */ s->base.num_insns--; tcg_remove_ops_after(s->prev_insn_end); + s->base.insn_start = s->prev_insn_start; s->base.is_jmp = DISAS_TOO_MANY; return false; default: @@ -6995,6 +6997,7 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); target_ulong pc_arg = dc->base.pc_next; + dc->prev_insn_start = dc->base.insn_start; dc->prev_insn_end = tcg_last_op(); if (tb_cflags(dcbase->tb) & CF_PCREL) { pc_arg &= ~TARGET_PAGE_MASK; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 7c8efbb42c..df49ff1880 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -390,12 +390,19 @@ static const VMStateDescription vmstate_freg = { } }; -static int fpu_post_load(void *opaque, int version) +static int fpu_pre_save(void *opaque) { M68kCPU *s = opaque; - cpu_m68k_restore_fp_status(&s->env); + s->env.fpsr = cpu_m68k_get_fpsr(&s->env); + return 0; +} + +static int fpu_post_load(void *opaque, int version) +{ + M68kCPU *s = opaque; + cpu_m68k_set_fpsr(&s->env, s->env.fpsr); return 0; } @@ -404,6 +411,7 @@ const VMStateDescription vmmstate_fpu = { .version_id = 1, .minimum_version_id = 1, .needed = fpu_needed, + .pre_save = fpu_pre_save, .post_load = fpu_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32(env.fpcr, M68kCPU), diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 346427e144..e184239a81 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -199,7 +199,8 @@ void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t); void cpu_m68k_set_sr(CPUM68KState *env, uint32_t); void cpu_m68k_restore_fp_status(CPUM68KState *env); void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val); - +uint32_t cpu_m68k_get_fpsr(CPUM68KState *env); +void cpu_m68k_set_fpsr(CPUM68KState *env, uint32_t val); /* * Instead of computing the condition codes after each m68k instruction, diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index ab120b5f59..8314791f50 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -164,6 +164,78 @@ void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val) cpu_m68k_set_fpcr(env, val); } +/* Convert host exception flags to cpu_m68k form. */ +static int cpu_m68k_exceptbits_from_host(int host_bits) +{ + int target_bits = 0; + + if (host_bits & float_flag_invalid) { + target_bits |= 0x80; + } + if (host_bits & float_flag_overflow) { + target_bits |= 0x40; + } + if (host_bits & (float_flag_underflow | float_flag_output_denormal)) { + target_bits |= 0x20; + } + if (host_bits & float_flag_divbyzero) { + target_bits |= 0x10; + } + if (host_bits & float_flag_inexact) { + target_bits |= 0x08; + } + return target_bits; +} + +/* Convert cpu_m68k exception flags to target form. */ +static int cpu_m68k_exceptbits_to_host(int target_bits) +{ + int host_bits = 0; + + if (target_bits & 0x80) { + host_bits |= float_flag_invalid; + } + if (target_bits & 0x40) { + host_bits |= float_flag_overflow; + } + if (target_bits & 0x20) { + host_bits |= float_flag_underflow; + } + if (target_bits & 0x10) { + host_bits |= float_flag_divbyzero; + } + if (target_bits & 0x08) { + host_bits |= float_flag_inexact; + } + return host_bits; +} + +uint32_t cpu_m68k_get_fpsr(CPUM68KState *env) +{ + int host_flags = get_float_exception_flags(&env->fp_status); + int target_flags = cpu_m68k_exceptbits_from_host(host_flags); + int except = (env->fpsr & ~(0xf8)) | target_flags; + return except; +} + +uint32_t HELPER(get_fpsr)(CPUM68KState *env) +{ + return cpu_m68k_get_fpsr(env); +} + +void cpu_m68k_set_fpsr(CPUM68KState *env, uint32_t val) +{ + env->fpsr = val; + + int host_flags = cpu_m68k_exceptbits_to_host((int) env->fpsr); + set_float_exception_flags(host_flags, &env->fp_status); +} + +void HELPER(set_fpsr)(CPUM68KState *env, uint32_t val) +{ + cpu_m68k_set_fpsr(env, val); +} + #define PREC_BEGIN(prec) \ do { \ FloatX80RoundPrec old = \ diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 1a475f082a..7a91f33b17 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -87,7 +87,7 @@ static int m68k_fpu_gdb_get_reg(CPUState *cs, GByteArray *mem_buf, int n) case 8: /* fpcontrol */ return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - return gdb_get_reg32(mem_buf, env->fpsr); + return gdb_get_reg32(mem_buf, cpu_m68k_get_fpsr(env)); case 10: /* fpiar, not implemented */ return gdb_get_reg32(mem_buf, 0); } @@ -109,7 +109,7 @@ static int m68k_fpu_gdb_set_reg(CPUState *cs, uint8_t *mem_buf, int n) cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); return 4; case 9: /* fpstatus */ - env->fpsr = ldl_p(mem_buf); + cpu_m68k_set_fpsr(env, ldl_p(mem_buf)); return 4; case 10: /* fpiar, not implemented */ return 4; diff --git a/target/m68k/helper.h b/target/m68k/helper.h index 2bbe0dc032..95aa5e53bb 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -54,6 +54,8 @@ DEF_HELPER_4(fsdiv, void, env, fp, fp, fp) DEF_HELPER_4(fddiv, void, env, fp, fp, fp) DEF_HELPER_4(fsgldiv, void, env, fp, fp, fp) DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_RWG, void, env, fp, fp) +DEF_HELPER_2(set_fpsr, void, env, i32) +DEF_HELPER_1(get_fpsr, i32, env) DEF_HELPER_FLAGS_2(set_fpcr, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_2(ftst, TCG_CALL_NO_RWG, void, env, fp) DEF_HELPER_3(fconst, void, env, fp, i32) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 6ae3df43bc..8a194f2f21 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -4686,7 +4686,7 @@ static void gen_load_fcr(DisasContext *s, TCGv res, int reg) tcg_gen_movi_i32(res, 0); break; case M68K_FPSR: - tcg_gen_ld_i32(res, tcg_env, offsetof(CPUM68KState, fpsr)); + gen_helper_get_fpsr(res, tcg_env); break; case M68K_FPCR: tcg_gen_ld_i32(res, tcg_env, offsetof(CPUM68KState, fpcr)); @@ -4700,7 +4700,7 @@ static void gen_store_fcr(DisasContext *s, TCGv val, int reg) case M68K_FPIAR: break; case M68K_FPSR: - tcg_gen_st_i32(val, tcg_env, offsetof(CPUM68KState, fpsr)); + gen_helper_set_fpsr(tcg_env, val); break; case M68K_FPCR: gen_helper_set_fpcr(tcg_env, val); diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 4e52ef32db..fc451befae 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -62,9 +62,6 @@ typedef struct DisasContext { DisasContextBase base; const MicroBlazeCPUConfig *cfg; - /* TCG op of the current insn_start. */ - TCGOp *insn_start; - TCGv_i32 r0; bool r0_set; @@ -699,14 +696,14 @@ static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) static void record_unaligned_ess(DisasContext *dc, int rd, MemOp size, bool store) { - uint32_t iflags = tcg_get_insn_start_param(dc->insn_start, 1); + uint32_t iflags = tcg_get_insn_start_param(dc->base.insn_start, 1); iflags |= ESR_ESS_FLAG; iflags |= rd << 5; iflags |= store * ESR_S; iflags |= (size == MO_32) * ESR_W; - tcg_set_insn_start_param(dc->insn_start, 1, iflags); + tcg_set_insn_start_param(dc->base.insn_start, 1, iflags); } #endif @@ -1624,7 +1621,6 @@ static void mb_tr_insn_start(DisasContextBase *dcb, CPUState *cs) DisasContext *dc = container_of(dcb, DisasContext, base); tcg_gen_insn_start(dc->base.pc_next, dc->tb_flags & ~MSR_TB_MASK); - dc->insn_start = tcg_last_op(); } static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 9d57089fcc..9ff09ebdb6 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -115,8 +115,7 @@ typedef struct DisasContext { bool itrigger; /* FRM is known to contain a valid value. */ bool frm_valid; - /* TCG of the current insn_start */ - TCGOp *insn_start; + bool insn_start_updated; } DisasContext; static inline bool has_ext(DisasContext *ctx, uint32_t ext) @@ -207,9 +206,9 @@ static void gen_check_nanbox_s(TCGv_i64 out, TCGv_i64 in) static void decode_save_opc(DisasContext *ctx) { - assert(ctx->insn_start != NULL); - tcg_set_insn_start_param(ctx->insn_start, 1, ctx->opcode); - ctx->insn_start = NULL; + assert(!ctx->insn_start_updated); + ctx->insn_start_updated = true; + tcg_set_insn_start_param(ctx->base.insn_start, 1, ctx->opcode); } static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, @@ -1224,7 +1223,7 @@ static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) } tcg_gen_insn_start(pc_next, 0); - ctx->insn_start = tcg_last_op(); + ctx->insn_start_updated = false; } static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 57b7db1ee9..90a74ee795 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -141,7 +141,6 @@ struct DisasFields { struct DisasContext { DisasContextBase base; const DisasInsn *insn; - TCGOp *insn_start; DisasFields fields; uint64_t ex_value; /* @@ -6314,7 +6313,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) insn = extract_insn(env, s); /* Update insn_start now that we know the ILEN. */ - tcg_set_insn_start_param(s->insn_start, 2, s->ilen); + tcg_set_insn_start_param(s->base.insn_start, 2, s->ilen); /* Not found means unimplemented/illegal opcode. */ if (insn == NULL) { @@ -6468,7 +6467,6 @@ static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) /* Delay the set of ilen until we've read the insn. */ tcg_gen_insn_start(dc->base.pc_next, dc->cc_op, 0); - dc->insn_start = tcg_last_op(); } static target_ulong get_next_pc(CPUS390XState *env, DisasContext *s, diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 9211da6bde..d928bcf006 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -155,12 +155,22 @@ typedef struct CPUArchState { uint32_t pc; /* program counter */ uint32_t delayed_pc; /* target of delayed branch */ uint32_t delayed_cond; /* condition of delayed branch */ - uint32_t mach; /* multiply and accumulate high */ - uint32_t macl; /* multiply and accumulate low */ uint32_t pr; /* procedure register */ uint32_t fpscr; /* floating point status/control register */ uint32_t fpul; /* floating point communication register */ + /* multiply and accumulate: high, low and combined. */ + union { + uint64_t mac; + struct { +#if HOST_BIG_ENDIAN + uint32_t mach, macl; +#else + uint32_t macl, mach; +#endif + }; + }; + /* float point status register */ float_status fp_status; diff --git a/target/sh4/helper.h b/target/sh4/helper.h index 8d792f6b55..29011d3dbb 100644 --- a/target/sh4/helper.h +++ b/target/sh4/helper.h @@ -11,8 +11,8 @@ DEF_HELPER_3(movcal, void, env, i32, i32) DEF_HELPER_1(discard_movcal_backup, void, env) DEF_HELPER_2(ocbi, void, env, i32) -DEF_HELPER_3(macl, void, env, i32, i32) -DEF_HELPER_3(macw, void, env, i32, i32) +DEF_HELPER_3(macl, void, env, s32, s32) +DEF_HELPER_3(macw, void, env, s32, s32) DEF_HELPER_2(ld_fpscr, void, env, i32) diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index 4559d0d376..99394b714c 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -158,38 +158,47 @@ void helper_ocbi(CPUSH4State *env, uint32_t address) } } -void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1) +void helper_macl(CPUSH4State *env, int32_t arg0, int32_t arg1) { + const int64_t min = -(1ll << 47); + const int64_t max = (1ll << 47) - 1; + int64_t mul = (int64_t)arg0 * arg1; + int64_t mac = env->mac; int64_t res; - res = ((uint64_t) env->mach << 32) | env->macl; - res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1; - env->mach = (res >> 32) & 0xffffffff; - env->macl = res & 0xffffffff; - if (env->sr & (1u << SR_S)) { - if (res < 0) - env->mach |= 0xffff0000; - else - env->mach &= 0x00007fff; + if (!(env->sr & (1u << SR_S))) { + res = mac + mul; + } else if (sadd64_overflow(mac, mul, &res)) { + res = mac < 0 ? min : max; + } else { + res = MIN(MAX(res, min), max); } + + env->mac = res; } -void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1) +void helper_macw(CPUSH4State *env, int32_t arg0, int32_t arg1) { - int64_t res; + /* Inputs are already sign-extended from 16 bits. */ + int32_t mul = arg0 * arg1; - res = ((uint64_t) env->mach << 32) | env->macl; - res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1; - env->mach = (res >> 32) & 0xffffffff; - env->macl = res & 0xffffffff; if (env->sr & (1u << SR_S)) { - if (res < -0x80000000) { - env->mach = 1; - env->macl = 0x80000000; - } else if (res > 0x000000007fffffff) { + /* + * In saturation arithmetic mode, the accumulator is 32-bit + * with carry. MACH is not considered during the addition + * operation nor the 32-bit saturation logic. + */ + int32_t res, macl = env->macl; + + if (sadd32_overflow(macl, mul, &res)) { + res = macl < 0 ? INT32_MIN : INT32_MAX; + /* If overflow occurs, the MACH register is set to 1. */ env->mach = 1; - env->macl = 0x7fffffff; } + env->macl = res; + } else { + /* In non-saturation arithmetic mode, the accumulator is 64-bit */ + env->mac += mul; } } diff --git a/target/sh4/translate.c b/target/sh4/translate.c index a9b1bc7524..ebb6c901bf 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -523,6 +523,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_movi_i32(REG(B11_8), B7_0s); return; case 0x9000: /* mov.w @(disp,PC),Rn */ + CHECK_NOT_DELAY_SLOT { TCGv addr = tcg_constant_i32(ctx->base.pc_next + 4 + B7_0 * 2); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, @@ -530,6 +531,7 @@ static void _decode_opc(DisasContext * ctx) } return; case 0xd000: /* mov.l @(disp,PC),Rn */ + CHECK_NOT_DELAY_SLOT { TCGv addr = tcg_constant_i32((ctx->base.pc_next + 4 + B7_0 * 4) & ~3); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, @@ -816,10 +818,10 @@ static void _decode_opc(DisasContext * ctx) TCGv arg0, arg1; arg0 = tcg_temp_new(); tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, - MO_TESL | MO_ALIGN); + MO_TESW | MO_ALIGN); arg1 = tcg_temp_new(); tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, - MO_TESL | MO_ALIGN); + MO_TESW | MO_ALIGN); gen_helper_macw(tcg_env, arg0, arg1); tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 2); tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2); @@ -1236,6 +1238,7 @@ static void _decode_opc(DisasContext * ctx) } return; case 0xc700: /* mova @(disp,PC),R0 */ + CHECK_NOT_DELAY_SLOT tcg_gen_movi_i32(REG(0), ((ctx->base.pc_next & 0xfffffffc) + 4 + B7_0 * 4) & ~3); return; diff --git a/tcg/optimize.c b/tcg/optimize.c index 275db77b42..2e9e5725a9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1990,16 +1990,10 @@ static bool fold_nand(OptContext *ctx, TCGOp *op) return false; } -static bool fold_neg(OptContext *ctx, TCGOp *op) +static bool fold_neg_no_const(OptContext *ctx, TCGOp *op) { - uint64_t z_mask; - - if (fold_const1(ctx, op)) { - return true; - } - /* Set to 1 all bits to the left of the rightmost. */ - z_mask = arg_info(op->args[1])->z_mask; + uint64_t z_mask = arg_info(op->args[1])->z_mask; ctx->z_mask = -(z_mask & -z_mask); /* @@ -2010,6 +2004,11 @@ static bool fold_neg(OptContext *ctx, TCGOp *op) return true; } +static bool fold_neg(OptContext *ctx, TCGOp *op) +{ + return fold_const1(ctx, op) || fold_neg_no_const(ctx, op); +} + static bool fold_nor(OptContext *ctx, TCGOp *op) { if (fold_const2_commutative(ctx, op) || @@ -2418,7 +2417,7 @@ static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) if (have_neg) { op->opc = neg_op; op->args[1] = op->args[2]; - return fold_neg(ctx, op); + return fold_neg_no_const(ctx, op); } return false; } @@ -1521,6 +1521,7 @@ void tcg_func_start(TCGContext *s) QTAILQ_INIT(&s->ops); QTAILQ_INIT(&s->free_ops); + s->emit_before_op = NULL; QSIMPLEQ_INIT(&s->labels); tcg_debug_assert(s->addr_type == TCG_TYPE_I32 || @@ -2332,7 +2333,11 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) op->args[pi++] = (uintptr_t)info; tcg_debug_assert(pi == total_args); - QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + if (tcg_ctx->emit_before_op) { + QTAILQ_INSERT_BEFORE(tcg_ctx->emit_before_op, op, link); + } else { + QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + } tcg_debug_assert(n_extend < ARRAY_SIZE(extend_free)); for (i = 0; i < n_extend; ++i) { @@ -3215,7 +3220,12 @@ static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs) TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs) { TCGOp *op = tcg_op_alloc(opc, nargs); - QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + + if (tcg_ctx->emit_before_op) { + QTAILQ_INSERT_BEFORE(tcg_ctx->emit_before_op, op, link); + } else { + QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + } return op; } diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 0efd565f05..70d728ae9a 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -10,7 +10,7 @@ VPATH += $(AARCH64_SRC) # Base architecture tests AARCH64_TESTS=fcvt pcalign-a64 lse2-fault -AARCH64_TESTS += test-2248 +AARCH64_TESTS += test-2248 test-2150 fcvt: LDFLAGS+=-lm diff --git a/tests/tcg/aarch64/test-2150.c b/tests/tcg/aarch64/test-2150.c new file mode 100644 index 0000000000..fb86c11958 --- /dev/null +++ b/tests/tcg/aarch64/test-2150.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2150 */ + +int main() +{ + asm volatile( + "movi v6.4s, #1\n" + "movi v7.4s, #0\n" + "sub v6.2d, v7.2d, v6.2d\n" + : : : "v6", "v7"); + return 0; +} diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 16eaa850a8..4d09291c0c 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -9,3 +9,11 @@ run-signals: signals $(call skip-test, $<, "BROKEN") run-plugin-signals-with-%: $(call skip-test, $<, "BROKEN") + +VPATH += $(SRC_PATH)/tests/tcg/sh4 + +test-macl: CFLAGS += -O -g +TESTS += test-macl + +test-macw: CFLAGS += -O -g +TESTS += test-macw diff --git a/tests/tcg/sh4/test-macl.c b/tests/tcg/sh4/test-macl.c new file mode 100644 index 0000000000..b66c854365 --- /dev/null +++ b/tests/tcg/sh4/test-macl.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +#define MACL_S_MIN (-(1ll << 47)) +#define MACL_S_MAX ((1ll << 47) - 1) + +int64_t mac_l(int64_t mac, const int32_t *a, const int32_t *b) +{ + register uint32_t macl __asm__("macl") = mac; + register uint32_t mach __asm__("mach") = mac >> 32; + + asm volatile("mac.l @%0+,@%1+" + : "+r"(a), "+r"(b), "+x"(macl), "+x"(mach)); + + return ((uint64_t)mach << 32) | macl; +} + +typedef struct { + int64_t mac; + int32_t a, b; + int64_t res[2]; +} Test; + +__attribute__((noinline)) +void test(const Test *t, int sat) +{ + int64_t res; + + if (sat) { + asm volatile("sets"); + } else { + asm volatile("clrs"); + } + res = mac_l(t->mac, &t->a, &t->b); + + if (res != t->res[sat]) { + fprintf(stderr, "%#llx + (%#x * %#x) = %#llx -- got %#llx\n", + t->mac, t->a, t->b, t->res[sat], res); + abort(); + } +} + +int main() +{ + static const Test tests[] = { + { 0x00007fff12345678ll, INT32_MAX, INT32_MAX, + { 0x40007ffe12345679ll, MACL_S_MAX } }, + { MACL_S_MIN, -1, 1, + { 0xffff7fffffffffffll, MACL_S_MIN } }, + { INT64_MIN, -1, 1, + { INT64_MAX, MACL_S_MIN } }, + { 0x00007fff00000000ll, INT32_MAX, INT32_MAX, + { 0x40007ffe00000001ll, MACL_S_MAX } }, + { 4, 1, 2, { 6, 6 } }, + { -4, -1, -2, { -2, -2 } }, + }; + + for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { + for (int j = 0; j < 2; ++j) { + test(&tests[i], j); + } + } + return 0; +} diff --git a/tests/tcg/sh4/test-macw.c b/tests/tcg/sh4/test-macw.c new file mode 100644 index 0000000000..4eceec8634 --- /dev/null +++ b/tests/tcg/sh4/test-macw.c @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +int64_t mac_w(int64_t mac, const int16_t *a, const int16_t *b) +{ + register uint32_t macl __asm__("macl") = mac; + register uint32_t mach __asm__("mach") = mac >> 32; + + asm volatile("mac.w @%0+,@%1+" + : "+r"(a), "+r"(b), "+x"(macl), "+x"(mach)); + + return ((uint64_t)mach << 32) | macl; +} + +typedef struct { + int64_t mac; + int16_t a, b; + int64_t res[2]; +} Test; + +__attribute__((noinline)) +void test(const Test *t, int sat) +{ + int64_t res; + + if (sat) { + asm volatile("sets"); + } else { + asm volatile("clrs"); + } + res = mac_w(t->mac, &t->a, &t->b); + + if (res != t->res[sat]) { + fprintf(stderr, "%#llx + (%#x * %#x) = %#llx -- got %#llx\n", + t->mac, t->a, t->b, t->res[sat], res); + abort(); + } +} + +int main() +{ + static const Test tests[] = { + { 0, 2, 3, { 6, 6 } }, + { 0x123456787ffffffell, 2, -3, + { 0x123456787ffffff8ll, 0x123456787ffffff8ll } }, + { 0xabcdef127ffffffall, 2, 3, + { 0xabcdef1280000000ll, 0x000000017fffffffll } }, + { 0xfffffffffll, INT16_MAX, INT16_MAX, + { 0x103fff0000ll, 0xf3fff0000ll } }, + }; + + for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { + for (int j = 0; j < 2; ++j) { + test(&tests[i], j); + } + } + return 0; +} |