Merge in-progress PowerPC "JIT1" engine for AMD64, IA-32, PPC.

The merge probably got wrong as there are some problems probably due to the
experiment begining with CR deferred evaluation. With nbench/ppc, performance
improvement was around 2x. With nbench on x86, performance improvement was
around 4x on average.

Incompatible change: instr_info_t has a new field in the middle. But since
insertion of PPC_I(XXX) identifiers is auto-generated, there is no problem.
This commit is contained in:
gbeauche 2003-11-24 23:45:52 +00:00
parent 7968a20100
commit 73d51962f6
35 changed files with 8836 additions and 141 deletions

File diff suppressed because it is too large Load Diff

View File

@ -45,7 +45,7 @@ class nv_mem_fun_t : public std::unary_function<T, R> {
public:
nv_mem_fun_t(pmf_t pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
R operator()(T *p) const { return (*pf)(p); }
operator bool () const { return pf; }
pf_t ptr() const { return pf; }
};
template< class R, class T >
@ -56,7 +56,7 @@ class const_nv_mem_fun_t : public std::unary_function<T, R> {
public:
const_nv_mem_fun_t(pmf_t const pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
R operator()(const T *p) const { return (*pf)(p); }
operator bool () const { return pf; }
pf_t ptr() const { return pf; }
};
template< class R, class T, class A >
@ -67,7 +67,7 @@ class nv_mem_fun1_t : public std::binary_function<T, A, R> {
public:
nv_mem_fun1_t(pmf_t pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
R operator()(T *p, A x) const { return (*pf)(p, x); }
operator bool () const { return pf; }
pf_t ptr() const { return pf; }
};
template< class R, class T, class A >
@ -78,45 +78,49 @@ class const_nv_mem_fun1_t : public std::binary_function<T, A, R> {
public:
const_nv_mem_fun1_t(pmf_t const pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
R operator()(const T *p, A x) const { return (*pf)(p, x); }
operator bool () const { return pf; }
pf_t ptr() const { return pf; }
};
#else
template< class R, class T >
class nv_mem_fun_t : public std::unary_function<T, R> {
R (T::*pf)();
typedef R (T::*pmf_t)();
pmf_t pf;
public:
nv_mem_fun_t(R (T::*pmf)()) : pf(pmf) {}
R operator()(T *p) const { return (p->*pf)(); }
operator bool () const { return pf; }
pmf_t ptr() const { return pf; }
};
template< class R, class T >
class const_nv_mem_fun_t : public std::unary_function<T, R> {
R (T::*pf)() const;
typedef R (T::*pmf_t)() const;
pmf_t pf;
public:
const_nv_mem_fun_t(R (T::*pmf)() const) : pf(pmf) {}
R operator()(const T *p) const { return (p->*pf)(); }
operator bool () const { return pf; }
pmf_t ptr() const { return pf; }
};
template< class R, class T, class A >
class nv_mem_fun1_t : public std::binary_function<T, A, R> {
R (T::*pf)(A);
typedef R (T::*pmf_t)(A);
pmf_t pf;
public:
nv_mem_fun1_t(R (T::*pmf)(A)) : pf(pmf) {}
R operator()(T *p, A x) const { return (p->*pf)(x); }
operator bool () const { return pf; }
pmf_t ptr() const { return pf; }
};
template< class R, class T, class A >
class const_nv_mem_fun1_t : public std::binary_function<T, A, R> {
R (T::*pf)(A) const;
typedef R (T::*pmf_t)(A) const;
pmf_t pf;
public:
const_nv_mem_fun1_t(R (T::*pmf)(A) const) : pf(pmf) {}
R operator()(const T *p, A x) const { return (p->*pf)(x); }
operator bool () const { return pf; }
pmf_t ptr() const { return pf; }
};
#endif

View File

@ -30,6 +30,7 @@
#include "sigsegv.h"
#include "cpu/ppc/ppc-cpu.hpp"
#include "cpu/ppc/ppc-operations.hpp"
#include "cpu/ppc/ppc-instructions.hpp"
// Used for NativeOp trampolines
#include "video.h"
@ -97,6 +98,11 @@ static sigsegv_return_t sigsegv_handler(sigsegv_address_t, sigsegv_address_t);
* PowerPC emulator glue with special 'sheep' opcodes
**/
enum {
PPC_I(SHEEP) = PPC_I(MAX),
PPC_I(SHEEP_MAX)
};
class sheepshaver_cpu
: public powerpc_cpu
{
@ -165,6 +171,7 @@ void sheepshaver_cpu::init_decoder()
{ "sheep",
(execute_pmf)&sheepshaver_cpu::execute_sheep,
NULL,
PPC_I(SHEEP),
D_form, 6, 0, CFLOW_JUMP | CFLOW_TRAP
}
};

View File

@ -0,0 +1,45 @@
/*
* dyngen defines for micro operation code
*
* Copyright (c) 2003 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DYNGEN_TARGET_EXEC_H
#define DYNGEN_TARGET_EXEC_H
enum {
/* callee save registers */
#define AREG0 "rbp"
AREG0_ID = 5,
#define AREG1 "rbx"
AREG1_ID = 3,
#define AREG2 "r12"
AREG2_ID = 12,
#define AREG3 "r13"
AREG3_ID = 13,
#define AREG4 "r14"
AREG4_ID = 14,
#define AREG5 "r15"
AREG5_ID = 15,
};
#endif /* DYNGEN_TARGET_EXEC_H */

View File

@ -0,0 +1 @@
#include "cpu/jit/t-dummy/jit-target-cache.hpp"

View File

@ -0,0 +1,548 @@
/*
* dyngen-ops.hpp - Synthetic opcodes
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "cpu/vm.hpp"
#include "cpu/jit/dyngen-exec.h"
#include <math.h>
// We need at least 5 general purpose registers
struct basic_cpu;
#ifdef REG_CPU
register basic_cpu *CPU asm(REG_CPU);
#else
#define CPU ((basic_cpu *)CPUPARAM)
#endif
#if SIZEOF_VOID_P == 8
#define REG32(X) ((uint32)X)
#else
#define REG32(X) X
#endif
#define A0 REG32(reg_A0)
register uintptr reg_A0 asm(REG_A0);
#define T0 REG32(reg_T0)
register uintptr reg_T0 asm(REG_T0);
#define T1 REG32(reg_T1)
register uintptr reg_T1 asm(REG_T1);
#ifdef REG_T2
#define T2 REG32(reg_T2)
register uintptr reg_T2 asm(REG_T2);
#endif
#ifdef REG_T3
#define T3 REG32(reg_T3)
register uintptr reg_T3 asm(REG_T3);
#endif
/**
* Native ALU operations optimization
**/
#ifndef do_udiv_32
#define do_udiv_32(x, y) ((uint32)x / (uint32)y)
#endif
#ifndef do_sdiv_32
#define do_sdiv_32(x, y) ((int32)x / (int32)y)
#endif
#ifndef do_rol_32
#define do_rol_32(x, y) ((x << y) | (x >> (32 - y)))
#endif
#ifndef do_ror_32
#define do_ror_32(x, y) ((x >> y) | (x << (32 - y)))
#endif
/**
* ALU operations
**/
#define DEFINE_OP(NAME, CODE) \
void OPPROTO op_##NAME(void) \
{ \
CODE; \
}
// Register moves
DEFINE_OP(mov_32_T0_im, T0 = PARAM1);
DEFINE_OP(mov_32_T0_T1, T0 = T1);
DEFINE_OP(mov_32_T0_A0, T0 = A0);
DEFINE_OP(mov_32_T1_im, T1 = PARAM1);
DEFINE_OP(mov_32_T1_T0, T1 = T0);
DEFINE_OP(mov_32_T1_A0, T1 = A0);
DEFINE_OP(mov_32_A0_im, A0 = PARAM1);
DEFINE_OP(mov_32_A0_T0, A0 = T0);
DEFINE_OP(mov_32_A0_T1, A0 = T1);
void OPPROTO op_mov_ad_A0_im(void)
{
#if SIZEOF_VOID_P == 8
#if defined(__x86_64__)
asm volatile ("movabsq $__op_param1,%" REG_A0);
#else
#error "unsupported 64-bit value move in"
#endif
#else
A0 = PARAM1;
#endif
}
// Arithmetic operations
DEFINE_OP(add_32_T0_T1, T0 += T1);
DEFINE_OP(add_32_T0_im, T0 += PARAM1);
DEFINE_OP(add_32_T0_1, T0 += 1);
DEFINE_OP(add_32_T0_2, T0 += 2);
DEFINE_OP(add_32_T0_4, T0 += 4);
DEFINE_OP(add_32_T0_8, T0 += 8);
DEFINE_OP(sub_32_T0_T1, T0 -= T1);
DEFINE_OP(sub_32_T0_im, T0 -= PARAM1);
DEFINE_OP(sub_32_T0_1, T0 -= 1);
DEFINE_OP(sub_32_T0_2, T0 -= 2);
DEFINE_OP(sub_32_T0_4, T0 -= 4);
DEFINE_OP(sub_32_T0_8, T0 -= 8);
DEFINE_OP(add_32_T1_T0, T1 += T0);
DEFINE_OP(add_32_T1_im, T1 += PARAM1);
DEFINE_OP(add_32_T1_1, T1 += 1);
DEFINE_OP(add_32_T1_2, T1 += 2);
DEFINE_OP(add_32_T1_4, T1 += 4);
DEFINE_OP(add_32_T1_8, T1 += 8);
DEFINE_OP(sub_32_T1_T0, T1 -= T0);
DEFINE_OP(sub_32_T1_im, T1 -= PARAM1);
DEFINE_OP(sub_32_T1_1, T1 -= 1);
DEFINE_OP(sub_32_T1_2, T1 -= 2);
DEFINE_OP(sub_32_T1_4, T1 -= 4);
DEFINE_OP(sub_32_T1_8, T1 -= 8);
DEFINE_OP(add_32_A0_T1, A0 += T1);
DEFINE_OP(add_32_A0_im, A0 += PARAM1);
DEFINE_OP(add_32_A0_1, A0 += 1);
DEFINE_OP(add_32_A0_2, A0 += 2);
DEFINE_OP(add_32_A0_4, A0 += 4);
DEFINE_OP(add_32_A0_8, A0 += 8);
DEFINE_OP(sub_32_A0_T1, A0 -= T1);
DEFINE_OP(sub_32_A0_im, A0 -= PARAM1);
DEFINE_OP(sub_32_A0_1, A0 -= 1);
DEFINE_OP(sub_32_A0_2, A0 -= 2);
DEFINE_OP(sub_32_A0_4, A0 -= 4);
DEFINE_OP(sub_32_A0_8, A0 -= 8);
DEFINE_OP(umul_32_T0_T1, T0 = (uint32)T0 * (uint32)T1);
DEFINE_OP(smul_32_T0_T1, T0 = (int32)T0 * (int32)T1);
DEFINE_OP(udiv_32_T0_T1, T0 = do_udiv_32(T0, T1));
DEFINE_OP(sdiv_32_T0_T1, T0 = do_sdiv_32(T0, T1));
DEFINE_OP(xchg_32_T0_T1, { uint32 tmp = T0; T0 = T1; T1 = tmp; });
DEFINE_OP(bswap_16_T0, T0 = bswap_16(T0));
DEFINE_OP(bswap_32_T0, T0 = bswap_32(T0));
// Logical operations
DEFINE_OP(neg_32_T0, T0 = -T0);
DEFINE_OP(not_32_T0, T0 = !T0);
DEFINE_OP(not_32_T1, T1 = !T1);
DEFINE_OP(and_32_T0_T1, T0 &= T1);
DEFINE_OP(and_32_T0_im, T0 &= PARAM1);
DEFINE_OP(or_32_T0_T1, T0 |= T1);
DEFINE_OP(or_32_T0_im, T0 |= PARAM1);
DEFINE_OP(xor_32_T0_T1, T0 ^= T1);
DEFINE_OP(xor_32_T0_im, T0 ^= PARAM1);
DEFINE_OP(orc_32_T0_T1, T0 |= ~T1);
DEFINE_OP(andc_32_T0_T1, T0 &= ~T1);
DEFINE_OP(nand_32_T0_T1, T0 = ~(T0 & T1));
DEFINE_OP(nor_32_T0_T1, T0 = ~(T0 | T1));
DEFINE_OP(eqv_32_T0_T1, T0 = ~(T0 ^ T1));
DEFINE_OP(and_logical_T0_T1, T0 = T0 && T1);
DEFINE_OP(or_logical_T0_T1, T0 = T0 || T1);
// Shift/Rotate operations
DEFINE_OP(lsl_32_T0_T1, T0 = T0 << T1);
DEFINE_OP(lsl_32_T0_im, T0 = T0 << PARAM1);
DEFINE_OP(lsr_32_T0_T1, T0 = T0 >> T1);
DEFINE_OP(lsr_32_T0_im, T0 = T0 >> PARAM1);
DEFINE_OP(asr_32_T0_T1, T0 = ((int32)T0) >> T1);
DEFINE_OP(asr_32_T0_im, T0 = ((int32)T0) >> PARAM1);
DEFINE_OP(rol_32_T0_T1, T0 = do_rol_32(T0, T1));
DEFINE_OP(rol_32_T0_im, T0 = do_rol_32(T0, PARAM1));
DEFINE_OP(ror_32_T0_T1, T0 = do_ror_32(T0, T1));
DEFINE_OP(ror_32_T0_im, T0 = do_ror_32(T0, PARAM1));
// Sign-/Zero-extension
DEFINE_OP(se_16_32_T0, T0 = (int32)(int16)T0);
DEFINE_OP(ze_16_32_T0, T0 = (uint32)(uint16)T0);
DEFINE_OP(se_8_32_T0, T0 = (int32)(int8)T0);
DEFINE_OP(ze_8_32_T0, T0 = (uint32)(uint8)T0);
#undef DEFINE_OP
/**
* Native FP operations optimization
**/
#ifndef do_fabs
#define do_fabs(x) fabs(x)
#endif
#ifndef do_fadd
#define do_fadd(x, y) x + y
#endif
#ifndef do_fdiv
#define do_fdiv(x, y) x / y
#endif
#ifndef do_fmadd
#define do_fmadd(x, y, z) ((x * y) + z)
#endif
#ifndef do_fmsub
#define do_fmsub(x, y, z) ((x * y) - z)
#endif
#ifndef do_fmul
#define do_fmul(x, y) (x * y)
#endif
#ifndef do_fnabs
#define do_fnabs(x) -fabs(x)
#endif
#ifndef do_fneg
#define do_fneg(x) -x
#endif
#ifndef do_fnmadd
#define do_fnmadd(x, y, z) -((x * y) + z)
#endif
#ifndef do_fnmsub
#define do_fnmsub(x, y, z) -((x * y) - z)
#endif
#ifndef do_fsub
#define do_fsub(x, y) x - y
#endif
#ifndef do_fmov
#define do_fmov(x) x
#endif
/**
* FP double operations
**/
#if 0
double OPPROTO op_lfd(void)
{
union { double d; uint64 j; } r;
r.j = vm_do_read_memory_8((uint64 *)T1);
return r.d;
}
float OPPROTO op_lfs(void)
{
union { float f; uint32 i; } r;
r.i = vm_do_read_memory_4((uint32 *)T1);
return r.f;
}
#define DEFINE_OP(NAME, OP, ARGS) \
double OPPROTO op_##NAME(double F0, double F1, double F2) \
{ \
return do_##OP ARGS; \
}
DEFINE_OP(fmov_F1, fmov, (F1));
DEFINE_OP(fmov_F2, fmov, (F2));
DEFINE_OP(fabs, fabs, (F0));
DEFINE_OP(fadd, fadd, (F0, F1));
DEFINE_OP(fdiv, fdiv, (F0, F1));
DEFINE_OP(fmadd, fmadd, (F0, F1, F2));
DEFINE_OP(fmsub, fmsub, (F0, F1, F2));
DEFINE_OP(fmul, fmul, (F0, F1));
DEFINE_OP(fnabs, fnabs, (F0));
DEFINE_OP(fneg, fneg, (F0));
DEFINE_OP(fnmadd, fnmadd, (F0, F1, F2));
DEFINE_OP(fnmsub, fnmsub, (F0, F1, F2));
DEFINE_OP(fsub, fsub, (F0, F1));
#undef DEFINE_OP
/**
* FP single operations
**/
#define DEFINE_OP(NAME, OP, ARGS) \
float OPPROTO op_##NAME(float F0, float F1, float F2) \
{ \
return do_##OP ARGS; \
}
DEFINE_OP(fmovs_F1, fmov, (F1));
DEFINE_OP(fmovs_F2, fmov, (F2));
DEFINE_OP(fabss_F0, fabs, (F0));
DEFINE_OP(fadds_F0_F1, fadd, (F0, F1));
DEFINE_OP(fdivs_F0_F1, fdiv, (F0, F1));
DEFINE_OP(fmadds_F0_F1_F2, fmadd, (F0, F1, F2));
DEFINE_OP(fmsubs_F0_F1_F2, fmsub, (F0, F1, F2));
DEFINE_OP(fmuls_F0_F1, fmul, (F0, F1));
DEFINE_OP(fnabss_F0, fnabs, (F0));
DEFINE_OP(fnegs_F0, fneg, (F0));
DEFINE_OP(fnmadds_F0_F1_F2, fnmadd, (F0, F1, F2));
DEFINE_OP(fnmsubs_F0_F1_F2, fnmsub, (F0, F1, F2));
DEFINE_OP(fsubs_F0_F1, fsub, (F0, F1));
#undef DEFINE_OP
#endif
/**
* Load/Store instructions
**/
#define im PARAM1
#define DEFINE_OP(BITS,REG,SIZE,OFFSET) \
void OPPROTO op_load_u##BITS##_##REG##_A0_##OFFSET(void) \
{ \
REG = (uint32)(uint##BITS)vm_read_memory_##SIZE(A0 + OFFSET); \
} \
void OPPROTO op_load_s##BITS##_##REG##_A0_##OFFSET(void) \
{ \
REG = (int32)(int##BITS)vm_read_memory_##SIZE(A0 + OFFSET); \
} \
void OPPROTO op_store_##BITS##_##REG##_A0_##OFFSET(void) \
{ \
vm_write_memory_##SIZE(A0 + OFFSET, REG); \
}
DEFINE_OP(32,T0,4,0);
DEFINE_OP(32,T0,4,im);
DEFINE_OP(32,T0,4,T1);
DEFINE_OP(16,T0,2,0);
DEFINE_OP(16,T0,2,im);
DEFINE_OP(16,T0,2,T1);
DEFINE_OP(8,T0,1,0);
DEFINE_OP(8,T0,1,im);
DEFINE_OP(8,T0,1,T1);
#undef im
#undef DEFINE_OP
/**
* Control flow
**/
#ifdef __i386__
#define FORCE_RET() asm volatile ("ret")
#endif
#ifdef __x86_64__
#define FORCE_RET() asm volatile ("ret")
#endif
#ifdef __powerpc__
#define FORCE_RET() asm volatile ("blr")
#endif
#ifdef __s390__
#define FORCE_RET() asm volatile ("br %r14")
#endif
#ifdef __alpha__
#define FORCE_RET() asm volatile ("ret")
#endif
#ifdef __ia64__
#define FORCE_RET() asm volatile ("br.ret.sptk.many b0;;")
#endif
#ifdef __sparc__
#define FORCE_RET() asm volatile ("jmpl %i0 + 8, %g0\n" \
"nop")
#endif
#ifdef __arm__
#define FORCE_RET() asm volatile ("b exec_loop")
#endif
#ifdef __mc68000
#define FORCE_RET() asm volatile ("rts")
#endif
#define SLOW_DISPATCH(TARGET) do { \
static const void __attribute__((unused)) *label1 = &&dummy_label1; \
static const void __attribute__((unused)) *label2 = &&dummy_label2; \
goto *((void *)TARGET); \
dummy_label1: \
dummy_label2: \
dyngen_barrier(); \
} while (0)
#if defined(__powerpc__)
#define FAST_DISPATCH(TARGET) asm volatile ("b " #TARGET)
#endif
#if defined(__i386__) || defined(__x86_64__)
#define FAST_DISPATCH(TARGET) asm volatile ("jmp " #TARGET)
#endif
extern "C" void OPPROTO op_execute(uint8 *entry_point, basic_cpu *this_cpu);
void OPPROTO op_execute(uint8 *entry_point, basic_cpu *this_cpu)
{
typedef void (*func_t)(void);
func_t func = (func_t)entry_point;
#ifdef REG_CPU
volatile uintptr saved_CPU = (uintptr)CPU;
CPU = this_cpu;
#endif
#ifdef REG_A0
volatile uintptr saved_A0 = reg_A0;
#endif
#ifdef REG_T0
volatile uintptr saved_T0 = reg_T0;
#endif
#ifdef REG_T1
volatile uintptr saved_T1 = reg_T1;
#endif
#ifdef REG_T2
volatile uintptr saved_T2 = reg_T2;
#endif
#ifdef REG_T3
volatile uintptr saved_T3 = reg_T3;
#endif
SLOW_DISPATCH(entry_point);
func(); // NOTE: never called, fake to make compiler save return point
asm volatile (".section \".data\"");
asm volatile (".global op_exec_return_offset");
asm volatile ("op_exec_return_offset:");
asm volatile (".long 1f-op_execute");
asm volatile (".size op_exec_return_offset,.-op_exec_return_offset");
asm volatile (".previous");
asm volatile ("1:");
#ifdef REG_T3
reg_T3 = saved_T3;
#endif
#ifdef REG_T2
reg_T2 = saved_T2;
#endif
#ifdef REG_T1
reg_T1 = saved_T1;
#endif
#ifdef REG_T0
reg_T0 = saved_T0;
#endif
#ifdef REG_A0
reg_A0 = saved_A0;
#endif
#ifdef REG_CPU
CPU = (basic_cpu *)saved_CPU;
#endif
}
void OPPROTO op_jmp_slow(void)
{
SLOW_DISPATCH(PARAM1);
}
void OPPROTO op_jmp_fast(void)
{
#ifdef FAST_DISPATCH
FAST_DISPATCH(__op_param1);
#else
SLOW_DISPATCH(PARAM1);
#endif
}
// Register calling conventions based arches don't need a stack frame
#if defined(__powerpc__) || defined(__x86_64__)
#define DEFINE_OP(NAME, CODE) \
static void OPPROTO impl_##NAME(void) \
{ \
asm volatile (#NAME ":"); \
CODE; \
FORCE_RET(); \
asm volatile ("." #NAME ":"); \
asm volatile (".size " #NAME ",." #NAME "-" #NAME); \
} \
void OPPROTO helper_##NAME(void) __attribute__((weak, alias(#NAME)));
#else
#define DEFINE_OP(NAME, CODE) \
void OPPROTO NAME(void) \
{ \
CODE; \
}
#endif
#define CALL(CALL_CODE) CALL_CODE
DEFINE_OP(op_invoke, {
typedef void (*func_t)(void);
func_t func = (func_t)reg_A0;
CALL(func());
});
DEFINE_OP(op_invoke_T0, {
typedef void (*func_t)(uint32);
func_t func = (func_t)reg_A0;
CALL(func(T0));
});
DEFINE_OP(op_invoke_im, {
typedef void (*func_t)(uint32);
func_t func = (func_t)reg_A0;
CALL(func(PARAM1));
});
DEFINE_OP(op_invoke_CPU, {
typedef void (*func_t)(void *);
func_t func = (func_t)reg_A0;
CALL(func(CPU));
});
DEFINE_OP(op_invoke_CPU_T0, {
typedef void (*func_t)(void *, uint32);
func_t func = (func_t)reg_A0;
CALL(func(CPU, T0));
});
DEFINE_OP(op_invoke_CPU_im, {
typedef void (*func_t)(void *, uint32);
func_t func = (func_t)reg_A0;
CALL(func(CPU, PARAM1));
});
DEFINE_OP(op_invoke_direct, {
typedef void (*func_t)(void);
func_t func = (func_t)PARAM1;
CALL(func());
});
DEFINE_OP(op_invoke_direct_T0, {
typedef void (*func_t)(uint32);
func_t func = (func_t)PARAM1;
CALL(func(T0));
});
DEFINE_OP(op_invoke_direct_im, {
typedef void (*func_t)(uint32);
func_t func = (func_t)PARAM1;
CALL(func(PARAM2));
});
DEFINE_OP(op_invoke_direct_CPU, {
typedef void (*func_t)(void *);
func_t func = (func_t)PARAM1;
CALL(func(CPU));
});
DEFINE_OP(op_invoke_direct_CPU_T0, {
typedef void (*func_t)(void *, uint32);
func_t func = (func_t)PARAM1;
CALL(func(CPU, T0));
});
DEFINE_OP(op_invoke_direct_CPU_im, {
typedef void (*func_t)(void *, uint32);
func_t func = (func_t)PARAM1;
CALL(func(CPU, PARAM2));
});
#undef DEFINE_OP

View File

@ -0,0 +1,104 @@
/*
* dyngen-glue.hpp - Glue to QEMU dyngen infrastructure
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "basic-dyngen.hpp"
int __op_param1, __op_param2, __op_param3;
int __op_jmp0, __op_jmp1;
#define DYNGEN_IMPL 1
#define DEFINE_GEN(NAME,ARGS) void basic_dyngen::NAME ARGS
#include "basic-dyngen-ops.hpp"
basic_dyngen::basic_dyngen(dyngen_cpu_base cpu)
: parent_cpu(cpu)
{
execute_func = gen_start();
gen_op_execute();
gen_end();
set_code_start(code_ptr());
}
void
basic_dyngen::gen_invoke(void (*func)())
{
if (direct_call_possible((uintptr)func))
gen_op_invoke_direct((uintptr)func);
else {
gen_op_mov_ad_A0_im((uintptr)func);
gen_op_invoke();
}
}
void
basic_dyngen::gen_invoke_T0(void (*func)(uint32))
{
if (direct_call_possible((uintptr)func))
gen_op_invoke_direct_T0((uintptr)func);
else {
gen_op_mov_ad_A0_im((uintptr)func);
gen_op_invoke_T0();
}
}
void
basic_dyngen::gen_invoke_im(void (*func)(uint32), uint32 value)
{
if (direct_call_possible((uintptr)func))
gen_op_invoke_direct_im((uintptr)func, value);
else {
gen_op_mov_ad_A0_im((uintptr)func);
gen_op_invoke_im(value);
}
}
void
basic_dyngen::gen_invoke_CPU(void (*func)(dyngen_cpu_base))
{
if (direct_call_possible((uintptr)func))
gen_op_invoke_direct_CPU((uintptr)func);
else {
gen_op_mov_ad_A0_im((uintptr)func);
gen_op_invoke_CPU();
}
}
void
basic_dyngen::gen_invoke_CPU_T0(void (*func)(dyngen_cpu_base, uint32))
{
if (direct_call_possible((uintptr)func))
gen_op_invoke_direct_CPU_T0((uintptr)func);
else {
gen_op_mov_ad_A0_im((uintptr)func);
gen_op_invoke_CPU_T0();
}
}
void
basic_dyngen::gen_invoke_CPU_im(void (*func)(dyngen_cpu_base, uint32), uint32 value)
{
if (direct_call_possible((uintptr)func))
gen_op_invoke_direct_CPU_im((uintptr)func, value);
else {
gen_op_mov_ad_A0_im((uintptr)func);
gen_op_invoke_CPU_im(value);
}
}

View File

@ -0,0 +1,315 @@
/*
* basic-dyngen.hpp - Basic code generator
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef BASIC_DYNGEN_H
#define BASIC_DYNGEN_H
#include "cpu/jit/jit-config.hpp"
#include "cpu/jit/jit-cache.hpp"
#include JIT_TARGET_INCLUDE(jit-target-cache.hpp)
#ifdef SHEEPSHAVER
class powerpc_cpu;
typedef powerpc_cpu *dyngen_cpu_base;
#else
class basic_cpu;
typedef basic_cpu *dyngen_cpu_base;
#endif
class basic_dyngen
: public basic_jit_cache
{
uint8 *execute_func;
uint8 *gen_code_start;
dyngen_cpu_base parent_cpu;
// Can we generate a direct call to target function?
bool direct_jump_possible(uintptr target) const;
bool direct_call_possible(uintptr target) const;
// Generic code generators
# define DEFINE_CST(NAME,VALUE) static const unsigned long NAME = VALUE;
# define DEFINE_GEN(NAME,ARGS) void NAME ARGS;
# include "basic-dyngen-ops.hpp"
public:
// Constructor, parent CPU required
basic_dyngen(dyngen_cpu_base cpu);
// Return CPU context associated to this code generator
dyngen_cpu_base cpu() const
{ return parent_cpu; }
// Start code generation of a new block
// Align on 16-byte boundaries
// Returns pointer to entry point
uint8 *gen_start();
// Stop code generation of the block
// Returns FALSE if translation cache is full
bool gen_end() const;
// Execute compiled function at ENTRY_POINT
void execute(uint8 *entry_point);
// Return from compiled code
void gen_exec_return();
// Function calls
void gen_jmp(const uint8 *target);
void gen_invoke(void (*func)(void));
void gen_invoke_T0(void (*func)(uint32));
void gen_invoke_im(void (*func)(uint32), uint32 value);
void gen_invoke_CPU(void (*func)(dyngen_cpu_base));
void gen_invoke_CPU_T0(void (*func)(dyngen_cpu_base, uint32));
void gen_invoke_CPU_im(void (*func)(dyngen_cpu_base, uint32), uint32 value);
// Raw aliases
#define DEFINE_ALIAS_RAW(NAME, ARGLIST, ARGS) \
void gen_##NAME ARGLIST { gen_op_##NAME ARGS; }
#define DEFINE_ALIAS_0(NAME) DEFINE_ALIAS_RAW(NAME,(),())
#define DEFINE_ALIAS_1(NAME) DEFINE_ALIAS_RAW(NAME,(long p1),(p1))
#define DEFINE_ALIAS_2(NAME) DEFINE_ALIAS_RAW(NAME,(long p1,long p2),(p1,p2))
#define DEFINE_ALIAS_3(NAME) DEFINE_ALIAS_RAW(NAME,(long p1,long p2,long p3),(p1,p2,p3))
#define DEFINE_ALIAS(NAME, N) DEFINE_ALIAS_##N(NAME)
// Register moves
DEFINE_ALIAS(mov_32_T0_im,1);
DEFINE_ALIAS(mov_32_T0_T1,0);
DEFINE_ALIAS(mov_32_T0_A0,0);
DEFINE_ALIAS(mov_32_T1_im,1);
DEFINE_ALIAS(mov_32_T1_T0,0);
DEFINE_ALIAS(mov_32_T1_A0,0);
DEFINE_ALIAS(mov_32_A0_im,1);
DEFINE_ALIAS(mov_32_A0_T0,0);
DEFINE_ALIAS(mov_32_A0_T1,0);
DEFINE_ALIAS(mov_ad_A0_im,1);
// Arithmetic operations
DEFINE_ALIAS(add_32_T0_T1,0);
void gen_add_32_T0_im(int32 value);
DEFINE_ALIAS(sub_32_T0_T1,0);
void gen_sub_32_T0_im(int32 value);
DEFINE_ALIAS(add_32_T1_T0,0);
void gen_add_32_T1_im(int32 value);
DEFINE_ALIAS(sub_32_T1_T0,0);
void gen_sub_32_T1_im(int32 value);
DEFINE_ALIAS(add_32_A0_T1,0);
void gen_add_32_A0_im(int32 value);
DEFINE_ALIAS(sub_32_A0_T1,0);
void gen_sub_32_A0_im(int32 value);
DEFINE_ALIAS(umul_32_T0_T1,0);
DEFINE_ALIAS(smul_32_T0_T1,0);
DEFINE_ALIAS(udiv_32_T0_T1,0);
DEFINE_ALIAS(sdiv_32_T0_T1,0);
DEFINE_ALIAS(xchg_32_T0_T1,0);
DEFINE_ALIAS(bswap_16_T0,0);
DEFINE_ALIAS(bswap_32_T0,0);
// Logical operations
DEFINE_ALIAS(neg_32_T0,0);
DEFINE_ALIAS(not_32_T0,0);
DEFINE_ALIAS(not_32_T1,0);
DEFINE_ALIAS(and_32_T0_T1,0);
DEFINE_ALIAS(and_32_T0_im,1);
DEFINE_ALIAS(or_32_T0_T1,0);
DEFINE_ALIAS(or_32_T0_im,1);
DEFINE_ALIAS(xor_32_T0_T1,0);
DEFINE_ALIAS(xor_32_T0_im,1);
DEFINE_ALIAS(orc_32_T0_T1,0);
DEFINE_ALIAS(andc_32_T0_T1,0);
DEFINE_ALIAS(nand_32_T0_T1,0);
DEFINE_ALIAS(nor_32_T0_T1,0);
DEFINE_ALIAS(eqv_32_T0_T1,0);
DEFINE_ALIAS(and_logical_T0_T1,0);
DEFINE_ALIAS(or_logical_T0_T1,0);
// Shift/Rotate operations
DEFINE_ALIAS(lsl_32_T0_T1,0);
DEFINE_ALIAS(lsl_32_T0_im,1);
DEFINE_ALIAS(lsr_32_T0_T1,0);
DEFINE_ALIAS(lsr_32_T0_im,1);
DEFINE_ALIAS(asr_32_T0_T1,0);
DEFINE_ALIAS(asr_32_T0_im,1);
DEFINE_ALIAS(rol_32_T0_T1,0);
DEFINE_ALIAS(rol_32_T0_im,1);
DEFINE_ALIAS(ror_32_T0_T1,0);
DEFINE_ALIAS(ror_32_T0_im,1);
// Sign-/Zero-extension
DEFINE_ALIAS(se_16_32_T0,0);
DEFINE_ALIAS(ze_16_32_T0,0);
DEFINE_ALIAS(se_8_32_T0,0);
DEFINE_ALIAS(ze_8_32_T0,0);
// Jump instructions
DEFINE_ALIAS(jmp_slow,1);
DEFINE_ALIAS(jmp_fast,1);
// Load/Store instructions
DEFINE_ALIAS(load_u32_T0_A0_T1,0);
void gen_load_u32_T0_A0_im(int32 offset);
DEFINE_ALIAS(load_s32_T0_A0_T1,0);
void gen_load_s32_T0_A0_im(int32 offset);
DEFINE_ALIAS(load_u16_T0_A0_T1,0);
void gen_load_u16_T0_A0_im(int32 offset);
DEFINE_ALIAS(load_s16_T0_A0_T1,0);
void gen_load_s16_T0_A0_im(int32 offset);
DEFINE_ALIAS(load_u8_T0_A0_T1,0);
void gen_load_u8_T0_A0_im(int32 offset);
DEFINE_ALIAS(load_s8_T0_A0_T1,0);
void gen_load_s8_T0_A0_im(int32 offset);
DEFINE_ALIAS(store_32_T0_A0_T1,0);
void gen_store_32_T0_A0_im(int32 offset);
DEFINE_ALIAS(store_16_T0_A0_T1,0);
void gen_store_16_T0_A0_im(int32 offset);
DEFINE_ALIAS(store_8_T0_A0_T1,0);
void gen_store_8_T0_A0_im(int32 offset);
#undef DEFINE_ALIAS
#undef DEFINE_ALIAS_0
#undef DEFINE_ALIAS_1
#undef DEFINE_ALIAS_2
#undef DEFINE_ALIAS_3
#undef DEFINE_ALIAS_RAW
};
inline bool
basic_dyngen::direct_jump_possible(uintptr target) const
{
#if defined(__powerpc__)
const uintptr LI_OFFSET_MAX = 1 << 26;
return (((target - (uintptr)code_ptr()) < LI_OFFSET_MAX) ||
(((uintptr)code_ptr() - target) < LI_OFFSET_MAX));
#endif
#if defined(__i386__)
return true;
#endif
#if defined(__x86_64__)
const intptr offset = (intptr)target - (intptr)code_ptr() - sizeof(void *);
return offset <= 0xffffffff;
#endif
return false;
}
inline void
basic_dyngen::gen_jmp(const uint8 *target_p)
{
const uintptr target = (uintptr)target_p;
if (direct_jump_possible(target))
gen_op_jmp_fast(target);
else
gen_op_jmp_slow(target);
}
inline void
basic_dyngen::execute(uint8 *entry_point)
{
typedef void (*func_t)(uint8 *, dyngen_cpu_base);
func_t func = (func_t)execute_func;
func(entry_point, parent_cpu);
}
inline void
basic_dyngen::gen_exec_return()
{
gen_jmp(execute_func + op_exec_return_offset);
}
inline bool
basic_dyngen::direct_call_possible(uintptr target) const
{
#if defined(__powerpc__)
const uintptr LI_OFFSET_MAX = 1 << 26;
return (((target - (uintptr)code_ptr()) < LI_OFFSET_MAX) ||
(((uintptr)code_ptr() - target) < LI_OFFSET_MAX));
#endif
#if defined(__i386__)
return true;
#endif
#if defined(__x86_64__)
const intptr offset = (intptr)target - (intptr)code_ptr() - sizeof(void *);
return offset <= 0xffffffff;
#endif
return false;
}
inline uint8 *
basic_dyngen::gen_start()
{
while ((uintptr)code_ptr() & 15)
inc_code_ptr(1);
gen_code_start = code_ptr();
return gen_code_start;
}
inline bool
basic_dyngen::gen_end() const
{
flush_icache_range((unsigned long)gen_code_start, (unsigned long)code_ptr());
return !full_translation_cache();
}
#define DEFINE_OP(OP,REG) \
inline void \
basic_dyngen::gen_##OP##_32_##REG##_im(int32 value) \
{ \
if (value == 0) return; \
else if (value == 1) gen_op_##OP##_32_##REG##_1(); \
else if (value == 2) gen_op_##OP##_32_##REG##_2(); \
else if (value == 4) gen_op_##OP##_32_##REG##_4(); \
else if (value == 8) gen_op_##OP##_32_##REG##_8(); \
else gen_op_##OP##_32_##REG##_im(value); \
}
DEFINE_OP(add,A0);
DEFINE_OP(add,T0);
DEFINE_OP(add,T1);
DEFINE_OP(sub,A0);
DEFINE_OP(sub,T0);
DEFINE_OP(sub,T1);
#undef DEFINE_OP
#define DEFINE_OP(NAME,REG,SIZE) \
inline void \
basic_dyngen::gen_##NAME##_##SIZE##_##REG##_A0_im(int32 offset) \
{ \
if (offset == 0) \
gen_op_##NAME##_##SIZE##_##REG##_A0_0(); \
else \
gen_op_##NAME##_##SIZE##_##REG##_A0_im(offset); \
}
DEFINE_OP(load,T0,u32);
DEFINE_OP(load,T0,s32);
DEFINE_OP(store,T0,32);
DEFINE_OP(load,T0,u16);
DEFINE_OP(load,T0,s16);
DEFINE_OP(store,T0,16);
DEFINE_OP(load,T0,u8);
DEFINE_OP(load,T0,s8);
DEFINE_OP(store,T0,8);
#undef DEFINE_OP
#endif /* BASIC_DYNGEN_H */

View File

@ -0,0 +1,32 @@
/*
* cxxdemangle.cpp - C++ demangler
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "cxxdemangle.h"
#if defined(__GNUC__) && (__GXX_ABI_VERSION > 0)
#include <cxxabi.h>
char *
cxx_demangle(const char *mangled_name, char *buf, size_t *n, int *status)
{
return abi::__cxa_demangle(mangled_name, buf, n, status);
}
#endif

View File

@ -0,0 +1,55 @@
/*
* cxxdemangle.h - C++ demangler
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef CXX_DEMANGLE_H
#define CXX_DEMANGLE_H
/**
* cxx_demangle
*
* Following GCC 3.0 ABI:
* <http://www.codesourcery.com/cxx-abi/abi.html>
*
* - MANGLED-NAME is a pointer to a null-terminated array of
* characters
*
* - BUF may be null. If it is non-null, then N must also be
* nonnull, and BUF is a pointer to an array, of at least *N
* characters, that was allocated using malloc().
*
* - STATUS points to an int that is used as an error indicator. It
* is permitted to be null, in which case the user just doesn't
* get any detailed error information.
*
* Codes: 0: success
* -1: memory allocation failure
* -2: invalid mangled name
* -3: invalid arguments (e.g. BUG nonnull and N null)
**/
#ifdef __cplusplus
extern "C"
#endif
char *cxx_demangle(const char *mangled_name,
char *buf,
size_t *n,
int *status);
#endif /* CXX_DEMANGLE_H */

View File

@ -0,0 +1,28 @@
/*
* jit-target-cache.hpp - Target specific code to invalidate cache
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef JIT_TARGET_CACHE_H
#define JIT_TARGET_CACHE_H
static inline void flush_icache_range(unsigned long, unsigned long)
{
}
#endif /* JIT_TARGET_CACHE_H */

View File

@ -0,0 +1,103 @@
/*
* dyngen defines for micro operation code
*
* Copyright (c) 2003 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DYNGEN_EXEC_H
#define DYNGEN_EXEC_H
#include "cpu/jit/jit-config.hpp"
#include JIT_TARGET_INCLUDE(dyngen-target-exec.h)
/* define virtual register set */
#define REG_A0 AREG0
#define REG_A0_ID AREG0_ID
#define REG_T0 AREG1
#define REG_T0_ID AREG1_ID
#define REG_T1 AREG2
#define REG_T1_ID AREG2_ID
#ifdef AREG3
#define REG_T2 AREG3
#define REG_T2_ID AREG3_ID
#endif
#ifdef AREG4
#define REG_T3 AREG4
#define REG_T3_ID AREG4_ID
#endif
#ifdef AREG5
#define REG_CPU AREG5
#define REG_CPU_ID AREG5_ID
#endif
#ifdef FREG0
#define REG_F0 FREG0
#define REG_F0_ID FREG0_ID
#endif
#ifdef FREG1
#define REG_F1 FREG1
#define REG_F1_ID FREG1_ID
#endif
#ifdef FREG2
#define REG_F2 FREG2
#define REG_F2_ID FREG2_ID
#endif
#ifdef FREG3
#define REG_F3 FREG3
#define REG_F3_ID FREG3_ID
#endif
// Force only one return point
#define dyngen_barrier() asm volatile ("")
#ifndef OPPROTO
#define OPPROTO
#endif
#ifdef __alpha__
/* the symbols are considered non exported so a br immediate is generated */
#define __hidden __attribute__((visibility("hidden")))
#else
#define __hidden
#endif
#ifdef __alpha__
/* Suggested by Richard Henderson. This will result in code like
ldah $0,__op_param1($29) !gprelhigh
lda $0,__op_param1($0) !gprellow
We can then conveniently change $29 to $31 and adapt the offsets to
emit the appropriate constant. */
#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; })
#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; })
#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; })
extern int __op_param1 __hidden;
extern int __op_param2 __hidden;
extern int __op_param3 __hidden;
#else
extern int __op_param1, __op_param2, __op_param3;
#define PARAM1 ((long)(&__op_param1))
#define PARAM2 ((long)(&__op_param2))
#define PARAM3 ((long)(&__op_param3))
#endif
#ifndef REG_CPU
extern int __op_cpuparam;
#define CPUPARAM ((long)(&__op_cpuparam))
#endif
extern int __op_jmp0, __op_jmp1;
#endif /* DYNGEN_EXEC_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
/*
* jit-cache.cpp - Translation cache management
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "vm_alloc.h"
#include "cpu/jit/jit-cache.hpp"
#define DEBUG 0
#include "debug.h"
basic_jit_cache::basic_jit_cache(uint32 init_cache_size)
: tcode_start(NULL), code_start(NULL), code_p(NULL), code_end(NULL)
{
init_translation_cache(init_cache_size);
}
basic_jit_cache::~basic_jit_cache()
{
kill_translation_cache();
}
bool
basic_jit_cache::init_translation_cache(uint32 size)
{
// Round up translation cache size to next 16 KB boundaries
const uint32 roundup = 16 * 1024;
cache_size = (size + JIT_CACHE_SIZE_GUARD + roundup - 1) & -roundup;
tcode_start = (uint8 *)vm_acquire(cache_size, VM_MAP_PRIVATE | VM_MAP_32BIT);
if (tcode_start == VM_MAP_FAILED) {
tcode_start = NULL;
return false;
}
if (vm_protect(tcode_start, cache_size,
VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
vm_release(tcode_start, cache_size);
tcode_start = NULL;
return false;
}
D(bug("basic_jit_cache: Translation cache: %d KB at %p\n", cache_size / 1024, tcode_start));
code_start = tcode_start;
code_p = code_start;
code_end = code_p + cache_size;
return true;
}
void
basic_jit_cache::kill_translation_cache()
{
if (tcode_start)
vm_release(tcode_start, cache_size);
}

View File

@ -0,0 +1,112 @@
/*
* jit-cache.hpp - Translation cache management
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef JIT_CACHE_H
#define JIT_CACHE_H
/**
* Basic translation cache
**/
class basic_jit_cache
{
// Default cache size (2 MB)
static const uint32 JIT_CACHE_SIZE = 2 * 1024 * 1024;
static const uint32 JIT_CACHE_SIZE_GUARD = 4096;
uint32 cache_size;
// Translation cache (allocated base, current pointer, end pointer)
uint8 *tcode_start;
uint8 *code_start;
uint8 *code_p;
uint8 *code_end;
protected:
// Initialize translation cache
bool init_translation_cache(uint32 size);
void kill_translation_cache();
// Initialize user code start
void set_code_start(uint8 *ptr);
// Get & increase current position
void inc_code_ptr(int offset) { code_p += offset; }
public:
uint8 *code_ptr() const { return code_p; }
public:
// Default constructor & destructor
basic_jit_cache(uint32 init_cache_size = JIT_CACHE_SIZE);
~basic_jit_cache();
// Invalidate translation cache
void invalidate_cache();
bool full_translation_cache() const
{ return code_p >= code_end; }
// Emit code to translation cache
template< typename T >
void emit_generic(T v);
void emit_8(uint8 v) { emit_generic<uint8>(v); }
void emit_16(uint16 v) { emit_generic<uint16>(v); }
void emit_32(uint32 v) { emit_generic<uint32>(v); }
void emit_64(uint64 v) { emit_generic<uint64>(v); }
void emit_ptr(uintptr v) { emit_generic<uintptr>(v); }
void copy_block(const uint8 *block, uint32 size);
void emit_block(const uint8 *block, uint32 size);
};
inline void
basic_jit_cache::set_code_start(uint8 *ptr)
{
assert(ptr >= tcode_start && ptr < code_end);
code_start = ptr;
}
inline void
basic_jit_cache::invalidate_cache()
{
code_p = code_start;
}
template< class T >
inline void
basic_jit_cache::emit_generic(T v)
{
*((T *)code_ptr()) = v;
inc_code_ptr(sizeof(T));
}
inline void
basic_jit_cache::copy_block(const uint8 *block, uint32 size)
{
memcpy(code_ptr(), block, size);
}
inline void
basic_jit_cache::emit_block(const uint8 *block, uint32 size)
{
copy_block(block, size);
inc_code_ptr(size);
}
#endif /* JIT_CACHE_H */

View File

@ -0,0 +1,86 @@
/*
* jit-config.hpp - JIT config utils
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef JIT_CONFIG_H
#define JIT_CONFIG_H
/**
* ENABLE_DYNGEN
*
* Define to enable the portable "JIT1" engine based on code
* inlining technique as implemented in QEMU.
**/
#ifndef ENABLE_DYNGEN
#define ENABLE_DYNGEN 0
#endif
/**
* DYNGEN_ASM_OPTS
*
* Define to permit host inline asm optimizations. This is
* particularly useful to compute emulated condition code
* registers.
**/
#ifndef DYNGEN_ASM_OPTS
#define DYNGEN_ASM_OPTS 1
#endif
/**
* Helpers to reach JIT backends headers
**/
#if defined(__powerpc__) || defined(__ppc__)
#define JIT_TARGET ppc
#endif
#if defined(__i386__)
#define JIT_TARGET x86
#endif
#if defined(__x86_64__)
#define JIT_TARGET amd64
#endif
#if defined(__s390__)
#define JIT_TARGET s390
#endif
#if defined(__alpha__)
#define JIT_TARGET alpha
#endif
#if defined(__ia64__)
#define JIT_TARGET ia64
#endif
#if defined(__sparc__)
#define JIT_TARGET sparc
#endif
#if defined(__arm__)
#define JIT_TARGET arm
#endif
#if defined(__mc68000)
#define JIT_TARGET m68k
#endif
#ifndef JIT_TARGET
#error "Unsupport architecture for JIT1"
#endif
#define JIT_PATH_CONCAT(X, Y) X/Y
#define JIT_MAKE_HEADER(PATH, HEADER) <JIT_PATH_CONCAT(PATH,HEADER)>
#define JIT_TARGET_INCLUDE(HEADER) JIT_MAKE_HEADER(JIT_PATH_CONCAT(cpu/jit,JIT_TARGET),HEADER)
#endif /* JIT_CONFIG_H */

View File

@ -0,0 +1,71 @@
/*
* dyngen defines for micro operation code
*
* Copyright (c) 2003 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DYNGEN_TARGET_EXEC_H
#define DYNGEN_TARGET_EXEC_H
enum {
#define AREG0 "r27"
AREG0_ID = 27,
#define AREG1 "r24"
AREG1_ID = 24,
#define AREG2 "r25"
AREG2_ID = 25,
#define AREG3 "r26"
AREG3_ID = 26,
#define AREG4 "r16"
AREG4_ID = 16,
#define AREG5 "r17"
AREG5_ID = 17,
#define AREG6 "r18"
AREG6_ID = 18,
#define AREG7 "r19"
AREG7_ID = 19,
#define AREG8 "r20"
AREG8_ID = 20,
#define AREG9 "r21"
AREG9_ID = 21,
#define AREG10 "r22"
AREG10_ID = 22,
#define AREG11 "r23"
AREG11_ID = 23,
#define FREG0 "f1"
FREG0_ID = 1,
#define FREG1 "f2"
FREG1_ID = 2,
#define FREG2 "f3"
FREG2_ID = 3,
};
#endif /* DYNGEN_TARGET_EXEC_H */

View File

@ -0,0 +1,44 @@
/*
* jit-target-cache.hpp - Target specific code to invalidate cache
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef JIT_TARGET_CACHE_H
#define JIT_TARGET_CACHE_H
static inline void flush_icache_range(unsigned long start, unsigned long stop)
{
const int MIN_CACHE_LINE_SIZE = 8; /* conservative value */
unsigned long p;
p = start & ~(MIN_CACHE_LINE_SIZE - 1);
stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
}
asm volatile ("sync" : : : "memory");
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
}
asm volatile ("sync" : : : "memory");
asm volatile ("isync" : : : "memory");
}
#endif /* JIT_TARGET_CACHE_H */

View File

@ -0,0 +1,39 @@
/*
* dyngen defines for micro operation code
*
* Copyright (c) 2003 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DYNGEN_TARGET_EXEC_H
#define DYNGEN_TARGET_EXEC_H
enum {
/* callee save registers */
#define AREG0 "ebp"
AREG0_ID = 5,
#define AREG1 "ebx"
AREG1_ID = 3,
#define AREG2 "esi"
AREG2_ID = 6,
#define AREG3 "edi"
AREG3_ID = 7,
};
#endif /* DYNGEN_TARGET_EXEC_H */

View File

@ -0,0 +1 @@
#include "cpu/jit/t-dummy/jit-target-cache.hpp"

View File

@ -37,7 +37,12 @@ struct powerpc_block_info
uint32 opcode;
};
#if PPC_DECODE_CACHE
decode_info * di;
#endif
#if PPC_ENABLE_JIT
uint8 * entry_point;
#endif
};
#endif /* PPC_BLOCKINFO_H */

View File

@ -90,6 +90,18 @@
#endif
/**
* PPC_ENABLE_JIT
*
* Define to 1 if dynamic translation is used. This requires
* dyngen to be enabled first.
**/
#ifndef PPC_ENABLE_JIT
#define PPC_ENABLE_JIT ENABLE_DYNGEN
#endif
/**
* PPC_EXECUTE_DUMP_STATE
*
@ -115,6 +127,19 @@
#endif
/**
* PPC_PROFILE_COMPILE_TIME
*
* Define to enable some compile time statistics. This concerns
* time spent into the decoder (PPC_DECODE_CACHE case) or total
* time spent into the dynamic translator (PPC_ENABLE_JIT case).
**/
#ifndef PPC_PROFILE_COMPILE_TIME
#define PPC_PROFILE_COMPILE_TIME 0
#endif
/**
* Sanity checks and features enforcements
**/
@ -124,4 +149,8 @@
#undef PPC_NO_STATIC_II_INDEX_TABLE
#endif
#if PPC_ENABLE_JIT
#undef PPC_DECODE_CACHE
#endif
#endif /* PPC_CONFIG_H */

View File

@ -22,6 +22,13 @@
#include "vm_alloc.h"
#include "cpu/vm.hpp"
#include "cpu/ppc/ppc-cpu.hpp"
#ifndef SHEEPSHAVER
#include "basic-kernel.hpp"
#endif
#if PPC_ENABLE_JIT
#include "cpu/jit/dyngen-exec.h"
#endif
#if ENABLE_MON
#include "mon.h"
@ -31,9 +38,6 @@
#define DEBUG 0
#include "debug.h"
// Define to gather some compile time statistics
#define PROFILE_COMPILE_TIME 1
void powerpc_cpu::set_register(int id, any_register const & value)
{
if (id >= powerpc_registers::GPR(0) && id <= powerpc_registers::GPR(31)) {
@ -198,6 +202,19 @@ void powerpc_cpu::dump_log(const char *filename)
}
#endif
#if ENABLE_MON
static uint32 mon_read_byte_ppc(uintptr addr)
{
return *((uint8 *)addr);
}
static void mon_write_byte_ppc(uintptr addr, uint32 b)
{
uint8 *m = (uint8 *)addr;
*m = b;
}
#endif
void powerpc_cpu::initialize()
{
init_flight_recorder();
@ -210,6 +227,7 @@ void powerpc_cpu::initialize()
// Init syscalls handler
execute_do_syscall = NULL;
syscall_exit_code = -1;
// Init field2mask
for (int i = 0; i < 256; i++) {
@ -227,9 +245,11 @@ void powerpc_cpu::initialize()
#if ENABLE_MON
mon_init();
mon_read_byte = mon_read_byte_ppc;
mon_write_byte = mon_write_byte_ppc;
#endif
#if PROFILE_COMPILE_TIME
#if PPC_PROFILE_COMPILE_TIME
compile_count = 0;
compile_time = 0;
emul_start_time = clock();
@ -238,10 +258,13 @@ void powerpc_cpu::initialize()
powerpc_cpu::~powerpc_cpu()
{
#if PROFILE_COMPILE_TIME
#if PPC_PROFILE_COMPILE_TIME
clock_t emul_end_time = clock();
const char *type = NULL;
#if PPC_ENABLE_JIT
type = "compile";
#endif
#if PPC_DECODE_CACHE
type = "predecode";
#endif
@ -312,6 +335,12 @@ bool powerpc_cpu::check_spcflags()
#endif
if (spcflags().test(SPCFLAG_CPU_EXEC_RETURN)) {
spcflags().clear(SPCFLAG_CPU_EXEC_RETURN);
#ifndef SHEEPSHAVER
// FIXME: add unwind info to the translation cache? Otherwise
// we have to manually handle the exit syscall here
if (syscall_exit_code >= 0)
throw kernel_syscall_exit(syscall_exit_code);
#endif
return false;
}
if (spcflags().test(SPCFLAG_CPU_ENTER_MON)) {
@ -338,10 +367,37 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
#if PPC_EXECUTE_DUMP_STATE
const bool dump_state = true;
#endif
#if PPC_ENABLE_JIT
if (enable_cache) {
for (;;) {
block_info *bi = compile_block(pc());
// Execute all cached blocks
for (;;) {
codegen.execute(bi->entry_point);
if (!spcflags().empty()) {
if (!check_spcflags())
return;
// Force redecoding if cache was invalidated
if (spcflags().test(SPCFLAG_JIT_EXEC_RETURN)) {
spcflags().clear(SPCFLAG_JIT_EXEC_RETURN);
break;
}
}
if ((bi->pc != pc()) && ((bi = block_cache.find(pc())) == NULL))
break;
}
}
return;
}
#endif
#if PPC_DECODE_CACHE
if (enable_cache) {
for (;;) {
#if PROFILE_COMPILE_TIME
#if PPC_PROFILE_COMPILE_TIME
compile_count++;
clock_t start_time = clock();
#endif
@ -359,6 +415,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
if (dump_state) {
di->opcode = opcode;
di->execute = nv_mem_fun(&powerpc_cpu::dump_instruction);
di++;
}
#endif
#if PPC_FLIGHT_RECORDER
@ -369,7 +426,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
}
#endif
di->opcode = opcode;
di->execute = ii->decode ? ii->decode(this, opcode) : ii->execute;
di->execute = ii->decode.ptr() ? ii->decode(this, opcode) : ii->execute;
di++;
#if PPC_EXECUTE_DUMP_STATE
if (dump_state) {
@ -392,7 +449,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
block_cache.add_to_cl_list(bi);
block_cache.add_to_active_list(bi);
decode_cache_p += bi->size;
#if PROFILE_COMPILE_TIME
#if PPC_PROFILE_COMPILE_TIME
compile_time += (clock() - start_time);
#endif
@ -440,7 +497,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
if (is_logging())
record_step(opcode);
#endif
assert(ii->execute != 0);
assert(ii->execute.ptr() != 0);
ii->execute(this, opcode);
#if PPC_EXECUTE_DUMP_STATE
if (dump_state)
@ -476,7 +533,9 @@ void powerpc_cpu::init_decode_cache()
// Leave enough room to last calls to dump state functions
decode_cache_end_p -= 2;
#endif
#endif
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
block_cache.initialize();
#endif
}
@ -490,18 +549,23 @@ void powerpc_cpu::kill_decode_cache()
void powerpc_cpu::invalidate_cache()
{
#if PPC_DECODE_CACHE
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
block_cache.clear();
block_cache.initialize();
decode_cache_p = decode_cache;
spcflags().set(SPCFLAG_JIT_EXEC_RETURN);
#endif
#if PPC_ENABLE_JIT
codegen.invalidate_cache();
#endif
#if PPC_DECODE_CACHE
decode_cache_p = decode_cache;
#endif
}
void powerpc_cpu::invalidate_cache_range(uintptr start, uintptr end)
{
D(bug("Invalidate cache block [%08x - %08x]\n", start, end));
#if PPC_DECODE_CACHE
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
block_cache.clear_range(start, end);
#endif
}

View File

@ -29,6 +29,7 @@
#include "cpu/ppc/ppc-bitfields.hpp"
#include "cpu/ppc/ppc-blockinfo.hpp"
#include "cpu/ppc/ppc-registers.hpp"
#include "cpu/ppc/ppc-dyngen.hpp"
#include <vector>
class powerpc_cpu
@ -139,12 +140,13 @@ protected:
// Instruction information structure
struct instr_info_t {
char name[8]; // Mnemonic
char name[8]; // Instruction name
execute_fn execute; // Semantic routine for this instruction
decode_fn decode; // Specialized instruction decoder
uint16 mnemo; // Mnemonic
uint16 format; // Instruction format (XO-form, D-form, etc.)
uint16 opcode; // Primary opcode
uint16 xo; // Extended opcode
uint32 opcode:6; // Primary opcode
uint32 xo:10; // Extended opcode
uint16 cflow; // Mask of control flow information
};
@ -170,6 +172,7 @@ private:
// Syscall callback must return TRUE if no error occurred
typedef bool (*syscall_fn)(powerpc_cpu *cpu);
syscall_fn execute_do_syscall;
int syscall_exit_code;
#ifdef PPC_NO_STATIC_II_INDEX_TABLE
#define PPC_STATIC_II_TABLE
@ -202,9 +205,15 @@ public:
// Initialization & finalization
#ifdef PPC_NO_BASIC_CPU_BASE
powerpc_cpu()
#if PPC_ENABLE_JIT
: codegen(this)
#endif
#else
powerpc_cpu(task_struct *parent_task)
: basic_cpu(parent_task)
#if PPC_ENABLE_JIT
, codegen(this)
#endif
#endif
{ initialize(); }
void initialize();
@ -256,6 +265,22 @@ protected:
// Init decoder with one instruction info
void init_decoder_entry(const instr_info_t * ii);
#if PPC_ENABLE_JIT
// Dynamic translation engine
struct codegen_context_t {
powerpc_dyngen & codegen;
uint32 entry_point;
uint32 pc;
uint32 opcode;
const instr_info_t *instr_info;
codegen_context_t(powerpc_dyngen & codegen_init)
: codegen(codegen_init)
{ }
};
virtual bool compile1(codegen_context_t & cg_context) { return false; }
#endif
private:
// Initializers & destructors
@ -280,6 +305,25 @@ private:
block_info::decode_info * decode_cache_p;
block_info::decode_info * decode_cache_end_p;
#if PPC_ENABLE_JIT
// Dynamic translation engine
friend class powerpc_dyngen_helper;
friend class powerpc_dyngen;
powerpc_dyngen codegen;
block_info *compile_block(uint32 entry);
powerpc_dyngen *codegen_ptr() { return &codegen; }
#endif
// Semantic action templates
template< bool SB, bool OE >
uint32 do_execute_divide(uint32, uint32);
template< bool EX, bool CA, bool OE >
uint32 do_execute_addition(uint32, uint32);
template< bool CA, bool OE >
uint32 do_execute_subtract(uint32, uint32);
template< bool OE >
uint32 do_execute_subtract_extended(uint32, uint32);
// Instruction handlers
void execute_nop(uint32 opcode);
void execute_illegal(uint32 opcode);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,942 @@
/*
* ppc-dyngen-ops.hpp - PowerPC synthetic instructions
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "cpu/vm.hpp"
#include "cpu/jit/dyngen-exec.h"
#define NO_DEFINE_ALIAS 1
#include "cpu/ppc/ppc-cpu.hpp"
#include "cpu/ppc/ppc-bitfields.hpp"
#include "cpu/ppc/ppc-registers.hpp"
#include "cpu/ppc/ppc-operations.hpp"
// We need at least 4 general purpose registers
#ifdef REG_CPU
register struct powerpc_cpu *CPU asm(REG_CPU);
#else
#define CPU ((powerpc_cpu *)CPUPARAM)
#endif
register uint32 A0 asm(REG_A0);
register uint32 T0 asm(REG_T0);
register uint32 T1 asm(REG_T1);
#ifdef REG_T2
register uint32 CC_LHS asm(REG_T2);
#else
#define CC_LHS powerpc_dyngen_helper::cc_lhs()
#endif
#ifdef REG_T3
register uint32 CC_RHS asm(REG_T3);
#else
#define CC_RHS powerpc_dyngen_helper::cc_rhs()
#endif
// Semantic action templates
#define DYNGEN_OPS
#include "ppc-execute.hpp"
/**
* Helper class to access protected CPU context
**/
struct powerpc_dyngen_helper {
static inline uint32 get_pc() { return CPU->pc(); }
static inline void set_pc(uint32 value) { CPU->pc() = value; }
static inline void inc_pc(int32 offset) { CPU->pc() += offset; }
static inline uint32 get_lr() { return CPU->lr(); }
static inline void set_lr(uint32 value) { CPU->lr() = value; }
static inline uint32 get_ctr() { return CPU->ctr(); }
static inline void set_ctr(uint32 value) { CPU->ctr() = value; }
static inline uint32 get_cr() { return CPU->cr().get(); }
static inline void set_cr(uint32 value) { CPU->cr().set(value); }
static inline uint32 get_xer() { return CPU->xer().get(); }
static inline void set_xer(uint32 value) { CPU->xer().set(value); }
static inline void record(int crf, int32 v) { CPU->record_cr(crf, v); }
static inline powerpc_cr_register & cr() { return CPU->cr(); }
static inline powerpc_xer_register & xer() { return CPU->xer(); }
static inline uint32 & cc_lhs() { return CPU->codegen_ptr()->rc_cache.cc_lhs; }
static inline uint32 & cc_rhs() { return CPU->codegen_ptr()->rc_cache.cc_rhs; }
};
/**
* Load/store general purpose registers
**/
#define DEFINE_OP(REG, N) \
void OPPROTO op_load_##REG##_GPR##N(void) \
{ \
REG = CPU->gpr(N); \
} \
void OPPROTO op_store_##REG##_GPR##N(void) \
{ \
CPU->gpr(N) = REG; \
}
#define DEFINE_REG(N) \
DEFINE_OP(A0,N); \
DEFINE_OP(T0,N); \
DEFINE_OP(T1,N);
DEFINE_REG(0);
DEFINE_REG(1);
DEFINE_REG(2);
DEFINE_REG(3);
DEFINE_REG(4);
DEFINE_REG(5);
DEFINE_REG(6);
DEFINE_REG(7);
DEFINE_REG(8);
DEFINE_REG(9);
DEFINE_REG(10);
DEFINE_REG(11);
DEFINE_REG(12);
DEFINE_REG(13);
DEFINE_REG(14);
DEFINE_REG(15);
DEFINE_REG(16);
DEFINE_REG(17);
DEFINE_REG(18);
DEFINE_REG(19);
DEFINE_REG(20);
DEFINE_REG(21);
DEFINE_REG(22);
DEFINE_REG(23);
DEFINE_REG(24);
DEFINE_REG(25);
DEFINE_REG(26);
DEFINE_REG(27);
DEFINE_REG(28);
DEFINE_REG(29);
DEFINE_REG(30);
DEFINE_REG(31);
#undef DEFINE_REG
#undef DEFINE_OP
/**
* Condition Registers
**/
void OPPROTO op_load_T0_CR(void)
{
T0 = powerpc_dyngen_helper::get_cr();
}
void OPPROTO op_store_T0_CR(void)
{
powerpc_dyngen_helper::set_cr(T0);
}
void OPPROTO op_load_T0_XER(void)
{
T0 = powerpc_dyngen_helper::get_xer();
}
void OPPROTO op_store_T0_XER(void)
{
powerpc_dyngen_helper::set_xer(T0);
}
#define DEFINE_OP(REG, N) \
void OPPROTO op_load_##REG##_crb##N(void) \
{ \
REG = bit_field<N,N>::extract(powerpc_dyngen_helper::get_cr()); \
} \
void OPPROTO op_store_##REG##_crb##N(void) \
{ \
uint32 cr = powerpc_dyngen_helper::get_cr(); \
bit_field<N,N>::insert(cr, REG); \
powerpc_dyngen_helper::set_cr(cr); \
}
#define DEFINE_REG(N) \
DEFINE_OP(T0, N); \
DEFINE_OP(T1, N);
DEFINE_REG(0);
DEFINE_REG(1);
DEFINE_REG(2);
DEFINE_REG(3);
DEFINE_REG(4);
DEFINE_REG(5);
DEFINE_REG(6);
DEFINE_REG(7);
DEFINE_REG(8);
DEFINE_REG(9);
DEFINE_REG(10);
DEFINE_REG(11);
DEFINE_REG(12);
DEFINE_REG(13);
DEFINE_REG(14);
DEFINE_REG(15);
DEFINE_REG(16);
DEFINE_REG(17);
DEFINE_REG(18);
DEFINE_REG(19);
DEFINE_REG(20);
DEFINE_REG(21);
DEFINE_REG(22);
DEFINE_REG(23);
DEFINE_REG(24);
DEFINE_REG(25);
DEFINE_REG(26);
DEFINE_REG(27);
DEFINE_REG(28);
DEFINE_REG(29);
DEFINE_REG(30);
DEFINE_REG(31);
#undef DEFINE_REG
#undef DEFINE_OP
#define DEFINE_OP(CRF, REG) \
void OPPROTO op_load_##REG##_cr##CRF(void) \
{ \
T0 = powerpc_dyngen_helper::cr().get(CRF); \
} \
void OPPROTO op_store_##REG##_cr##CRF(void) \
{ \
powerpc_dyngen_helper::cr().set(CRF, REG); \
}
DEFINE_OP(0, T0);
DEFINE_OP(1, T0);
DEFINE_OP(2, T0);
DEFINE_OP(3, T0);
DEFINE_OP(4, T0);
DEFINE_OP(5, T0);
DEFINE_OP(6, T0);
DEFINE_OP(7, T0);
#undef DEFINE_OP
#define DEFINE_OP(CRF) \
void OPPROTO op_commit_so_cache_cr##CRF(void) \
{ \
int so = powerpc_dyngen_helper::xer().get_so(); \
powerpc_dyngen_helper::cr().set_so(CRF, so); \
}
DEFINE_OP(0);
DEFINE_OP(1);
DEFINE_OP(2);
DEFINE_OP(3);
DEFINE_OP(4);
DEFINE_OP(5);
DEFINE_OP(6);
DEFINE_OP(7);
#undef DEFINE_OP
#define DEFINE_OP_1(NAME,TYPE,CRF) \
void OPPROTO op_##NAME##_rc_cache_cr##CRF(void) \
{ \
uint32 cr = powerpc_dyngen_helper::get_cr(); \
cr &= ~(CR_LT_field<CRF>::mask() | \
CR_GT_field<CRF>::mask() | \
CR_EQ_field<CRF>::mask()); \
\
if ((TYPE)CC_LHS < (TYPE)CC_RHS) \
cr |= CR_LT_field<CRF>::mask(); \
else if ((TYPE)CC_LHS > (TYPE)CC_RHS) \
cr |= CR_GT_field<CRF>::mask(); \
else \
cr |= CR_EQ_field<CRF>::mask(); \
\
powerpc_dyngen_helper::set_cr(cr); \
dyngen_barrier(); \
}
#define DEFINE_OP(CRF) \
DEFINE_OP_1(commit,int32,CRF); \
DEFINE_OP_1(commit_logical,uint32,CRF)
DEFINE_OP(0);
DEFINE_OP(1);
DEFINE_OP(2);
DEFINE_OP(3);
DEFINE_OP(4);
DEFINE_OP(5);
DEFINE_OP(6);
DEFINE_OP(7);
#undef DEFINE_OP
#undef DEFINE_OP_1
/**
* Special purpose registers
**/
void OPPROTO op_load_T0_PC(void)
{
T0 = powerpc_dyngen_helper::get_pc();
}
void OPPROTO op_store_T0_PC(void)
{
powerpc_dyngen_helper::set_pc(T0);
}
void OPPROTO op_set_PC_im(void)
{
powerpc_dyngen_helper::set_pc(PARAM1);
}
void OPPROTO op_set_PC_T1(void)
{
powerpc_dyngen_helper::set_pc(T1);
}
void OPPROTO op_inc_PC(void)
{
powerpc_dyngen_helper::inc_pc(PARAM1);
}
void OPPROTO op_load_T0_LR(void)
{
T0 = powerpc_dyngen_helper::get_lr();
}
void OPPROTO op_store_T0_LR(void)
{
powerpc_dyngen_helper::set_lr(T0);
}
void OPPROTO op_load_T0_CTR(void)
{
T0 = powerpc_dyngen_helper::get_ctr();
}
void OPPROTO op_load_T1_CTR(void)
{
T1 = powerpc_dyngen_helper::get_ctr();
}
void OPPROTO op_store_T0_CTR(void)
{
powerpc_dyngen_helper::set_ctr(T0);
}
void OPPROTO op_store_T1_CTR(void)
{
powerpc_dyngen_helper::set_ctr(T1);
}
void OPPROTO op_load_T1_PC(void)
{
T1 = powerpc_dyngen_helper::get_pc();
}
void OPPROTO op_load_T1_LR(void)
{
T1 = powerpc_dyngen_helper::get_lr();
}
void OPPROTO op_store_im_LR(void)
{
powerpc_dyngen_helper::set_lr(PARAM1);
}
/**
* Branch instructions
**/
void OPPROTO op_decrement_ctr_T1(void)
{
T1 = powerpc_dyngen_helper::get_ctr() - 1;
powerpc_dyngen_helper::set_ctr(T1);
}
void OPPROTO op_branch_if_T0(void)
{
if (T0)
powerpc_dyngen_helper::set_pc(PARAM1);
else
powerpc_dyngen_helper::set_pc(PARAM2);
dyngen_barrier();
}
void OPPROTO op_branch_if_not_T0(void)
{
if (!T0)
powerpc_dyngen_helper::set_pc(PARAM1);
else
powerpc_dyngen_helper::set_pc(PARAM2);
dyngen_barrier();
}
void OPPROTO op_branch_if_T1(void)
{
if (T1)
powerpc_dyngen_helper::set_pc(PARAM1);
else
powerpc_dyngen_helper::set_pc(PARAM2);
dyngen_barrier();
}
void OPPROTO op_branch_if_not_T1(void)
{
if (!T1)
powerpc_dyngen_helper::set_pc(PARAM1);
else
powerpc_dyngen_helper::set_pc(PARAM2);
dyngen_barrier();
}
void OPPROTO op_branch_if_T0_T1(void)
{
if (T0 && T1)
powerpc_dyngen_helper::set_pc(PARAM1);
else
powerpc_dyngen_helper::set_pc(PARAM2);
dyngen_barrier();
}
void OPPROTO op_branch_T1_if_T0(void)
{
if (T0)
powerpc_dyngen_helper::set_pc(T1);
else
powerpc_dyngen_helper::set_pc(PARAM1);
dyngen_barrier();
}
template< class branch_cond, class ctr_cond >
static inline void do_execute_branch(uint32 tpc, uint32 npc)
{
if (branch_cond::test() && ctr_cond::test())
powerpc_dyngen_helper::set_pc(PARAM1);
else
powerpc_dyngen_helper::set_pc(PARAM2);
dyngen_barrier();
}
struct LT_comparator { static inline bool test() { return T0 < 0; } };
struct GT_comparator { static inline bool test() { return T0 > 0; } };
struct EQ_comparator { static inline bool test() { return T0 == 0; } };
template< int crb >
struct CR_comparator { static inline bool test() { return (T0 & crb); } };
template< bool br_true, class comparator >
struct bool_condition {
static inline bool test() {
return br_true ? comparator::test() : !comparator::test();
}
};
typedef bool_condition< true, CR_comparator<8> > blt_condition;
typedef bool_condition<false, CR_comparator<8> > bnlt_condition;
typedef bool_condition< true, CR_comparator<4> > bgt_condition;
typedef bool_condition<false, CR_comparator<4> > bngt_condition;
typedef bool_condition< true, CR_comparator<2> > beq_condition;
typedef bool_condition<false, CR_comparator<2> > bneq_condition;
typedef bool_condition< true, CR_comparator<1> > bso_condition;
typedef bool_condition<false, CR_comparator<1> > bnso_condition;
struct ctr_0x_condition {
static inline bool test() {
return true;
}
};
struct ctr_10_condition {
static inline bool test() {
uint32 ctr = powerpc_dyngen_helper::get_ctr() - 1;
powerpc_dyngen_helper::set_ctr(ctr);
return ctr != 0;
}
};
struct ctr_11_condition {
static inline bool test() {
uint32 ctr = powerpc_dyngen_helper::get_ctr() - 1;
powerpc_dyngen_helper::set_ctr(ctr);
return ctr == 0;
}
};
#define DEFINE_OP_CTR(COND,CTR) \
void OPPROTO op_##COND##_##CTR(void) \
{ \
do_execute_branch<COND##_condition, ctr_##CTR##_condition>(PARAM1, PARAM2); \
}
#define DEFINE_OP(COND) \
DEFINE_OP_CTR(COND,0x); \
DEFINE_OP_CTR(COND,10); \
DEFINE_OP_CTR(COND,11)
DEFINE_OP(blt);
DEFINE_OP(bgt);
DEFINE_OP(beq);
DEFINE_OP(bso);
DEFINE_OP(bnlt);
DEFINE_OP(bngt);
DEFINE_OP(bneq);
DEFINE_OP(bnso);
#undef DEFINE_OP
#undef DEFINE_OP_CTR
/**
* Compare & Record instructions
**/
void OPPROTO op_record_nego_T0(void)
{
powerpc_dyngen_helper::xer().set_ov(T0 == 0x80000000);
dyngen_barrier();
}
void OPPROTO op_record_cr0_T0(void)
{
uint32 cr = powerpc_dyngen_helper::get_cr();
cr &= ~(CR_LT_field<0>::mask() |
CR_GT_field<0>::mask() |
CR_EQ_field<0>::mask() |
CR_SO_field<0>::mask());
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 v;
asm volatile ("cmpwi 0,%1,0 ; mfcr %0" : "=r" (v) : "r" (T0) : "cr0");
cr |= (v & (0xe0000000));
cr |= (powerpc_dyngen_helper::xer().get_so() << (31 - 3));
goto end;
#endif
#endif
if (powerpc_dyngen_helper::xer().get_so())
cr |= CR_SO_field<0>::mask();
if ((int32)T0 < 0)
cr |= CR_LT_field<0>::mask();
else if ((int32)T0 > 0)
cr |= CR_GT_field<0>::mask();
else
cr |= CR_EQ_field<0>::mask();
end:
powerpc_dyngen_helper::set_cr(cr);
dyngen_barrier();
}
#define im PARAM1
#define DEFINE_OP(LHS, RHS) \
void OPPROTO op_compare_##LHS##_##RHS(void) \
{ \
CC_LHS = LHS; \
CC_RHS = RHS; \
}
DEFINE_OP(T0,T1);
DEFINE_OP(T0,im);
DEFINE_OP(T0,0);
#undef DEFINE_OP
#if DYNGEN_ASM_OPTS && defined(__powerpc__)
#define DEFINE_OP(NAME, COMP, LHS, RHST, RHS) \
void OPPROTO op_##NAME##_##LHS##_##RHS(void) \
{ \
uint32 cr = powerpc_dyngen_helper::xer().get_so(); \
uint32 v; \
asm volatile (COMP " 7,%1,%2 ; mfcr %0" : "=r" (v) : "r" (LHS), RHST (RHS) : "cr7"); \
T0 = cr | (v & 0xe); \
}
DEFINE_OP(do_compare,"cmpw",T0,"r",T1);
DEFINE_OP(do_compare,"cmpw",T0,"r",im);
DEFINE_OP(do_compare,"cmpwi",T0,"i",0);
DEFINE_OP(do_compare_logical,"cmplw",T0,"r",T1);
DEFINE_OP(do_compare_logical,"cmplw",T0,"r",im);
DEFINE_OP(do_compare_logical,"cmplwi",T0,"i",0);
#else
#define DEFINE_OP(NAME, TYPE, LHS, RHS) \
void OPPROTO op_##NAME##_##LHS##_##RHS(void) \
{ \
uint32 cr = powerpc_dyngen_helper::xer().get_so(); \
if ((TYPE)LHS < (TYPE)RHS) \
cr |= standalone_CR_LT_field::mask(); \
else if ((TYPE)LHS > (TYPE)RHS) \
cr |= standalone_CR_GT_field::mask(); \
else \
cr |= standalone_CR_EQ_field::mask(); \
T0 = cr; \
dyngen_barrier(); \
}
DEFINE_OP(do_compare,int32,T0,T1);
DEFINE_OP(do_compare,int32,T0,im);
DEFINE_OP(do_compare,int32,T0,0);
DEFINE_OP(do_compare_logical,uint32,T0,T1);
DEFINE_OP(do_compare_logical,uint32,T0,im);
DEFINE_OP(do_compare_logical,uint32,T0,0);
#endif
#undef im
#undef DEFINE_OP
/**
* Divide instructions
**/
#if DYNGEN_ASM_OPTS && defined(__powerpc__)
#define get_ov() ({ uint32 xer; asm volatile ("mfxer %0" : "=r" (xer)); XER_OV_field::extract(xer); })
#endif
void OPPROTO op_divw_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
asm volatile ("divw %0,%0,%1" : "=r" (T0) : "r" (T1));
return;
#endif
#endif
T0 = do_execute_divide<true, false>(T0, T1);
}
void OPPROTO op_divwo_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
asm volatile ("divwo %0,%0,%1" : "=r" (T0) : "r" (T1));
powerpc_dyngen_helper::xer().set_ov(get_ov());
return;
#endif
#endif
T0 = do_execute_divide<true, true>(T0, T1);
}
void OPPROTO op_divwu_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
asm volatile ("divwu %0,%0,%1" : "=r" (T0) : "r" (T1));
return;
#endif
#endif
T0 = do_execute_divide<false, false>(T0, T1);
}
void OPPROTO op_divwuo_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
asm volatile ("divwuo %0,%0,%1" : "=r" (T0) : "r" (T1));
powerpc_dyngen_helper::xer().set_ov(get_ov());
return;
#endif
#endif
T0 = do_execute_divide<false, true>(T0, T1);
}
/**
* Multiply instructions
**/
void OPPROTO op_mulhw_T0_T1(void)
{
T0 = (((int64)(int32)T0) * ((int64)(int32)T1)) >> 32;
}
void OPPROTO op_mulhwu_T0_T1(void)
{
T0 = (((uint64)T0) * ((uint64)T1)) >> 32;
}
void OPPROTO op_mulli_T0_im(void)
{
T0 = (int32)T0 * (int32)PARAM1;
}
void OPPROTO op_mullwo_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
asm volatile ("mullwo %0,%0,%1" : "=r" (T0) : "r" (T1));
powerpc_dyngen_helper::xer().set_ov(get_ov());
return;
#endif
#endif
int64 RD = (int64)(int32)T0 * (int64)(int32)T1;
powerpc_dyngen_helper::xer().set_ov((int32)RD != RD);
T0 = RD;
dyngen_barrier();
}
/**
* Shift/Rotate instructions
**/
void OPPROTO op_slw_T0_T1(void)
{
T0 = T0 << (T1 & 0x3f);
}
void OPPROTO op_srw_T0_T1(void)
{
T0 = T0 >> (T1 & 0x3f);
}
void OPPROTO op_sraw_T0_T1(void)
{
const uint32 n = T1 & 0x3f;
const uint32 RD = ((int32)T0) >> n;
const bool ca = (((int32)T0) < 0) && (T0 & ~(0xffffffff << n));
powerpc_dyngen_helper::xer().set_ca(ca);
T0 = RD;
dyngen_barrier();
}
void OPPROTO op_sraw_T0_im(void)
{
const uint32 n = PARAM1;
const uint32 RD = ((int32)T0) >> n;
const bool ca = (((int32)T0) < 0) && (T0 & ~(0xffffffff << n));
powerpc_dyngen_helper::xer().set_ca(ca);
T0 = RD;
dyngen_barrier();
}
void OPPROTO op_rlwimi_T0_T1(void)
{
T0 = op_ppc_rlwimi::apply(T1, PARAM1, PARAM2, T0);
}
void OPPROTO op_rlwinm_T0_T1(void)
{
T0 = op_rotl::apply(T0, PARAM1) & PARAM2;
}
void OPPROTO op_rlwnm_T0_T1(void)
{
T0 = op_rotl::apply(T0, T1) & PARAM1;
}
void OPPROTO op_cntlzw_32_T0(void)
{
uint32 n;
uint32 m = 0x80000000;
for (n = 0; n < 32; n++, m >>= 1)
if (T0 & m)
break;
T0 = n;
dyngen_barrier();
}
/**
* Addition/Subtraction
**/
void OPPROTO op_addo_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer;
asm volatile ("addo %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<false, false, true>(T0, T1);
}
void OPPROTO op_addc_T0_im(void)
{
T0 = do_execute_addition<false, true, false>(T0, PARAM1);
}
void OPPROTO op_addc_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer;
asm volatile ("addc %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<false, true, false>(T0, T1);
}
void OPPROTO op_addco_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer;
asm volatile ("addco %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<false, true, true>(T0, T1);
}
void OPPROTO op_adde_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
asm volatile ("adde %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<true, false, false>(T0, T1);
}
void OPPROTO op_addeo_T0_T1(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
asm volatile ("addeo %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<true, false, true>(T0, T1);
}
void OPPROTO op_addme_T0(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
asm volatile ("addme %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<true, false, false>(T0, 0xffffffff);
}
void OPPROTO op_addmeo_T0(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
asm volatile ("addmeo %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<true, false, true>(T0, 0xffffffff);
}
void OPPROTO op_addze_T0(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
asm volatile ("addze %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<true, false, false>(T0, 0);
}
void OPPROTO op_addzeo_T0(void)
{
#if DYNGEN_ASM_OPTS
#if defined(__powerpc__)
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
asm volatile ("addzeo %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
return;
#endif
#endif
T0 = do_execute_addition<true, false, true>(T0, 0);
}
void OPPROTO op_subf_T0_T1(void)
{
T0 = T1 - T0;
}
void OPPROTO op_subfo_T0_T1(void)
{
T0 = do_execute_subtract<false, true>(T0, T1);
}
void OPPROTO op_subfc_T0_im(void)
{
T0 = do_execute_subtract<true, false>(T0, PARAM1);
}
void OPPROTO op_subfc_T0_T1(void)
{
T0 = do_execute_subtract<true, false>(T0, T1);
}
void OPPROTO op_subfco_T0_T1(void)
{
T0 = do_execute_subtract<true, true>(T0, T1);
}
void OPPROTO op_subfe_T0_T1(void)
{
T0 = do_execute_subtract_extended<false>(T0, T1);
}
void OPPROTO op_subfeo_T0_T1(void)
{
T0 = do_execute_subtract_extended<true>(T0, T1);
}
void OPPROTO op_subfme_T0(void)
{
T0 = do_execute_subtract_extended<false>(T0, 0xffffffff);
}
void OPPROTO op_subfmeo_T0(void)
{
T0 = do_execute_subtract_extended<true>(T0, 0xffffffff);
}
void OPPROTO op_subfze_T0(void)
{
T0 = do_execute_subtract_extended<false>(T0, 0);
}
void OPPROTO op_subfzeo_T0(void)
{
T0 = do_execute_subtract_extended<true>(T0, 0);
}

View File

@ -0,0 +1,254 @@
/*
* ppc-dyngen.cpp - PowerPC dynamic translation
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "cpu/ppc/ppc-dyngen.hpp"
#include "cpu/ppc/ppc-bitfields.hpp"
#include <assert.h>
#include <stdlib.h>
#define DYNGEN_IMPL 1
#define DEFINE_GEN(NAME,ARGS) void powerpc_dyngen::NAME ARGS
#include "ppc-dyngen-ops.hpp"
void powerpc_dyngen::invalidate_so_cache()
{
rc_cache.so_status = RC_cache::STATUS_TRASH;
}
void powerpc_dyngen::invalidate_cr_cache()
{
invalidate_so_cache();
rc_cache.val_status = RC_cache::STATUS_TRASH;
rc_cache.crf = -1;
}
void powerpc_dyngen::do_gen_commit_cr()
{
gen_commit_so();
switch (rc_cache.val_status) {
case RC_cache::STATUS_VALID:
gen_commit_rc_cache_cr(rc_cache.crf);
break;
case RC_cache::STATUS_VALID_LOGICAL:
gen_commit_logical_rc_cache_cr(rc_cache.crf);
break;
default:
abort();
}
invalidate_cr_cache();
}
void powerpc_dyngen::do_gen_commit_so()
{
gen_commit_so_cache_cr(rc_cache.crf);
invalidate_so_cache();
}
void powerpc_dyngen::gen_commit_cr()
{
if (rc_cache.val_status != RC_cache::STATUS_TRASH) {
assert(rc_cache.crf != -1);
do_gen_commit_cr();
}
}
void powerpc_dyngen::gen_commit_so()
{
if (rc_cache.so_status != RC_cache::STATUS_TRASH) {
assert(rc_cache.crf != -1);
do_gen_commit_so();
}
}
void powerpc_dyngen::gen_compare_T0_T1(int crf)
{
if (!rc_cache.has_field(crf))
gen_commit_cr();
gen_op_compare_T0_T1();
rc_cache.cache_field(crf);
}
void powerpc_dyngen::gen_compare_T0_im(int crf, int32 value)
{
if (!rc_cache.has_field(crf))
gen_commit_cr();
if (value == 0)
gen_op_compare_T0_0();
else
gen_op_compare_T0_im(value);
rc_cache.cache_field(crf);
}
void powerpc_dyngen::gen_compare_logical_T0_T1(int crf)
{
if (!rc_cache.has_field(crf))
gen_commit_cr();
gen_op_compare_T0_T1();
rc_cache.cache_field(crf, RC_cache::STATUS_VALID_LOGICAL);
}
void powerpc_dyngen::gen_compare_logical_T0_im(int crf, int32 value)
{
if (!rc_cache.has_field(crf))
gen_commit_cr();
if (value == 0)
gen_op_compare_T0_0();
else
gen_op_compare_T0_im(value);
rc_cache.cache_field(crf, RC_cache::STATUS_VALID_LOGICAL);
}
/**
* Load/store registers
**/
#define DEFINE_INSN(OP, REG, REGT) \
void powerpc_dyngen::gen_##OP##_##REG##_##REGT(int i) \
{ \
switch (i) { \
case 0: gen_op_##OP##_##REG##_##REGT##0(); break; \
case 1: gen_op_##OP##_##REG##_##REGT##1(); break; \
case 2: gen_op_##OP##_##REG##_##REGT##2(); break; \
case 3: gen_op_##OP##_##REG##_##REGT##3(); break; \
case 4: gen_op_##OP##_##REG##_##REGT##4(); break; \
case 5: gen_op_##OP##_##REG##_##REGT##5(); break; \
case 6: gen_op_##OP##_##REG##_##REGT##6(); break; \
case 7: gen_op_##OP##_##REG##_##REGT##7(); break; \
case 8: gen_op_##OP##_##REG##_##REGT##8(); break; \
case 9: gen_op_##OP##_##REG##_##REGT##9(); break; \
case 10: gen_op_##OP##_##REG##_##REGT##10(); break; \
case 11: gen_op_##OP##_##REG##_##REGT##11(); break; \
case 12: gen_op_##OP##_##REG##_##REGT##12(); break; \
case 13: gen_op_##OP##_##REG##_##REGT##13(); break; \
case 14: gen_op_##OP##_##REG##_##REGT##14(); break; \
case 15: gen_op_##OP##_##REG##_##REGT##15(); break; \
case 16: gen_op_##OP##_##REG##_##REGT##16(); break; \
case 17: gen_op_##OP##_##REG##_##REGT##17(); break; \
case 18: gen_op_##OP##_##REG##_##REGT##18(); break; \
case 19: gen_op_##OP##_##REG##_##REGT##19(); break; \
case 20: gen_op_##OP##_##REG##_##REGT##20(); break; \
case 21: gen_op_##OP##_##REG##_##REGT##21(); break; \
case 22: gen_op_##OP##_##REG##_##REGT##22(); break; \
case 23: gen_op_##OP##_##REG##_##REGT##23(); break; \
case 24: gen_op_##OP##_##REG##_##REGT##24(); break; \
case 25: gen_op_##OP##_##REG##_##REGT##25(); break; \
case 26: gen_op_##OP##_##REG##_##REGT##26(); break; \
case 27: gen_op_##OP##_##REG##_##REGT##27(); break; \
case 28: gen_op_##OP##_##REG##_##REGT##28(); break; \
case 29: gen_op_##OP##_##REG##_##REGT##29(); break; \
case 30: gen_op_##OP##_##REG##_##REGT##30(); break; \
case 31: gen_op_##OP##_##REG##_##REGT##31(); break; \
default: abort(); \
} \
}
// General purpose registers
DEFINE_INSN(load, A0, GPR);
DEFINE_INSN(load, T0, GPR);
DEFINE_INSN(load, T1, GPR);
DEFINE_INSN(store, A0, GPR);
DEFINE_INSN(store, T0, GPR);
DEFINE_INSN(store, T1, GPR);
// Condition register bitfield
DEFINE_INSN(load, T0, crb);
DEFINE_INSN(load, T1, crb);
DEFINE_INSN(store, T0, crb);
DEFINE_INSN(store, T1, crb);
#undef DEFINE_INSN
#define DEFINE_INSN(OP, REG) \
void powerpc_dyngen::gen_##OP##_##REG##_cr(int crf) \
{ \
switch (crf) { \
case 0: gen_op_##OP##_##REG##_cr0(); break; \
case 1: gen_op_##OP##_##REG##_cr1(); break; \
case 2: gen_op_##OP##_##REG##_cr2(); break; \
case 3: gen_op_##OP##_##REG##_cr3(); break; \
case 4: gen_op_##OP##_##REG##_cr4(); break; \
case 5: gen_op_##OP##_##REG##_cr5(); break; \
case 6: gen_op_##OP##_##REG##_cr6(); break; \
case 7: gen_op_##OP##_##REG##_cr7(); break; \
default: abort(); \
} \
}
DEFINE_INSN(load, T0);
DEFINE_INSN(store, T0);
DEFINE_INSN(commit, so_cache);
DEFINE_INSN(commit, rc_cache);
DEFINE_INSN(commit_logical, rc_cache);
#undef DEFINE_INSN
void powerpc_dyngen::gen_record_cr0_T0(void)
{
gen_compare_T0_im(0, 0);
}
void powerpc_dyngen::gen_bc(int bo, int bi, uint32 tpc, uint32 npc)
{
gen_commit_cr();
if (BO_CONDITIONAL_BRANCH(bo)) {
enum { lt, gt, eq, so };
gen_load_T0_cr(bi / 4);
const int n = ((bi % 4) << 2) | ((bo >> 1) & 3);
#define _(CR,DCTR,CTR0) (((CR) << 2) | ((DCTR) ? 0 : 2) | ((CTR0) ? 1 : 0))
if (BO_BRANCH_IF_TRUE(bo)) {
switch (n) {
#define C(CR) \
case _(CR,0,0): gen_b##CR##_0x(tpc, npc); break; \
case _(CR,0,1): gen_b##CR##_0x(tpc, npc); break; \
case _(CR,1,0): gen_b##CR##_10(tpc, npc); break; \
case _(CR,1,1): gen_b##CR##_11(tpc, npc); break;
C(lt); C(gt); C(eq); C(so);
#undef C
}
}
else {
switch (n) {
#define C(CR) \
case _(CR,0,0): gen_bn##CR##_0x(tpc, npc); break; \
case _(CR,0,1): gen_bn##CR##_0x(tpc, npc); break; \
case _(CR,1,0): gen_bn##CR##_10(tpc, npc); break; \
case _(CR,1,1): gen_bn##CR##_11(tpc, npc); break;
C(lt); C(gt); C(eq); C(so);
#undef C
}
}
#undef _
}
else {
if (BO_DECREMENT_CTR(bo)) {
gen_decrement_ctr_T1();
if (BO_BRANCH_IF_CTR_ZERO(bo))
gen_branch_if_not_T1(tpc, npc);
else
gen_branch_if_T1(tpc, npc);
}
else {
// Branch always
gen_set_PC_im(tpc);
}
}
}

View File

@ -0,0 +1,231 @@
/*
* ppc-dyngen.hpp - PowerPC dynamic translation
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef PPC_DYNGEN_H
#define PPC_DYNGEN_H
#include "sysdeps.h"
#include "cpu/ppc/ppc-config.hpp"
#if PPC_ENABLE_JIT
#include "cpu/jit/jit-config.hpp"
#include "cpu/jit/basic-dyngen.hpp"
class powerpc_dyngen
: public basic_dyngen
{
// Code generators for PowerPC synthetic instructions
#ifndef NO_DEFINE_ALIAS
# define DEFINE_GEN(NAME,ARGS) void NAME ARGS;
# include "ppc-dyngen-ops.hpp"
#endif
struct RC_cache {
enum {
STATUS_TRASH,
STATUS_VALID,
STATUS_VALID_LOGICAL
};
int val_status;
int so_status;
int crf;
// Used only by generated code if not enough native registers
// are available to cache them all
uint32 cc_lhs;
uint32 cc_rhs;
RC_cache()
: val_status(STATUS_TRASH), so_status(STATUS_TRASH), crf(-1)
{ }
bool has_field(int test_crf)
{ return val_status != STATUS_TRASH && crf == test_crf; }
void cache_field(int new_crf, int new_status = STATUS_VALID)
{ val_status = so_status = new_status; crf = new_crf; }
};
RC_cache rc_cache;
public:
// Make rc_cache accessible to codegen helper
friend class powerpc_dyngen_helper;
// Default constructor
powerpc_dyngen(dyngen_cpu_base cpu)
: basic_dyngen(cpu)
{ }
// Load/store registers
void gen_load_A0_GPR(int i);
void gen_load_T0_GPR(int i);
void gen_load_T1_GPR(int i);
void gen_store_A0_GPR(int i);
void gen_store_T0_GPR(int i);
void gen_store_T1_GPR(int i);
// Raw aliases
#define DEFINE_ALIAS_RAW(NAME, PRE, POST, ARGLIST, ARGS) \
void gen_##NAME ARGLIST { PRE; gen_op_##NAME ARGS; POST; }
#define DEFINE_ALIAS_0(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(),())
#define DEFINE_ALIAS_1(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(long p1),(p1))
#define DEFINE_ALIAS_2(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(long p1,long p2),(p1,p2))
#define DEFINE_ALIAS_3(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(long p1,long p2,long p3),(p1,p2,p3))
#ifdef NO_DEFINE_ALIAS
#define DEFINE_ALIAS(NAME,N)
#define DEFINE_ALIAS_CLOBBER_SO(NAME,N)
#define DEFINE_ALIAS_CLOBBER_CR(NAME,N)
#else
#define DEFINE_ALIAS(NAME,N) DEFINE_ALIAS_##N(NAME,,)
#define DEFINE_ALIAS_CLOBBER_CR(NAME,N) DEFINE_ALIAS_##N(NAME,gen_commit_cr(),)
#define DEFINE_ALIAS_CLOBBER_SO(NAME,N) DEFINE_ALIAS_##N(NAME,gen_commit_so(),)
#endif
// Condition registers
private:
void do_gen_commit_so();
void do_gen_commit_cr();
void gen_commit_so_cache_cr(int crf);
void gen_commit_rc_cache_cr(int crf);
void gen_commit_logical_rc_cache_cr(int crf);
public:
void invalidate_so_cache();
void invalidate_cr_cache();
void gen_commit_so();
void gen_commit_cr();
DEFINE_ALIAS_CLOBBER_CR(load_T0_CR,0);
DEFINE_ALIAS_CLOBBER_CR(store_T0_CR,0);
DEFINE_ALIAS(load_T0_XER,0);
DEFINE_ALIAS(store_T0_XER,0);
void gen_load_T0_crb(int i);
void gen_load_T1_crb(int i);
void gen_store_T0_crb(int i);
void gen_store_T1_crb(int i);
void gen_load_T0_cr(int crf);
void gen_store_T0_cr(int crf);
// Special purpose registers
DEFINE_ALIAS(load_T0_PC,0);
DEFINE_ALIAS(store_T0_PC,0);
DEFINE_ALIAS(set_PC_T1,0);
DEFINE_ALIAS(set_PC_im,1);
DEFINE_ALIAS(inc_PC,1);
DEFINE_ALIAS(load_T0_LR,0);
DEFINE_ALIAS(store_T0_LR,0);
DEFINE_ALIAS(load_T0_CTR,0);
DEFINE_ALIAS(load_T1_CTR,0);
DEFINE_ALIAS(store_T0_CTR,0);
DEFINE_ALIAS(store_T1_CTR,0);
DEFINE_ALIAS(load_T1_PC,0);
DEFINE_ALIAS(load_T1_LR,0);
DEFINE_ALIAS(store_im_LR,1);
// Control Flow
DEFINE_ALIAS(decrement_ctr_T1,0);
DEFINE_ALIAS(branch_if_T0,2);
DEFINE_ALIAS(branch_if_T1,2);
DEFINE_ALIAS(branch_if_not_T0,2);
DEFINE_ALIAS(branch_if_not_T1,2);
DEFINE_ALIAS(branch_if_T0_T1,2);
DEFINE_ALIAS(branch_T1_if_T0,1);
// Compare & Record instructions
DEFINE_ALIAS_CLOBBER_SO(record_nego_T0,0);
void gen_record_cr0_T0();
DEFINE_ALIAS(compare_T0_T1,0);
DEFINE_ALIAS(compare_T0_0,0);
DEFINE_ALIAS(compare_T0_im,1);
void gen_compare_T0_T1(int crf);
void gen_compare_T0_im(int crf, int32 value);
void gen_compare_logical_T0_T1(int crf);
void gen_compare_logical_T0_im(int crf, int32 value);
// Multiply/Divide instructions
DEFINE_ALIAS(mulhw_T0_T1,0);
DEFINE_ALIAS(mulhwu_T0_T1,0);
DEFINE_ALIAS(mulli_T0_im,1);
DEFINE_ALIAS_CLOBBER_SO(mullwo_T0_T1,0);
DEFINE_ALIAS(divw_T0_T1,0);
DEFINE_ALIAS_CLOBBER_SO(divwo_T0_T1,0);
DEFINE_ALIAS(divwu_T0_T1,0);
DEFINE_ALIAS_CLOBBER_SO(divwuo_T0_T1,0);
// Shift/Rotate instructions
DEFINE_ALIAS(slw_T0_T1,0);
DEFINE_ALIAS(srw_T0_T1,0);
DEFINE_ALIAS(sraw_T0_T1,0);
DEFINE_ALIAS(sraw_T0_im,1);
DEFINE_ALIAS(rlwimi_T0_T1,2);
DEFINE_ALIAS(rlwinm_T0_T1,2);
DEFINE_ALIAS(rlwnm_T0_T1,1);
DEFINE_ALIAS(cntlzw_32_T0,0);
// Add/Sub related instructions
DEFINE_ALIAS(addo_T0_T1,0);
DEFINE_ALIAS(addc_T0_im,1);
DEFINE_ALIAS(addc_T0_T1,0);
DEFINE_ALIAS_CLOBBER_SO(addco_T0_T1,0);
DEFINE_ALIAS(adde_T0_T1,0);
DEFINE_ALIAS_CLOBBER_SO(addeo_T0_T1,0);
DEFINE_ALIAS(addme_T0,0);
DEFINE_ALIAS_CLOBBER_SO(addmeo_T0,0);
DEFINE_ALIAS(addze_T0,0);
DEFINE_ALIAS_CLOBBER_SO(addzeo_T0,0);
DEFINE_ALIAS(subf_T0_T1,0);
DEFINE_ALIAS_CLOBBER_SO(subfo_T0_T1,0);
DEFINE_ALIAS(subfc_T0_im,1);
DEFINE_ALIAS(subfc_T0_T1,0);
DEFINE_ALIAS_CLOBBER_SO(subfco_T0_T1,0);
DEFINE_ALIAS(subfe_T0_T1,0);
DEFINE_ALIAS_CLOBBER_SO(subfeo_T0_T1,0);
DEFINE_ALIAS(subfme_T0,0);
DEFINE_ALIAS_CLOBBER_SO(subfmeo_T0,0);
DEFINE_ALIAS(subfze_T0,0);
DEFINE_ALIAS_CLOBBER_SO(subfzeo_T0,0);
// Branch instructions
void gen_bc(int bo, int bi, uint32 tpc, uint32 npc);
#define DEFINE_ALIAS_GRP_1(CR,CTR) \
DEFINE_ALIAS(b##CR##_##CTR,2); \
DEFINE_ALIAS(bn##CR##_##CTR,2);
#define DEFINE_ALIAS_GRP_2(CR) \
DEFINE_ALIAS_GRP_1(CR,0x); \
DEFINE_ALIAS_GRP_1(CR,10); \
DEFINE_ALIAS_GRP_1(CR,11);
DEFINE_ALIAS_GRP_2(lt);
DEFINE_ALIAS_GRP_2(gt);
DEFINE_ALIAS_GRP_2(eq);
DEFINE_ALIAS_GRP_2(so);
#undef DEFINE_ALAIS_GRP_2
#undef DEFINE_ALIAS_GRP_1
#undef DEFINE_ALIAS
#undef DEFINE_ALIAS_0
#undef DEFINE_ALIAS_1
#undef DEFINE_ALIAS_2
#undef DEFINE_ALIAS_3
#undef DEFINE_ALIAS_RAW
};
#endif /* PPC_ENABLE_JIT */
#endif /* PPC_DYNGEN_H */

View File

@ -28,6 +28,11 @@
#include "cpu/ppc/ppc-bitfields.hpp"
#include "cpu/ppc/ppc-operands.hpp"
#include "cpu/ppc/ppc-operations.hpp"
#include "cpu/ppc/ppc-execute.hpp"
#ifndef SHEEPSHAVER
#include "basic-kernel.hpp"
#endif
#if ENABLE_MON
#include "mon.h"
@ -378,22 +383,11 @@ void powerpc_cpu::execute_divide(uint32 opcode)
const uint32 b = operand_RB::get(this, opcode);
uint32 d;
if (b == 0 || (SB && a == 0x80000000 && b == 0xffffffff)) {
// Reference manual says rD is undefined
d = 0;
if (SB) {
// However, checking against a real PowerPC (7410) yields
// that rD gets all bits set to rA MSB
d = -(a >> 31);
}
if (OE::test(opcode))
xer().set_ov(1);
}
else {
d = SB ? (int32)a/(int32)b : a/b;
if (OE::test(opcode))
xer().set_ov(0);
}
// Specialize divide semantic action
if (OE::test(opcode))
d = do_execute_divide<SB, true>(a, b);
else
d = do_execute_divide<SB, false>(a, b);
// Set CR0 (LT, GT, EQ, SO) if instruction has Rc set
if (Rc::test(opcode))
@ -926,8 +920,21 @@ void powerpc_cpu::execute_fp_round(uint32 opcode)
void powerpc_cpu::execute_syscall(uint32 opcode)
{
cr().set_so(0, execute_do_syscall && !execute_do_syscall(this));
#ifdef SHEEPSHAVER
D(bug("syscall\n"));
increment_pc(4);
#else
try {
cr().set_so(0, execute_do_syscall && !execute_do_syscall(this));
increment_pc(4);
}
catch (kernel_syscall_exit & sc_exit) {
// FIXME: add unwind info to the translation cache? Otherwise
// we have to manually forward the exception to execution loop
syscall_exit_code = sc_exit.status;
spcflags().set(SPCFLAG_CPU_EXEC_RETURN);
}
#endif
}
/**
@ -1026,17 +1033,15 @@ void powerpc_cpu::execute_mfmsr(uint32 opcode)
template< class SPR >
void powerpc_cpu::execute_mfspr(uint32 opcode)
{
uint32 spr = SPR::get(this, opcode);
const uint32 spr = SPR::get(this, opcode);
uint32 d;
switch (spr) {
case 1: d = xer().get(); break;
case 8: d = lr(); break;
case 9: d = ctr(); break;
case powerpc_registers::SPR_XER: d = xer().get();break;
case powerpc_registers::SPR_LR: d = lr(); break;
case powerpc_registers::SPR_CTR: d = ctr(); break;
#ifdef SHEEPSHAVER
case 25: // SDR1
d = 0xdead001f;
break;
case 287: { // PVR
case powerpc_registers::SPR_SDR1: d = 0xdead001f; break;
case powerpc_registers::SPR_PVR: {
extern uint32 PVR;
d = PVR;
break;
@ -1053,13 +1058,13 @@ void powerpc_cpu::execute_mfspr(uint32 opcode)
template< class SPR >
void powerpc_cpu::execute_mtspr(uint32 opcode)
{
uint32 spr = SPR::get(this, opcode);
const uint32 spr = SPR::get(this, opcode);
const uint32 s = operand_RS::get(this, opcode);
switch (spr) {
case 1: xer().set(s); break;
case 8: lr() = s; break;
case 9: ctr() = s; break;
case powerpc_registers::SPR_XER: xer().set(s); break;
case powerpc_registers::SPR_LR: lr() = s; break;
case powerpc_registers::SPR_CTR: ctr() = s; break;
#ifndef SHEEPSHAVER
default: execute_illegal(opcode);
#endif

View File

@ -0,0 +1,134 @@
/*
* ppc-execute.hpp - PowerPC semantic action templates
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef PPC_EXECUTE_H
#define PPC_EXECUTE_H
// This file is designed to be included from implementation files only.
#ifdef DYNGEN_OPS
#define PPC_CPU powerpc_dyngen_helper
#define DEFINE_HELPER(NAME, ARGS) static inline uint32 NAME ARGS
#define RETURN(VAL) dyngen_barrier(); return (VAL)
#else
#define PPC_CPU powerpc_cpu
#define DEFINE_HELPER(NAME, ARGS) inline uint32 powerpc_cpu::NAME ARGS
#define RETURN(VAL) return (VAL)
#endif
template< bool SB > struct register_value { typedef uint32 type; };
template< > struct register_value< true > { typedef int32 type; };
/**
* Add instruction templates
**/
template< bool EX, bool CA, bool OE >
DEFINE_HELPER(do_execute_addition, (uint32 RA, uint32 RB))
{
uint32 RD = RA + RB + (EX ? PPC_CPU::xer().get_ca() : 0);
const bool _RA = ((int32)RA) < 0;
const bool _RB = ((int32)RB) < 0;
const bool _RD = ((int32)RD) < 0;
if (EX) {
const bool ca = _RB ^ ((_RB ^ _RA) & (_RA ^ _RD));
PPC_CPU::xer().set_ca(ca);
}
else if (CA) {
const bool ca = (uint32)RD < (uint32)RA;
PPC_CPU::xer().set_ca(ca);
}
if (OE)
PPC_CPU::xer().set_ov((_RB ^ _RD) & (_RA ^ _RD));
RETURN(RD);
}
/**
* Subtract instruction templates
**/
template< bool CA, bool OE >
DEFINE_HELPER(do_execute_subtract, (uint32 RA, uint32 RB))
{
uint32 RD = RB - RA;
const bool _RA = ((int32)RA) < 0;
const bool _RB = ((int32)RB) < 0;
const bool _RD = ((int32)RD) < 0;
if (CA)
PPC_CPU::xer().set_ca((uint32)RD <= (uint32)RB);
if (OE)
PPC_CPU::xer().set_ov((_RA ^ _RB) & (_RD ^ _RB));
RETURN(RD);
}
template< bool OE >
DEFINE_HELPER(do_execute_subtract_extended, (uint32 RA, uint32 RB))
{
const uint32 RD = ~RA + RB + PPC_CPU::xer().get_ca();
const bool _RA = ((int32)RA) < 0;
const bool _RB = ((int32)RB) < 0;
const bool _RD = ((int32)RD) < 0;
const bool ca = !_RA ^ ((_RA ^ _RD) & (_RB ^ _RD));
PPC_CPU::xer().set_ca(ca);
if (OE)
PPC_CPU::xer().set_ov((_RA ^ _RB) & (_RD ^ _RB));
RETURN(RD);
}
/**
* Divide instruction templates
**/
template< bool SB, bool OE >
DEFINE_HELPER(do_execute_divide, (uint32 RA, uint32 RB))
{
typename register_value<SB>::type a = RA;
typename register_value<SB>::type b = RB;
uint32 RD;
if (b == 0 || (SB && a == 0x80000000 && b == -1)) {
// Reference manual says result is undefined but it gets all
// bits set to MSB on a real processor
RD = SB ? ((int32)RA >> 31) : 0;
if (OE)
PPC_CPU::xer().set_ov(1);
}
else {
RD = a / b;
if (OE)
PPC_CPU::xer().set_ov(0);
}
RETURN(RD);
}
#endif /* PPC_EXECUTE_H */

View File

@ -0,0 +1,204 @@
/*
* ppc-instructions.hpp - PowerPC instructions IDs
*
* Kheperix (C) 2003 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef PPC_INSTRUCTIONS_H
#define PPC_INSTRUCTIONS_H
/**
* Define PowerPC instruction types
**/
#define PPC_I(X) powerpc_instruction_##X
enum powerpc_instruction {
PPC_I(INVALID),
PPC_I(ADD),
PPC_I(ADDC),
PPC_I(ADDE),
PPC_I(ADDI),
PPC_I(ADDIC),
PPC_I(ADDIC_),
PPC_I(ADDIS),
PPC_I(ADDME),
PPC_I(ADDZE),
PPC_I(AND),
PPC_I(ANDC),
PPC_I(ANDI),
PPC_I(ANDIS),
PPC_I(B),
PPC_I(BC),
PPC_I(BCCTR),
PPC_I(BCLR),
PPC_I(CMP),
PPC_I(CMPI),
PPC_I(CMPL),
PPC_I(CMPLI),
PPC_I(CNTLZW),
PPC_I(CRAND),
PPC_I(CRANDC),
PPC_I(CREQV),
PPC_I(CRNAND),
PPC_I(CRNOR),
PPC_I(CROR),
PPC_I(CRORC),
PPC_I(CRXOR),
PPC_I(DCBA),
PPC_I(DCBF),
PPC_I(DCBI),
PPC_I(DCBST),
PPC_I(DCBT),
PPC_I(DCBTST),
PPC_I(DCBZ),
PPC_I(DIVW),
PPC_I(DIVWU),
PPC_I(ECIWX),
PPC_I(ECOWX),
PPC_I(EIEIO),
PPC_I(EQV),
PPC_I(EXTSB),
PPC_I(EXTSH),
PPC_I(FABS),
PPC_I(FADD),
PPC_I(FADDS),
PPC_I(FCMPO),
PPC_I(FCMPU),
PPC_I(FCTIW),
PPC_I(FCTIWZ),
PPC_I(FDIV),
PPC_I(FDIVS),
PPC_I(FMADD),
PPC_I(FMADDS),
PPC_I(FMR),
PPC_I(FMSUB),
PPC_I(FMSUBS),
PPC_I(FMUL),
PPC_I(FMULS),
PPC_I(FNABS),
PPC_I(FNEG),
PPC_I(FNMADD),
PPC_I(FNMADDS),
PPC_I(FNMSUB),
PPC_I(FNMSUBS),
PPC_I(FRSP),
PPC_I(FSUB),
PPC_I(FSUBS),
PPC_I(ICBI),
PPC_I(ISYNC),
PPC_I(LBZ),
PPC_I(LBZU),
PPC_I(LBZUX),
PPC_I(LBZX),
PPC_I(LFD),
PPC_I(LFDU),
PPC_I(LFDUX),
PPC_I(LFDX),
PPC_I(LFS),
PPC_I(LFSU),
PPC_I(LFSUX),
PPC_I(LFSX),
PPC_I(LHA),
PPC_I(LHAU),
PPC_I(LHAUX),
PPC_I(LHAX),
PPC_I(LHBRX),
PPC_I(LHZ),
PPC_I(LHZU),
PPC_I(LHZUX),
PPC_I(LHZX),
PPC_I(LMW),
PPC_I(LSWI),
PPC_I(LSWX),
PPC_I(LWARX),
PPC_I(LWBRX),
PPC_I(LWZ),
PPC_I(LWZU),
PPC_I(LWZUX),
PPC_I(LWZX),
PPC_I(MCRF),
PPC_I(MFCR),
PPC_I(MFFS),
PPC_I(MFMSR),
PPC_I(MFSPR),
PPC_I(MFTB),
PPC_I(MTCRF),
PPC_I(MTFSB0),
PPC_I(MTFSB1),
PPC_I(MTFSF),
PPC_I(MTFSFI),
PPC_I(MTSPR),
PPC_I(MULHW),
PPC_I(MULHWU),
PPC_I(MULLI),
PPC_I(MULLW),
PPC_I(NAND),
PPC_I(NEG),
PPC_I(NOR),
PPC_I(OR),
PPC_I(ORC),
PPC_I(ORI),
PPC_I(ORIS),
PPC_I(RLWIMI),
PPC_I(RLWINM),
PPC_I(RLWNM),
PPC_I(SC),
PPC_I(SLW),
PPC_I(SRAW),
PPC_I(SRAWI),
PPC_I(SRW),
PPC_I(STB),
PPC_I(STBU),
PPC_I(STBUX),
PPC_I(STBX),
PPC_I(STFD),
PPC_I(STFDU),
PPC_I(STFDUX),
PPC_I(STFDX),
PPC_I(STFS),
PPC_I(STFSU),
PPC_I(STFSUX),
PPC_I(STFSX),
PPC_I(STH),
PPC_I(STHBRX),
PPC_I(STHU),
PPC_I(STHUX),
PPC_I(STHX),
PPC_I(STMW),
PPC_I(STSWI),
PPC_I(STSWX),
PPC_I(STW),
PPC_I(STWBRX),
PPC_I(STWCX),
PPC_I(STWU),
PPC_I(STWUX),
PPC_I(STWX),
PPC_I(SUBF),
PPC_I(SUBFC),
PPC_I(SUBFE),
PPC_I(SUBFIC),
PPC_I(SUBFME),
PPC_I(SUBFZE),
PPC_I(SYNC),
PPC_I(XOR),
PPC_I(XORI),
PPC_I(XORIS),
PPC_I(MAX) // Total number of instruction types
};
#endif /* PPC_INSTRUCTIONS_H */

View File

@ -120,13 +120,16 @@ struct immediate_value {
};
struct mask_operand {
static inline uint32 get(powerpc_cpu *, uint32 opcode) {
const uint32 mb = MB_field::extract(opcode);
const uint32 me = ME_field::extract(opcode);
static inline uint32 compute(uint32 mb, uint32 me) {
return ((mb > me) ?
~(((uint32)-1 >> mb) ^ ((me >= 31) ? 0 : (uint32)-1 >> (me + 1))) :
(((uint32)-1 >> mb) ^ ((me >= 31) ? 0 : (uint32)-1 >> (me + 1))));
}
static inline uint32 get(powerpc_cpu *, uint32 opcode) {
const uint32 mb = MB_field::extract(opcode);
const uint32 me = ME_field::extract(opcode);
return compute(mb, me);
}
};
/**

View File

@ -105,27 +105,27 @@ powerpc_cr_register::test(int condition) const
class powerpc_xer_register
{
bool so;
bool ov;
bool ca;
uint32 byte_count;
uint8 so;
uint8 ov;
uint8 ca;
uint8 byte_count;
public:
powerpc_xer_register();
void set(uint32 xer);
uint32 get() const;
void set_so(bool v) { so = v; }
bool get_so() const { return so; }
void set_ov(bool v) { ov = v; if (v) so = true; }
bool get_ov() const { return ov; }
void set_ca(bool v) { ca = v; }
bool get_ca() const { return ca; }
void set_count(uint32 v) { byte_count = v; }
uint32 get_count() const { return byte_count; }
void set_so(int v) { so = v; }
int get_so() const { return so; }
void set_ov(int v) { ov = v; so |= v; }
int get_ov() const { return ov; }
void set_ca(int v) { ca = v; }
int get_ca() const { return ca; }
void set_count(int v) { byte_count = v; }
int get_count() const { return byte_count; }
};
inline
powerpc_xer_register::powerpc_xer_register()
: so(false), ov(false), ca(false), byte_count(0)
: so(0), ov(0), ca(0), byte_count(0)
{ }
inline uint32
@ -178,7 +178,15 @@ struct powerpc_registers
PC,
SP = GPR_BASE + 1
};
enum {
SPR_XER = 1,
SPR_LR = 8,
SPR_CTR = 9,
SPR_SDR1 = 25,
SPR_PVR = 287,
};
static inline int GPR(int r) { return GPR_BASE + r; }
static inline int FPR(int r) { return FPR_BASE + r; }

File diff suppressed because it is too large Load Diff

View File

@ -21,10 +21,13 @@
#include <stdlib.h>
#include "sysdeps.h"
#include "cpu/ppc/ppc-cpu.hpp"
#include "cpu/ppc/ppc-instructions.hpp"
// Disassemblers needed for debugging purposes
#if ENABLE_MON
#include "mon.h"
#include "mon_disass.h"
#endif
#define TEST_ADD 1
#define TEST_SUB 1
@ -86,6 +89,10 @@ static void inline flush_icache_range(uint32 *start_p, uint32 length)
asm volatile ("sync" : : : "memory");
asm volatile ("isync" : : : "memory");
}
#else
static void inline flush_icache_range(uint32 *start_p, uint32 length)
{
}
#endif
struct exec_return { };
@ -105,6 +112,7 @@ class powerpc_test_cpu
void emul_set_cr(uint32 value)
{ cr().set(value); }
#if defined(__powerpc__)
uint32 native_get_xer() const
{ uint32 xer; asm volatile ("mfxer %0" : "=r" (xer)); return xer; }
@ -116,6 +124,10 @@ class powerpc_test_cpu
void native_set_cr(uint32 cr) const
{ asm volatile ("mtcr %0" : : "r" (cr)); }
#endif
void flush_icache_range(uint32 *start, uint32 size)
{ invalidate_cache(); ::flush_icache_range(start, size); }
void init_decoder();
void print_flags(uint32 cr, uint32 xer, int crf = 0) const;
@ -124,12 +136,8 @@ class powerpc_test_cpu
public:
powerpc_test_cpu()
: powerpc_cpu(NULL)
{ mon_init(); init_decoder(); }
~powerpc_test_cpu()
{ mon_exit(); }
powerpc_test_cpu();
~powerpc_test_cpu();
bool test(void);
@ -154,11 +162,13 @@ private:
static const uint32 msk_values[];
void test_one(uint32 *code, const char *insn, uint32 a1, uint32 a2, uint32 a3, uint32 a0 = 0);
void test_instruction_CNTLZ(const char *insn, uint32 opcode);
void test_instruction_RR___(const char *insn, uint32 opcode);
void test_instruction_RRI__(const char *insn, uint32 opcode);
#define test_instruction_RRK__ test_instruction_RRI__
void test_instruction_RRS__(const char *insn, uint32 opcode);
void test_instruction_RRR__(const char *insn, uint32 opcode);
void test_instruction_RRRSH(const char *insn, uint32 opcode);
void test_instruction_RRIII(const char *insn, uint32 opcode);
void test_instruction_RRRII(const char *insn, uint32 opcode);
void test_instruction_CRR__(const char *insn, uint32 opcode);
@ -178,9 +188,25 @@ private:
void test_load_multiple(void);
};
powerpc_test_cpu::powerpc_test_cpu()
: powerpc_cpu(NULL)
{
#if ENABLE_MON
mon_init();
#endif
init_decoder();
}
powerpc_test_cpu::~powerpc_test_cpu()
{
#if ENABLE_MON
mon_exit();
#endif
}
void powerpc_test_cpu::execute_return(uint32 opcode)
{
throw exec_return();
spcflags().set(SPCFLAG_CPU_EXEC_RETURN);
}
void powerpc_test_cpu::init_decoder()
@ -196,7 +222,8 @@ void powerpc_test_cpu::init_decoder()
{ "return",
(execute_pmf)&powerpc_test_cpu::execute_return,
NULL,
D_form, 6, 0, CFLOW_TRAP
PPC_I(MAX),
D_form, 6, 0, CFLOW_JUMP
}
};
@ -211,16 +238,11 @@ void powerpc_test_cpu::init_decoder()
void powerpc_test_cpu::execute(uint32 *code_p)
{
static uint32 code[] = { POWERPC_BLR | 1, POWERPC_EMUL_OP };
lr() = (uint32)code_p;
assert((uintptr)code <= INT_MAX);
lr() = (uintptr)code_p;
try {
invalidate_cache();
pc() = (uintptr)code;
powerpc_cpu::execute();
}
catch (exec_return const &) {
// Nothing, simply return
}
pc() = (uintptr)code;
powerpc_cpu::execute();
}
void powerpc_test_cpu::print_flags(uint32 cr, uint32 xer, int crf) const
@ -242,6 +264,7 @@ void powerpc_test_cpu::print_flags(uint32 cr, uint32 xer, int crf) const
void powerpc_test_cpu::test_one(uint32 *code, const char *insn, uint32 a1, uint32 a2, uint32 a3, uint32 a0)
{
#if defined(__powerpc__)
// Invoke native code
const uint32 save_xer = native_get_xer();
const uint32 save_cr = native_get_cr();
@ -254,6 +277,12 @@ void powerpc_test_cpu::test_one(uint32 *code, const char *insn, uint32 a1, uint3
const uint32 native_cr = native_get_cr();
native_set_xer(save_xer);
native_set_cr(save_cr);
#else
// FIXME: Restore context from results file
const uint32 native_rd = 0;
const uint32 native_xer = 0;
const uint32 native_cr = 0;
#endif
// Invoke emulated code
emul_set_xer(init_xer);
@ -284,7 +313,9 @@ void powerpc_test_cpu::test_one(uint32 *code, const char *insn, uint32 a1, uint3
}
if (!ok || verbose) {
#if ENABLE_MON
disass_ppc(stdout, (uintptr)code, code[0]);
#endif
#define PRINT_OPERANDS(PREFIX) do { \
printf(" %08x, %08x, %08x, %08x => %08x [", \
a0, a1, a2, a3, PREFIX##_rd); \
@ -333,6 +364,34 @@ const uint32 powerpc_test_cpu::msk_values[] = {
30, 31
};
void powerpc_test_cpu::test_instruction_CNTLZ(const char *insn, uint32 opcode)
{
// Test code
static uint32 code[] = {
POWERPC_ILLEGAL, POWERPC_BLR,
POWERPC_MR(0, RA), POWERPC_ILLEGAL, POWERPC_BLR
};
// Input values
const int n_values = sizeof(reg_values)/sizeof(reg_values[0]);
code[0] = code[3] = opcode; // <op> RD,RA,RB
rA_field::insert(code[3], 0); // <op> RD,R0,RB
flush_icache_range(code, sizeof(code));
for (uint32 mask = 0x800000000; mask != 0; mask >>= 1) {
uint32 ra = mask;
test_one(&code[0], insn, ra, 0, 0);
test_one(&code[2], insn, ra, 0, 0);
}
// random values (including zero)
for (int i = 0; i < n_values; i++) {
uint32 ra = reg_values[i];
test_one(&code[0], insn, ra, 0, 0);
test_one(&code[2], insn, ra, 0, 0);
}
}
void powerpc_test_cpu::test_instruction_RR___(const char *insn, uint32 opcode)
{
// Test code
@ -431,6 +490,31 @@ void powerpc_test_cpu::test_instruction_RRR__(const char *insn, uint32 opcode)
}
}
void powerpc_test_cpu::test_instruction_RRRSH(const char *insn, uint32 opcode)
{
// Test code
static uint32 code[] = {
POWERPC_ILLEGAL, POWERPC_BLR,
POWERPC_MR(0, RA), POWERPC_ILLEGAL, POWERPC_BLR
};
// Input values
const int n_values = sizeof(reg_values)/sizeof(reg_values[0]);
code[0] = code[3] = opcode; // <op> RD,RA,RB
rA_field::insert(code[3], 0); // <op> RD,R0,RB
flush_icache_range(code, sizeof(code));
for (int i = 0; i < n_values; i++) {
const uint32 ra = reg_values[i];
for (int j = 0; j <= 64; j++) {
const uint32 rb = j;
test_one(&code[0], insn, ra, rb, 0);
test_one(&code[2], insn, ra, rb, 0);
}
}
}
void powerpc_test_cpu::test_instruction_RRIII(const char *insn, uint32 opcode)
{
// Test code
@ -590,55 +674,81 @@ void powerpc_test_cpu::test_instruction_CCC__(const char *insn, uint32 opcode)
void powerpc_test_cpu::test_add(void)
{
#if TEST_ADD
TEST_INSTRUCTION(RRR__,"add", _XO(31,RD,RA,RB,0,266,0));
TEST_INSTRUCTION(RRR__,"add.", _XO(31,RD,RA,RB,0,266,1));
TEST_INSTRUCTION(RRR__,"addo", _XO(31,RD,RA,RB,1,266,0));
TEST_INSTRUCTION(RRR__,"addo." , _XO(31,RD,RA,RB,1,266,1));
TEST_INSTRUCTION(RRR__,"addc.", _XO(31,RD,RA,RB,0, 10,1));
TEST_INSTRUCTION(RRR__,"addco.", _XO(31,RD,RA,RB,1, 10,1));
TEST_INSTRUCTION(RRR__,"adde.", _XO(31,RD,RA,RB,0,138,1));
TEST_INSTRUCTION(RRR__,"addeo.", _XO(31,RD,RA,RB,1,138,1));
TEST_INSTRUCTION(RRI__,"addi", _D (14,RD,RA,00));
TEST_INSTRUCTION(RRI__,"addic", _D (12,RD,RA,00));
TEST_INSTRUCTION(RRI__,"addic.", _D (13,RD,RA,00));
TEST_INSTRUCTION(RRI__,"addis", _D (15,RD,RA,00));
TEST_INSTRUCTION(RR___,"addme.", _XO(31,RD,RA,00,0,234,1));
TEST_INSTRUCTION(RR___,"addmeo.", _XO(31,RD,RA,00,1,234,1));
TEST_INSTRUCTION(RR___,"addze.", _XO(31,RD,RA,00,0,202,1));
TEST_INSTRUCTION(RR___,"addzeo.", _XO(31,RD,RA,00,1,202,1));
init_xer |= XER_CA_field::mask();
TEST_INSTRUCTION(RRR__,"adde.", _XO(31,RD,RA,RB,0,138,1));
TEST_INSTRUCTION(RRR__,"addeo.", _XO(31,RD,RA,RB,1,138,1));
TEST_INSTRUCTION(RR___,"addme.", _XO(31,RD,RA,00,0,234,1));
TEST_INSTRUCTION(RR___,"addmeo.", _XO(31,RD,RA,00,1,234,1));
TEST_INSTRUCTION(RR___,"addze.", _XO(31,RD,RA,00,0,202,1));
TEST_INSTRUCTION(RR___,"addzeo.", _XO(31,RD,RA,00,1,202,1));
init_xer &= ~XER_CA_field::mask();
const int n_xer_values = 3;
uint32 xer_values[n_xer_values];
xer_values[0] = init_xer;
xer_values[1] = init_xer | XER_OV_field::mask();
xer_values[2] = init_xer | XER_CA_field::mask();
// Iterate over some specific XER values so that we make sure we
// only update them when actually needed
for (int i = 0; i < n_xer_values; i++) {
const uint32 saved_xer = init_xer;
init_xer = xer_values[i];
TEST_INSTRUCTION(RRR__,"add", _XO(31,RD,RA,RB,0,266,0));
TEST_INSTRUCTION(RRR__,"add.", _XO(31,RD,RA,RB,0,266,1));
TEST_INSTRUCTION(RRR__,"addo", _XO(31,RD,RA,RB,1,266,0));
TEST_INSTRUCTION(RRR__,"addo." , _XO(31,RD,RA,RB,1,266,1));
TEST_INSTRUCTION(RRR__,"addc.", _XO(31,RD,RA,RB,0, 10,1));
TEST_INSTRUCTION(RRR__,"addco.", _XO(31,RD,RA,RB,1, 10,1));
TEST_INSTRUCTION(RRR__,"adde", _XO(31,RD,RA,RB,0,138,0));
TEST_INSTRUCTION(RRR__,"adde.", _XO(31,RD,RA,RB,0,138,1));
TEST_INSTRUCTION(RRR__,"addeo", _XO(31,RD,RA,RB,1,138,0));
TEST_INSTRUCTION(RRR__,"addeo.", _XO(31,RD,RA,RB,1,138,1));
TEST_INSTRUCTION(RRI__,"addi", _D (14,RD,RA,00));
TEST_INSTRUCTION(RRI__,"addic", _D (12,RD,RA,00));
TEST_INSTRUCTION(RRI__,"addic.", _D (13,RD,RA,00));
TEST_INSTRUCTION(RRI__,"addis", _D (15,RD,RA,00));
TEST_INSTRUCTION(RR___,"addme", _XO(31,RD,RA,00,0,234,0));
TEST_INSTRUCTION(RR___,"addme.", _XO(31,RD,RA,00,0,234,1));
TEST_INSTRUCTION(RR___,"addmeo", _XO(31,RD,RA,00,1,234,0));
TEST_INSTRUCTION(RR___,"addmeo.", _XO(31,RD,RA,00,1,234,1));
TEST_INSTRUCTION(RR___,"addze", _XO(31,RD,RA,00,0,202,0));
TEST_INSTRUCTION(RR___,"addze.", _XO(31,RD,RA,00,0,202,1));
TEST_INSTRUCTION(RR___,"addzeo", _XO(31,RD,RA,00,1,202,0));
TEST_INSTRUCTION(RR___,"addzeo.", _XO(31,RD,RA,00,1,202,1));
init_xer = saved_xer;
}
#endif
}
void powerpc_test_cpu::test_sub(void)
{
#if TEST_SUB
TEST_INSTRUCTION(RRR__,"subf.", _XO(31,RD,RA,RB,0, 40,1));
TEST_INSTRUCTION(RRR__,"subfo.", _XO(31,RD,RA,RB,1, 40,1));
TEST_INSTRUCTION(RRR__,"subfc.", _XO(31,RD,RA,RB,0, 8,1));
TEST_INSTRUCTION(RRR__,"subfco.", _XO(31,RD,RA,RB,1, 8,1));
TEST_INSTRUCTION(RRR__,"subfe.", _XO(31,RD,RA,RB,0,136,1));
TEST_INSTRUCTION(RRR__,"subfeo.", _XO(31,RD,RA,RB,1,136,1));
TEST_INSTRUCTION(RRI__,"subfic", _D ( 8,RD,RA,00));
TEST_INSTRUCTION(RR___,"subfme.", _XO(31,RD,RA,00,0,232,1));
TEST_INSTRUCTION(RR___,"subfmeo.", _XO(31,RD,RA,00,1,232,1));
TEST_INSTRUCTION(RR___,"subfze.", _XO(31,RD,RA,00,0,200,1));
TEST_INSTRUCTION(RR___,"subfzeo.", _XO(31,RD,RA,00,1,200,1));
init_xer |= XER_CA_field::mask();
TEST_INSTRUCTION(RRR__,"subfe.", _XO(31,RD,RA,RB,0,136,1));
TEST_INSTRUCTION(RRR__,"subfeo.", _XO(31,RD,RA,RB,1,136,1));
TEST_INSTRUCTION(RR___,"subfme.", _XO(31,RD,RA,00,0,232,1));
TEST_INSTRUCTION(RR___,"subfmeo.", _XO(31,RD,RA,00,1,232,1));
TEST_INSTRUCTION(RR___,"subfze.", _XO(31,RD,RA,00,0,200,1));
TEST_INSTRUCTION(RR___,"subfzeo.", _XO(31,RD,RA,00,1,200,1));
init_xer &= ~XER_CA_field::mask();
const int n_xer_values = 3;
uint32 xer_values[n_xer_values];
xer_values[0] = init_xer;
xer_values[1] = init_xer | XER_OV_field::mask();
xer_values[2] = init_xer | XER_CA_field::mask();
// Iterate over some specific XER values so that we make sure we
// only update them when actually needed
for (int i = 0; i < n_xer_values; i++) {
const uint32 saved_xer = init_xer;
init_xer = xer_values[i];
TEST_INSTRUCTION(RRR__,"subf", _XO(31,RD,RA,RB,0, 40,0));
TEST_INSTRUCTION(RRR__,"subf.", _XO(31,RD,RA,RB,0, 40,1));
TEST_INSTRUCTION(RRR__,"subfo", _XO(31,RD,RA,RB,1, 40,0));
TEST_INSTRUCTION(RRR__,"subfo.", _XO(31,RD,RA,RB,1, 40,1));
TEST_INSTRUCTION(RRR__,"subfc", _XO(31,RD,RA,RB,0, 8,0));
TEST_INSTRUCTION(RRR__,"subfc.", _XO(31,RD,RA,RB,0, 8,1));
TEST_INSTRUCTION(RRR__,"subfco", _XO(31,RD,RA,RB,1, 8,0));
TEST_INSTRUCTION(RRR__,"subfco.", _XO(31,RD,RA,RB,1, 8,1));
TEST_INSTRUCTION(RRR__,"subfe", _XO(31,RD,RA,RB,0,136,0));
TEST_INSTRUCTION(RRR__,"subfe.", _XO(31,RD,RA,RB,0,136,1));
TEST_INSTRUCTION(RRR__,"subfeo", _XO(31,RD,RA,RB,1,136,0));
TEST_INSTRUCTION(RRR__,"subfeo.", _XO(31,RD,RA,RB,1,136,1));
TEST_INSTRUCTION(RRI__,"subfic", _D ( 8,RD,RA,00));
TEST_INSTRUCTION(RR___,"subfme", _XO(31,RD,RA,00,0,232,0));
TEST_INSTRUCTION(RR___,"subfme.", _XO(31,RD,RA,00,0,232,1));
TEST_INSTRUCTION(RR___,"subfmeo", _XO(31,RD,RA,00,1,232,0));
TEST_INSTRUCTION(RR___,"subfmeo.", _XO(31,RD,RA,00,1,232,1));
TEST_INSTRUCTION(RR___,"subfze", _XO(31,RD,RA,00,0,200,0));
TEST_INSTRUCTION(RR___,"subfze.", _XO(31,RD,RA,00,0,200,1));
TEST_INSTRUCTION(RR___,"subfzeo", _XO(31,RD,RA,00,1,200,0));
TEST_INSTRUCTION(RR___,"subfzeo.", _XO(31,RD,RA,00,1,200,1));
init_xer = saved_xer;
}
#endif
}
@ -674,22 +784,35 @@ void powerpc_test_cpu::test_div(void)
void powerpc_test_cpu::test_logical(void)
{
#if TEST_LOGICAL
TEST_INSTRUCTION(RRR__,"and", _X (31,RA,RD,RB,28,0));
TEST_INSTRUCTION(RRR__,"and.", _X (31,RA,RD,RB,28,1));
TEST_INSTRUCTION(RRR__,"andc", _X (31,RA,RD,RB,60,0));
TEST_INSTRUCTION(RRR__,"andc.", _X (31,RA,RD,RB,60,1));
TEST_INSTRUCTION(RRK__,"andi.", _D (28,RA,RD,00));
TEST_INSTRUCTION(RRK__,"andis.", _D (29,RA,RD,00));
TEST_INSTRUCTION(RR___,"cntlzw.", _X (31,RA,RD,00,26,1));
TEST_INSTRUCTION(CNTLZ,"cntlzw", _X (31,RA,RD,00,26,0));
TEST_INSTRUCTION(CNTLZ,"cntlzw.", _X (31,RA,RD,00,26,1));
TEST_INSTRUCTION(RRR__,"eqv", _X (31,RA,RD,RB,284,0));
TEST_INSTRUCTION(RRR__,"eqv.", _X (31,RA,RD,RB,284,1));
TEST_INSTRUCTION(RR___,"extsb", _X (31,RA,RD,00,954,0));
TEST_INSTRUCTION(RR___,"extsb.", _X (31,RA,RD,00,954,1));
TEST_INSTRUCTION(RR___,"extsh", _X (31,RA,RD,00,922,0));
TEST_INSTRUCTION(RR___,"extsh.", _X (31,RA,RD,00,922,1));
TEST_INSTRUCTION(RRR__,"nand", _X (31,RA,RD,RB,476,0));
TEST_INSTRUCTION(RRR__,"nand.", _X (31,RA,RD,RB,476,1));
TEST_INSTRUCTION(RR___,"neg", _XO(31,RD,RA,RB,0,104,0));
TEST_INSTRUCTION(RR___,"neg.", _XO(31,RD,RA,RB,0,104,1));
TEST_INSTRUCTION(RR___,"nego", _XO(31,RD,RA,RB,1,104,0));
TEST_INSTRUCTION(RR___,"nego.", _XO(31,RD,RA,RB,1,104,1));
TEST_INSTRUCTION(RRR__,"nor", _X (31,RA,RD,RB,124,0));
TEST_INSTRUCTION(RRR__,"nor.", _X (31,RA,RD,RB,124,1));
TEST_INSTRUCTION(RRR__,"or", _X (31,RA,RD,RB,444,0));
TEST_INSTRUCTION(RRR__,"or.", _X (31,RA,RD,RB,444,1));
TEST_INSTRUCTION(RRR__,"orc", _X (31,RA,RD,RB,412,0));
TEST_INSTRUCTION(RRR__,"orc.", _X (31,RA,RD,RB,412,1));
TEST_INSTRUCTION(RRK__,"ori", _D (24,RA,RD,00));
TEST_INSTRUCTION(RRK__,"oris", _D (25,RA,RD,00));
TEST_INSTRUCTION(RRR__,"xor", _X (31,RA,RD,RB,316,0));
TEST_INSTRUCTION(RRR__,"xor.", _X (31,RA,RD,RB,316,1));
TEST_INSTRUCTION(RRK__,"xori", _D (26,RA,RD,00));
TEST_INSTRUCTION(RRK__,"xoris", _D (27,RA,RD,00));
@ -699,22 +822,25 @@ void powerpc_test_cpu::test_logical(void)
void powerpc_test_cpu::test_shift(void)
{
#if TEST_SHIFT
TEST_INSTRUCTION(RRR__,"slw", _X (31,RA,RD,RB, 24,0));
TEST_INSTRUCTION(RRR__,"slw.", _X (31,RA,RD,RB, 24,1));
TEST_INSTRUCTION(RRR__,"sraw", _X (31,RA,RD,RB,792,0));
TEST_INSTRUCTION(RRR__,"sraw.", _X (31,RA,RD,RB,792,1));
TEST_INSTRUCTION(RRRSH,"slw", _X (31,RA,RD,RB, 24,0));
TEST_INSTRUCTION(RRRSH,"slw.", _X (31,RA,RD,RB, 24,1));
TEST_INSTRUCTION(RRRSH,"sraw", _X (31,RA,RD,RB,792,0));
TEST_INSTRUCTION(RRRSH,"sraw.", _X (31,RA,RD,RB,792,1));
TEST_INSTRUCTION(RRS__,"srawi", _X (31,RA,RD,00,824,0));
TEST_INSTRUCTION(RRS__,"srawi.", _X (31,RA,RD,00,824,1));
TEST_INSTRUCTION(RRR__,"srw", _X (31,RA,RD,RB,536,0));
TEST_INSTRUCTION(RRR__,"srw.", _X (31,RA,RD,RB,536,1));
TEST_INSTRUCTION(RRRSH,"srw", _X (31,RA,RD,RB,536,0));
TEST_INSTRUCTION(RRRSH,"srw.", _X (31,RA,RD,RB,536,1));
#endif
}
void powerpc_test_cpu::test_rotate(void)
{
#if TEST_ROTATE
TEST_INSTRUCTION(RRIII,"rlwimi", _M (20,RA,RD,00,00,00,0));
TEST_INSTRUCTION(RRIII,"rlwimi.", _M (20,RA,RD,00,00,00,1));
TEST_INSTRUCTION(RRIII,"rlwinm", _M (21,RA,RD,00,00,00,0));
TEST_INSTRUCTION(RRIII,"rlwinm.", _M (21,RA,RD,00,00,00,1));
TEST_INSTRUCTION(RRRII,"rlwnm", _M (23,RA,RD,RB,00,00,0));
TEST_INSTRUCTION(RRRII,"rlwnm.", _M (23,RA,RD,RB,00,00,1));
#endif
}
@ -759,8 +885,12 @@ bool powerpc_test_cpu::test(void)
{
// Tests initialization
tests = errors = 0;
#if defined(__powerpc__)
init_cr = native_get_cr() & ~CR_field<0>::mask();
init_xer = native_get_xer() & ~(XER_OV_field::mask() | XER_CA_field::mask());
#else
init_cr = init_xer = 0;
#endif
// Tests execution
test_add();