Date: Sun, 12 Nov 2006 12:49:44 -0500 From: Daniel Jacobowitz To: address@hidden Subject: Re: [Qemu-devel] [PATCH] Huge TLB performance improvement target-mips/cpu.h | 3 ++- target-mips/exec.h | 1 + target-mips/helper.c | 2 +- target-mips/mips-defs.h | 1 + target-mips/op_helper.c | 43 +++++++++++++++++++++++++++++++++++++------ target-mips/translate.c | 1 + 6 files changed, 43 insertions(+), 8 deletions(-) Index: qemu-work/target-mips/cpu.h =================================================================== --- qemu-work.orig/target-mips/cpu.h 2006-11-15 22:40:28.000000000 +0000 +++ qemu-work/target-mips/cpu.h 2006-11-15 22:41:04.000000000 +0000 @@ -94,7 +94,8 @@ #endif #if defined(MIPS_USES_R4K_TLB) - tlb_t tlb[MIPS_TLB_NB]; + tlb_t tlb[MIPS_TLB_MAX]; + uint32_t tlb_in_use; #endif uint32_t CP0_index; uint32_t CP0_random; Index: qemu-work/target-mips/exec.h =================================================================== --- qemu-work.orig/target-mips/exec.h 2006-11-15 22:40:28.000000000 +0000 +++ qemu-work/target-mips/exec.h 2006-11-15 22:41:04.000000000 +0000 @@ -115,5 +115,6 @@ void cpu_mips_store_count (CPUState *env, uint32_t value); void cpu_mips_store_compare (CPUState *env, uint32_t value); void cpu_mips_clock_init (CPUState *env); +void cpu_mips_tlb_flush (CPUState *env, int flush_global); #endif /* !defined(__QEMU_MIPS_EXEC_H__) */ Index: qemu-work/target-mips/helper.c =================================================================== --- qemu-work.orig/target-mips/helper.c 2006-11-15 15:13:46.000000000 +0000 +++ qemu-work/target-mips/helper.c 2006-11-15 22:41:04.000000000 +0000 @@ -46,7 +46,7 @@ tlb_t *tlb; int i, n; - for (i = 0; i < MIPS_TLB_NB; i++) { + for (i = 0; i < env->tlb_in_use; i++) { tlb = &env->tlb[i]; /* Check ASID, virtual page number & size */ if ((tlb->G == 1 || tlb->ASID == ASID) && Index: qemu-work/target-mips/mips-defs.h =================================================================== --- qemu-work.orig/target-mips/mips-defs.h 2006-11-15 15:12:16.000000000 +0000 +++ qemu-work/target-mips/mips-defs.h 2006-11-15 22:41:04.000000000 +0000 @@ -22,6 +22,7 @@ /* Uses MIPS R4Kc TLB model */ #define MIPS_USES_R4K_TLB #define MIPS_TLB_NB 16 +#define MIPS_TLB_MAX 128 /* basic FPU register support */ #define MIPS_USES_FPU 1 /* Define a implementation number of 1. Index: qemu-work/target-mips/op_helper.c =================================================================== --- qemu-work.orig/target-mips/op_helper.c 2006-11-15 22:40:28.000000000 +0000 +++ qemu-work/target-mips/op_helper.c 2006-11-15 22:41:04.000000000 +0000 @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "exec.h" +#include #define MIPS_DEBUG_DISAS @@ -367,7 +368,7 @@ env->CP0_EntryHi = val; /* If the ASID changes, flush qemu's TLB. */ if ((old & 0xFF) != (val & 0xFF)) - tlb_flush (env, 1); + cpu_mips_tlb_flush (env, 1); rn = "EntryHi"; break; case 11: @@ -568,7 +569,14 @@ /* TLB management */ #if defined(MIPS_USES_R4K_TLB) -static void invalidate_tlb (int idx) +void cpu_mips_tlb_flush (CPUState *env, int flush_global) +{ + /* Flush qemu's TLB and discard all shadowed entries. */ + tlb_flush (env, flush_global); + env->tlb_in_use = MIPS_TLB_NB; +} + +static void invalidate_tlb (int idx, int use_extra) { tlb_t *tlb; target_ulong addr; @@ -583,6 +591,15 @@ return; } + if (use_extra && env->tlb_in_use < MIPS_TLB_MAX) { + /* For tlbwr, we can shadow the discarded entry into + a new (fake) TLB entry, as long as the guest can not + tell that it's there. */ + memcpy (&env->tlb[env->tlb_in_use], tlb, sizeof (*tlb)); + env->tlb_in_use++; + return; + } + if (tlb->V0) { tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN); addr = tlb->VPN; @@ -601,6 +618,13 @@ } } +static void mips_tlb_flush_extra (CPUState *env) +{ + while (env->tlb_in_use > MIPS_TLB_NB) { + invalidate_tlb(--env->tlb_in_use, 0); + } +} + static void fill_tlb (int idx) { tlb_t *tlb; @@ -627,9 +651,14 @@ void do_tlbwi (void) { + /* Discard cached TLB entries. We could avoid doing this if the + tlbwi is just upgrading access permissions on the current entry; + that might be a further win. */ + mips_tlb_flush_extra (env); + /* Wildly undefined effects for CP0_index containing a too high value and MIPS_TLB_NB not being a power of two. But so does real silicon. */ - invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1)); + invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1), 0); fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1)); } @@ -637,7 +666,7 @@ { int r = cpu_mips_get_random(env); - invalidate_tlb(r); + invalidate_tlb(r, 1); fill_tlb(r); } @@ -674,8 +703,10 @@ tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)]; /* If this will change the current ASID, flush qemu's TLB. */ - if (ASID != tlb->ASID && tlb->G != 1) - tlb_flush (env, 1); + if (ASID != tlb->ASID) + cpu_mips_tlb_flush (env, 1); + + mips_tlb_flush_extra(env); env->CP0_EntryHi = tlb->VPN | tlb->ASID; size = (tlb->end - tlb->VPN) >> 12; Index: qemu-work/target-mips/translate.c =================================================================== --- qemu-work.orig/target-mips/translate.c 2006-11-15 22:40:28.000000000 +0000 +++ qemu-work/target-mips/translate.c 2006-11-15 22:41:04.000000000 +0000 @@ -2430,6 +2430,7 @@ env->PC = 0xBFC00000; #if defined (MIPS_USES_R4K_TLB) env->CP0_random = MIPS_TLB_NB - 1; + env->tlb_in_use = MIPS_TLB_NB; #endif env->CP0_Wired = 0; env->CP0_Config0 = MIPS_CONFIG0;