mirror of https://github.com/xemu-project/xemu.git
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----- 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>
This commit is contained in:
commit
fee571c7af
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -390,12 +390,19 @@ static const VMStateDescription vmstate_freg = {
|
|||
}
|
||||
};
|
||||
|
||||
static int fpu_pre_save(void *opaque)
|
||||
{
|
||||
M68kCPU *s = opaque;
|
||||
|
||||
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_restore_fp_status(&s->env);
|
||||
|
||||
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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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) {
|
||||
/*
|
||||
* 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 = 0x80000000;
|
||||
} else if (res > 0x000000007fffffff) {
|
||||
env->mach = 1;
|
||||
env->macl = 0x7fffffff;
|
||||
}
|
||||
env->macl = res;
|
||||
} else {
|
||||
/* In non-saturation arithmetic mode, the accumulator is 64-bit */
|
||||
env->mac += mul;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
14
tcg/tcg.c
14
tcg/tcg.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue