mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-03-21 14:37:53 +00:00
This is a prerequisite for supporting multiple TCG contexts, since we will have threads generating code in separate regions of code_gen_buffer. For this we need a new field (.size) in struct tb_tc to keep track of the size of the translated code. This field uses a size_t to avoid adding a hole to the struct, although really an unsigned int would have been enough. The comparison function we use is optimized for the common case: insertions. Profiling shows that upon booting debian-arm, 98% of comparisons are between existing tb's (i.e. a->size and b->size are both !0), which happens during insertions (and removals, but those are rare). The remaining cases are lookups. From reading the glib sources we see that the first key is always the lookup key. However, the code does not assume this to always be the case because this behaviour is not guaranteed in the glib docs. However, we embed this knowledge in the code as a branch hint for the compiler. Note that tb_free does not free space in the code_gen_buffer anymore, since we cannot easily know whether the tb is the last one inserted in code_gen_buffer. The next patch in this series renames tb_free to tb_remove to reflect this. Performance-wise, lookups in tb_find_pc are the same as before: O(log n). However, insertions are O(log n) instead of O(1), which results in a small slowdown when booting debian-arm: Performance counter stats for 'build/arm-softmmu/qemu-system-arm \ -machine type=virt -nographic -smp 1 -m 4096 \ -netdev user,id=unet,hostfwd=tcp::2222-:22 \ -device virtio-net-device,netdev=unet \ -drive file=img/arm/jessie-arm32.qcow2,id=myblock,index=0,if=none \ -device virtio-blk-device,drive=myblock \ -kernel img/arm/aarch32-current-linux-kernel-only.img \ -append console=ttyAMA0 root=/dev/vda1 \ -name arm,debug-threads=on -smp 1' (10 runs): - Before: 8048.598422 task-clock (msec) # 0.931 CPUs utilized ( +- 0.28% ) 16,974 context-switches # 0.002 M/sec ( +- 0.12% ) 0 cpu-migrations # 0.000 K/sec 10,125 page-faults # 0.001 M/sec ( +- 1.23% ) 35,144,901,879 cycles # 4.367 GHz ( +- 0.14% ) <not supported> stalled-cycles-frontend <not supported> stalled-cycles-backend 65,758,252,643 instructions # 1.87 insns per cycle ( +- 0.33% ) 10,871,298,668 branches # 1350.707 M/sec ( +- 0.41% ) 192,322,212 branch-misses # 1.77% of all branches ( +- 0.32% ) 8.640869419 seconds time elapsed ( +- 0.57% ) - After: 8146.242027 task-clock (msec) # 0.923 CPUs utilized ( +- 1.23% ) 17,016 context-switches # 0.002 M/sec ( +- 0.40% ) 0 cpu-migrations # 0.000 K/sec 18,769 page-faults # 0.002 M/sec ( +- 0.45% ) 35,660,956,120 cycles # 4.378 GHz ( +- 1.22% ) <not supported> stalled-cycles-frontend <not supported> stalled-cycles-backend 65,095,366,607 instructions # 1.83 insns per cycle ( +- 1.73% ) 10,803,480,261 branches # 1326.192 M/sec ( +- 1.95% ) 195,601,289 branch-misses # 1.81% of all branches ( +- 0.39% ) 8.828660235 seconds time elapsed ( +- 0.38% ) Backports commit 2ac01d6dafabd4a726254eea98824c798d416ee4 from qemu
232 lines
8.8 KiB
C
232 lines
8.8 KiB
C
/* Unicorn Emulator Engine */
|
|
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "cpu.h"
|
|
#include "hw/boards.h"
|
|
#include "hw/arm/arm.h"
|
|
#include "sysemu/cpus.h"
|
|
#include "unicorn.h"
|
|
#include "unicorn_common.h"
|
|
#include "uc_priv.h"
|
|
|
|
const int ARM64_REGS_STORAGE_SIZE = offsetof(CPUARMState, tlb_table);
|
|
|
|
static void arm64_set_pc(struct uc_struct *uc, uint64_t address)
|
|
{
|
|
CPUArchState *state = uc->cpu->env_ptr;
|
|
|
|
state->pc = address;
|
|
}
|
|
|
|
void arm64_release(void* ctx);
|
|
|
|
void arm64_release(void* ctx)
|
|
{
|
|
TCGContext *s = (TCGContext *) ctx;
|
|
struct uc_struct* uc = s->uc;
|
|
ARMCPU* cpu = ARM_CPU(uc, uc->cpu);
|
|
|
|
g_tree_destroy(s->tb_ctx.tb_tree);
|
|
g_free(cpu->cpreg_indexes);
|
|
g_free(cpu->cpreg_values);
|
|
g_free(cpu->cpreg_vmstate_indexes);
|
|
g_free(cpu->cpreg_vmstate_values);
|
|
|
|
release_common(ctx);
|
|
}
|
|
|
|
void arm64_reg_reset(struct uc_struct *uc)
|
|
{
|
|
CPUArchState *env = uc->cpu->env_ptr;
|
|
memset(env->xregs, 0, sizeof(env->xregs));
|
|
|
|
env->pc = 0;
|
|
}
|
|
|
|
int arm64_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int count)
|
|
{
|
|
CPUState *mycpu = uc->cpu;
|
|
CPUARMState *state = &ARM_CPU(uc, mycpu)->env;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
unsigned int regid = regs[i];
|
|
void *value = vals[i];
|
|
// V & Q registers are the same
|
|
if (regid >= UC_ARM64_REG_V0 && regid <= UC_ARM64_REG_V31) {
|
|
regid += UC_ARM64_REG_Q0 - UC_ARM64_REG_V0;
|
|
}
|
|
if (regid >= UC_ARM64_REG_X0 && regid <= UC_ARM64_REG_X28) {
|
|
*(int64_t *)value = state->xregs[regid - UC_ARM64_REG_X0];
|
|
} else if (regid >= UC_ARM64_REG_W0 && regid <= UC_ARM64_REG_W30) {
|
|
*(int32_t *)value = READ_DWORD(state->xregs[regid - UC_ARM64_REG_W0]);
|
|
} else if (regid >= UC_ARM64_REG_Q0 && regid <= UC_ARM64_REG_Q31) {
|
|
float64 *dst = (float64*) value;
|
|
const uint32_t reg_index = regid - UC_ARM64_REG_Q0;
|
|
const float64 *q_reg = aa64_vfp_qreg(state, reg_index);
|
|
dst[0] = q_reg[0];
|
|
dst[1] = q_reg[1];
|
|
} else if (regid >= UC_ARM64_REG_D0 && regid <= UC_ARM64_REG_D31) {
|
|
const float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_D0));
|
|
*(float64*)value = *d_reg;
|
|
} else if (regid >= UC_ARM64_REG_S0 && regid <= UC_ARM64_REG_S31) {
|
|
const float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_S0));
|
|
*(int32_t*)value = READ_DWORD(*d_reg);
|
|
} else if (regid >= UC_ARM64_REG_H0 && regid <= UC_ARM64_REG_H31) {
|
|
const float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_H0));
|
|
*(int16_t*)value = READ_WORD(*d_reg);
|
|
} else if (regid >= UC_ARM64_REG_B0 && regid <= UC_ARM64_REG_B31) {
|
|
const float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_B0));
|
|
*(int8_t*)value = READ_BYTE_L(*d_reg);
|
|
} else {
|
|
switch(regid) {
|
|
default: break;
|
|
case UC_ARM64_REG_CPACR_EL1:
|
|
*(uint32_t *)value = state->cp15.cpacr_el1;
|
|
break;
|
|
case UC_ARM64_REG_ESR:
|
|
*(uint32_t *)value = state->exception.syndrome;
|
|
break;
|
|
case UC_ARM64_REG_TPIDR_EL0:
|
|
*(int64_t *)value = state->cp15.tpidr_el[0];
|
|
break;
|
|
case UC_ARM64_REG_TPIDRRO_EL0:
|
|
*(int64_t *)value = state->cp15.tpidrro_el[0];
|
|
break;
|
|
case UC_ARM64_REG_TPIDR_EL1:
|
|
*(int64_t *)value = state->cp15.tpidr_el[1];
|
|
break;
|
|
case UC_ARM64_REG_X29:
|
|
*(int64_t *)value = state->xregs[29];
|
|
break;
|
|
case UC_ARM64_REG_X30:
|
|
*(int64_t *)value = state->xregs[30];
|
|
break;
|
|
case UC_ARM64_REG_PC:
|
|
*(uint64_t *)value = state->pc;
|
|
break;
|
|
case UC_ARM64_REG_SP:
|
|
*(int64_t *)value = state->xregs[31];
|
|
break;
|
|
case UC_ARM64_REG_NZCV:
|
|
*(int32_t *)value = cpsr_read(state) & CPSR_NZCV;
|
|
break;
|
|
case UC_ARM64_REG_PSTATE:
|
|
*(uint32_t *)value = pstate_read(state);
|
|
break;
|
|
case UC_ARM64_REG_FPCR:
|
|
*(uint32_t *)value = vfp_get_fpcr(state);
|
|
break;
|
|
case UC_ARM64_REG_FPSR:
|
|
*(uint32_t *)value = vfp_get_fpsr(state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int arm64_reg_write(struct uc_struct *uc, unsigned int *regs, void* const* vals, int count)
|
|
{
|
|
CPUState *mycpu = uc->cpu;
|
|
CPUARMState *state = &ARM_CPU(uc, mycpu)->env;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
unsigned int regid = regs[i];
|
|
const void *value = vals[i];
|
|
if (regid >= UC_ARM64_REG_V0 && regid <= UC_ARM64_REG_V31) {
|
|
regid += UC_ARM64_REG_Q0 - UC_ARM64_REG_V0;
|
|
}
|
|
if (regid >= UC_ARM64_REG_X0 && regid <= UC_ARM64_REG_X28) {
|
|
state->xregs[regid - UC_ARM64_REG_X0] = *(uint64_t *)value;
|
|
} else if (regid >= UC_ARM64_REG_W0 && regid <= UC_ARM64_REG_W30) {
|
|
WRITE_DWORD(state->xregs[regid - UC_ARM64_REG_W0], *(uint32_t *)value);
|
|
} else if (regid >= UC_ARM64_REG_Q0 && regid <= UC_ARM64_REG_Q31) {
|
|
const float64 *src = (const float64*) value;
|
|
const uint32_t reg_index = regid - UC_ARM64_REG_Q0;
|
|
float64 *q_reg = aa64_vfp_qreg(state, reg_index);
|
|
q_reg[0] = src[0];
|
|
q_reg[1] = src[1];
|
|
} else if (regid >= UC_ARM64_REG_D0 && regid <= UC_ARM64_REG_D31) {
|
|
float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_D0));
|
|
*d_reg = *(float64*) value;
|
|
} else if (regid >= UC_ARM64_REG_S0 && regid <= UC_ARM64_REG_S31) {
|
|
float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_S0));
|
|
WRITE_DWORD(*d_reg, *(int32_t*) value);
|
|
} else if (regid >= UC_ARM64_REG_H0 && regid <= UC_ARM64_REG_H31) {
|
|
float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_H0));
|
|
WRITE_WORD(*d_reg, *(int16_t*) value);
|
|
} else if (regid >= UC_ARM64_REG_B0 && regid <= UC_ARM64_REG_B31) {
|
|
float64 *d_reg = aa32_vfp_dreg(state, 2 * (regid - UC_ARM64_REG_B0));
|
|
WRITE_BYTE_L(*d_reg, *(int8_t*) value);
|
|
} else {
|
|
switch(regid) {
|
|
default: break;
|
|
case UC_ARM64_REG_CPACR_EL1:
|
|
state->cp15.cpacr_el1 = *(uint32_t *)value;
|
|
break;
|
|
case UC_ARM64_REG_TPIDR_EL0:
|
|
state->cp15.tpidr_el[0] = *(uint64_t *)value;
|
|
break;
|
|
case UC_ARM64_REG_TPIDRRO_EL0:
|
|
state->cp15.tpidrro_el[0] = *(uint64_t *)value;
|
|
break;
|
|
case UC_ARM64_REG_TPIDR_EL1:
|
|
state->cp15.tpidr_el[1] = *(uint64_t *)value;
|
|
break;
|
|
case UC_ARM64_REG_X29:
|
|
state->xregs[29] = *(uint64_t *)value;
|
|
break;
|
|
case UC_ARM64_REG_X30:
|
|
state->xregs[30] = *(uint64_t *)value;
|
|
break;
|
|
case UC_ARM64_REG_PC:
|
|
state->pc = *(uint64_t *)value;
|
|
// force to quit execution and flush TB
|
|
uc->quit_request = true;
|
|
uc_emu_stop(uc);
|
|
break;
|
|
case UC_ARM64_REG_SP:
|
|
state->xregs[31] = *(uint64_t *)value;
|
|
break;
|
|
case UC_ARM64_REG_NZCV:
|
|
cpsr_write(state, *(uint32_t *) value, CPSR_NZCV, CPSRWriteRaw);
|
|
break;
|
|
case UC_ARM64_REG_PSTATE:
|
|
pstate_write(state, *(uint32_t *)value);
|
|
break;
|
|
case UC_ARM64_REG_FPCR:
|
|
vfp_set_fpcr(state, *(uint32_t *)value);
|
|
break;
|
|
case UC_ARM64_REG_FPSR:
|
|
vfp_set_fpsr(state, *(uint32_t *)value);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEFAULT_VISIBILITY
|
|
#ifdef TARGET_WORDS_BIGENDIAN
|
|
void arm64eb_uc_init(struct uc_struct* uc)
|
|
#else
|
|
void arm64_uc_init(struct uc_struct* uc)
|
|
#endif
|
|
{
|
|
register_accel_types(uc);
|
|
arm_cpu_register_types(uc);
|
|
aarch64_cpu_register_types(uc);
|
|
machvirt_machine_init(uc);
|
|
uc->reg_read = arm64_reg_read;
|
|
uc->reg_write = arm64_reg_write;
|
|
uc->reg_reset = arm64_reg_reset;
|
|
uc->set_pc = arm64_set_pc;
|
|
uc->release = arm64_release;
|
|
uc_common_init(uc);
|
|
}
|