From a9d9eb8fd45279fa8455afa03331296dbe2871ff Mon Sep 17 00:00:00 2001 From: j_mayer Date: Sun, 7 Oct 2007 18:19:26 +0000 Subject: [PATCH] Implement PowerPC Altivec load & stores, used by Apple firmware for memcpy. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3349 c046a42c-6fe2-441c-8c8c-71466251a162 --- target-ppc/cpu.h | 11 ++- target-ppc/exec.h | 6 +- target-ppc/op_mem.h | 60 ++++++++++++++ target-ppc/op_template.h | 40 ++++++++++ target-ppc/translate.c | 165 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 274 insertions(+), 8 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index ef70fa0347..9cbd1c9aa5 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -292,7 +292,7 @@ typedef struct CPUPPCState CPUPPCState; typedef struct ppc_tb_t ppc_tb_t; typedef struct ppc_spr_t ppc_spr_t; typedef struct ppc_dcr_t ppc_dcr_t; -typedef struct ppc_avr_t ppc_avr_t; +typedef union ppc_avr_t ppc_avr_t; typedef union ppc_tlb_t ppc_tlb_t; /* SPR access micro-ops generations callbacks */ @@ -311,8 +311,11 @@ struct ppc_spr_t { }; /* Altivec registers (128 bits) */ -struct ppc_avr_t { - uint32_t u[4]; +union ppc_avr_t { + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; + uint64_t u64[2]; }; /* Software TLB cache */ @@ -454,7 +457,7 @@ struct CPUPPCState { */ ppc_gpr_t t0, t1, t2; #endif - ppc_avr_t t0_avr, t1_avr, t2_avr; + ppc_avr_t avr0, avr1, avr2; /* general purpose registers */ ppc_gpr_t gpr[32]; diff --git a/target-ppc/exec.h b/target-ppc/exec.h index 0c53de42b1..3fedf493e3 100644 --- a/target-ppc/exec.h +++ b/target-ppc/exec.h @@ -54,9 +54,9 @@ register unsigned long T2 asm(AREG3); #define T2_64 T2 #endif /* Provision for Altivec */ -#define T0_avr (env->t0_avr) -#define T1_avr (env->t1_avr) -#define T2_avr (env->t2_avr) +#define AVR0 (env->avr0) +#define AVR1 (env->avr1) +#define AVR2 (env->avr2) #define FT0 (env->ft0) #define FT1 (env->ft1) diff --git a/target-ppc/op_mem.h b/target-ppc/op_mem.h index ea1b4a3472..ca48f5de0a 100644 --- a/target-ppc/op_mem.h +++ b/target-ppc/op_mem.h @@ -1054,6 +1054,66 @@ void OPPROTO glue(op_POWER2_stfq_le, MEMSUFFIX) (void) RETURN(); } +/* Altivec vector extension */ +#if defined(WORDS_BIGENDIAN) +#define VR_DWORD0 0 +#define VR_DWORD1 1 +#else +#define VR_DWORD0 1 +#define VR_DWORD1 0 +#endif +void OPPROTO glue(op_vr_lvx, MEMSUFFIX) (void) +{ + AVR0.u64[VR_DWORD0] = glue(ldq, MEMSUFFIX)((uint32_t)T0); + AVR0.u64[VR_DWORD1] = glue(ldq, MEMSUFFIX)((uint32_t)T0 + 8); +} + +void OPPROTO glue(op_vr_lvx_le, MEMSUFFIX) (void) +{ + AVR0.u64[VR_DWORD1] = glue(ldq, MEMSUFFIX)((uint32_t)T0); + AVR0.u64[VR_DWORD0] = glue(ldq, MEMSUFFIX)((uint32_t)T0 + 8); +} + +void OPPROTO glue(op_vr_stvx, MEMSUFFIX) (void) +{ + glue(stq, MEMSUFFIX)((uint32_t)T0, AVR0.u64[VR_DWORD0]); + glue(stq, MEMSUFFIX)((uint32_t)T0 + 8, AVR0.u64[VR_DWORD1]); +} + +void OPPROTO glue(op_vr_stvx_le, MEMSUFFIX) (void) +{ + glue(stq, MEMSUFFIX)((uint32_t)T0, AVR0.u64[VR_DWORD1]); + glue(stq, MEMSUFFIX)((uint32_t)T0 + 8, AVR0.u64[VR_DWORD0]); +} + +#if defined(TARGET_PPC64) +void OPPROTO glue(op_vr_lvx_64, MEMSUFFIX) (void) +{ + AVR0.u64[VR_DWORD0] = glue(ldq, MEMSUFFIX)((uint64_t)T0); + AVR0.u64[VR_DWORD1] = glue(ldq, MEMSUFFIX)((uint64_t)T0 + 8); +} + +void OPPROTO glue(op_vr_lvx_le_64, MEMSUFFIX) (void) +{ + AVR0.u64[VR_DWORD1] = glue(ldq, MEMSUFFIX)((uint64_t)T0); + AVR0.u64[VR_DWORD0] = glue(ldq, MEMSUFFIX)((uint64_t)T0 + 8); +} + +void OPPROTO glue(op_vr_stvx_64, MEMSUFFIX) (void) +{ + glue(stq, MEMSUFFIX)((uint64_t)T0, AVR0.u64[VR_DWORD0]); + glue(stq, MEMSUFFIX)((uint64_t)T0 + 8, AVR0.u64[VR_DWORD1]); +} + +void OPPROTO glue(op_vr_stvx_le_64, MEMSUFFIX) (void) +{ + glue(stq, MEMSUFFIX)((uint64_t)T0, AVR0.u64[VR_DWORD1]); + glue(stq, MEMSUFFIX)((uint64_t)T0 + 8, AVR0.u64[VR_DWORD0]); +} +#endif +#undef VR_DWORD0 +#undef VR_DWORD1 + #if defined(TARGET_PPCEMB) /* SPE extension */ #define _PPC_SPE_LD_OP(name, op) \ diff --git a/target-ppc/op_template.h b/target-ppc/op_template.h index d45062592a..28dc59c942 100644 --- a/target-ppc/op_template.h +++ b/target-ppc/op_template.h @@ -57,6 +57,7 @@ void OPPROTO glue(op_store_T2_gpr_gpr, REG) (void) } #endif +/* General purpose registers containing vector operands moves */ #if defined(TARGET_PPCEMB) void OPPROTO glue(op_load_gpr64_T0_gpr, REG) (void) { @@ -99,6 +100,45 @@ void OPPROTO glue(op_store_T2_gpr64_gpr, REG) (void) #endif #endif /* defined(TARGET_PPCEMB) */ +/* Altivec registers moves */ +void OPPROTO glue(op_load_avr_A0_avr, REG) (void) +{ + AVR0 = env->avr[REG]; + RETURN(); +} + +void OPPROTO glue(op_load_avr_A1_avr, REG) (void) +{ + AVR1 = env->avr[REG]; + RETURN(); +} + +void OPPROTO glue(op_load_avr_A2_avr, REG) (void) +{ + AVR2 = env->avr[REG]; + RETURN(); +} + +void OPPROTO glue(op_store_A0_avr_avr, REG) (void) +{ + env->avr[REG] = AVR0; + RETURN(); +} + +void OPPROTO glue(op_store_A1_avr_avr, REG) (void) +{ + env->avr[REG] = AVR1; + RETURN(); +} + +#if 0 // unused +void OPPROTO glue(op_store_A2_avr_avr, REG) (void) +{ + env->avr[REG] = AVR2; + RETURN(); +} +#endif + #if REG <= 7 /* Condition register moves */ void OPPROTO glue(op_load_crf_T0_crf, REG) (void) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 1ff4b17bbe..a17066341d 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -164,6 +164,7 @@ typedef struct DisasContext { int sf_mode; #endif int fpu_enabled; + int altivec_enabled; #if defined(TARGET_PPCEMB) int spe_enabled; #endif @@ -235,6 +236,9 @@ GEN_EXCP(ctx, POWERPC_EXCP_FPU, 0) #define GEN_EXCP_NO_AP(ctx) \ GEN_EXCP(ctx, POWERPC_EXCP_APU, 0) +#define GEN_EXCP_NO_VR(ctx) \ +GEN_EXCP(ctx, POWERPC_EXCP_VPU, 0) + /* Stop translation */ static always_inline void GEN_STOP (DisasContext *ctx) { @@ -5530,6 +5534,161 @@ GEN_HANDLER(icbt_440, 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE) */ } +/*** Altivec vector extension ***/ +/* Altivec registers moves */ +GEN32(gen_op_load_avr_A0, gen_op_load_avr_A0_avr); +GEN32(gen_op_load_avr_A1, gen_op_load_avr_A1_avr); +GEN32(gen_op_load_avr_A2, gen_op_load_avr_A2_avr); + +GEN32(gen_op_store_A0_avr, gen_op_store_A0_avr_avr); +GEN32(gen_op_store_A1_avr, gen_op_store_A1_avr_avr); +#if 0 // unused +GEN32(gen_op_store_A2_avr, gen_op_store_A2_avr_avr); +#endif + +#define op_vr_ldst(name) (*gen_op_##name[ctx->mem_idx])() +#if defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +/* User-mode only - 64 bits mode */ +#define OP_VR_LD_TABLE(name) \ +static GenOpFunc *gen_op_vr_l##name[] = { \ + &gen_op_vr_l##name##_raw, \ + &gen_op_vr_l##name##_le_raw, \ + &gen_op_vr_l##name##_64_raw, \ + &gen_op_vr_l##name##_le_64_raw, \ +}; +#define OP_VR_ST_TABLE(name) \ +static GenOpFunc *gen_op_vr_st##name[] = { \ + &gen_op_vr_st##name##_raw, \ + &gen_op_vr_st##name##_le_raw, \ + &gen_op_vr_st##name##_64_raw, \ + &gen_op_vr_st##name##_le_64_raw, \ +}; +#else /* defined(TARGET_PPC64) */ +/* User-mode only - 32 bits mode */ +#define OP_VR_LD_TABLE(name) \ +static GenOpFunc *gen_op_vr_l##name[] = { \ + &gen_op_vr_l##name##_raw, \ + &gen_op_vr_l##name##_le_raw, \ +}; +#define OP_VR_ST_TABLE(name) \ +static GenOpFunc *gen_op_vr_st##name[] = { \ + &gen_op_vr_st##name##_raw, \ + &gen_op_vr_st##name##_le_raw, \ +}; +#endif /* defined(TARGET_PPC64) */ +#else /* defined(CONFIG_USER_ONLY) */ +#if defined(TARGET_PPC64H) +/* Full system with hypervisor mode */ +#define OP_VR_LD_TABLE(name) \ +static GenOpFunc *gen_op_vr_l##name[] = { \ + &gen_op_vr_l##name##_user, \ + &gen_op_vr_l##name##_le_user, \ + &gen_op_vr_l##name##_64_user, \ + &gen_op_vr_l##name##_le_64_user, \ + &gen_op_vr_l##name##_kernel, \ + &gen_op_vr_l##name##_le_kernel, \ + &gen_op_vr_l##name##_64_kernel, \ + &gen_op_vr_l##name##_le_64_kernel, \ + &gen_op_vr_l##name##_hypv, \ + &gen_op_vr_l##name##_le_hypv, \ + &gen_op_vr_l##name##_64_hypv, \ + &gen_op_vr_l##name##_le_64_hypv, \ +}; +#define OP_VR_ST_TABLE(name) \ +static GenOpFunc *gen_op_vr_st##name[] = { \ + &gen_op_vr_st##name##_user, \ + &gen_op_vr_st##name##_le_user, \ + &gen_op_vr_st##name##_64_user, \ + &gen_op_vr_st##name##_le_64_user, \ + &gen_op_vr_st##name##_kernel, \ + &gen_op_vr_st##name##_le_kernel, \ + &gen_op_vr_st##name##_64_kernel, \ + &gen_op_vr_st##name##_le_64_kernel, \ + &gen_op_vr_st##name##_hypv, \ + &gen_op_vr_st##name##_le_hypv, \ + &gen_op_vr_st##name##_64_hypv, \ + &gen_op_vr_st##name##_le_64_hypv, \ +}; +#elif defined(TARGET_PPC64) +/* Full system - 64 bits mode */ +#define OP_VR_LD_TABLE(name) \ +static GenOpFunc *gen_op_vr_l##name[] = { \ + &gen_op_vr_l##name##_user, \ + &gen_op_vr_l##name##_le_user, \ + &gen_op_vr_l##name##_64_user, \ + &gen_op_vr_l##name##_le_64_user, \ + &gen_op_vr_l##name##_kernel, \ + &gen_op_vr_l##name##_le_kernel, \ + &gen_op_vr_l##name##_64_kernel, \ + &gen_op_vr_l##name##_le_64_kernel, \ +}; +#define OP_VR_ST_TABLE(name) \ +static GenOpFunc *gen_op_vr_st##name[] = { \ + &gen_op_vr_st##name##_user, \ + &gen_op_vr_st##name##_le_user, \ + &gen_op_vr_st##name##_64_user, \ + &gen_op_vr_st##name##_le_64_user, \ + &gen_op_vr_st##name##_kernel, \ + &gen_op_vr_st##name##_le_kernel, \ + &gen_op_vr_st##name##_64_kernel, \ + &gen_op_vr_st##name##_le_64_kernel, \ +}; +#else /* defined(TARGET_PPC64) */ +/* Full system - 32 bits mode */ +#define OP_VR_LD_TABLE(name) \ +static GenOpFunc *gen_op_vr_l##name[] = { \ + &gen_op_vr_l##name##_user, \ + &gen_op_vr_l##name##_le_user, \ + &gen_op_vr_l##name##_kernel, \ + &gen_op_vr_l##name##_le_kernel, \ +}; +#define OP_VR_ST_TABLE(name) \ +static GenOpFunc *gen_op_vr_st##name[] = { \ + &gen_op_vr_st##name##_user, \ + &gen_op_vr_st##name##_le_user, \ + &gen_op_vr_st##name##_kernel, \ + &gen_op_vr_st##name##_le_kernel, \ +}; +#endif /* defined(TARGET_PPC64) */ +#endif /* defined(CONFIG_USER_ONLY) */ + +#define GEN_VR_LDX(name, opc2, opc3) \ +GEN_HANDLER(l##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) \ +{ \ + if (unlikely(!ctx->altivec_enabled)) { \ + GEN_EXCP_NO_VR(ctx); \ + return; \ + } \ + gen_addr_reg_index(ctx); \ + op_vr_ldst(vr_l##name); \ + gen_op_store_A0_avr(rD(ctx->opcode)); \ +} + +#define GEN_VR_STX(name, opc2, opc3) \ +GEN_HANDLER(st##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) \ +{ \ + if (unlikely(!ctx->altivec_enabled)) { \ + GEN_EXCP_NO_VR(ctx); \ + return; \ + } \ + gen_addr_reg_index(ctx); \ + gen_op_load_avr_A0(rS(ctx->opcode)); \ + op_vr_ldst(vr_st##name); \ +} + +OP_VR_LD_TABLE(vx); +GEN_VR_LDX(vx, 0x07, 0x03); +/* As we don't emulate the cache, lvxl is stricly equivalent to lvx */ +#define gen_op_vr_lvxl gen_op_vr_lvx +GEN_VR_LDX(vxl, 0x07, 0x0B); + +OP_VR_ST_TABLE(vx); +GEN_VR_STX(vx, 0x07, 0x07); +/* As we don't emulate the cache, stvxl is stricly equivalent to stvx */ +#define gen_op_vr_stvxl gen_op_vr_stvx +GEN_VR_STX(vxl, 0x07, 0x0F); + #if defined(TARGET_PPCEMB) /*** SPE extension ***/ @@ -6553,11 +6712,15 @@ static always_inline int gen_intermediate_code_internal (CPUState *env, ctx.dcache_line_size = env->dcache_line_size; ctx.fpu_enabled = msr_fp; #if defined(TARGET_PPCEMB) - if (env->flags & POWERPC_FLAG_SPE) + if ((env->flags & POWERPC_FLAG_SPE) && msr_spe) ctx.spe_enabled = msr_spe; else ctx.spe_enabled = 0; #endif + if ((env->flags & POWERPC_FLAG_VRE) && msr_vr) + ctx.altivec_enabled = msr_vr; + else + ctx.altivec_enabled = 0; if ((env->flags & POWERPC_FLAG_SE) && msr_se) single_step = 1; else