diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c389bc34f..e0c071e142 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,7 @@ option(UNICORN_BUILD_TESTS "Build unicorn tests" ${PROJECT_IS_TOP_LEVEL}) option(UNICORN_INSTALL "Enable unicorn installation" ${PROJECT_IS_TOP_LEVEL}) set(UNICORN_ARCH "x86;arm;aarch64;riscv;mips;sparc;m68k;ppc;s390x;tricore" CACHE STRING "Enabled unicorn architectures") option(UNICORN_TRACER "Trace unicorn execution" OFF) +option(UNICORN_INTERPRETER "Use interpreter mode" OFF) foreach(ARCH_LOOP ${UNICORN_ARCH}) string(TOUPPER "${ARCH_LOOP}" ARCH_LOOP) @@ -276,6 +277,10 @@ else() endwhile(TRUE) endif() + if(UNICORN_INTERPRETER) + set(UNICORN_TARGET_ARCH "tci") + endif() + set(EXTRA_CFLAGS "--extra-cflags=") if(UNICORN_HAS_X86) set(EXTRA_CFLAGS "${EXTRA_CFLAGS}-DUNICORN_HAS_X86 ") @@ -361,10 +366,17 @@ else() set(TARGET_LIST "${TARGET_LIST} ") # GEN config-host.mak & target directories + set(UNICORN_EXECUTION_MODE "") + if(UNICORN_INTERPRETER) + set(UNICORN_EXECUTION_MODE "--enable-interpreter") + else() + set(UNICORN_EXECUTION_MODE "--disable-interpreter") + endif() execute_process(COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/qemu/configure --cc=${CMAKE_C_COMPILER} ${EXTRA_CFLAGS} ${TARGET_LIST} + ${UNICORN_EXECUTION_MODE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) execute_process(COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/qemu/scripts/create_config @@ -506,6 +518,10 @@ set(UNICORN_ARCH_COMMON qemu/softmmu/unicorn_vtlb.c ) +if(UNICORN_INTERPRETER) + list(APPEND UNICORN_ARCH_COMMON qemu/tcg/tci.c) +endif() + if(UNICORN_HAS_X86) add_library(x86_64-softmmu STATIC ${UNICORN_ARCH_COMMON} diff --git a/qemu/accel/tcg/translate-all.c b/qemu/accel/tcg/translate-all.c index 366f3a4f5c..5bdee33161 100644 --- a/qemu/accel/tcg/translate-all.c +++ b/qemu/accel/tcg/translate-all.c @@ -1019,12 +1019,18 @@ void free_code_gen_buffer(struct uc_struct *uc) static inline void *alloc_code_gen_buffer(struct uc_struct *uc) { TCGContext *tcg_ctx = uc->tcg_ctx; +#if CONFIG_TCG_INTERPRETER + int prot = PROT_WRITE | PROT_READ; +#else int prot = PROT_WRITE | PROT_READ | PROT_EXEC; +#endif int flags = MAP_PRIVATE | MAP_ANONYMOUS; size_t size = tcg_ctx->code_gen_buffer_size; void *buf; +#if !CONFIG_TCG_INTERPRETER #ifdef USE_MAP_JIT flags |= MAP_JIT; +#endif #endif buf = mmap(NULL, size, prot, flags, -1, 0); if (buf == MAP_FAILED) { diff --git a/qemu/configure b/qemu/configure index 11f8b0f5ae..f78684b28a 100755 --- a/qemu/configure +++ b/qemu/configure @@ -272,6 +272,7 @@ supported_cpu="no" supported_os="no" bogus_os="no" malloc_trim="" +interpreter="yes" # parse CC options first for opt do @@ -308,6 +309,10 @@ for opt do eval "cross_cc_${cc_arch}=\$optarg" cross_cc_vars="$cross_cc_vars cross_cc_${cc_arch}" ;; + --enable-interpreter) interpreter="yes" + ;; + --disable-interpreter) interpreter="no" + ;; esac done # OS specific @@ -689,6 +694,10 @@ for opt do ;; --disable-debug-info) ;; + --enable-interpreter) + ;; + --disable-interpreter) + ;; --cross-cc-*) ;; --cpu=*) @@ -922,6 +931,7 @@ disabled with --disable-FEATURE, default is enabled if available: jemalloc jemalloc support avx2 AVX2 optimization support avx512f AVX512F optimization support + interpreter Interpreter mode NOTE: The object files are built at the place where configure is launched EOF @@ -2136,7 +2146,7 @@ fi ########################################## # check for Apple Silicon JIT function -if [ "$darwin" = "yes" ] ; then +if [ "$darwin" = "yes" ] && [ "$interpreter" = "no" ] ; then cat > $TMPC << EOF #include int main() { pthread_jit_write_protect_np(0); return 0;} @@ -2314,6 +2324,7 @@ echo "tcmalloc support $tcmalloc" echo "jemalloc support $jemalloc" echo "avx2 optimization $avx2_opt" echo "avx512f optimization $avx512f_opt" +echo "interpreter $interpreter" if test "$supported_cpu" = "no"; then echo @@ -2558,6 +2569,10 @@ if test "$have_sprr" = "yes" ; then echo "HAVE_SPRR=y" >> $config_host_mak fi +if test "$interpreter" = "yes" ; then + echo "CONFIG_TCG_INTERPRETER=y" >> $config_host_mak +fi + # Hold two types of flag: # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on # a thread we have a handle to diff --git a/qemu/include/tcg/tcg.h b/qemu/include/tcg/tcg.h index fef0cc455a..960eb7f33a 100644 --- a/qemu/include/tcg/tcg.h +++ b/qemu/include/tcg/tcg.h @@ -695,6 +695,7 @@ struct TCGContext { struct jit_code_entry *one_entry; /* qemu/tcg/tcg-common.c */ TCGOpDef *tcg_op_defs; + size_t tcg_op_defs_max; // Unicorn engine variables struct uc_struct *uc; diff --git a/qemu/tcg/tcg.c b/qemu/tcg/tcg.c index a91f2de6f1..326556e4bf 100644 --- a/qemu/tcg/tcg.c +++ b/qemu/tcg/tcg.c @@ -62,6 +62,10 @@ #include +#if CONFIG_TCG_INTERPRETER +#include "tcg/tcg.h" +#endif + /* Forward declarations for functions declared in tcg-target.inc.c and used here. */ static void tcg_target_init(TCGContext *s); @@ -659,6 +663,7 @@ static const TCGOpDef tcg_op_defs_org[] = { #include "tcg/tcg-opc.h" #undef DEF }; +static const size_t tcg_op_defs_max_org = ARRAY_SIZE(tcg_op_defs_org); static void process_op_defs(TCGContext *s); static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type, @@ -727,6 +732,7 @@ void tcg_context_init(TCGContext *s) // copy original tcg_op_defs_org for private usage s->tcg_op_defs = g_malloc0(sizeof(tcg_op_defs_org)); memcpy(s->tcg_op_defs, tcg_op_defs_org, sizeof(tcg_op_defs_org)); + s->tcg_op_defs_max = tcg_op_defs_max_org; /* Count total number of arguments and allocate the corresponding space */ diff --git a/qemu/tcg/tci.c b/qemu/tcg/tci.c new file mode 100644 index 0000000000..46fe9ce63f --- /dev/null +++ b/qemu/tcg/tci.c @@ -0,0 +1,1275 @@ +/* + * Tiny Code Interpreter for QEMU + * + * Copyright (c) 2009, 2011, 2016 Stefan Weil + * + * 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, see . + */ + +#include "qemu/osdep.h" + +/* Enable TCI assertions only when debugging TCG (and without NDEBUG defined). + * Without assertions, the interpreter runs much faster. */ +#if defined(CONFIG_DEBUG_TCG) +# define tci_assert(cond) assert(cond) +#else +# define tci_assert(cond) ((void)0) +#endif + +#include "qemu-common.h" +#include "tcg/tcg.h" /* MAX_OPC_PARAM_IARGS */ +#include "exec/cpu_ldst.h" +#include "tcg/tcg-op.h" + +/* Marker for missing code. */ +#define TODO() \ + do { \ + fprintf(stderr, "TODO %s:%u: %s()\n", \ + __FILE__, __LINE__, __func__); \ + tcg_abort(); \ + } while (0) + +#if MAX_OPC_PARAM_IARGS != 6 +# error Fix needed, number of supported input arguments changed! +#endif +#if TCG_TARGET_REG_BITS == 32 +typedef uint64_t (*helper_function)(tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong); +#else +typedef uint64_t (*helper_function)(tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong); +#endif + +static tcg_target_ulong tci_read_reg(const tcg_target_ulong *regs, TCGReg index) +{ + tci_assert(index < TCG_TARGET_NB_REGS); + return regs[index]; +} + +#if TCG_TARGET_HAS_ext8s_i32 || TCG_TARGET_HAS_ext8s_i64 +static int8_t tci_read_reg8s(const tcg_target_ulong *regs, TCGReg index) +{ + return (int8_t)tci_read_reg(regs, index); +} +#endif + +#if TCG_TARGET_HAS_ext16s_i32 || TCG_TARGET_HAS_ext16s_i64 +static int16_t tci_read_reg16s(const tcg_target_ulong *regs, TCGReg index) +{ + return (int16_t)tci_read_reg(regs, index); +} +#endif + +#if TCG_TARGET_REG_BITS == 64 +static int32_t tci_read_reg32s(const tcg_target_ulong *regs, TCGReg index) +{ + return (int32_t)tci_read_reg(regs, index); +} +#endif + +static uint8_t tci_read_reg8(const tcg_target_ulong *regs, TCGReg index) +{ + return (uint8_t)tci_read_reg(regs, index); +} + +static uint16_t tci_read_reg16(const tcg_target_ulong *regs, TCGReg index) +{ + return (uint16_t)tci_read_reg(regs, index); +} + +static uint32_t tci_read_reg32(const tcg_target_ulong *regs, TCGReg index) +{ + return (uint32_t)tci_read_reg(regs, index); +} + +#if TCG_TARGET_REG_BITS == 64 +static uint64_t tci_read_reg64(const tcg_target_ulong *regs, TCGReg index) +{ + return tci_read_reg(regs, index); +} +#endif + +static void +tci_write_reg(tcg_target_ulong *regs, TCGReg index, tcg_target_ulong value) +{ + tci_assert(index < TCG_TARGET_NB_REGS); + tci_assert(index != TCG_AREG0); + tci_assert(index != TCG_REG_CALL_STACK); + regs[index] = value; +} + +#if TCG_TARGET_REG_BITS == 64 +static void +tci_write_reg32s(tcg_target_ulong *regs, TCGReg index, int32_t value) +{ + tci_write_reg(regs, index, value); +} +#endif + +static void tci_write_reg8(tcg_target_ulong *regs, TCGReg index, uint8_t value) +{ + tci_write_reg(regs, index, value); +} + +static void +tci_write_reg16(tcg_target_ulong *regs, TCGReg index, uint16_t value) +{ + tci_write_reg(regs, index, value); +} + +static void +tci_write_reg32(tcg_target_ulong *regs, TCGReg index, uint32_t value) +{ + tci_write_reg(regs, index, value); +} + +#if TCG_TARGET_REG_BITS == 32 +static void tci_write_reg64(tcg_target_ulong *regs, uint32_t high_index, + uint32_t low_index, uint64_t value) +{ + tci_write_reg(regs, low_index, value); + tci_write_reg(regs, high_index, value >> 32); +} +#elif TCG_TARGET_REG_BITS == 64 +static void +tci_write_reg64(tcg_target_ulong *regs, TCGReg index, uint64_t value) +{ + tci_write_reg(regs, index, value); +} +#endif + +#if TCG_TARGET_REG_BITS == 32 +/* Create a 64 bit value from two 32 bit values. */ +static uint64_t tci_uint64(uint32_t high, uint32_t low) +{ + return ((uint64_t)high << 32) + low; +} +#endif + +/* Read constant (native size) from bytecode. */ +static tcg_target_ulong tci_read_i(uint8_t **tb_ptr) +{ + tcg_target_ulong value = *(tcg_target_ulong *)(*tb_ptr); + *tb_ptr += sizeof(value); + return value; +} + +/* Read unsigned constant (32 bit) from bytecode. */ +static uint32_t tci_read_i32(uint8_t **tb_ptr) +{ + uint32_t value = *(uint32_t *)(*tb_ptr); + *tb_ptr += sizeof(value); + return value; +} + +/* Read signed constant (32 bit) from bytecode. */ +static int32_t tci_read_s32(uint8_t **tb_ptr) +{ + int32_t value = *(int32_t *)(*tb_ptr); + *tb_ptr += sizeof(value); + return value; +} + +#if TCG_TARGET_REG_BITS == 64 +/* Read constant (64 bit) from bytecode. */ +static uint64_t tci_read_i64(uint8_t **tb_ptr) +{ + uint64_t value = *(uint64_t *)(*tb_ptr); + *tb_ptr += sizeof(value); + return value; +} +#endif + +/* Read indexed register (native size) from bytecode. */ +static tcg_target_ulong +tci_read_r(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + tcg_target_ulong value = tci_read_reg(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} + +/* Read indexed register (8 bit) from bytecode. */ +static uint8_t tci_read_r8(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint8_t value = tci_read_reg8(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} + +#if TCG_TARGET_HAS_ext8s_i32 || TCG_TARGET_HAS_ext8s_i64 +/* Read indexed register (8 bit signed) from bytecode. */ +static int8_t tci_read_r8s(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + int8_t value = tci_read_reg8s(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} +#endif + +/* Read indexed register (16 bit) from bytecode. */ +static uint16_t tci_read_r16(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint16_t value = tci_read_reg16(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} + +#if TCG_TARGET_HAS_ext16s_i32 || TCG_TARGET_HAS_ext16s_i64 +/* Read indexed register (16 bit signed) from bytecode. */ +static int16_t tci_read_r16s(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + int16_t value = tci_read_reg16s(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} +#endif + +/* Read indexed register (32 bit) from bytecode. */ +static uint32_t tci_read_r32(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint32_t value = tci_read_reg32(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} + +#if TCG_TARGET_REG_BITS == 32 +/* Read two indexed registers (2 * 32 bit) from bytecode. */ +static uint64_t tci_read_r64(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint32_t low = tci_read_r32(regs, tb_ptr); + return tci_uint64(tci_read_r32(regs, tb_ptr), low); +} +#elif TCG_TARGET_REG_BITS == 64 +/* Read indexed register (32 bit signed) from bytecode. */ +static int32_t tci_read_r32s(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + int32_t value = tci_read_reg32s(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} + +/* Read indexed register (64 bit) from bytecode. */ +static uint64_t tci_read_r64(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint64_t value = tci_read_reg64(regs, **tb_ptr); + *tb_ptr += 1; + return value; +} +#endif + +/* Read indexed register(s) with target address from bytecode. */ +static target_ulong +tci_read_ulong(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + target_ulong taddr = tci_read_r(regs, tb_ptr); +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS + taddr += (uint64_t)tci_read_r(regs, tb_ptr) << 32; +#endif + return taddr; +} + +/* Read indexed register or constant (native size) from bytecode. */ +static tcg_target_ulong +tci_read_ri(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + tcg_target_ulong value; + TCGReg r = **tb_ptr; + *tb_ptr += 1; + if (r == TCG_CONST) { + value = tci_read_i(tb_ptr); + } else { + value = tci_read_reg(regs, r); + } + return value; +} + +/* Read indexed register or constant (32 bit) from bytecode. */ +static uint32_t tci_read_ri32(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint32_t value; + TCGReg r = **tb_ptr; + *tb_ptr += 1; + if (r == TCG_CONST) { + value = tci_read_i32(tb_ptr); + } else { + value = tci_read_reg32(regs, r); + } + return value; +} + +#if TCG_TARGET_REG_BITS == 32 +/* Read two indexed registers or constants (2 * 32 bit) from bytecode. */ +static uint64_t tci_read_ri64(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint32_t low = tci_read_ri32(regs, tb_ptr); + return tci_uint64(tci_read_ri32(regs, tb_ptr), low); +} +#elif TCG_TARGET_REG_BITS == 64 +/* Read indexed register or constant (64 bit) from bytecode. */ +static uint64_t tci_read_ri64(const tcg_target_ulong *regs, uint8_t **tb_ptr) +{ + uint64_t value; + TCGReg r = **tb_ptr; + *tb_ptr += 1; + if (r == TCG_CONST) { + value = tci_read_i64(tb_ptr); + } else { + value = tci_read_reg64(regs, r); + } + return value; +} +#endif + +static tcg_target_ulong tci_read_label(uint8_t **tb_ptr) +{ + tcg_target_ulong label = tci_read_i(tb_ptr); + tci_assert(label != 0); + return label; +} + +static bool tci_compare32(uint32_t u0, uint32_t u1, TCGCond condition) +{ + bool result = false; + int32_t i0 = u0; + int32_t i1 = u1; + switch (condition) { + case TCG_COND_EQ: + result = (u0 == u1); + break; + case TCG_COND_NE: + result = (u0 != u1); + break; + case TCG_COND_LT: + result = (i0 < i1); + break; + case TCG_COND_GE: + result = (i0 >= i1); + break; + case TCG_COND_LE: + result = (i0 <= i1); + break; + case TCG_COND_GT: + result = (i0 > i1); + break; + case TCG_COND_LTU: + result = (u0 < u1); + break; + case TCG_COND_GEU: + result = (u0 >= u1); + break; + case TCG_COND_LEU: + result = (u0 <= u1); + break; + case TCG_COND_GTU: + result = (u0 > u1); + break; + default: + TODO(); + } + return result; +} + +static bool tci_compare64(uint64_t u0, uint64_t u1, TCGCond condition) +{ + bool result = false; + int64_t i0 = u0; + int64_t i1 = u1; + switch (condition) { + case TCG_COND_EQ: + result = (u0 == u1); + break; + case TCG_COND_NE: + result = (u0 != u1); + break; + case TCG_COND_LT: + result = (i0 < i1); + break; + case TCG_COND_GE: + result = (i0 >= i1); + break; + case TCG_COND_LE: + result = (i0 <= i1); + break; + case TCG_COND_GT: + result = (i0 > i1); + break; + case TCG_COND_LTU: + result = (u0 < u1); + break; + case TCG_COND_GEU: + result = (u0 >= u1); + break; + case TCG_COND_LEU: + result = (u0 <= u1); + break; + case TCG_COND_GTU: + result = (u0 > u1); + break; + default: + TODO(); + } + return result; +} + +#ifdef CONFIG_SOFTMMU +# define qemu_ld_ub \ + helper_ret_ldub_mmu(env, taddr, oi, (uintptr_t)tb_ptr) +# define qemu_ld_leuw \ + helper_le_lduw_mmu(env, taddr, oi, (uintptr_t)tb_ptr) +# define qemu_ld_leul \ + helper_le_ldul_mmu(env, taddr, oi, (uintptr_t)tb_ptr) +# define qemu_ld_leq \ + helper_le_ldq_mmu(env, taddr, oi, (uintptr_t)tb_ptr) +# define qemu_ld_beuw \ + helper_be_lduw_mmu(env, taddr, oi, (uintptr_t)tb_ptr) +# define qemu_ld_beul \ + helper_be_ldul_mmu(env, taddr, oi, (uintptr_t)tb_ptr) +# define qemu_ld_beq \ + helper_be_ldq_mmu(env, taddr, oi, (uintptr_t)tb_ptr) +# define qemu_st_b(X) \ + helper_ret_stb_mmu(env, taddr, X, oi, (uintptr_t)tb_ptr) +# define qemu_st_lew(X) \ + helper_le_stw_mmu(env, taddr, X, oi, (uintptr_t)tb_ptr) +# define qemu_st_lel(X) \ + helper_le_stl_mmu(env, taddr, X, oi, (uintptr_t)tb_ptr) +# define qemu_st_leq(X) \ + helper_le_stq_mmu(env, taddr, X, oi, (uintptr_t)tb_ptr) +# define qemu_st_bew(X) \ + helper_be_stw_mmu(env, taddr, X, oi, (uintptr_t)tb_ptr) +# define qemu_st_bel(X) \ + helper_be_stl_mmu(env, taddr, X, oi, (uintptr_t)tb_ptr) +# define qemu_st_beq(X) \ + helper_be_stq_mmu(env, taddr, X, oi, (uintptr_t)tb_ptr) +#else +# define qemu_ld_ub ldub_p(g2h(taddr)) +# define qemu_ld_leuw lduw_le_p(g2h(taddr)) +# define qemu_ld_leul (uint32_t)ldl_le_p(g2h(taddr)) +# define qemu_ld_leq ldq_le_p(g2h(taddr)) +# define qemu_ld_beuw lduw_be_p(g2h(taddr)) +# define qemu_ld_beul (uint32_t)ldl_be_p(g2h(taddr)) +# define qemu_ld_beq ldq_be_p(g2h(taddr)) +# define qemu_st_b(X) stb_p(g2h(taddr), X) +# define qemu_st_lew(X) stw_le_p(g2h(taddr), X) +# define qemu_st_lel(X) stl_le_p(g2h(taddr), X) +# define qemu_st_leq(X) stq_le_p(g2h(taddr), X) +# define qemu_st_bew(X) stw_be_p(g2h(taddr), X) +# define qemu_st_bel(X) stl_be_p(g2h(taddr), X) +# define qemu_st_beq(X) stq_be_p(g2h(taddr), X) +#endif + +/* Interpret pseudo code in tb. */ +uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) +{ + tcg_target_ulong regs[TCG_TARGET_NB_REGS]; + long tcg_temps[CPU_TEMP_BUF_NLONGS]; + uintptr_t sp_value = (uintptr_t)(tcg_temps + CPU_TEMP_BUF_NLONGS); + uintptr_t ret = 0; + + regs[TCG_AREG0] = (tcg_target_ulong)env; + regs[TCG_REG_CALL_STACK] = sp_value; + tci_assert(tb_ptr); + + for (;;) { + TCGOpcode opc = tb_ptr[0]; +#if defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) + uint8_t op_size = tb_ptr[1]; + uint8_t *old_code_ptr = tb_ptr; +#endif + tcg_target_ulong t0; + tcg_target_ulong t1; + tcg_target_ulong t2; + tcg_target_ulong label; + TCGCond condition; + target_ulong taddr; + uint8_t tmp8; + uint16_t tmp16; + uint32_t tmp32; + uint64_t tmp64; +#if TCG_TARGET_REG_BITS == 32 + uint64_t v64; +#endif + TCGMemOpIdx oi; + +#if defined(GETPC) + tci_tb_ptr = (uintptr_t)tb_ptr; +#endif + + /* Skip opcode and size entry. */ + tb_ptr += 2; + + switch (opc) { + case INDEX_op_call: + t0 = tci_read_ri(regs, &tb_ptr); +#if TCG_TARGET_REG_BITS == 32 + tmp64 = ((helper_function)t0)(tci_read_reg(regs, TCG_REG_R0), + tci_read_reg(regs, TCG_REG_R1), + tci_read_reg(regs, TCG_REG_R2), + tci_read_reg(regs, TCG_REG_R3), + tci_read_reg(regs, TCG_REG_R5), + tci_read_reg(regs, TCG_REG_R6), + tci_read_reg(regs, TCG_REG_R7), + tci_read_reg(regs, TCG_REG_R8), + tci_read_reg(regs, TCG_REG_R9), + tci_read_reg(regs, TCG_REG_R10), + tci_read_reg(regs, TCG_REG_R11), + tci_read_reg(regs, TCG_REG_R12)); + tci_write_reg(regs, TCG_REG_R0, tmp64); + tci_write_reg(regs, TCG_REG_R1, tmp64 >> 32); +#else + tmp64 = ((helper_function)t0)(tci_read_reg(regs, TCG_REG_R0), + tci_read_reg(regs, TCG_REG_R1), + tci_read_reg(regs, TCG_REG_R2), + tci_read_reg(regs, TCG_REG_R3), + tci_read_reg(regs, TCG_REG_R5), + tci_read_reg(regs, TCG_REG_R6)); + tci_write_reg(regs, TCG_REG_R0, tmp64); +#endif + break; + case INDEX_op_br: + label = tci_read_label(&tb_ptr); + tci_assert(tb_ptr == old_code_ptr + op_size); + tb_ptr = (uint8_t *)label; + continue; + case INDEX_op_setcond_i32: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + condition = *tb_ptr++; + tci_write_reg32(regs, t0, tci_compare32(t1, t2, condition)); + break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_setcond2_i32: + t0 = *tb_ptr++; + tmp64 = tci_read_r64(regs, &tb_ptr); + v64 = tci_read_ri64(regs, &tb_ptr); + condition = *tb_ptr++; + tci_write_reg32(regs, t0, tci_compare64(tmp64, v64, condition)); + break; +#elif TCG_TARGET_REG_BITS == 64 + case INDEX_op_setcond_i64: + t0 = *tb_ptr++; + t1 = tci_read_r64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + condition = *tb_ptr++; + tci_write_reg64(regs, t0, tci_compare64(t1, t2, condition)); + break; +#endif + case INDEX_op_mov_i32: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1); + break; + case INDEX_op_movi_i32: + t0 = *tb_ptr++; + t1 = tci_read_i32(&tb_ptr); + tci_write_reg32(regs, t0, t1); + break; + + /* Load/store operations (32 bit). */ + + case INDEX_op_ld8u_i32: + t0 = *tb_ptr++; + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_write_reg8(regs, t0, *(uint8_t *)(t1 + t2)); + break; + case INDEX_op_ld8s_i32: + TODO(); + break; + case INDEX_op_ld16u_i32: + TODO(); + break; + case INDEX_op_ld16s_i32: + TODO(); + break; + case INDEX_op_ld_i32: + t0 = *tb_ptr++; + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_write_reg32(regs, t0, *(uint32_t *)(t1 + t2)); + break; + case INDEX_op_st8_i32: + t0 = tci_read_r8(regs, &tb_ptr); + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + *(uint8_t *)(t1 + t2) = t0; + break; + case INDEX_op_st16_i32: + t0 = tci_read_r16(regs, &tb_ptr); + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + *(uint16_t *)(t1 + t2) = t0; + break; + case INDEX_op_st_i32: + t0 = tci_read_r32(regs, &tb_ptr); + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_assert(t1 != sp_value || (int32_t)t2 < 0); + *(uint32_t *)(t1 + t2) = t0; + break; + + /* Arithmetic operations (32 bit). */ + + case INDEX_op_add_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 + t2); + break; + case INDEX_op_sub_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 - t2); + break; + case INDEX_op_mul_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 * t2); + break; +#if TCG_TARGET_HAS_div_i32 + case INDEX_op_div_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, (int32_t)t1 / (int32_t)t2); + break; + case INDEX_op_divu_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 / t2); + break; + case INDEX_op_rem_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, (int32_t)t1 % (int32_t)t2); + break; + case INDEX_op_remu_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 % t2); + break; +#elif TCG_TARGET_HAS_div2_i32 + case INDEX_op_div2_i32: + case INDEX_op_divu2_i32: + TODO(); + break; +#endif + case INDEX_op_and_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 & t2); + break; + case INDEX_op_or_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 | t2); + break; + case INDEX_op_xor_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 ^ t2); + break; + + /* Shift/rotate operations (32 bit). */ + + case INDEX_op_shl_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 << (t2 & 31)); + break; + case INDEX_op_shr_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1 >> (t2 & 31)); + break; + case INDEX_op_sar_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, ((int32_t)t1 >> (t2 & 31))); + break; +#if TCG_TARGET_HAS_rot_i32 + case INDEX_op_rotl_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, rol32(t1, t2 & 31)); + break; + case INDEX_op_rotr_i32: + t0 = *tb_ptr++; + t1 = tci_read_ri32(regs, &tb_ptr); + t2 = tci_read_ri32(regs, &tb_ptr); + tci_write_reg32(regs, t0, ror32(t1, t2 & 31)); + break; +#endif +#if TCG_TARGET_HAS_deposit_i32 + case INDEX_op_deposit_i32: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + t2 = tci_read_r32(regs, &tb_ptr); + tmp16 = *tb_ptr++; + tmp8 = *tb_ptr++; + tmp32 = (((1 << tmp8) - 1) << tmp16); + tci_write_reg32(regs, t0, (t1 & ~tmp32) | ((t2 << tmp16) & tmp32)); + break; +#endif + case INDEX_op_brcond_i32: + t0 = tci_read_r32(regs, &tb_ptr); + t1 = tci_read_ri32(regs, &tb_ptr); + condition = *tb_ptr++; + label = tci_read_label(&tb_ptr); + if (tci_compare32(t0, t1, condition)) { + tci_assert(tb_ptr == old_code_ptr + op_size); + tb_ptr = (uint8_t *)label; + continue; + } + break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_add2_i32: + t0 = *tb_ptr++; + t1 = *tb_ptr++; + tmp64 = tci_read_r64(regs, &tb_ptr); + tmp64 += tci_read_r64(regs, &tb_ptr); + tci_write_reg64(regs, t1, t0, tmp64); + break; + case INDEX_op_sub2_i32: + t0 = *tb_ptr++; + t1 = *tb_ptr++; + tmp64 = tci_read_r64(regs, &tb_ptr); + tmp64 -= tci_read_r64(regs, &tb_ptr); + tci_write_reg64(regs, t1, t0, tmp64); + break; + case INDEX_op_brcond2_i32: + tmp64 = tci_read_r64(regs, &tb_ptr); + v64 = tci_read_ri64(regs, &tb_ptr); + condition = *tb_ptr++; + label = tci_read_label(&tb_ptr); + if (tci_compare64(tmp64, v64, condition)) { + tci_assert(tb_ptr == old_code_ptr + op_size); + tb_ptr = (uint8_t *)label; + continue; + } + break; + case INDEX_op_mulu2_i32: + t0 = *tb_ptr++; + t1 = *tb_ptr++; + t2 = tci_read_r32(regs, &tb_ptr); + tmp64 = tci_read_r32(regs, &tb_ptr); + tci_write_reg64(regs, t1, t0, t2 * tmp64); + break; +#endif /* TCG_TARGET_REG_BITS == 32 */ +#if TCG_TARGET_HAS_ext8s_i32 + case INDEX_op_ext8s_i32: + t0 = *tb_ptr++; + t1 = tci_read_r8s(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_ext16s_i32 + case INDEX_op_ext16s_i32: + t0 = *tb_ptr++; + t1 = tci_read_r16s(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_ext8u_i32 + case INDEX_op_ext8u_i32: + t0 = *tb_ptr++; + t1 = tci_read_r8(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_ext16u_i32 + case INDEX_op_ext16u_i32: + t0 = *tb_ptr++; + t1 = tci_read_r16(regs, &tb_ptr); + tci_write_reg32(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_bswap16_i32 + case INDEX_op_bswap16_i32: + t0 = *tb_ptr++; + t1 = tci_read_r16(regs, &tb_ptr); + tci_write_reg32(regs, t0, bswap16(t1)); + break; +#endif +#if TCG_TARGET_HAS_bswap32_i32 + case INDEX_op_bswap32_i32: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + tci_write_reg32(regs, t0, bswap32(t1)); + break; +#endif +#if TCG_TARGET_HAS_not_i32 + case INDEX_op_not_i32: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + tci_write_reg32(regs, t0, ~t1); + break; +#endif +#if TCG_TARGET_HAS_neg_i32 + case INDEX_op_neg_i32: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + tci_write_reg32(regs, t0, -t1); + break; +#endif +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_mov_i64: + t0 = *tb_ptr++; + t1 = tci_read_r64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1); + break; + case INDEX_op_movi_i64: + t0 = *tb_ptr++; + t1 = tci_read_i64(&tb_ptr); + tci_write_reg64(regs, t0, t1); + break; + + /* Load/store operations (64 bit). */ + + case INDEX_op_ld8u_i64: + t0 = *tb_ptr++; + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_write_reg8(regs, t0, *(uint8_t *)(t1 + t2)); + break; + case INDEX_op_ld8s_i64: + TODO(); + break; + case INDEX_op_ld16u_i64: + t0 = *tb_ptr++; + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_write_reg16(regs, t0, *(uint16_t *)(t1 + t2)); + break; + case INDEX_op_ld16s_i64: + TODO(); + break; + case INDEX_op_ld32u_i64: + t0 = *tb_ptr++; + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_write_reg32(regs, t0, *(uint32_t *)(t1 + t2)); + break; + case INDEX_op_ld32s_i64: + t0 = *tb_ptr++; + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_write_reg32s(regs, t0, *(int32_t *)(t1 + t2)); + break; + case INDEX_op_ld_i64: + t0 = *tb_ptr++; + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_write_reg64(regs, t0, *(uint64_t *)(t1 + t2)); + break; + case INDEX_op_st8_i64: + t0 = tci_read_r8(regs, &tb_ptr); + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + *(uint8_t *)(t1 + t2) = t0; + break; + case INDEX_op_st16_i64: + t0 = tci_read_r16(regs, &tb_ptr); + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + *(uint16_t *)(t1 + t2) = t0; + break; + case INDEX_op_st32_i64: + t0 = tci_read_r32(regs, &tb_ptr); + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + *(uint32_t *)(t1 + t2) = t0; + break; + case INDEX_op_st_i64: + t0 = tci_read_r64(regs, &tb_ptr); + t1 = tci_read_r(regs, &tb_ptr); + t2 = tci_read_s32(&tb_ptr); + tci_assert(t1 != sp_value || (int32_t)t2 < 0); + *(uint64_t *)(t1 + t2) = t0; + break; + + /* Arithmetic operations (64 bit). */ + + case INDEX_op_add_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 + t2); + break; + case INDEX_op_sub_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 - t2); + break; + case INDEX_op_mul_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 * t2); + break; +#if TCG_TARGET_HAS_div_i64 + case INDEX_op_div_i64: + case INDEX_op_divu_i64: + case INDEX_op_rem_i64: + case INDEX_op_remu_i64: + TODO(); + break; +#elif TCG_TARGET_HAS_div2_i64 + case INDEX_op_div2_i64: + case INDEX_op_divu2_i64: + TODO(); + break; +#endif + case INDEX_op_and_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 & t2); + break; + case INDEX_op_or_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 | t2); + break; + case INDEX_op_xor_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 ^ t2); + break; + + /* Shift/rotate operations (64 bit). */ + + case INDEX_op_shl_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 << (t2 & 63)); + break; + case INDEX_op_shr_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1 >> (t2 & 63)); + break; + case INDEX_op_sar_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, ((int64_t)t1 >> (t2 & 63))); + break; +#if TCG_TARGET_HAS_rot_i64 + case INDEX_op_rotl_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, rol64(t1, t2 & 63)); + break; + case INDEX_op_rotr_i64: + t0 = *tb_ptr++; + t1 = tci_read_ri64(regs, &tb_ptr); + t2 = tci_read_ri64(regs, &tb_ptr); + tci_write_reg64(regs, t0, ror64(t1, t2 & 63)); + break; +#endif +#if TCG_TARGET_HAS_deposit_i64 + case INDEX_op_deposit_i64: + t0 = *tb_ptr++; + t1 = tci_read_r64(regs, &tb_ptr); + t2 = tci_read_r64(regs, &tb_ptr); + tmp16 = *tb_ptr++; + tmp8 = *tb_ptr++; + tmp64 = (((1ULL << tmp8) - 1) << tmp16); + tci_write_reg64(regs, t0, (t1 & ~tmp64) | ((t2 << tmp16) & tmp64)); + break; +#endif + case INDEX_op_brcond_i64: + t0 = tci_read_r64(regs, &tb_ptr); + t1 = tci_read_ri64(regs, &tb_ptr); + condition = *tb_ptr++; + label = tci_read_label(&tb_ptr); + if (tci_compare64(t0, t1, condition)) { + tci_assert(tb_ptr == old_code_ptr + op_size); + tb_ptr = (uint8_t *)label; + continue; + } + break; +#if TCG_TARGET_HAS_ext8u_i64 + case INDEX_op_ext8u_i64: + t0 = *tb_ptr++; + t1 = tci_read_r8(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_ext8s_i64 + case INDEX_op_ext8s_i64: + t0 = *tb_ptr++; + t1 = tci_read_r8s(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_ext16s_i64 + case INDEX_op_ext16s_i64: + t0 = *tb_ptr++; + t1 = tci_read_r16s(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_ext16u_i64 + case INDEX_op_ext16u_i64: + t0 = *tb_ptr++; + t1 = tci_read_r16(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1); + break; +#endif +#if TCG_TARGET_HAS_ext32s_i64 + case INDEX_op_ext32s_i64: +#endif + case INDEX_op_ext_i32_i64: + t0 = *tb_ptr++; + t1 = tci_read_r32s(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1); + break; +#if TCG_TARGET_HAS_ext32u_i64 + case INDEX_op_ext32u_i64: +#endif + case INDEX_op_extu_i32_i64: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + tci_write_reg64(regs, t0, t1); + break; +#if TCG_TARGET_HAS_bswap16_i64 + case INDEX_op_bswap16_i64: + t0 = *tb_ptr++; + t1 = tci_read_r16(regs, &tb_ptr); + tci_write_reg64(regs, t0, bswap16(t1)); + break; +#endif +#if TCG_TARGET_HAS_bswap32_i64 + case INDEX_op_bswap32_i64: + t0 = *tb_ptr++; + t1 = tci_read_r32(regs, &tb_ptr); + tci_write_reg64(regs, t0, bswap32(t1)); + break; +#endif +#if TCG_TARGET_HAS_bswap64_i64 + case INDEX_op_bswap64_i64: + t0 = *tb_ptr++; + t1 = tci_read_r64(regs, &tb_ptr); + tci_write_reg64(regs, t0, bswap64(t1)); + break; +#endif +#if TCG_TARGET_HAS_not_i64 + case INDEX_op_not_i64: + t0 = *tb_ptr++; + t1 = tci_read_r64(regs, &tb_ptr); + tci_write_reg64(regs, t0, ~t1); + break; +#endif +#if TCG_TARGET_HAS_neg_i64 + case INDEX_op_neg_i64: + t0 = *tb_ptr++; + t1 = tci_read_r64(regs, &tb_ptr); + tci_write_reg64(regs, t0, -t1); + break; +#endif +#endif /* TCG_TARGET_REG_BITS == 64 */ + + /* QEMU specific operations. */ + + case INDEX_op_exit_tb: + ret = *(uint64_t *)tb_ptr; + goto exit; + break; + case INDEX_op_goto_tb: + /* Jump address is aligned */ + tb_ptr = QEMU_ALIGN_PTR_UP(tb_ptr, 4); + t0 = atomic_read((int32_t *)tb_ptr); + tb_ptr += sizeof(int32_t); + tci_assert(tb_ptr == old_code_ptr + op_size); + tb_ptr += (int32_t)t0; + continue; + case INDEX_op_qemu_ld_i32: + t0 = *tb_ptr++; + taddr = tci_read_ulong(regs, &tb_ptr); + oi = tci_read_i(&tb_ptr); + switch (get_memop(oi) & (MO_BSWAP | MO_SSIZE)) { + case MO_UB: + tmp32 = qemu_ld_ub; + break; + case MO_SB: + tmp32 = (int8_t)qemu_ld_ub; + break; + case MO_LEUW: + tmp32 = qemu_ld_leuw; + break; + case MO_LESW: + tmp32 = (int16_t)qemu_ld_leuw; + break; + case MO_LEUL: + tmp32 = qemu_ld_leul; + break; + case MO_BEUW: + tmp32 = qemu_ld_beuw; + break; + case MO_BESW: + tmp32 = (int16_t)qemu_ld_beuw; + break; + case MO_BEUL: + tmp32 = qemu_ld_beul; + break; + default: + tcg_abort(); + } + tci_write_reg(regs, t0, tmp32); + break; + case INDEX_op_qemu_ld_i64: + t0 = *tb_ptr++; + if (TCG_TARGET_REG_BITS == 32) { + t1 = *tb_ptr++; + } + taddr = tci_read_ulong(regs, &tb_ptr); + oi = tci_read_i(&tb_ptr); + switch (get_memop(oi) & (MO_BSWAP | MO_SSIZE)) { + case MO_UB: + tmp64 = qemu_ld_ub; + break; + case MO_SB: + tmp64 = (int8_t)qemu_ld_ub; + break; + case MO_LEUW: + tmp64 = qemu_ld_leuw; + break; + case MO_LESW: + tmp64 = (int16_t)qemu_ld_leuw; + break; + case MO_LEUL: + tmp64 = qemu_ld_leul; + break; + case MO_LESL: + tmp64 = (int32_t)qemu_ld_leul; + break; + case MO_LEQ: + tmp64 = qemu_ld_leq; + break; + case MO_BEUW: + tmp64 = qemu_ld_beuw; + break; + case MO_BESW: + tmp64 = (int16_t)qemu_ld_beuw; + break; + case MO_BEUL: + tmp64 = qemu_ld_beul; + break; + case MO_BESL: + tmp64 = (int32_t)qemu_ld_beul; + break; + case MO_BEQ: + tmp64 = qemu_ld_beq; + break; + default: + tcg_abort(); + } + tci_write_reg(regs, t0, tmp64); + if (TCG_TARGET_REG_BITS == 32) { + tci_write_reg(regs, t1, tmp64 >> 32); + } + break; + case INDEX_op_qemu_st_i32: + t0 = tci_read_r(regs, &tb_ptr); + taddr = tci_read_ulong(regs, &tb_ptr); + oi = tci_read_i(&tb_ptr); + switch (get_memop(oi) & (MO_BSWAP | MO_SIZE)) { + case MO_UB: + qemu_st_b(t0); + break; + case MO_LEUW: + qemu_st_lew(t0); + break; + case MO_LEUL: + qemu_st_lel(t0); + break; + case MO_BEUW: + qemu_st_bew(t0); + break; + case MO_BEUL: + qemu_st_bel(t0); + break; + default: + tcg_abort(); + } + break; + case INDEX_op_qemu_st_i64: + tmp64 = tci_read_r64(regs, &tb_ptr); + taddr = tci_read_ulong(regs, &tb_ptr); + oi = tci_read_i(&tb_ptr); + switch (get_memop(oi) & (MO_BSWAP | MO_SIZE)) { + case MO_UB: + qemu_st_b(tmp64); + break; + case MO_LEUW: + qemu_st_lew(tmp64); + break; + case MO_LEUL: + qemu_st_lel(tmp64); + break; + case MO_LEQ: + qemu_st_leq(tmp64); + break; + case MO_BEUW: + qemu_st_bew(tmp64); + break; + case MO_BEUL: + qemu_st_bel(tmp64); + break; + case MO_BEQ: + qemu_st_beq(tmp64); + break; + default: + tcg_abort(); + } + break; + case INDEX_op_mb: + /* Ensure ordering for all kinds */ + smp_mb(); + break; + default: + TODO(); + break; + } + tci_assert(tb_ptr == old_code_ptr + op_size); + } +exit: + return ret; +} diff --git a/qemu/tcg/tci/README b/qemu/tcg/tci/README new file mode 100644 index 0000000000..386c3c7507 --- /dev/null +++ b/qemu/tcg/tci/README @@ -0,0 +1,130 @@ +TCG Interpreter (TCI) - Copyright (c) 2011 Stefan Weil. + +This file is released under the BSD license. + +1) Introduction + +TCG (Tiny Code Generator) is a code generator which translates +code fragments ("basic blocks") from target code (any of the +targets supported by QEMU) to a code representation which +can be run on a host. + +QEMU can create native code for some hosts (arm, i386, ia64, ppc, ppc64, +s390, sparc, x86_64). For others, unofficial host support was written. + +By adding a code generator for a virtual machine and using an +interpreter for the generated bytecode, it is possible to +support (almost) any host. + +This is what TCI (Tiny Code Interpreter) does. + +2) Implementation + +Like each TCG host frontend, TCI implements the code generator in +tcg-target.inc.c, tcg-target.h. Both files are in directory tcg/tci. + +The additional file tcg/tci.c adds the interpreter. + +The bytecode consists of opcodes (same numeric values as those used by +TCG), command length and arguments of variable size and number. + +3) Usage + +For hosts without native TCG, the interpreter TCI must be enabled by + + configure --enable-tcg-interpreter + +If configure is called without --enable-tcg-interpreter, it will +suggest using this option. Setting it automatically would need +additional code in configure which must be fixed when new native TCG +implementations are added. + +System emulation should work on any 32 or 64 bit host. +User mode emulation might work. Maybe a new linker script (*.ld) +is needed. Byte order might be wrong (on big endian hosts) +and need fixes in configure. + +For hosts with native TCG, the interpreter TCI can be enabled by + + configure --enable-tcg-interpreter + +The only difference from running QEMU with TCI to running without TCI +should be speed. Especially during development of TCI, it was very +useful to compare runs with and without TCI. Create /tmp/qemu.log by + + qemu-system-i386 -d in_asm,op_opt,cpu -D /tmp/qemu.log -singlestep + +once with interpreter and once without interpreter and compare the resulting +qemu.log files. This is also useful to see the effects of additional +registers or additional opcodes (it is easy to modify the virtual machine). +It can also be used to verify native TCGs. + +Hosts with native TCG can also enable TCI by claiming to be unsupported: + + configure --cpu=unknown --enable-tcg-interpreter + +configure then no longer uses the native linker script (*.ld) for +user mode emulation. + + +4) Status + +TCI needs special implementation for 32 and 64 bit host, 32 and 64 bit target, +host and target with same or different endianness. + + | host (le) host (be) + | 32 64 32 64 +------------+------------------------------------------------------------ +target (le) | s0, u0 s1, u1 s?, u? s?, u? +32 bit | + | +target (le) | sc, uc s1, u1 s?, u? s?, u? +64 bit | + | +target (be) | sc, u0 sc, uc s?, u? s?, u? +32 bit | + | +target (be) | sc, uc sc, uc s?, u? s?, u? +64 bit | + | + +System emulation +s? = untested +sc = compiles +s0 = bios works +s1 = grub works +s2 = Linux boots + +Linux user mode emulation +u? = untested +uc = compiles +u0 = static hello works +u1 = linux-user-test works + +5) Todo list + +* TCI is not widely tested. It was written and tested on a x86_64 host + running i386 and x86_64 system emulation and Linux user mode. + A cross compiled QEMU for i386 host also works with the same basic tests. + A cross compiled QEMU for mipsel host works, too. It is terribly slow + because I run it in a mips malta emulation, so it is an interpreted + emulation in an emulation. + A cross compiled QEMU for arm host works (tested with pc bios). + A cross compiled QEMU for ppc host works at least partially: + i386-linux-user/qemu-i386 can run a simple hello-world program + (tested in a ppc emulation). + +* Some TCG opcodes are either missing in the code generator and/or + in the interpreter. These opcodes raise a runtime exception, so it is + possible to see where code must be added. + +* The pseudo code is not optimized and still ugly. For hosts with special + alignment requirements, it needs some fixes (maybe aligned bytecode + would also improve speed for hosts which support byte alignment). + +* A better disassembler for the pseudo code would be nice (a very primitive + disassembler is included in tcg-target.inc.c). + +* It might be useful to have a runtime option which selects the native TCG + or TCI, so QEMU would have to include two TCGs. Today, selecting TCI + is a configure option, so you need two compilations of QEMU. diff --git a/qemu/tcg/tci/tcg-target.h b/qemu/tcg/tci/tcg-target.h new file mode 100644 index 0000000000..8b90ab71cb --- /dev/null +++ b/qemu/tcg/tci/tcg-target.h @@ -0,0 +1,213 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009, 2011 Stefan Weil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * This code implements a TCG which does not generate machine code for some + * real target machine but which generates virtual machine code for an + * interpreter. Interpreted pseudo code is slow, but it works on any host. + * + * Some remarks might help in understanding the code: + * + * "target" or "TCG target" is the machine which runs the generated code. + * This is different to the usual meaning in QEMU where "target" is the + * emulated machine. So normally QEMU host is identical to TCG target. + * Here the TCG target is a virtual machine, but this virtual machine must + * use the same word size like the real machine. + * Therefore, we need both 32 and 64 bit virtual machines (interpreter). + */ + +#ifndef TCG_TARGET_H +#define TCG_TARGET_H + +#define TCG_TARGET_INTERPRETER 1 +#define TCG_TARGET_INSN_UNIT_SIZE 1 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32 + +#if UINTPTR_MAX == UINT32_MAX +# define TCG_TARGET_REG_BITS 32 +#elif UINTPTR_MAX == UINT64_MAX +# define TCG_TARGET_REG_BITS 64 +#else +# error Unknown pointer size for tci target +#endif + +#ifdef CONFIG_DEBUG_TCG +/* Enable debug output. */ +#define CONFIG_DEBUG_TCG_INTERPRETER +#endif + +/* Optional instructions. */ + +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 1 +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_andc_i32 0 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 0 +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_extract2_i32 0 +#define TCG_TARGET_HAS_eqv_i32 0 +#define TCG_TARGET_HAS_nand_i32 0 +#define TCG_TARGET_HAS_nor_i32 0 +#define TCG_TARGET_HAS_clz_i32 0 +#define TCG_TARGET_HAS_ctz_i32 0 +#define TCG_TARGET_HAS_ctpop_i32 0 +#define TCG_TARGET_HAS_neg_i32 1 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_orc_i32 0 +#define TCG_TARGET_HAS_rot_i32 1 +#define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_goto_ptr 0 +#define TCG_TARGET_HAS_direct_jump 1 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_extrl_i64_i32 0 +#define TCG_TARGET_HAS_extrh_i64_i32 0 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extract2_i64 0 +#define TCG_TARGET_HAS_div_i64 0 +#define TCG_TARGET_HAS_rem_i64 0 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_andc_i64 0 +#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 0 +#define TCG_TARGET_HAS_clz_i64 0 +#define TCG_TARGET_HAS_ctz_i64 0 +#define TCG_TARGET_HAS_ctpop_i64 0 +#define TCG_TARGET_HAS_neg_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_orc_i64 0 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 0 +#define TCG_TARGET_HAS_mulsh_i64 0 +#else +#define TCG_TARGET_HAS_mulu2_i32 1 +#endif /* TCG_TARGET_REG_BITS == 64 */ + +/* Number of registers available. + For 32 bit hosts, we need more than 8 registers (call arguments). */ +/* #define TCG_TARGET_NB_REGS 8 */ +#define TCG_TARGET_NB_REGS 16 +/* #define TCG_TARGET_NB_REGS 32 */ + +/* List of registers which are used by TCG. */ +typedef enum { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, +#if TCG_TARGET_NB_REGS >= 16 + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, +#if TCG_TARGET_NB_REGS >= 32 + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, +#endif +#endif + /* Special value UINT8_MAX is used by TCI to encode constant values. */ + TCG_CONST = UINT8_MAX +} TCGReg; + +#define TCG_AREG0 (TCG_TARGET_NB_REGS - 2) + +/* Used for function call generation. */ +#define TCG_REG_CALL_STACK (TCG_TARGET_NB_REGS - 1) +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_STACK_ALIGN 16 + +void tci_disas(uint8_t opc); + +#define HAVE_TCG_QEMU_TB_EXEC + +static inline void flush_icache_range(uintptr_t start, uintptr_t stop) +{ +} + +/* We could notice __i386__ or __s390x__ and reduce the barriers depending + on the host. But if you want performance, you use the normal backend. + We prefer consistency across hosts on this. */ +#define TCG_TARGET_DEFAULT_MO (0) + +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 + +static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, + uintptr_t jmp_addr, uintptr_t addr) +{ + /* patch the branch destination */ + atomic_set((int32_t *)jmp_addr, addr - (jmp_addr + 4)); + /* no need to flush icache explicitly */ +} + +#endif /* TCG_TARGET_H */ diff --git a/qemu/tcg/tci/tcg-target.inc.c b/qemu/tcg/tci/tcg-target.inc.c new file mode 100644 index 0000000000..ab3114532f --- /dev/null +++ b/qemu/tcg/tci/tcg-target.inc.c @@ -0,0 +1,896 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009, 2011 Stefan Weil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* TODO list: + * - See TODO comments in code. + */ + +/* Marker for missing code. */ +#define TODO() \ + do { \ + fprintf(stderr, "TODO %s:%u: %s()\n", \ + __FILE__, __LINE__, __func__); \ + tcg_abort(); \ + } while (0) + +/* Bitfield n...m (in 32 bit value). */ +#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) + +/* Macros used in tcg_target_op_defs. */ +#define R "r" +#define RI "ri" +#if TCG_TARGET_REG_BITS == 32 +# define R64 "r", "r" +#else +# define R64 "r" +#endif +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS +# define L "L", "L" +# define S "S", "S" +#else +# define L "L" +# define S "S" +#endif + +/* TODO: documentation. */ +static const TCGTargetOpDef tcg_target_op_defs[] = { + { INDEX_op_exit_tb, { NULL } }, + { INDEX_op_goto_tb, { NULL } }, + { INDEX_op_br, { NULL } }, + + { INDEX_op_ld8u_i32, { R, R } }, + { INDEX_op_ld8s_i32, { R, R } }, + { INDEX_op_ld16u_i32, { R, R } }, + { INDEX_op_ld16s_i32, { R, R } }, + { INDEX_op_ld_i32, { R, R } }, + { INDEX_op_st8_i32, { R, R } }, + { INDEX_op_st16_i32, { R, R } }, + { INDEX_op_st_i32, { R, R } }, + + { INDEX_op_add_i32, { R, RI, RI } }, + { INDEX_op_sub_i32, { R, RI, RI } }, + { INDEX_op_mul_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_div_i32 + { INDEX_op_div_i32, { R, R, R } }, + { INDEX_op_divu_i32, { R, R, R } }, + { INDEX_op_rem_i32, { R, R, R } }, + { INDEX_op_remu_i32, { R, R, R } }, +#elif TCG_TARGET_HAS_div2_i32 + { INDEX_op_div2_i32, { R, R, "0", "1", R } }, + { INDEX_op_divu2_i32, { R, R, "0", "1", R } }, +#endif + /* TODO: Does R, RI, RI result in faster code than R, R, RI? + If both operands are constants, we can optimize. */ + { INDEX_op_and_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_andc_i32 + { INDEX_op_andc_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_eqv_i32 + { INDEX_op_eqv_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nand_i32 + { INDEX_op_nand_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nor_i32 + { INDEX_op_nor_i32, { R, RI, RI } }, +#endif + { INDEX_op_or_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_orc_i32 + { INDEX_op_orc_i32, { R, RI, RI } }, +#endif + { INDEX_op_xor_i32, { R, RI, RI } }, + { INDEX_op_shl_i32, { R, RI, RI } }, + { INDEX_op_shr_i32, { R, RI, RI } }, + { INDEX_op_sar_i32, { R, RI, RI } }, +#if TCG_TARGET_HAS_rot_i32 + { INDEX_op_rotl_i32, { R, RI, RI } }, + { INDEX_op_rotr_i32, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_deposit_i32 + { INDEX_op_deposit_i32, { R, "0", R } }, +#endif + + { INDEX_op_brcond_i32, { R, RI } }, + + { INDEX_op_setcond_i32, { R, R, RI } }, +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_setcond_i64, { R, R, RI } }, +#endif /* TCG_TARGET_REG_BITS == 64 */ + +#if TCG_TARGET_REG_BITS == 32 + /* TODO: Support R, R, R, R, RI, RI? Will it be faster? */ + { INDEX_op_add2_i32, { R, R, R, R, R, R } }, + { INDEX_op_sub2_i32, { R, R, R, R, R, R } }, + { INDEX_op_brcond2_i32, { R, R, RI, RI } }, + { INDEX_op_mulu2_i32, { R, R, R, R } }, + { INDEX_op_setcond2_i32, { R, R, R, RI, RI } }, +#endif + +#if TCG_TARGET_HAS_not_i32 + { INDEX_op_not_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_neg_i32 + { INDEX_op_neg_i32, { R, R } }, +#endif + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_ld8u_i64, { R, R } }, + { INDEX_op_ld8s_i64, { R, R } }, + { INDEX_op_ld16u_i64, { R, R } }, + { INDEX_op_ld16s_i64, { R, R } }, + { INDEX_op_ld32u_i64, { R, R } }, + { INDEX_op_ld32s_i64, { R, R } }, + { INDEX_op_ld_i64, { R, R } }, + + { INDEX_op_st8_i64, { R, R } }, + { INDEX_op_st16_i64, { R, R } }, + { INDEX_op_st32_i64, { R, R } }, + { INDEX_op_st_i64, { R, R } }, + + { INDEX_op_add_i64, { R, RI, RI } }, + { INDEX_op_sub_i64, { R, RI, RI } }, + { INDEX_op_mul_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_div_i64 + { INDEX_op_div_i64, { R, R, R } }, + { INDEX_op_divu_i64, { R, R, R } }, + { INDEX_op_rem_i64, { R, R, R } }, + { INDEX_op_remu_i64, { R, R, R } }, +#elif TCG_TARGET_HAS_div2_i64 + { INDEX_op_div2_i64, { R, R, "0", "1", R } }, + { INDEX_op_divu2_i64, { R, R, "0", "1", R } }, +#endif + { INDEX_op_and_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_andc_i64 + { INDEX_op_andc_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_eqv_i64 + { INDEX_op_eqv_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nand_i64 + { INDEX_op_nand_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_nor_i64 + { INDEX_op_nor_i64, { R, RI, RI } }, +#endif + { INDEX_op_or_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_orc_i64 + { INDEX_op_orc_i64, { R, RI, RI } }, +#endif + { INDEX_op_xor_i64, { R, RI, RI } }, + { INDEX_op_shl_i64, { R, RI, RI } }, + { INDEX_op_shr_i64, { R, RI, RI } }, + { INDEX_op_sar_i64, { R, RI, RI } }, +#if TCG_TARGET_HAS_rot_i64 + { INDEX_op_rotl_i64, { R, RI, RI } }, + { INDEX_op_rotr_i64, { R, RI, RI } }, +#endif +#if TCG_TARGET_HAS_deposit_i64 + { INDEX_op_deposit_i64, { R, "0", R } }, +#endif + { INDEX_op_brcond_i64, { R, RI } }, + +#if TCG_TARGET_HAS_ext8s_i64 + { INDEX_op_ext8s_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16s_i64 + { INDEX_op_ext16s_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext32s_i64 + { INDEX_op_ext32s_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext8u_i64 + { INDEX_op_ext8u_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16u_i64 + { INDEX_op_ext16u_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext32u_i64 + { INDEX_op_ext32u_i64, { R, R } }, +#endif + { INDEX_op_ext_i32_i64, { R, R } }, + { INDEX_op_extu_i32_i64, { R, R } }, +#if TCG_TARGET_HAS_bswap16_i64 + { INDEX_op_bswap16_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_bswap32_i64 + { INDEX_op_bswap32_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_bswap64_i64 + { INDEX_op_bswap64_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_not_i64 + { INDEX_op_not_i64, { R, R } }, +#endif +#if TCG_TARGET_HAS_neg_i64 + { INDEX_op_neg_i64, { R, R } }, +#endif +#endif /* TCG_TARGET_REG_BITS == 64 */ + + { INDEX_op_qemu_ld_i32, { R, L } }, + { INDEX_op_qemu_ld_i64, { R64, L } }, + + { INDEX_op_qemu_st_i32, { R, S } }, + { INDEX_op_qemu_st_i64, { R64, S } }, + +#if TCG_TARGET_HAS_ext8s_i32 + { INDEX_op_ext8s_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16s_i32 + { INDEX_op_ext16s_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext8u_i32 + { INDEX_op_ext8u_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_ext16u_i32 + { INDEX_op_ext16u_i32, { R, R } }, +#endif + +#if TCG_TARGET_HAS_bswap16_i32 + { INDEX_op_bswap16_i32, { R, R } }, +#endif +#if TCG_TARGET_HAS_bswap32_i32 + { INDEX_op_bswap32_i32, { R, R } }, +#endif + + { INDEX_op_mb, { } }, + { -1 }, +}; + +static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op) +{ + int i, n = ARRAY_SIZE(tcg_target_op_defs); + + for (i = 0; i < n; ++i) { + if (tcg_target_op_defs[i].op == op) { + return &tcg_target_op_defs[i]; + } + } + return NULL; +} + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, +#if 0 /* used for TCG_REG_CALL_STACK */ + TCG_REG_R4, +#endif + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, +#if TCG_TARGET_NB_REGS >= 16 + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, +#endif +}; + +#if MAX_OPC_PARAM_IARGS != 6 +# error Fix needed, number of supported input arguments changed! +#endif + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, +#if 0 /* used for TCG_REG_CALL_STACK */ + TCG_REG_R4, +#endif + TCG_REG_R5, + TCG_REG_R6, +#if TCG_TARGET_REG_BITS == 32 + /* 32 bit hosts need 2 * MAX_OPC_PARAM_IARGS registers. */ + TCG_REG_R7, +#if TCG_TARGET_NB_REGS >= 16 + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, +#else +# error Too few input registers available +#endif +#endif +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_R0, +#if TCG_TARGET_REG_BITS == 32 + TCG_REG_R1 +#endif +}; + +#ifdef CONFIG_DEBUG_TCG +static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r00", + "r01", + "r02", + "r03", + "r04", + "r05", + "r06", + "r07", +#if TCG_TARGET_NB_REGS >= 16 + "r08", + "r09", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", +#if TCG_TARGET_NB_REGS >= 32 + "r16", + "r17", + "r18", + "r19", + "r20", + "r21", + "r22", + "r23", + "r24", + "r25", + "r26", + "r27", + "r28", + "r29", + "r30", + "r31" +#endif +#endif +}; +#endif + +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + /* tcg_out_reloc always uses the same type, addend. */ + tcg_debug_assert(type == sizeof(tcg_target_long)); + tcg_debug_assert(addend == 0); + tcg_debug_assert(value != 0); + if (TCG_TARGET_REG_BITS == 32) { + tcg_patch32(code_ptr, value); + } else { + tcg_patch64(code_ptr, value); + } + return true; +} + +/* Parse target specific constraints. */ +static const char *target_parse_constraint(TCGArgConstraint *ct, + const char *ct_str, TCGType type) +{ + switch (*ct_str++) { + case 'r': + case 'L': /* qemu_ld constraint */ + case 'S': /* qemu_st constraint */ + ct->ct |= TCG_CT_REG; + ct->u.regs = BIT(TCG_TARGET_NB_REGS) - 1; + break; + default: + return NULL; + } + return ct_str; +} + +#if defined(CONFIG_DEBUG_TCG_INTERPRETER) +/* Show current bytecode. Used by tcg interpreter. */ +void tci_disas(uint8_t opc) +{ + const TCGOpDef *def = &tcg_op_defs[opc]; + fprintf(stderr, "TCG %s %u, %u, %u\n", + def->name, def->nb_oargs, def->nb_iargs, def->nb_cargs); +} +#endif + +/* Write value (native size). */ +static void tcg_out_i(TCGContext *s, tcg_target_ulong v) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out32(s, v); + } else { + tcg_out64(s, v); + } +} + +/* Write opcode. */ +static void tcg_out_op_t(TCGContext *s, TCGOpcode op) +{ + tcg_out8(s, op); + tcg_out8(s, 0); +} + +/* Write register. */ +static void tcg_out_r(TCGContext *s, TCGArg t0) +{ + tcg_debug_assert(t0 < TCG_TARGET_NB_REGS); + tcg_out8(s, t0); +} + +/* Write register or constant (native size). */ +static void tcg_out_ri(TCGContext *s, int const_arg, TCGArg arg) +{ + if (const_arg) { + tcg_debug_assert(const_arg == 1); + tcg_out8(s, TCG_CONST); + tcg_out_i(s, arg); + } else { + tcg_out_r(s, arg); + } +} + +/* Write register or constant (32 bit). */ +static void tcg_out_ri32(TCGContext *s, int const_arg, TCGArg arg) +{ + if (const_arg) { + tcg_debug_assert(const_arg == 1); + tcg_out8(s, TCG_CONST); + tcg_out32(s, arg); + } else { + tcg_out_r(s, arg); + } +} + +#if TCG_TARGET_REG_BITS == 64 +/* Write register or constant (64 bit). */ +static void tcg_out_ri64(TCGContext *s, int const_arg, TCGArg arg) +{ + if (const_arg) { + tcg_debug_assert(const_arg == 1); + tcg_out8(s, TCG_CONST); + tcg_out64(s, arg); + } else { + tcg_out_r(s, arg); + } +} +#endif + +/* Write label. */ +static void tci_out_label(TCGContext *s, TCGLabel *label) +{ + if (label->has_value) { + tcg_out_i(s, label->u.value); + tcg_debug_assert(label->u.value); + } else { + tcg_out_reloc(s, s->code_ptr, sizeof(tcg_target_ulong), label, 0); + s->code_ptr += sizeof(tcg_target_ulong); + } +} + +static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, + intptr_t arg2) +{ + uint8_t *old_code_ptr = s->code_ptr; + if (type == TCG_TYPE_I32) { + tcg_out_op_t(s, INDEX_op_ld_i32); + tcg_out_r(s, ret); + tcg_out_r(s, arg1); + tcg_out32(s, arg2); + } else { + tcg_debug_assert(type == TCG_TYPE_I64); +#if TCG_TARGET_REG_BITS == 64 + tcg_out_op_t(s, INDEX_op_ld_i64); + tcg_out_r(s, ret); + tcg_out_r(s, arg1); + tcg_debug_assert(arg2 == (int32_t)arg2); + tcg_out32(s, arg2); +#else + TODO(); +#endif + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +{ + uint8_t *old_code_ptr = s->code_ptr; + tcg_debug_assert(ret != arg); +#if TCG_TARGET_REG_BITS == 32 + tcg_out_op_t(s, INDEX_op_mov_i32); +#else + tcg_out_op_t(s, INDEX_op_mov_i64); +#endif + tcg_out_r(s, ret); + tcg_out_r(s, arg); + old_code_ptr[1] = s->code_ptr - old_code_ptr; + return true; +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg t0, tcg_target_long arg) +{ + uint8_t *old_code_ptr = s->code_ptr; + uint32_t arg32 = arg; + if (type == TCG_TYPE_I32 || arg == arg32) { + tcg_out_op_t(s, INDEX_op_movi_i32); + tcg_out_r(s, t0); + tcg_out32(s, arg32); + } else { + tcg_debug_assert(type == TCG_TYPE_I64); +#if TCG_TARGET_REG_BITS == 64 + tcg_out_op_t(s, INDEX_op_movi_i64); + tcg_out_r(s, t0); + tcg_out64(s, arg); +#else + TODO(); +#endif + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static inline void tcg_out_call(TCGContext *s, tcg_insn_unit *arg) +{ + uint8_t *old_code_ptr = s->code_ptr; + tcg_out_op_t(s, INDEX_op_call); + tcg_out_ri(s, 1, (uintptr_t)arg); + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ + uint8_t *old_code_ptr = s->code_ptr; + + tcg_out_op_t(s, opc); + + switch (opc) { + case INDEX_op_exit_tb: + tcg_out64(s, args[0]); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_insn_offset) { + /* Direct jump method. */ + /* Align for atomic patching and thread safety */ + s->code_ptr = QEMU_ALIGN_PTR_UP(s->code_ptr, 4); + s->tb_jmp_insn_offset[args[0]] = tcg_current_code_size(s); + tcg_out32(s, 0); + } else { + /* Indirect jump method. */ + TODO(); + } + set_jmp_reset_offset(s, args[0]); + break; + case INDEX_op_br: + tci_out_label(s, arg_label(args[0])); + break; + case INDEX_op_setcond_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + tcg_out8(s, args[3]); /* condition */ + break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_setcond2_i32: + /* setcond2_i32 cond, t0, t1_low, t1_high, t2_low, t2_high */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_out_ri32(s, const_args[3], args[3]); + tcg_out_ri32(s, const_args[4], args[4]); + tcg_out8(s, args[5]); /* condition */ + break; +#elif TCG_TARGET_REG_BITS == 64 + case INDEX_op_setcond_i64: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_ri64(s, const_args[2], args[2]); + tcg_out8(s, args[3]); /* condition */ + break; +#endif + case INDEX_op_ld8u_i32: + case INDEX_op_ld8s_i32: + case INDEX_op_ld16u_i32: + case INDEX_op_ld16s_i32: + case INDEX_op_ld_i32: + case INDEX_op_st8_i32: + case INDEX_op_st16_i32: + case INDEX_op_st_i32: + case INDEX_op_ld8u_i64: + case INDEX_op_ld8s_i64: + case INDEX_op_ld16u_i64: + case INDEX_op_ld16s_i64: + case INDEX_op_ld32u_i64: + case INDEX_op_ld32s_i64: + case INDEX_op_ld_i64: + case INDEX_op_st8_i64: + case INDEX_op_st16_i64: + case INDEX_op_st32_i64: + case INDEX_op_st_i64: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_debug_assert(args[2] == (int32_t)args[2]); + tcg_out32(s, args[2]); + break; + case INDEX_op_add_i32: + case INDEX_op_sub_i32: + case INDEX_op_mul_i32: + case INDEX_op_and_i32: + case INDEX_op_andc_i32: /* Optional (TCG_TARGET_HAS_andc_i32). */ + case INDEX_op_eqv_i32: /* Optional (TCG_TARGET_HAS_eqv_i32). */ + case INDEX_op_nand_i32: /* Optional (TCG_TARGET_HAS_nand_i32). */ + case INDEX_op_nor_i32: /* Optional (TCG_TARGET_HAS_nor_i32). */ + case INDEX_op_or_i32: + case INDEX_op_orc_i32: /* Optional (TCG_TARGET_HAS_orc_i32). */ + case INDEX_op_xor_i32: + case INDEX_op_shl_i32: + case INDEX_op_shr_i32: + case INDEX_op_sar_i32: + case INDEX_op_rotl_i32: /* Optional (TCG_TARGET_HAS_rot_i32). */ + case INDEX_op_rotr_i32: /* Optional (TCG_TARGET_HAS_rot_i32). */ + tcg_out_r(s, args[0]); + tcg_out_ri32(s, const_args[1], args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + break; + case INDEX_op_deposit_i32: /* Optional (TCG_TARGET_HAS_deposit_i32). */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_debug_assert(args[3] <= UINT8_MAX); + tcg_out8(s, args[3]); + tcg_debug_assert(args[4] <= UINT8_MAX); + tcg_out8(s, args[4]); + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_add_i64: + case INDEX_op_sub_i64: + case INDEX_op_mul_i64: + case INDEX_op_and_i64: + case INDEX_op_andc_i64: /* Optional (TCG_TARGET_HAS_andc_i64). */ + case INDEX_op_eqv_i64: /* Optional (TCG_TARGET_HAS_eqv_i64). */ + case INDEX_op_nand_i64: /* Optional (TCG_TARGET_HAS_nand_i64). */ + case INDEX_op_nor_i64: /* Optional (TCG_TARGET_HAS_nor_i64). */ + case INDEX_op_or_i64: + case INDEX_op_orc_i64: /* Optional (TCG_TARGET_HAS_orc_i64). */ + case INDEX_op_xor_i64: + case INDEX_op_shl_i64: + case INDEX_op_shr_i64: + case INDEX_op_sar_i64: + case INDEX_op_rotl_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ + case INDEX_op_rotr_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */ + tcg_out_r(s, args[0]); + tcg_out_ri64(s, const_args[1], args[1]); + tcg_out_ri64(s, const_args[2], args[2]); + break; + case INDEX_op_deposit_i64: /* Optional (TCG_TARGET_HAS_deposit_i64). */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_debug_assert(args[3] <= UINT8_MAX); + tcg_out8(s, args[3]); + tcg_debug_assert(args[4] <= UINT8_MAX); + tcg_out8(s, args[4]); + break; + case INDEX_op_div_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + case INDEX_op_divu_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + case INDEX_op_rem_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + case INDEX_op_remu_i64: /* Optional (TCG_TARGET_HAS_div_i64). */ + TODO(); + break; + case INDEX_op_div2_i64: /* Optional (TCG_TARGET_HAS_div2_i64). */ + case INDEX_op_divu2_i64: /* Optional (TCG_TARGET_HAS_div2_i64). */ + TODO(); + break; + case INDEX_op_brcond_i64: + tcg_out_r(s, args[0]); + tcg_out_ri64(s, const_args[1], args[1]); + tcg_out8(s, args[2]); /* condition */ + tci_out_label(s, arg_label(args[3])); + break; + case INDEX_op_bswap16_i64: /* Optional (TCG_TARGET_HAS_bswap16_i64). */ + case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ + case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ + case INDEX_op_not_i64: /* Optional (TCG_TARGET_HAS_not_i64). */ + case INDEX_op_neg_i64: /* Optional (TCG_TARGET_HAS_neg_i64). */ + case INDEX_op_ext8s_i64: /* Optional (TCG_TARGET_HAS_ext8s_i64). */ + case INDEX_op_ext8u_i64: /* Optional (TCG_TARGET_HAS_ext8u_i64). */ + case INDEX_op_ext16s_i64: /* Optional (TCG_TARGET_HAS_ext16s_i64). */ + case INDEX_op_ext16u_i64: /* Optional (TCG_TARGET_HAS_ext16u_i64). */ + case INDEX_op_ext32s_i64: /* Optional (TCG_TARGET_HAS_ext32s_i64). */ + case INDEX_op_ext32u_i64: /* Optional (TCG_TARGET_HAS_ext32u_i64). */ + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: +#endif /* TCG_TARGET_REG_BITS == 64 */ + case INDEX_op_neg_i32: /* Optional (TCG_TARGET_HAS_neg_i32). */ + case INDEX_op_not_i32: /* Optional (TCG_TARGET_HAS_not_i32). */ + case INDEX_op_ext8s_i32: /* Optional (TCG_TARGET_HAS_ext8s_i32). */ + case INDEX_op_ext16s_i32: /* Optional (TCG_TARGET_HAS_ext16s_i32). */ + case INDEX_op_ext8u_i32: /* Optional (TCG_TARGET_HAS_ext8u_i32). */ + case INDEX_op_ext16u_i32: /* Optional (TCG_TARGET_HAS_ext16u_i32). */ + case INDEX_op_bswap16_i32: /* Optional (TCG_TARGET_HAS_bswap16_i32). */ + case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + break; + case INDEX_op_div_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + case INDEX_op_divu_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + case INDEX_op_rem_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + case INDEX_op_remu_i32: /* Optional (TCG_TARGET_HAS_div_i32). */ + tcg_out_r(s, args[0]); + tcg_out_ri32(s, const_args[1], args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + break; + case INDEX_op_div2_i32: /* Optional (TCG_TARGET_HAS_div2_i32). */ + case INDEX_op_divu2_i32: /* Optional (TCG_TARGET_HAS_div2_i32). */ + TODO(); + break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_add2_i32: + case INDEX_op_sub2_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_out_r(s, args[3]); + tcg_out_r(s, args[4]); + tcg_out_r(s, args[5]); + break; + case INDEX_op_brcond2_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_ri32(s, const_args[2], args[2]); + tcg_out_ri32(s, const_args[3], args[3]); + tcg_out8(s, args[4]); /* condition */ + tci_out_label(s, arg_label(args[5])); + break; + case INDEX_op_mulu2_i32: + tcg_out_r(s, args[0]); + tcg_out_r(s, args[1]); + tcg_out_r(s, args[2]); + tcg_out_r(s, args[3]); + break; +#endif + case INDEX_op_brcond_i32: + tcg_out_r(s, args[0]); + tcg_out_ri32(s, const_args[1], args[1]); + tcg_out8(s, args[2]); /* condition */ + tci_out_label(s, arg_label(args[3])); + break; + case INDEX_op_qemu_ld_i32: + tcg_out_r(s, *args++); + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_qemu_ld_i64: + tcg_out_r(s, *args++); + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_r(s, *args++); + } + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_qemu_st_i32: + tcg_out_r(s, *args++); + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_qemu_st_i64: + tcg_out_r(s, *args++); + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_r(s, *args++); + } + tcg_out_r(s, *args++); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + tcg_out_r(s, *args++); + } + tcg_out_i(s, *args++); + break; + case INDEX_op_mb: + break; + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ + default: + tcg_abort(); + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, + intptr_t arg2) +{ + uint8_t *old_code_ptr = s->code_ptr; + if (type == TCG_TYPE_I32) { + tcg_out_op_t(s, INDEX_op_st_i32); + tcg_out_r(s, arg); + tcg_out_r(s, arg1); + tcg_out32(s, arg2); + } else { + tcg_debug_assert(type == TCG_TYPE_I64); +#if TCG_TARGET_REG_BITS == 64 + tcg_out_op_t(s, INDEX_op_st_i64); + tcg_out_r(s, arg); + tcg_out_r(s, arg1); + tcg_out32(s, arg2); +#else + TODO(); +#endif + } + old_code_ptr[1] = s->code_ptr - old_code_ptr; +} + +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + return false; +} + +/* Test if a constant matches the constraint. */ +static int tcg_target_const_match(tcg_target_long val, TCGType type, + const TCGArgConstraint *arg_ct) +{ + /* No need to return 0 or 1, 0 or != 0 is good enough. */ + return arg_ct->ct & TCG_CT_CONST; +} + +static void tcg_target_init(TCGContext *s) +{ +#if defined(CONFIG_DEBUG_TCG_INTERPRETER) + const char *envval = getenv("DEBUG_TCG"); + if (envval) { + qemu_set_log(strtol(envval, NULL, 0)); + } +#endif + + /* The current code uses uint8_t for tcg operations. */ + tcg_debug_assert(s->tcg_op_defs_max <= UINT8_MAX); + + /* Registers available for 32 bit operations. */ + s->tcg_target_available_regs[TCG_TYPE_I32] = BIT(TCG_TARGET_NB_REGS) - 1; + /* Registers available for 64 bit operations. */ + s->tcg_target_available_regs[TCG_TYPE_I64] = BIT(TCG_TARGET_NB_REGS) - 1; + /* TODO: Which registers should be set here? */ + s->tcg_target_call_clobber_regs = BIT(TCG_TARGET_NB_REGS) - 1; + + s->reserved_regs = 0; + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + + /* We use negative offsets from "sp" so that we can distinguish + stores that might pretend to be call arguments. */ + tcg_set_frame(s, TCG_REG_CALL_STACK, + -CPU_TEMP_BUF_NLONGS * sizeof(long), + CPU_TEMP_BUF_NLONGS * sizeof(long)); +} + +/* Generate global QEMU prologue and epilogue code. */ +static inline void tcg_target_qemu_prologue(TCGContext *s) +{ +}