Fix exceptions handling for MIPS and i386
v2 cover Fix exceptions handling for MIPS and i386
This set of patches fixes exception handling for MIPS and i386 targets. These targets contain instructions that break correct execution in icount/TCG modes (MIPS) and in regular TCG mode (i386).
Incorrect execution for i386 is causes by exceptions raised by MMU functions. MMU helper functions are called from generated code and other helper functions. In both cases they try to get function's return address for restoring virtual CPU state.
When MMU helper is called from some other helper function (like helper_maskmov_xmm) through cpu_st* function, the return address will point to that helper. That is why CPU state cannot be restored in the case of MMU fault.
This bug can occur when maskmov instruction is located in the middle of the translation block.
Execution sequence for this example:
TB start:
PC1: instr1
instr2
PC2: maskmov <page fault>
<page fault processing>
PC1: instr1
instr2
maskmov
At the start of TB execution guest PC points to instr1. When page fault occurs QEMU tries to restore guest PC (which should be equal to PC2). It reads host PC from the call stack and checks whether it points to TB or not. Bug in ldst helpers implementation provides incorrect host PC, which is not located within the TB. That's why QEMU cannot recover guest PC and it remains the same (PC1). After page fault processing QEMU restarts TB and executes instr1 and instr2 for the second time, because guest PC was not recovered.
Bugs in MIPS helper functions do not break the execution in regular TCG mode, because PC value is updated before calling the functions that can raise an exception. But icount value cannot be updated this way. Therefore exceptions make execution in icount mode non-determinisic.
In icount mode every translation block looks as follows:
if icount < n then exit
icount -= n
instr1
instr2
...
instrn
exit
When one of these instructions initiates an exception, icount should be restored and adjusted number of instructions should be subtracted from icount instead of initial n.
tlb_fill function passes retaddr to raise_exception, which allows restoring current instructions in TB and correct icount calculation.
When exception triggered with other function (e.g. by embedding call to exception raising helper into TB), then PC is not passed as retaddr and correct icount is not recovered. In such cases icount will be decreased by the value equal to the size of TB.
This behavior leads to incorrect values of virtual clock and non-deterministic execution of the code.
These patches passes pointer to the translation block code to the exception handler. It allows correct restoring of PC and icount values.
v2 changes:
* Added softmmu functions to pass TB return value into memory operations handlers
* Fixed memory operations handling for MIPS
* Disabled updates of the PC that are overridden with cpu_restore_state
* Fixed memory operations and exceptions invoked by i386 helpers
---
Pavel Dovgalyuk (3):
softmmu: add helper function to pass through retaddr
target-mips: exceptions handling in icount mode
target-i386: fix memory operations in helpers
include/exec/cpu_ldst_template.h | 42 ++ include/exec/exec-all.h | 27 + softmmu_template.h | 18 + target-i386/cc_helper.c | 2 target-i386/cpu.h | 5 target-i386/excp_helper.c | 23 + target-i386/fpu_helper.c | 146 ++++---- target-i386/helper.c | 4 target-i386/int_helper.c | 32 +- target-i386/mem_helper.c | 39 +- target-i386/misc_helper.c | 12 - target-i386/ops_sse.h | 2 target-i386/seg_helper.c | 712 +++++++++++++++++++------------------- target-i386/svm_helper.c | 4 target-i386/translate.c | 25 - target-mips/cpu.h | 28 + target-mips/helper.h | 1 target-mips/msa_helper.c | 5 target-mips/op_helper.c | 183 +++++----- target-mips/translate.c | 46 +- 20 files changed, 726 insertions(+), 630 deletions(-)
Signature · 1 line
Pavel Dovgalyuk
PATCH v2 1/3 softmmu: add helper function to pass through retaddr
This patch introduces several helpers to pass return address which points to the TB. Correct return address allows correct restoring of the guest PC and icount. These functions should be used when helpers embedded into TB invoke memory operations.
--- include/exec/cpu_ldst_template.h | 42 +++++++++++++++++++++++++++++++------- include/exec/exec-all.h | 27 ++++++++++++++++++++++++ softmmu_template.h | 18 ++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-)
include/exec/cpu_ldst_template.h +34 -8
@@ -62,7 +62,9 @@/* generic load/store macros */ static inline RES_TYPE -glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr) +glue (glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX ),_ra )(CPUArchState *env, +target_ulong ptr, +uintptr_t retaddr) {int page_index;RES_TYPE res;
@@ -74,7 +76,8 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) mmu_idx =CPU_MMU_INDEX ;if (unlikely (env->tlb_table[mmu_idx][page_index].ADDR_READ != (addr & (TARGET_PAGE_MASK | (DATA_SIZE -1 ))))) { - res =glue (glue (helper_ld,SUFFIX ),MMUSUFFIX )(env, addr, mmu_idx); + res =glue (glue (helper_call_ld,SUFFIX ),MMUSUFFIX )(env, addr, + mmu_idx, retaddr); }else {uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; res =glue (glue (ld,USUFFIX ), _p)((uint8_t *)hostaddr);
@@ -82,9 +85,17 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr)return res; } +static inline RES_TYPE +glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr) +{ +return glue (glue (glue (cpu_ld,USUFFIX ),MEMSUFFIX ), _ra)(env, ptr,0 ); +} +#if DATA_SIZE <=2 static inline int -glue (glue (cpu_lds ,SUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr) +glue (glue (glue (cpu_lds ,SUFFIX ),MEMSUFFIX ),_ra )(CPUArchState *env, +target_ulong ptr, +uintptr_t retaddr) {int res, page_index;target_ulong addr;
@@ -95,14 +106,20 @@ glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) mmu_idx =CPU_MMU_INDEX ;if (unlikely (env->tlb_table[mmu_idx][page_index].ADDR_READ != (addr & (TARGET_PAGE_MASK | (DATA_SIZE -1 ))))) { - res = (DATA_STYPE )glue (glue (helper_ld,SUFFIX ), -MMUSUFFIX )(env, addr, mmu_idx); + res = (DATA_STYPE )glue (glue (helper_call_ld,SUFFIX ), +MMUSUFFIX )(env, addr, mmu_idx, retaddr); }else {uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; res =glue (glue (lds,SUFFIX ), _p)((uint8_t *)hostaddr); } return res; } + +static inline int +glue (glue (cpu_lds ,SUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr) +{ +return glue (glue (glue (cpu_lds,SUFFIX ),MEMSUFFIX ), _ra)(env, ptr,0 ); +}#endif #ifndef SOFTMMU_CODE_ACCESS
@@ -110,8 +127,9 @@ glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr)/* generic store macro */ static inline void -glue (glue (cpu_st ,SUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr, -RES_TYPE v) +glue (glue (glue (cpu_st ,SUFFIX ),MEMSUFFIX ),_ra )(CPUArchState *env, +target_ulong ptr, +RES_TYPE v,uintptr_t retaddr) {int page_index;target_ulong addr;
@@ -122,13 +140,21 @@ glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, mmu_idx =CPU_MMU_INDEX ;if (unlikely (env->tlb_table[mmu_idx][page_index].addr_write != (addr & (TARGET_PAGE_MASK | (DATA_SIZE -1 ))))) { -glue (glue (helper_st,SUFFIX ),MMUSUFFIX )(env, addr, v, mmu_idx); +glue (glue (helper_call_st,SUFFIX ),MMUSUFFIX )(env, addr, v, mmu_idx, + retaddr); }else {uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;glue (glue (st,SUFFIX ), _p)((uint8_t *)hostaddr, v); } } +static inline void +glue (glue (cpu_st ,SUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr, +RES_TYPE v) +{ +glue (glue (glue (cpu_st,SUFFIX ),MEMSUFFIX ), _ra)(env, ptr, v,0 ); +} +#endif /* !SOFTMMU_CODE_ACCESS */ #undef RES_TYPE
include/exec/exec-all.h +27 -0
@@ -350,6 +350,33 @@ struct MemoryRegion *iotlb_to_region(CPUState *cpu,void tlb_fill (CPUState *cpu,target_ulong addr,int is_write,int mmu_idx,uintptr_t retaddr); +uint8_t helper_call_ldb_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); +uint16_t helper_call_ldw_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); +uint32_t helper_call_ldl_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); +uint64_t helper_call_ldq_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); + +uint8_t helper_call_ldb_mmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); +uint16_t helper_call_ldw_mmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); +uint32_t helper_call_ldl_mmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); +uint64_t helper_call_ldq_mmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr); + +void helper_call_stb_mmu (CPUArchState *env,target_ulong addr, +uint8_t val,int mmu_idx,uintptr_t retaddr); +void helper_call_stw_mmu (CPUArchState *env,target_ulong addr, +uint16_t val,int mmu_idx,uintptr_t retaddr); +void helper_call_stl_mmu (CPUArchState *env,target_ulong addr, +uint32_t val,int mmu_idx,uintptr_t retaddr); +void helper_call_stq_mmu (CPUArchState *env,target_ulong addr, +uint64_t val,int mmu_idx,uintptr_t retaddr); +#endif #if defined(CONFIG_USER_ONLY )
softmmu_template.h +18 -0
@@ -343,6 +343,15 @@ glue(glue(helper_ld, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr,return helper_te_ld_name (env, addr, oi,GETRA ()); } +DATA_TYPE +glue (glue (helper_call_ld ,SUFFIX ),MMUSUFFIX )(CPUArchState *env, +target_ulong addr, +int mmu_idx, +uintptr_t retaddr) +{ +return helper_te_ld_name (env, addr, mmu_idx, retaddr); +} +#ifndef SOFTMMU_CODE_ACCESS /* Provide signed versions of the load routines as well. We can of course
@@ -548,6 +557,15 @@ glue(glue(helper_st, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr, helper_te_st_name(env, addr, val, oi, GETRA()); } +void +glue (glue (helper_call_st ,SUFFIX ),MMUSUFFIX )(CPUArchState *env, +target_ulong addr, +DATA_TYPE val,int mmu_idx, +uintptr_t retaddr) +{ +helper_te_st_name (env, addr, val, mmu_idx, retaddr); +} +#endif /* !defined(SOFTMMU_CODE_ACCESS) */ #undef READ_ACCESS_TYPE
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
Quoted 17 lines · Pavel Dovgalyuk
This patch introduces several helpers to pass return address which points to the TB. Correct return address allows correct restoring of the guest PC and icount. These functions should be used when helpers embedded into TB invoke memory operations.
Signed-off-by Pavel Dovgalyuk <[email protected]>--- include/exec/cpu_ldst_template.h | 42 +++++++++++++++++++++++++++++++------- include/exec/exec-all.h | 27 ++++++++++++++++++++++++ softmmu_template.h | 18 ++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-)include/exec/cpu_ldst_template.h +0
-0
include/exec/cpu_ldst_template.h line 67
Quoted 7 lines
include/exec/cpu_ldst_template.h +3
-1@@ -62,7 +62,9 @@/* generic load/store macros */ static inline RES_TYPE -glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr) +glue (glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX ),_ra )(CPUArchState *env, +target_ulong ptr, +uintptr_t retaddr)
Would it make sense to call these helper_cpu_ld##USUFFIX##MEMSUFFIX?
include/exec/exec-all.h struct MemoryRegion *iotlb_to_region(CPUState *cpu, · line 354
Quoted 10 lines
include/exec/exec-all.h +2
-0@@ -350,6 +350,33 @@ struct MemoryRegion *iotlb_to_region(CPUState *cpu,void tlb_fill (CPUState *cpu,target_ulong addr,int is_write,int mmu_idx,uintptr_t retaddr); +uint8_t helper_call_ldb_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr);
Here we already have helper_ret_ldb_cmmu, so the new function is only needed if DATA_SIZE != 1.
include/exec/exec-all.h struct MemoryRegion *iotlb_to_region(CPUState *cpu, · line 356
Quoted 2 lines
include/exec/exec-all.h +2
-0+uint16_t helper_call_ldw_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr);
What about helper_ret_ldw_cmmu for consistency with the DATA_SIZE == 1 case?
Paolo
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
Quoted 21 lines
From: Paolo Bonzini [mailto:[email protected]]
Quoted 19 lines · Pavel Dovgalyuk
This patch introduces several helpers to pass return address which points to the TB. Correct return address allows correct restoring of the guest PC and icount. These functions should be used when helpers embedded into TB invoke memory operations.
Signed-off-by Pavel Dovgalyuk <[email protected]>--- include/exec/cpu_ldst_template.h | 42 +++++++++++++++++++++++++++++++------- include/exec/exec-all.h | 27 ++++++++++++++++++++++++ softmmu_template.h | 18 ++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-)include/exec/cpu_ldst_template.h +0
-0@@ -62,7 +62,9 @@/* generic load/store macros */
Quoted 7 lines
include/exec/cpu_ldst_template.h line 67
Quoted 5 lines
include/exec/cpu_ldst_template.h +3
-1static inline RES_TYPE -glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr) +glue (glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX ),_ra )(CPUArchState *env, +target_ulong ptr, +uintptr_t retaddr)Would it make sense to call these helper_cpu_ld##USUFFIX##MEMSUFFIX?
I don't want to use 'helper' prefix, because helper functions are usually called directly from TB.
Pavel Dovgalyuk
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
Quoted 10 lines · Pavel Dovgaluk
Quoted 8 lines
Quoted 8 lines
Quoted 6 lines
include/exec/cpu_ldst_template.h line 67
Quoted 6 lines
static inline
RES_TYPEinclude/exec/cpu_ldst_template.h +3
-1-glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +glue (glue (glue (cpu_ld,USUFFIX ),MEMSUFFIX ), _ra)(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr)Would it make sense to call these helper_cpu_ld##USUFFIX##MEMSUFFIX?
I don't want to use 'helper' prefix, because helper functions are usually called directly from TB.
True, but in the end these have the same functionality as helpers, just they're indirectly called from other helpers.
Paolo
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
Quoted 11 lines · Paolo Bonzini
Quoted 8 lines · Pavel Dovgaluk
Quoted 8 lines
Quoted 8 lines
Quoted 6 lines
include/exec/cpu_ldst_template.h line 67
Quoted 6 lines
static inline
RES_TYPEinclude/exec/cpu_ldst_template.h +3
-1-glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +glue (glue (glue (cpu_ld,USUFFIX ),MEMSUFFIX ), _ra)(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr)Would it make sense to call these helper_cpu_ld##USUFFIX##MEMSUFFIX?
Quoted 5 lines
Quoted 2 lines
I don't want to use 'helper' prefix, because helper functions are usually called directly from TB.
True, but in the end these have the same functionality as helpers, just they're indirectly called from other helpers.
Not fully. The idea is that the helpers are non-inline functions handling the slow path. The cpu_ld##USUFFIX##MEMSUFFIX are inline functions handling the fast path, and calling the helpers for the slow path. That allows for example GCC to optimize the fast path when the cpu_ld functions are used in a loop.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
In reply to Paolo BonziniQuoted 43 lines
From: Paolo Bonzini [mailto:[email protected]]
Quoted 17 lines · Pavel Dovgalyuk
This patch introduces several helpers to pass return address which points to the TB. Correct return address allows correct restoring of the guest PC and icount. These functions should be used when helpers embedded into TB invoke memory operations.
Signed-off-by Pavel Dovgalyuk <[email protected]>--- include/exec/cpu_ldst_template.h | 42 +++++++++++++++++++++++++++++++------- include/exec/exec-all.h | 27 ++++++++++++++++++++++++ softmmu_template.h | 18 ++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-)include/exec/cpu_ldst_template.h +0
-0include/exec/cpu_ldst_template.h line 67
Quoted 7 lines
include/exec/cpu_ldst_template.h +3
-1@@ -62,7 +62,9 @@/* generic load/store macros */ static inline RES_TYPE -glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX )(CPUArchState *env,target_ulong ptr) +glue (glue (glue (cpu_ld ,USUFFIX ),MEMSUFFIX ),_ra )(CPUArchState *env, +target_ulong ptr, +uintptr_t retaddr)Would it make sense to call these helper_cpu_ld##USUFFIX##MEMSUFFIX?
include/exec/exec-all.h struct MemoryRegion *iotlb_to_region(CPUState *cpu, · line 354
Quoted 10 lines
include/exec/exec-all.h +2
-0@@ -350,6 +350,33 @@ struct MemoryRegion *iotlb_to_region(CPUState *cpu,void tlb_fill (CPUState *cpu,target_ulong addr,int is_write,int mmu_idx,uintptr_t retaddr); +uint8_t helper_call_ldb_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr);Here we already have
helper_ret_ldb_cmmu, so the new function is only needed ifDATA_SIZE!= 1.
Quoted 4 lines
include/exec/exec-all.h struct MemoryRegion *iotlb_to_region(CPUState *cpu, · line 356
Quoted 2 lines
include/exec/exec-all.h +2
-0+uint16_t helper_call_ldw_cmmu (CPUArchState *env,target_ulong addr, +int mmu_idx,uintptr_t retaddr);What about
helper_ret_ldw_cmmufor consistency with theDATA_SIZE== 1 case?
tcg.h breaks these definitions:
/* Temporary aliases until backends are converted. */ #ifdef TARGET_WORDS_BIGENDIAN
# define helper_ret_ldsw_mmu helper_be_ldsw_mmu # define helper_ret_lduw_mmu helper_be_lduw_mmu # define helper_ret_ldsl_mmu helper_be_ldsl_mmu # define helper_ret_ldul_mmu helper_be_ldul_mmu # define helper_ret_ldq_mmu helper_be_ldq_mmu # define helper_ret_stw_mmu helper_be_stw_mmu # define helper_ret_stl_mmu helper_be_stl_mmu # define helper_ret_stq_mmu helper_be_stq_mmu #else
Pavel Dovgalyuk
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
Quoted 8 lines · Pavel Dovgaluk
Quoted 4 lines
Quoted 4 lines
Quoted 2 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+uint16_t helper_call_ldw_cmmu(CPUArchState *env, target_ulong addr, + int mmu_idx, uintptr_t retaddr);What about
helper_ret_ldw_cmmufor consistency with theDATA_SIZE== 1 case?
tcg.hbreaks these definitions:/* Temporary aliases until backends are converted. */ #ifdef
TARGET_WORDS_BIGENDIAN
Quoted 9 lines
# define helper_ret_ldsw_mmu helper_be_ldsw_mmu # define helper_ret_lduw_mmu helper_be_lduw_mmu # define helper_ret_ldsl_mmu helper_be_ldsl_mmu # define helper_ret_ldul_mmu helper_be_ldul_mmu # define helper_ret_ldq_mmu helper_be_ldq_mmu # define helper_ret_stw_mmu helper_be_stw_mmu # define helper_ret_stl_mmu helper_be_stl_mmu # define helper_ret_stq_mmu helper_be_stq_mmu #else
Isn't this exactly the same as your helper_call_ldw_cmmu?
Paolo
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
Quoted 20 lines
From: Paolo Bonzini [mailto:[email protected]]
Quoted 8 lines · Pavel Dovgaluk
Quoted 4 lines
Quoted 4 lines
Quoted 2 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+uint16_t helper_call_ldw_cmmu(CPUArchState *env, target_ulong addr, + int mmu_idx, uintptr_t retaddr);What about
helper_ret_ldw_cmmufor consistency with theDATA_SIZE== 1 case?
tcg.hbreaks these definitions:/* Temporary aliases until backends are converted. */ #ifdef
TARGET_WORDS_BIGENDIANQuoted 9 lines
# define helper_ret_ldsw_mmu helper_be_ldsw_mmu # define helper_ret_lduw_mmu helper_be_lduw_mmu # define helper_ret_ldsl_mmu helper_be_ldsl_mmu # define helper_ret_ldul_mmu helper_be_ldul_mmu # define helper_ret_ldq_mmu helper_be_ldq_mmu # define helper_ret_stw_mmu helper_be_stw_mmu # define helper_ret_stl_mmu helper_be_stl_mmu # define helper_ret_stq_mmu helper_be_stq_mmu #else
Quoted 1 line
Isn't this exactly the same as your
helper_call_ldw_cmmu?
Yes, but I can't compile it yet.
Pavel Dovgalyuk
Re: [Qemu-devel] [PATCH v2 1/3] softmmu: add helper function to pass through retaddr
Quoted 12 lines · Pavel Dovgaluk
Quoted 12 lines
Quoted 11 lines
/* Temporary aliases until backends are converted. */ #ifdef
TARGET_WORDS_BIGENDIAN# define helper_ret_ldsw_mmu helper_be_ldsw_mmu # define helper_ret_lduw_mmu helper_be_lduw_mmu # define helper_ret_ldsl_mmu helper_be_ldsl_mmu # define helper_ret_ldul_mmu helper_be_ldul_mmu # define helper_ret_ldq_mmu helper_be_ldq_mmu # define helper_ret_stw_mmu helper_be_stw_mmu # define helper_ret_stl_mmu helper_be_stl_mmu # define helper_ret_stq_mmu helper_be_stq_mmu #else
Quoted 3 lines
Quoted 1 line
Isn't this exactly the same as your
helper_call_ldw_cmmu?Yes, but I can't compile it yet.
I'm not sure what's the problem. Can you just move this part of tcg/tcg.h to another header file?
Paolo
PATCH v2 2/3 target-mips: exceptions handling in icount mode
In reply to Pavel DovgalyukThis patch fixes exception handling in MIPS.
Instructions generate several types of exceptions.
When exception is generated, it breaks the execution of the current translation block. Implementation of the exceptions handling does not correctly restore icount for the instruction which caused the exception. In most cases icount will be decreased by the value equal to the size of TB. This patch passes pointer to the translation block internals to the exception handler. It allows correct restoring of the icount value.
--- target-mips/cpu.h | 28 +++++++ target-mips/helper.h | 1 target-mips/msa_helper.c | 5 + target-mips/op_helper.c | 183 ++++++++++++++++++++++------------------------ target-mips/translate.c | 46 ++++++------ 5 files changed, 141 insertions(+), 122 deletions(-)
target-mips/cpu.h +28 -0
@@ -1015,4 +1015,32 @@ static inline void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val) }#endif +static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, +uint32_t exception, +int error_code, +uintptr_t pc) +{ +CPUState *cs =CPU (mips_env_get_cpu (env)); + +if (exception <EXCP_SC ) { +qemu_log ("%s: %d %d\n" , __func__, exception, error_code); + } + cs->exception_index = exception; + env->error_code = error_code; + +if (pc) { +/* now we have a real cpu fault */ +cpu_restore_state (cs, pc); + } + +cpu_loop_exit (cs); +} + +static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env, +uint32_t exception, +uintptr_t pc) +{ +do_raise_exception_err (env, exception,0 , pc); +} +#endif /* !defined (__MIPS_CPU_H__) */
target-mips/helper.h +1 -0
@@ -1,5 +1,6 @@ DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int) DEF_HELPER_2(raise_exception, noreturn, env, i32) +DEF_HELPER_1(raise_exception_debug, noreturn, env)#ifdef TARGET_MIPS64 DEF_HELPER_4 (sdl, void, env, tl, tl, int)
target-mips/msa_helper.c +3 -2
@@ -1352,7 +1352,7 @@ void helper_msa_ctcmsa(CPUMIPSState *env, target_ulong elm, uint32_t cd)/* check exception */ if ((GET_FP_ENABLE (env->active_tc.msacsr) |FP_UNIMPLEMENTED ) &GET_FP_CAUSE (env->active_tc.msacsr)) { -helper_raise_exception (env,EXCP_MSAFPE ); +do_raise_exception (env,EXCP_MSAFPE ,GETPC ()); }break ; }
@@ -1512,7 +1512,8 @@ static inline void check_msacsr_cause(CPUMIPSState *env)UPDATE_FP_FLAGS (env->active_tc.msacsr,GET_FP_CAUSE (env->active_tc.msacsr)); }else { -helper_raise_exception (env,EXCP_MSAFPE ); +/* Will work only when check_msacsr_cause is actually inlined */ +do_raise_exception (env,EXCP_MSAFPE ,GETPC ()); } }
target-mips/op_helper.c +86 -97
465 lines not on this page · raw message
target-mips/translate.c +23 -23
174 lines not on this page · raw message
Re: [Qemu-devel] [PATCH v2 2/3] target-mips: exceptions handling in icount mode
Quoted 17 lines · Pavel Dovgalyuk
This patch fixes exception handling in MIPS.
Instructions generate several types of exceptions.
When exception is generated, it breaks the execution of the current translation block. Implementation of the exceptions handling does not correctly restore icount for the instruction which caused the exception. In most cases icount will be decreased by the value equal to the size of TB. This patch passes pointer to the translation block internals to the exception handler. It allows correct restoring of the icount value.Signed-off-by Pavel Dovgalyuk <[email protected]>--- target-mips/cpu.h | 28 +++++++ target-mips/helper.h | 1 target-mips/msa_helper.c | 5 + target-mips/op_helper.c | 183 ++++++++++++++++++++++------------------------ target-mips/translate.c | 46 ++++++------ 5 files changed, 141 insertions(+), 122 deletions(-)
[ snip ]
Quoted 30 lines
target-mips/op_helper.c +3
-18@@ -30,41 +30,23 @@ static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global);/*****************************************************************************/ /* Exceptions processing helpers */ -static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, -uint32_t exception, -int error_code, -uintptr_t pc) +void helper_raise_exception_err (CPUMIPSState *env,uint32_t exception, +int error_code) { -CPUState *cs =CPU (mips_env_get_cpu (env)); - -if (exception <EXCP_SC ) { -qemu_log ("%s: %d %d\n" , __func__, exception, error_code); - } - cs->exception_index = exception; - env->error_code = error_code; - -if (pc) { -/* now we have a real cpu fault */ -cpu_restore_state (cs, pc); - } - -cpu_loop_exit (cs); +do_raise_exception_err (env, exception, error_code,GETPC ());
target-mips/op_helper.c static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global); · line 42
Quoted 10 lines
target-mips/op_helper.c +2
-4} -static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env, -uint32_t exception, -uintptr_t pc) +void helper_raise_exception (CPUMIPSState *env,uint32_t exception) { -do_raise_exception_err (env, exception,0 , pc); +do_raise_exception (env, exception,GETPC ()); }
raise_exception is used to implement the SYSCALL instruction on MIPS. With this change, this mean that each TB containing a syscall will have to be translated at least twice, probably more. That's not something acceptable performance wise.
target-mips/translate.c generate_exception_err (DisasContext *ctx, int excp, int err) · line 1676
Quoted 10 lines
target-mips/translate.c +1
-1@@ -1673,7 +1673,7 @@ generate_exception_err (DisasContext *ctx, int excp, int err) {TCGv_i32 texcp =tcg_const_i32 (excp);TCGv_i32 terr =tcg_const_i32 (err); -save_cpu_state (ctx,1 ); +save_cpu_state (ctx,0 );
If retranslation is used, you don't even need to save the branch status, restore_state_to_opc can restore it.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
PATCH v2 3/3 target-i386: fix memory operations in helpers
In reply to Pavel DovgalyukThis patch passes TB return address into softmmu functions that are invoked from target helpers. This allows correct PC and icount recovering while handling MMU faults.
--- target-i386/cc_helper.c | 2 target-i386/cpu.h | 5 target-i386/excp_helper.c | 23 + target-i386/fpu_helper.c | 146 +++++---- target-i386/helper.c | 4 target-i386/int_helper.c | 32 +- target-i386/mem_helper.c | 39 +- target-i386/misc_helper.c | 12 - target-i386/ops_sse.h | 2 target-i386/seg_helper.c | 712 +++++++++++++++++++++++---------------------- target-i386/svm_helper.c | 4 target-i386/translate.c | 25 -- 12 files changed, 506 insertions(+), 500 deletions(-)
target-i386/cc_helper.c +1 -1
@@ -378,7 +378,7 @@ void helper_sti_vm(CPUX86State *env) { env->eflags |=VIF_MASK ;if (env->eflags &VIP_MASK ) { -raise_exception (env,EXCP0D_GPF ); +raise_exception (env,EXCP0D_GPF ,GETPC ()); } }#endif
target-i386/cpu.h +3 -2
@@ -1249,9 +1249,10 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, uint64_t misc, int flags);/* excp_helper.c */ -void QEMU_NORETURN raise_exception(CPUX86State *env, int exception_index); +void QEMU_NORETURN raise_exception(CPUX86State *env, int exception_index, +uintptr_t retaddr); void QEMU_NORETURN raise_exception_err(CPUX86State *env, int exception_index, -int error_code); +int error_code,uintptr_t retaddr); void QEMU_NORETURN raise_interrupt(CPUX86State *nenv, int intno, int is_int,int error_code,int next_eip_addend);
target-i386/excp_helper.c +14 -9
@@ -23,10 +23,10 @@#include "exec/helper-proto.h" #if 0 -#define raise_exception_err (env, a, b) \ +#define raise_exception_err (env, a, b, c) \ do { \ qemu_log("raise_exception line=%d\n", __LINE__); \ - (raise_exception_err)(env, a, b); \ + (raise_exception_err)(env, a, b, c); \ } while (0)#endif
@@ -37,7 +37,7 @@ void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend)void helper_raise_exception (CPUX86State *env,int exception_index) { -raise_exception (env, exception_index); +raise_exception (env, exception_index,0 ); }/*
@@ -92,7 +92,8 @@ static int check_exception(CPUX86State *env, int intno, int *error_code)*/ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno,int is_int,int error_code, -int next_eip_addend) +int next_eip_addend, +uintptr_t retaddr) {CPUState *cs =CPU (x86_env_get_cpu (env));
@@ -108,6 +109,10 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, env->error_code = error_code; env->exception_is_int = is_int; env->exception_next_eip = env->eip + next_eip_addend; +if (retaddr) { +/* now we have a real cpu fault */ +cpu_restore_state (cs, retaddr); + }cpu_loop_exit (cs); }
@@ -116,16 +121,16 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int,int error_code,int next_eip_addend) { -raise_interrupt2 (env, intno, is_int, error_code, next_eip_addend); +raise_interrupt2 (env, intno, is_int, error_code, next_eip_addend,0 ); }void raise_exception_err (CPUX86State *env,int exception_index, -int error_code) +int error_code,uintptr_t retaddr) { -raise_interrupt2 (env, exception_index,0 , error_code,0 ); +raise_interrupt2 (env, exception_index,0 , error_code,0 , retaddr); } -void raise_exception (CPUX86State *env,int exception_index) +void raise_exception (CPUX86State *env,int exception_index,uintptr_t retaddr) { -raise_interrupt2 (env, exception_index,0 ,0 ,0 ); +raise_interrupt2 (env, exception_index,0 ,0 ,0 , retaddr); }
target-i386/fpu_helper.c +80 -66
318 lines not on this page · raw message
target-i386/helper.c +2 -2
@@ -1014,7 +1014,7 @@ void breakpoint_handler(CPUState *cs)if (cs->watchpoint_hit->flags &BP_CPU ) { cs->watchpoint_hit =NULL ;if (check_hw_breakpoints (env, false)) { -raise_exception (env,EXCP01_DB ); +raise_exception (env,EXCP01_DB ,0 ); }else {cpu_resume_from_signal (cs,NULL ); }
@@ -1024,7 +1024,7 @@ void breakpoint_handler(CPUState *cs)if (bp->pc == env->eip) {if (bp->flags &BP_CPU ) {check_hw_breakpoints (env, true); -raise_exception (env,EXCP01_DB ); +raise_exception (env,EXCP01_DB ,0 ); }break ; }
target-i386/int_helper.c +16 -16
@@ -48,11 +48,11 @@ void helper_divb_AL(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX ] &0xffff ); den = (t0 &0xff );if (den ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q = (num / den);if (q >0xff ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q &=0xff ; r = (num % den) &0xff ;
@@ -66,11 +66,11 @@ void helper_idivb_AL(CPUX86State *env, target_ulong t0) num = (int16_t )env->regs[R_EAX ]; den = (int8_t )t0;if (den ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q = (num / den);if (q != (int8_t )q) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q &=0xff ; r = (num % den) &0xff ;
@@ -84,11 +84,11 @@ void helper_divw_AX(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX ] &0xffff ) | ((env->regs[R_EDX ] &0xffff ) <<16 ); den = (t0 &0xffff );if (den ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q = (num / den);if (q >0xffff ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q &=0xffff ; r = (num % den) &0xffff ;
@@ -103,11 +103,11 @@ void helper_idivw_AX(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX ] &0xffff ) | ((env->regs[R_EDX ] &0xffff ) <<16 ); den = (int16_t )t0;if (den ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q = (num / den);if (q != (int16_t )q) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q &=0xffff ; r = (num % den) &0xffff ;
@@ -123,12 +123,12 @@ void helper_divl_EAX(CPUX86State *env, target_ulong t0) num = ((uint32_t )env->regs[R_EAX ]) | ((uint64_t )((uint32_t )env->regs[R_EDX ]) <<32 ); den = t0;if (den ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q = (num / den); r = (num % den);if (q >0xffffffff ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } env->regs[R_EAX ] = (uint32_t )q; env->regs[R_EDX ] = (uint32_t )r;
@@ -142,12 +142,12 @@ void helper_idivl_EAX(CPUX86State *env, target_ulong t0) num = ((uint32_t )env->regs[R_EAX ]) | ((uint64_t )((uint32_t )env->regs[R_EDX ]) <<32 ); den = t0;if (den ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } q = (num / den); r = (num % den);if (q != (int32_t )q) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } env->regs[R_EAX ] = (uint32_t )q; env->regs[R_EDX ] = (uint32_t )r;
@@ -379,12 +379,12 @@ void helper_divq_EAX(CPUX86State *env, target_ulong t0)uint64_t r0, r1;if (t0 ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } r0 = env->regs[R_EAX ]; r1 = env->regs[R_EDX ];if (div64 (&r0, &r1, t0)) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } env->regs[R_EAX ] = r0; env->regs[R_EDX ] = r1;
@@ -395,12 +395,12 @@ void helper_idivq_EAX(CPUX86State *env, target_ulong t0)uint64_t r0, r1;if (t0 ==0 ) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } r0 = env->regs[R_EAX ]; r1 = env->regs[R_EDX ];if (idiv64 (&r0, &r1, t0)) { -raise_exception (env,EXCP00_DIVZ ); +raise_exception (env,EXCP00_DIVZ ,GETPC ()); } env->regs[R_EAX ] = r0; env->regs[R_EDX ] = r1;
target-i386/mem_helper.c +18 -21
@@ -41,13 +41,14 @@ void helper_cmpxchg8b(CPUX86State *env, target_ulong a0)int eflags; eflags =cpu_cc_compute_all (env,CC_OP ); - d =cpu_ldq_data (env, a0); + d =cpu_ldq_data_ra (env, a0,GETPC ());if (d == (((uint64_t )env->regs[R_EDX ] <<32 ) | (uint32_t )env->regs[R_EAX ])) { -cpu_stq_data (env, a0, ((uint64_t )env->regs[R_ECX ] <<32 ) | (uint32_t )env->regs[R_EBX ]); +cpu_stq_data_ra (env, a0, ((uint64_t )env->regs[R_ECX ] <<32 ) + | (uint32_t )env->regs[R_EBX ],GETPC ()); eflags |=CC_Z ; }else {/* always do the store */ -cpu_stq_data (env, a0, d); +cpu_stq_data_ra (env, a0, d,GETPC ()); env->regs[R_EDX ] = (uint32_t )(d >>32 ); env->regs[R_EAX ] = (uint32_t )d; eflags &= ~CC_Z ;
@@ -62,19 +63,19 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0)int eflags;if ((a0 &0xf ) !=0 ) { -raise_exception (env,EXCP0D_GPF ); +raise_exception (env,EXCP0D_GPF ,GETPC ()); } eflags =cpu_cc_compute_all (env,CC_OP ); - d0 =cpu_ldq_data (env, a0); - d1 =cpu_ldq_data (env, a0 +8 ); + d0 =cpu_ldq_data_ra (env, a0,GETPC ()); + d1 =cpu_ldq_data_ra (env, a0 +8 ,GETPC ());if (d0 == env->regs[R_EAX ] && d1 == env->regs[R_EDX ]) { -cpu_stq_data (env, a0, env->regs[R_EBX ]); -cpu_stq_data (env, a0 +8 , env->regs[R_ECX ]); +cpu_stq_data_ra (env, a0, env->regs[R_EBX ],GETPC ()); +cpu_stq_data_ra (env, a0 +8 , env->regs[R_ECX ],GETPC ()); eflags |=CC_Z ; }else {/* always do the store */ -cpu_stq_data (env, a0, d0); -cpu_stq_data (env, a0 +8 , d1); +cpu_stq_data_ra (env, a0, d0,GETPC ()); +cpu_stq_data_ra (env, a0 +8 , d1,GETPC ()); env->regs[R_EDX ] = d1; env->regs[R_EAX ] = d0; eflags &= ~CC_Z ;
@@ -87,11 +88,11 @@ void helper_boundw(CPUX86State *env, target_ulong a0, int v) {int low, high; - low =cpu_ldsw_data (env, a0); - high =cpu_ldsw_data (env, a0 +2 ); + low =cpu_ldsw_data_ra (env, a0,GETPC ()); + high =cpu_ldsw_data_ra (env, a0 +2 ,GETPC ()); v = (int16_t )v;if (v < low || v > high) { -raise_exception (env,EXCP05_BOUND ); +raise_exception (env,EXCP05_BOUND ,GETPC ()); } }
@@ -99,10 +100,10 @@ void helper_boundl(CPUX86State *env, target_ulong a0, int v) {int low, high; - low =cpu_ldl_data (env, a0); - high =cpu_ldl_data (env, a0 +4 ); + low =cpu_ldl_data_ra (env, a0,GETPC ()); + high =cpu_ldl_data_ra (env, a0 +4 ,GETPC ());if (v < low || v > high) { -raise_exception (env,EXCP05_BOUND ); +raise_exception (env,EXCP05_BOUND ,GETPC ()); } }
@@ -122,11 +123,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,X86CPU *cpu =X86_CPU (cs);CPUX86State *env = &cpu->env; -if (retaddr) { -/* now we have a real cpu fault */ -cpu_restore_state (cs, retaddr); - } - raise_exception_err(env, cs->exception_index, env->error_code); + raise_exception_err(env, cs->exception_index, env->error_code, retaddr); } }#endif
target-i386/misc_helper.c +6 -6
@@ -68,7 +68,7 @@ void helper_single_step(CPUX86State *env)check_hw_breakpoints (env, true); env->dr[6 ] |=DR6_BS ;#endif -raise_exception (env,EXCP01_DB ); +raise_exception (env,EXCP01_DB ,0 ); }void helper_cpuid (CPUX86State *env)
@@ -187,7 +187,7 @@ void helper_rdtsc(CPUX86State *env)uint64_t val;if ((env->cr[4 ] &CR4_TSD_MASK ) && ((env->hflags &HF_CPL_MASK ) !=0 )) { -raise_exception (env,EXCP0D_GPF ); +raise_exception (env,EXCP0D_GPF ,GETPC ()); }cpu_svm_check_intercept_param (env,SVM_EXIT_RDTSC ,0 );
@@ -205,13 +205,13 @@ void helper_rdtscp(CPUX86State *env)void helper_rdpmc (CPUX86State *env) {if ((env->cr[4 ] &CR4_PCE_MASK ) && ((env->hflags &HF_CPL_MASK ) !=0 )) { -raise_exception (env,EXCP0D_GPF ); +raise_exception (env,EXCP0D_GPF ,GETPC ()); }cpu_svm_check_intercept_param (env,SVM_EXIT_RDPMC ,0 );/* currently unimplemented */ qemu_log_mask (LOG_UNIMP ,"x86: unimplemented rdpmc\n" ); -raise_exception_err (env,EXCP06_ILLOP ,0 ); +raise_exception_err (env,EXCP06_ILLOP ,0 ,GETPC ()); }#if defined(CONFIG_USER_ONLY )
@@ -556,7 +556,7 @@ void helper_hlt(CPUX86State *env, int next_eip_addend)void helper_monitor (CPUX86State *env,target_ulong ptr) {if ((uint32_t )env->regs[R_ECX ] !=0 ) { -raise_exception (env,EXCP0D_GPF ); +raise_exception (env,EXCP0D_GPF ,GETPC ()); }/* XXX: store address? */ cpu_svm_check_intercept_param (env,SVM_EXIT_MONITOR ,0 );
@@ -568,7 +568,7 @@ void helper_mwait(CPUX86State *env, int next_eip_addend)X86CPU *cpu;if ((uint32_t )env->regs[R_ECX ] !=0 ) { -raise_exception (env,EXCP0D_GPF ); +raise_exception (env,EXCP0D_GPF ,GETPC ()); }cpu_svm_check_intercept_param (env,SVM_EXIT_MWAIT ,0 ); env->eip += next_eip_addend;
target-i386/ops_sse.h +1 -1
@@ -483,7 +483,7 @@ void glue(helper_maskmov, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,for (i =0 ; i < (8 <<SHIFT ); i++) {if (s->B (i) &0x80 ) { -cpu_stb_data (env, a0 + i, d->B (i)); +cpu_stb_data_ra (env, a0 + i, d->B (i),GETPC ()); } } }
target-i386/seg_helper.c +363 -349
1,614 lines not on this page · raw message
target-i386/svm_helper.c +2 -2
@@ -354,7 +354,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)void helper_vmmcall (CPUX86State *env) {cpu_svm_check_intercept_param (env,SVM_EXIT_VMMCALL ,0 ); -raise_exception (env,EXCP06_ILLOP ); +raise_exception (env,EXCP06_ILLOP ,GETPC ()); }void helper_vmload (CPUX86State *env,int aflag)
@@ -457,7 +457,7 @@ void helper_skinit(CPUX86State *env) {cpu_svm_check_intercept_param (env,SVM_EXIT_SKINIT ,0 );/* XXX: not implemented */ -raise_exception (env,EXCP06_ILLOP ); +raise_exception (env,EXCP06_ILLOP ,GETPC ()); }void helper_invlpga (CPUX86State *env,int aflag)
target-i386/translate.c +0 -25
@@ -2303,7 +2303,6 @@ static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip)if (s->pe && !s->vm86) {/* XXX: optimize by finding processor state dynamically */ gen_update_cc_op (s); -gen_jmp_im (cur_eip);tcg_gen_trunc_tl_i32 (cpu_tmp2_i32, cpu_T[0 ]);gen_helper_load_seg (cpu_env,tcg_const_i32 (seg_reg), cpu_tmp2_i32);/* abort translation because the addseg value may change or
@@ -4842,21 +4841,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,case 6 :/* div */ switch (ot) {case MO_8 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_divb_AL (cpu_env, cpu_T[0 ]);break ;case MO_16 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_divw_AX (cpu_env, cpu_T[0 ]);break ;default :case MO_32 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_divl_EAX (cpu_env, cpu_T[0 ]);break ;#ifdef TARGET_X86_64 case MO_64 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_divq_EAX (cpu_env, cpu_T[0 ]);break ;#endif
@@ -4865,21 +4860,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,case 7 :/* idiv */ switch (ot) {case MO_8 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_idivb_AL (cpu_env, cpu_T[0 ]);break ;case MO_16 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_idivw_AX (cpu_env, cpu_T[0 ]);break ;default :case MO_32 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_idivl_EAX (cpu_env, cpu_T[0 ]);break ;#ifdef TARGET_X86_64 case MO_64 : -gen_jmp_im (pc_start - s->cs_base);gen_helper_idivq_EAX (cpu_env, cpu_T[0 ]);break ;#endif
@@ -5212,7 +5203,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,if (dflag ==MO_64 ) {if (!(s->cpuid_ext_features &CPUID_EXT_CX16 )) goto illegal_op; -gen_jmp_im (pc_start - s->cs_base);gen_update_cc_op (s);gen_lea_modrm (env, s, modrm);gen_helper_cmpxchg16b (cpu_env, cpu_A0);
@@ -5221,7 +5211,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, {if (!(s->cpuid_features &CPUID_CX8 )) goto illegal_op; -gen_jmp_im (pc_start - s->cs_base);gen_update_cc_op (s);gen_lea_modrm (env, s, modrm);gen_helper_cmpxchg8b (cpu_env, cpu_A0);
@@ -5838,7 +5827,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,break ;case 0x0c :/* fldenv mem */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fldenv (cpu_env, cpu_A0,tcg_const_i32 (dflag -1 ));break ;case 0x0d :/* fldcw mem */
@@ -5848,7 +5836,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,break ;case 0x0e :/* fnstenv mem */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fstenv (cpu_env, cpu_A0,tcg_const_i32 (dflag -1 ));break ;case 0x0f :/* fnstcw mem */
@@ -5858,23 +5845,19 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,break ;case 0x1d :/* fldt mem */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fldt_ST0 (cpu_env, cpu_A0);break ;case 0x1f :/* fstpt mem */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fstt_ST0 (cpu_env, cpu_A0);gen_helper_fpop (cpu_env);break ;case 0x2c :/* frstor mem */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_frstor (cpu_env, cpu_A0,tcg_const_i32 (dflag -1 ));break ;case 0x2e :/* fnsave mem */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fsave (cpu_env, cpu_A0,tcg_const_i32 (dflag -1 ));break ;case 0x2f :/* fnstsw mem */
@@ -5889,7 +5872,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,break ;case 0x3e :/* fbstp */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fbst_ST0 (cpu_env, cpu_A0);gen_helper_fpop (cpu_env);break ;
@@ -5925,7 +5907,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,case 0 :/* fnop */ /* check exceptions (FreeBSD FPU probe) */ gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fwait (cpu_env);break ;default :
@@ -6896,7 +6877,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,gen_exception (s,EXCP07_PREX , pc_start - s->cs_base); }else {gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fwait (cpu_env); }break ;
@@ -6980,7 +6960,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, goto illegal_op;gen_op_mov_v_reg (ot, cpu_T[0 ], reg);gen_lea_modrm (env, s, modrm); -gen_jmp_im (pc_start - s->cs_base);tcg_gen_trunc_tl_i32 (cpu_tmp2_i32, cpu_T[0 ]);if (ot ==MO_16 ) {gen_helper_boundw (cpu_env, cpu_A0, cpu_tmp2_i32);
@@ -7172,7 +7151,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, }else {gen_svm_check_intercept (s, pc_start,SVM_EXIT_LDTR_WRITE );gen_ldst_modrm (env, s, modrm,MO_16 ,OR_TMP0 ,0 ); -gen_jmp_im (pc_start - s->cs_base);tcg_gen_trunc_tl_i32 (cpu_tmp2_i32, cpu_T[0 ]);gen_helper_lldt (cpu_env, cpu_tmp2_i32); }
@@ -7193,7 +7171,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, }else {gen_svm_check_intercept (s, pc_start,SVM_EXIT_TR_WRITE );gen_ldst_modrm (env, s, modrm,MO_16 ,OR_TMP0 ,0 ); -gen_jmp_im (pc_start - s->cs_base);tcg_gen_trunc_tl_i32 (cpu_tmp2_i32, cpu_T[0 ]);gen_helper_ltr (cpu_env, cpu_tmp2_i32); }
@@ -7727,7 +7704,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, }gen_lea_modrm (env, s, modrm);gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fxsave (cpu_env, cpu_A0,tcg_const_i32 (dflag ==MO_64 ));break ;case 1 :/* fxrstor */
@@ -7740,7 +7716,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, }gen_lea_modrm (env, s, modrm);gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_fxrstor (cpu_env, cpu_A0,tcg_const_i32 (dflag ==MO_64 ));break ;case 2 :/* ldmxcsr */
Re: [Qemu-devel] [PATCH v2 3/3] target-i386: fix memory operations in helpers
Quoted 19 lines · Pavel Dovgalyuk
This patch passes TB return address into softmmu functions that are invoked from target helpers. This allows correct PC and icount recovering while handling MMU faults.
Signed-off-by Pavel Dovgalyuk <[email protected]>--- target-i386/cc_helper.c | 2 target-i386/cpu.h | 5 target-i386/excp_helper.c | 23 + target-i386/fpu_helper.c | 146 +++++---- target-i386/helper.c | 4 target-i386/int_helper.c | 32 +- target-i386/mem_helper.c | 39 +- target-i386/misc_helper.c | 12 - target-i386/ops_sse.h | 2 target-i386/seg_helper.c | 712 +++++++++++++++++++++++---------------------- target-i386/svm_helper.c | 4 target-i386/translate.c | 25 -- 12 files changed, 506 insertions(+), 500 deletions(-)
[ snip ]
Quoted 5 lines
target-i386/excp_helper.c +0
-0
target-i386/excp_helper.c static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, · line 117
Quoted 9 lines
target-i386/excp_helper.c +4
-0@@ -108,6 +109,10 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, env->error_code = error_code; env->exception_is_int = is_int; env->exception_next_eip = env->eip + next_eip_addend; +if (retaddr) { +/* now we have a real cpu fault */ +cpu_restore_state (cs, retaddr); + }cpu_loop_exit (cs); }
If we have to add this pattern to all targets, it's probably better to add a cpu_loop_exit function which takes a return address in argument to the core code. This also has the advantage that we know that all code has been converted once cpu_loop_exit can be removed.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 3 lines · Pavel Dovgalyuk
This set of patches fixes exception handling for MIPS and i386 targets. These targets contain instructions that break correct execution in icount/TCG modes (MIPS) and in regular TCG mode (i386).
Just to be clear, this is not something specific to MIPS and i386. Every target which call cpu_loop_exit without doing a retranslation is affected by the icount bug.
Quoted 24 lines
Incorrect execution for i386 is causes by exceptions raised by MMU functions. MMU helper functions are called from generated code and other helper functions. In both cases they try to get function's return address for restoring virtual CPU state.
When MMU helper is called from some other helper function (like
helper_maskmov_xmm) through cpu_st* function, the return address will point to that helper. That is why CPU state cannot be restored in the case of MMU fault.This bug can occur when maskmov instruction is located in the middle of the translation block.
Execution sequence for this example:
TB start:
PC1: instr1
instr2
PC2: maskmov <page fault>
<page fault processing>
PC1: instr1
instr2
maskmov
Quoted 7 lines
At the start of TB execution guest PC points to instr1. When page fault occurs QEMU tries to restore guest PC (which should be equal to PC2). It reads host PC from the call stack and checks whether it points to TB or not. Bug in ldst helpers implementation provides incorrect host PC, which is not located within the TB. That's why QEMU cannot recover guest PC and it remains the same (PC1). After page fault processing QEMU restarts TB and executes instr1 and instr2 for the second time, because guest PC was not recovered.
Also just to be clear, the way you propose is just one way to fix the issue. The other way (which is used for all similar instructions) is to call just before the helper:
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);Quoted 26 lines
Bugs in MIPS helper functions do not break the execution in regular TCG mode, because PC value is updated before calling the functions that can raise an exception. But icount value cannot be updated this way. Therefore exceptions make execution in icount mode non-determinisic.
In icount mode every translation block looks as follows:if icount < n then exit
icount -= n
instr1
instr2
...
instrn
exitWhen one of these instructions initiates an exception, icount should be restored and adjusted number of instructions should be subtracted from icount instead of initial n.
tlb_fillfunction passes retaddr toraise_exception, which allows restoring current instructions in TB and correct icount calculation.When exception triggered with other function (e.g. by embedding call to exception raising helper into TB), then PC is not passed as retaddr and correct icount is not recovered. In such cases icount will be decreased by the value equal to the size of TB.
Quoted 5 lines
This behavior leads to incorrect values of virtual clock and non-deterministic execution of the code.
These patches passes pointer to the translation block code to the exception handler. It allows correct restoring of PC and icount values.
While I think it's the correct way for load/stores or FPU exceptions, I am not convinced we should do that in all cases. Retranslation has a cost, and when the exception is likely to occur, it's better to save the CPU state instead of going for a retranslation. Actually we had to rollback such a change on SH4, as it has some performances issues. See commit 1012740098d0307ce5d957ebbe9a7f020da7f574.
One way to fix that would be to reduce the cost of retranslation, for example by using a kind of exception table that we generate at the same time of the TB.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 9 lines
From: Aurelien Jarno [mailto:[email protected]]
Quoted 3 lines · Pavel Dovgalyuk
This set of patches fixes exception handling for MIPS and i386 targets. These targets contain instructions that break correct execution in icount/TCG modes (MIPS) and in regular TCG mode (i386).
Just to be clear, this is not something specific to MIPS and i386. Every target which call
cpu_loop_exitwithout doing a retranslation is affected by the icount bug.
True. But I haven't checked other platforms yet.
Quoted 32 lines
Quoted 24 lines
Incorrect execution for i386 is causes by exceptions raised by MMU functions. MMU helper functions are called from generated code and other helper functions. In both cases they try to get function's return address for restoring virtual CPU state.
When MMU helper is called from some other helper function (like
helper_maskmov_xmm) through cpu_st* function, the return address will point to that helper. That is why CPU state cannot be restored in the case of MMU fault.This bug can occur when maskmov instruction is located in the middle of the translation block.
Execution sequence for this example:
TB start:
PC1: instr1
instr2
PC2: maskmov <page fault>
<page fault processing>
PC1: instr1
instr2
maskmovQuoted 7 lines
At the start of TB execution guest PC points to instr1. When page fault occurs QEMU tries to restore guest PC (which should be equal to PC2). It reads host PC from the call stack and checks whether it points to TB or not. Bug in ldst helpers implementation provides incorrect host PC, which is not located within the TB. That's why QEMU cannot recover guest PC and it remains the same (PC1). After page fault processing QEMU restarts TB and executes instr1 and instr2 for the second time, because guest PC was not recovered.
Quoted 5 lines
Also just to be clear, the way you propose is just one way to fix the issue. The other way (which is used for all similar instructions) is to call just before the helper:
gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base);
This would work only for regular execution, not for icount one.
Quoted 32 lines
Quoted 26 lines
Bugs in MIPS helper functions do not break the execution in regular TCG mode, because PC value is updated before calling the functions that can raise an exception. But icount value cannot be updated this way. Therefore exceptions make execution in icount mode non-determinisic.
In icount mode every translation block looks as follows:if icount < n then exit
icount -= n
instr1
instr2
...
instrn
exitWhen one of these instructions initiates an exception, icount should be restored and adjusted number of instructions should be subtracted from icount instead of initial n.
tlb_fillfunction passes retaddr toraise_exception, which allows restoring current instructions in TB and correct icount calculation.When exception triggered with other function (e.g. by embedding call to exception raising helper into TB), then PC is not passed as retaddr and correct icount is not recovered. In such cases icount will be decreased by the value equal to the size of TB.
Quoted 5 lines
This behavior leads to incorrect values of virtual clock and non-deterministic execution of the code.
These patches passes pointer to the translation block code to the exception handler. It allows correct restoring of PC and icount values.
Quoted 6 lines
While I think it's the correct way for load/stores or FPU exceptions, I am not convinced we should do that in all cases. Retranslation has a cost, and when the exception is likely to occur, it's better to save the CPU state instead of going for a retranslation. Actually we had to rollback such a change on SH4, as it has some performances issues. See commit
1012740098d0307ce5d957ebbe9a7f020da7f574.
Ok, I'll double check the patch to make translation to stop when exception is likely to occur after current instruction.
Quoted 3 lines
One way to fix that would be to reduce the cost of retranslation, for example by using a kind of exception table that we generate at the same time of the TB.
What exactly do you mean?
Pavel Dovgalyuk
Quoted 14 lines · Pavel Dovgalyuk
In icount mode every translation block looks as follows:
if icount < n then exit
icount -= n
instr1
instr2
...
instrn
exitWhen one of these instructions initiates an exception, icount should be restored and adjusted number of instructions should be subtracted from icount instead of initial n.
Quoted 7 lines
tlb_fillfunction passes retaddr toraise_exception, which allows restoring current instructions in TB and correct icount calculation.When exception triggered with other function (e.g. by embedding call to exception raising helper into TB), then PC is not passed as retaddr and correct icount is not recovered. In such cases icount will be decreased by the value equal to the size of TB.
Looking at how icount work, I see it's basically a variable in the CPU state (icount_decr.u16.low), which is already accessed from the TB. Couldn't we adjust it using additional code before generating an exception, when in icount mode.
For example for MIPS, we can add some code before generate_exception which use the value from s->gen_opc_icount[j] to adjust the variable icount_decr.u16.low.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 24 lines
From: Aurelien Jarno [mailto:[email protected]]
Quoted 14 lines · Pavel Dovgalyuk
In icount mode every translation block looks as follows:
if icount < n then exit
icount -= n
instr1
instr2
...
instrn
exitWhen one of these instructions initiates an exception, icount should be restored and adjusted number of instructions should be subtracted from icount instead of initial n.
Quoted 7 lines
tlb_fillfunction passes retaddr toraise_exception, which allows restoring current instructions in TB and correct icount calculation.When exception triggered with other function (e.g. by embedding call to exception raising helper into TB), then PC is not passed as retaddr and correct icount is not recovered. In such cases icount will be decreased by the value equal to the size of TB.
Quoted 8 lines
Looking at how icount work, I see it's basically a variable in the CPU state (icount_decr.u16.low), which is already accessed from the TB. Couldn't we adjust it using additional code before generating an exception, when in icount mode.
For example for MIPS, we can add some code before
generate_exceptionwhich use the value from s->gen_opc_icount[j] to adjust the variable icount_decr.u16.low.
It is possible, but it will incur additional overhead, because we will have to update icount every time the exception might be generated. We'll have to update icount value before and after every helper call, that can cause an exception:
icount -= n
...instr_k
icount += n - k
helper
icount -= n - k
...
And this overhead will slowdown the code even if no exception occur.
Pavel Dovgalyuk
Quoted 46 lines · Pavel Dovgaluk
Quoted 24 lines
From: Aurelien Jarno [mailto:[email protected]]
Quoted 14 lines · Pavel Dovgalyuk
In icount mode every translation block looks as follows:
if icount < n then exit
icount -= n
instr1
instr2
...
instrn
exitWhen one of these instructions initiates an exception, icount should be restored and adjusted number of instructions should be subtracted from icount instead of initial n.
Quoted 7 lines
tlb_fillfunction passes retaddr toraise_exception, which allows restoring current instructions in TB and correct icount calculation.When exception triggered with other function (e.g. by embedding call to exception raising helper into TB), then PC is not passed as retaddr and correct icount is not recovered. In such cases icount will be decreased by the value equal to the size of TB.
Quoted 8 lines
Looking at how icount work, I see it's basically a variable in the CPU state (icount_decr.u16.low), which is already accessed from the TB. Couldn't we adjust it using additional code before generating an exception, when in icount mode.
For example for MIPS, we can add some code before
generate_exceptionwhich use the value from s->gen_opc_icount[j] to adjust the variable icount_decr.u16.low.It is possible, but it will incur additional overhead, because we will have to update icount every time the exception might be generated. We'll have to update icount value before and after every helper call, that can cause an exception:
icount -= n
...instr_k
icount += n - k
helper
icount -= n - k
...
Quoted 1 line
And this overhead will slowdown the code even if no exception occur.
That's where I might disagree. Retranslation seems a very good idea on the paper, but in practice it doesn't seems to always bring the performance improvement it should. In addition it seems to be highly dependent on the target. Just to give some numbers, on MIPS (as your patch originally concerns this architecture), 40% of code generation is actually due to retranslation. The problem is that over the time we have improved a lot the code generation (liveness analysis, better register allocation, constant propagation, ...) and thus we have increased the code generation time. While it clearly has some benefits when this code is actually executed, it's not the case when the code is simply retranslated. In short we spend more time to find the CPU state corresponding to an exception than before.
A simple way to show that is to apply the simple patch below, which disable retranslation and save the CPU state before each instruction:
target-mips/translate.c +1 -0
@@ -19435,6 +19435,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,LOG_DISAS ("\ntb %p idx %d hflags %04x\n" , tb, ctx.mem_idx, ctx.hflags);gen_tb_start (tb);while (ctx.bstate ==BS_NONE ) { +save_cpu_state (&ctx,1 );if (unlikely (!QTAILQ_EMPTY (&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {if (bp->pc == ctx.pc) {
translate-all.c +2 -0
@@ -212,6 +212,8 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,int64_t ti;#endif +return -1 ; +#ifdef CONFIG_PROFILER ti =profile_getclock ();#endif
On x86, this patch brings a 5% boot time improvement on MIPS. One of the reason is that the TCG code generator has a good knowledge about which TCG ops or helpers can trigger an exception, so it can optimize out part of the instructions saving the CPU state. I guess that the host CPUs have also evolved over the time, now being superscalar and out-of-order so that saving the CPU state can be done "in background". Also it's just a quick and dirty patch, we can probably even do better.
All of that to say that I am worried for the performances to see more paths through the retranslation code, especially on MIPS as it seems to be costly. That said I haven't really look in details at other targets, nor hosts.
Now to come back about your patches, we might want to simply fix icount first, even if it has some performance impact, and deal with the retranslation issue separately, as it concerns more than just icount.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 63 lines
From: Aurelien Jarno [mailto:[email protected]]
Quoted 46 lines · Pavel Dovgaluk
Quoted 24 lines
From: Aurelien Jarno [mailto:[email protected]]
Quoted 14 lines · Pavel Dovgalyuk
In icount mode every translation block looks as follows:
if icount < n then exit
icount -= n
instr1
instr2
...
instrn
exitWhen one of these instructions initiates an exception, icount should be restored and adjusted number of instructions should be subtracted from icount instead of initial n.
Quoted 7 lines
tlb_fillfunction passes retaddr toraise_exception, which allows restoring current instructions in TB and correct icount calculation.When exception triggered with other function (e.g. by embedding call to exception raising helper into TB), then PC is not passed as retaddr and correct icount is not recovered. In such cases icount will be decreased by the value equal to the size of TB.
Quoted 8 lines
Looking at how icount work, I see it's basically a variable in the CPU state (icount_decr.u16.low), which is already accessed from the TB. Couldn't we adjust it using additional code before generating an exception, when in icount mode.
For example for MIPS, we can add some code before
generate_exceptionwhich use the value from s->gen_opc_icount[j] to adjust the variable icount_decr.u16.low.It is possible, but it will incur additional overhead, because we will have to update icount every time the exception might be generated. We'll have to update icount value before and after every helper call, that can cause an exception:
icount -= n
...instr_k
icount += n - k
helper
icount -= n - k
...Quoted 1 line
And this overhead will slowdown the code even if no exception occur.
That's where I might disagree. Retranslation seems a very good idea on the paper, but in practice it doesn't seems to always bring the performance improvement it should. In addition it seems to be highly dependent on the target. Just to give some numbers, on MIPS (as your patch originally concerns this architecture), 40% of code generation is actually due to retranslation. The problem is that over the time we have improved a lot the code generation (liveness analysis, better register allocation, constant propagation, ...) and thus we have increased the code generation time. While it clearly has some benefits when this code is actually executed, it's not the case when the code is simply retranslated. In short we spend more time to find the CPU state corresponding to an exception than before.
...
Quoted 5 lines
All of that to say that I am worried for the performances to see more paths through the retranslation code, especially on MIPS as it seems to be costly. That said I haven't really look in details at other targets, nor hosts.
I fixed syscalls, exceptions that occur without any conditions,
and removed redundant calls to save_cpu_state. Then I measured the performance without enabling icount. And Linux boots even faster than with original version. I'll submit this version for review soon.
Quoted 3 lines
Now to come back about your patches, we might want to simply fix icount first, even if it has some performance impact, and deal with the retranslation issue separately, as it concerns more than just icount.
Pavel Dovgalyuk
Quoted 8 lines · Aurelien Jarno
On x86, this patch brings a 5% boot time improvement on MIPS. One of the reason is that the TCG code generator has a good knowledge about which TCG ops or helpers can trigger an exception, so it can optimize out part of the instructions saving the CPU state. I guess that the host CPUs have also evolved over the time, now being superscalar and out-of-order so that saving the CPU state can be done "in background". Also it's just a quick and dirty patch, we can probably even do better.
Quoted 4 lines
All of that to say that I am worried for the performances to see more paths through the retranslation code, especially on MIPS as it seems to be costly. That said I haven't really look in details at other targets, nor hosts.
For an i386 guest still on an x86 host, I get a 4% slower boot time by not using retranslation (see patch below). This is not that much compared to the complexity retranslation bring us.
target-i386/translate.c +3 -0
@@ -8001,6 +8001,9 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu,gen_tb_start (tb);for (;;) { +gen_update_cc_op (dc); +gen_jmp_im (pc_ptr - dc->cs_base); +if (unlikely (!QTAILQ_EMPTY (&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == pc_ptr &&
translate-all.c +2 -0
@@ -212,6 +212,8 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,int64_t ti;#endif +return -1 ; +#ifdef CONFIG_PROFILER ti =profile_getclock ();#endif
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 3 lines · Aurelien Jarno
For an i386 guest still on an x86 host, I get a 4% slower boot time by not using retranslation (see patch below). This is not that much compared to the complexity retranslation bring us.
QEMU could just always compute and store the restore_state information.
TCG needs to help filling it in (a new TCG opcode?), but it should be easy.
Paolo
Quoted 27 lines
target-i386/translate.c +3
-0@@ -8001,6 +8001,9 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu,gen_tb_start (tb);for (;;) { +gen_update_cc_op (dc); +gen_jmp_im (pc_ptr - dc->cs_base); +if (unlikely (!QTAILQ_EMPTY (&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == pc_ptr &&translate-all.c +2
-0@@ -212,6 +212,8 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,int64_t ti;#endif +return -1 ; +#ifdef CONFIG_PROFILER ti =profile_getclock ();#endif
Quoted 7 lines · Paolo Bonzini
Quoted 3 lines · Aurelien Jarno
For an i386 guest still on an x86 host, I get a 4% slower boot time by not using retranslation (see patch below). This is not that much compared to the complexity retranslation bring us.
QEMU could just always compute and store the
restore_stateinformation.
TCG needs to help filling it in (a new TCG opcode?), but it should be easy.
Yes, that was another approach I have in mind (I called it exception table in my other mail), but it requires a tiny more work than just saving the CPU state all the time. The problem is that the state information we want to save are varying for target to target. Going through a TCG opcode means we can use the liveness analysis pass to save the minimum amount of data.
That said I would like to push further the idea of always saving the CPU state a bit more to see if we can keep the same performances. There are still improvements to do, by removing more code on the core side (like finding the call to tb_finc_pc which is now useless), or on the target side by checking/improving helper flags. We might save the CPU state too often if a helper doesn't declare it doesn't touch globals.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 4 lines · Aurelien Jarno
Quoted 2 lines
Quoted 2 lines
QEMU could just always compute and store the
restore_stateinformation.
TCG needs to help filling it in (a new TCG opcode?), but it should be easy.Yes, that was another approach I have in mind (I called it exception table in my other mail),
Okay, understood. My idea was more like always generating the gen_op_* arrays.
Quoted 5 lines
but it requires a tiny more work than just
saving the CPU state all the time. The problem is that the state information we want to save are varying for target to target. Going through a TCG opcode means we can use the liveness analysis pass to save the minimum amount of data.
I mentioned a TCG opcode because the target PC is not available inside the translator. So the translator could pepper the TCG instruction stream with things like
checkpoint $target_pc, $target_cc_op, $0
TCG can then use them to fill in an array stored inside the TranslationBlock, together with the host PC. Since the gen_opc_pc, gen_opc_instr_start, gen_opc_icount arrays are inside tcg_ctx, it may be a good idea to store the checkpoint information compressed in a byte array (e.g. as a series of ULEB128 values---the host and target PCs can even be stored as deltas from the last value).
As a first step, gen_intermediate_code_pc and tcg_gen_code_search_pc can then be merged into a single target-independent function that uncompresses the byte array up to the required host PC into tcg_ctx. Later you can optimize them to remove the tcg_ctx arrays altogether.
So the patches could be something like this:
1) SPARC: put the jump target information directly in gen_opc_* without using gen_opc_jump_pc (not trivial)
2) a few targets: instead of gen_opc_* arrays, use a new generic member of tcg_ctx (similar to how csbase is used generically), e.g. tcg_ctx.gen_opc_target1[] and tcg_ctx.gen_opc_target2[].
3) all targets: always fill in tcg_ctx.gen_*, even if search_pc is false
4) TCG: add support for a checkpoint operation, make it fill in tcg_ctx.gen_*
5) all targets: change explicit filling of tcg_ctx.gen_* to use the checkpoint operation
6) TCG/translate-all: convert gen_intermediate_code_pc as outlined above
Quoted 6 lines
That said I would like to push further the idea of always saving the CPU state a bit more to see if we can keep the same performances. There are still improvements to do, by removing more code on the core side (like finding the call to
tb_finc_pcwhich is now useless), or on the target side by checking/improving helper flags. We might save the CPU state too often if a helper doesn't declare it doesn't touch globals.
True, on the other hand there are a lot of helpers to audit...
Paolo
Quoted 11 lines · Paolo Bonzini
Quoted 4 lines · Aurelien Jarno
Quoted 2 lines
Quoted 2 lines
QEMU could just always compute and store the
restore_stateinformation.
TCG needs to help filling it in (a new TCG opcode?), but it should be easy.Yes, that was another approach I have in mind (I called it exception table in my other mail),
Okay, understood. My idea was more like always generating the gen_op_* arrays.
Quoted 8 lines
Quoted 5 lines
but it requires a tiny more work than just
saving the CPU state all the time. The problem is that the state information we want to save are varying for target to target. Going through a TCG opcode means we can use the liveness analysis pass to save the minimum amount of data.I mentioned a TCG opcode because the target PC is not available inside the translator. So the translator could pepper the TCG instruction
Well it is available through s->gen_opc_pc, but that's not that clean.
Quoted 3 lines
stream with things like
checkpoint $target_pc, $target_cc_op, $0
Yes, it's clearly better to add an explicit instruction for that. As said we can pass it through the liveness analysis. But it means that this instruction will have a variable number of arguments.
Quoted 6 lines
TCG can then use them to fill in an array stored inside the TranslationBlock, together with the host PC. Since the
gen_opc_pc,gen_opc_instr_start,gen_opc_icountarrays are insidetcg_ctx, it may be a good idea to store the checkpoint information compressed in a byte array (e.g. as a series of ULEB128 values---the host and target PCs can even be stored as deltas from the last value).
Either as deltas to the last value or as delta from the start of the TB. What I am worried about is the size of the checkpoint information, even if we do some compression, we might have one per guest instruction. I have implemented a naive version of that without compression, storing the checkpoint data at the end of the generated code, and it's about 30% of the size of the TB for MIPS. It's probably smaller on architectures storing only the PC. Also it's size is quite variable. That's why it's probably not a good idea to store it directly in the TranslationBlock. I don't like storing it directly in the generated code either, especially given this part is supposed to be executable.
Quoted 16 lines
As a first step,
gen_intermediate_code_pcandtcg_gen_code_search_pccan then be merged into a single target-independent function that uncompresses the byte array up to the required host PC intotcg_ctx. Later you can optimize them to remove thetcg_ctxarrays altogether.So the patches could be something like this:
1) SPARC: put the jump target information directly in gen_opc_* without using
gen_opc_jump_pc(not trivial)2) a few targets: instead of gen_opc_* arrays, use a new generic member of
tcg_ctx(similar to how csbase is used generically), e.g. tcg_ctx.gen_opc_target1[] and tcg_ctx.gen_opc_target2[].3) all targets: always fill in tcg_ctx.gen_*, even if
search_pcis false
Quoted 7 lines
4) TCG: add support for a checkpoint operation, make it fill in tcg_ctx.gen_*
5) all targets: change explicit filling of tcg_ctx.gen_* to use the checkpoint operation
6) TCG/translate-all: convert
gen_intermediate_code_pcas outlined above
That's sounds like a plan when I have more time ;-)
Aurelien
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 40 lines
From: Aurelien Jarno [mailto:[email protected]]
Quoted 7 lines · Paolo Bonzini
TCG can then use them to fill in an array stored inside the TranslationBlock, together with the host PC. Since the
gen_opc_pc,gen_opc_instr_start,gen_opc_icountarrays are insidetcg_ctx, it may be a good idea to store the checkpoint information compressed in a byte array (e.g. as a series of ULEB128 values---the host and target PCs can even be stored as deltas from the last value).Either as deltas to the last value or as delta from the start of the TB. What I am worried about is the size of the checkpoint information, even if we do some compression, we might have one per guest instruction. I have implemented a naive version of that without compression, storing the checkpoint data at the end of the generated code, and it's about 30% of the size of the TB for MIPS. It's probably smaller on architectures storing only the PC. Also it's size is quite variable. That's why it's probably not a good idea to store it directly in the TranslationBlock. I don't like storing it directly in the generated code either, especially given this part is supposed to be executable.
Quoted 19 lines
As a first step,
gen_intermediate_code_pcandtcg_gen_code_search_pccan then be merged into a single target-independent function that uncompresses the byte array up to the required host PC intotcg_ctx. Later you can optimize them to remove thetcg_ctxarrays altogether.So the patches could be something like this:
1) SPARC: put the jump target information directly in gen_opc_* without using
gen_opc_jump_pc(not trivial)2) a few targets: instead of gen_opc_* arrays, use a new generic member of
tcg_ctx(similar to how csbase is used generically), e.g. tcg_ctx.gen_opc_target1[] and tcg_ctx.gen_opc_target2[].3) all targets: always fill in tcg_ctx.gen_*, even if
search_pcis false4) TCG: add support for a checkpoint operation, make it fill in tcg_ctx.gen_*
Quoted 6 lines
Quoted 4 lines
5) all targets: change explicit filling of tcg_ctx.gen_* to use the checkpoint operation
6) TCG/translate-all: convert
gen_intermediate_code_pcas outlined aboveThat's sounds like a plan when I have more time ;-)
Doesn't this approach still require my fixes to work correctly?
Pavel Dovgalyuk
Quoted 40 lines · Pavel Dovgaluk
Quoted 40 lines
From: Aurelien Jarno [mailto:[email protected]]
Quoted 7 lines · Paolo Bonzini
TCG can then use them to fill in an array stored inside the TranslationBlock, together with the host PC. Since the
gen_opc_pc,gen_opc_instr_start,gen_opc_icountarrays are insidetcg_ctx, it may be a good idea to store the checkpoint information compressed in a byte array (e.g. as a series of ULEB128 values---the host and target PCs can even be stored as deltas from the last value).Either as deltas to the last value or as delta from the start of the TB. What I am worried about is the size of the checkpoint information, even if we do some compression, we might have one per guest instruction. I have implemented a naive version of that without compression, storing the checkpoint data at the end of the generated code, and it's about 30% of the size of the TB for MIPS. It's probably smaller on architectures storing only the PC. Also it's size is quite variable. That's why it's probably not a good idea to store it directly in the TranslationBlock. I don't like storing it directly in the generated code either, especially given this part is supposed to be executable.
Quoted 19 lines
As a first step,
gen_intermediate_code_pcandtcg_gen_code_search_pccan then be merged into a single target-independent function that uncompresses the byte array up to the required host PC intotcg_ctx. Later you can optimize them to remove thetcg_ctxarrays altogether.So the patches could be something like this:
1) SPARC: put the jump target information directly in gen_opc_* without using
gen_opc_jump_pc(not trivial)2) a few targets: instead of gen_opc_* arrays, use a new generic member of
tcg_ctx(similar to how csbase is used generically), e.g. tcg_ctx.gen_opc_target1[] and tcg_ctx.gen_opc_target2[].3) all targets: always fill in tcg_ctx.gen_*, even if
search_pcis false4) TCG: add support for a checkpoint operation, make it fill in tcg_ctx.gen_*
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
5) all targets: change explicit filling of tcg_ctx.gen_* to use the checkpoint operation
6) TCG/translate-all: convert
gen_intermediate_code_pcas outlined aboveThat's sounds like a plan when I have more time ;-)
Doesn't this approach still require my fixes to work correctly?
Yes it does.
Aurélien
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 23 lines · Pavel Dovgaluk
Quoted 9 lines
From: Aurelien Jarno [mailto:[email protected]]
Looking at how icount work, I see it's basically a variable in the CPU state (icount_decr.u16.low), which is already accessed from the TB. Couldn't we adjust it using additional code before generating an exception, when in icount mode.For example for MIPS, we can add some code before
generate_exceptionwhich use the value from s->gen_opc_icount[j] to adjust the variable icount_decr.u16.low.It is possible, but it will incur additional overhead, because we will have to update icount every time the exception might be generated. We'll have to update icount value before and after every helper call, that can cause an exception:
icount -= n
...instr_k
icount += n - k
helper
icount -= n - k
...
Quoted 1 line
And this overhead will slowdown the code even if no exception occur.
Right, this is a tradeoff: in some cases it's faster to assume no exception and handle state resync by doing a retranslate. In some cases it's faster to assume there will be an exception and do a manual sync. Guest load/store is obviously in the first category. Guest doing an instruction which always takes an exception (like syscall insns) is in the second category. For other cases there's a choice. We need to support both approaches; obviously you can argue for any particular case whether it should be approach 1 or approach 2.
thanks
-- PMM
Quoted 27 lines
From: Peter Maydell [mailto:[email protected]]
Quoted 23 lines · Pavel Dovgaluk
Quoted 9 lines
From: Aurelien Jarno [mailto:[email protected]]
Looking at how icount work, I see it's basically a variable in the CPU state (icount_decr.u16.low), which is already accessed from the TB. Couldn't we adjust it using additional code before generating an exception, when in icount mode.For example for MIPS, we can add some code before
generate_exceptionwhich use the value from s->gen_opc_icount[j] to adjust the variable icount_decr.u16.low.It is possible, but it will incur additional overhead, because we will have to update icount every time the exception might be generated. We'll have to update icount value before and after every helper call, that can cause an exception:
icount -= n
...instr_k
icount += n - k
helper
icount -= n - k
...Quoted 1 line
And this overhead will slowdown the code even if no exception occur.
Quoted 9 lines
Right, this is a tradeoff: in some cases it's faster to assume no exception and handle state resync by doing a retranslate. In some cases it's faster to assume there will be an exception and do a manual sync. Guest load/store is obviously in the first category. Guest doing an instruction which always takes an exception (like syscall insns) is in the second category. For other cases there's a choice. We need to support both approaches; obviously you can argue for any particular case whether it should be approach 1 or approach 2.
Syscall and non-implemented instructions are in third category - they always take an exception. In this case the translation should be stopped without any additional actions.
By the way, I implemented this 'third category' approach for mips and measured the performance. It does not show any performance degradation when compared to original unfixed version.
All other exception-generating helpers and instructions use approach 1.
Pavel Dovgalyuk