Commit 814b9a47490c4500fd105b524b8354764e6655e5

Authored by ths
1 parent ec230928

MIPS TLB performance improvements, by Daniel Jacobowitz.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2220 c046a42c-6fe2-441c-8c8c-71466251a162
target-mips/cpu.h
@@ -94,7 +94,8 @@ struct CPUMIPSState { @@ -94,7 +94,8 @@ struct CPUMIPSState {
94 94
95 #endif 95 #endif
96 #if defined(MIPS_USES_R4K_TLB) 96 #if defined(MIPS_USES_R4K_TLB)
97 - tlb_t tlb[MIPS_TLB_NB]; 97 + tlb_t tlb[MIPS_TLB_MAX];
  98 + uint32_t tlb_in_use;
98 #endif 99 #endif
99 uint32_t CP0_index; 100 uint32_t CP0_index;
100 uint32_t CP0_random; 101 uint32_t CP0_random;
target-mips/exec.h
@@ -115,5 +115,6 @@ uint32_t cpu_mips_get_count (CPUState *env); @@ -115,5 +115,6 @@ uint32_t cpu_mips_get_count (CPUState *env);
115 void cpu_mips_store_count (CPUState *env, uint32_t value); 115 void cpu_mips_store_count (CPUState *env, uint32_t value);
116 void cpu_mips_store_compare (CPUState *env, uint32_t value); 116 void cpu_mips_store_compare (CPUState *env, uint32_t value);
117 void cpu_mips_clock_init (CPUState *env); 117 void cpu_mips_clock_init (CPUState *env);
  118 +void cpu_mips_tlb_flush (CPUState *env, int flush_global);
118 119
119 #endif /* !defined(__QEMU_MIPS_EXEC_H__) */ 120 #endif /* !defined(__QEMU_MIPS_EXEC_H__) */
target-mips/helper.c
@@ -46,7 +46,7 @@ static int map_address (CPUState *env, target_ulong *physical, int *prot, @@ -46,7 +46,7 @@ static int map_address (CPUState *env, target_ulong *physical, int *prot,
46 tlb_t *tlb; 46 tlb_t *tlb;
47 int i, n; 47 int i, n;
48 48
49 - for (i = 0; i < MIPS_TLB_NB; i++) { 49 + for (i = 0; i < env->tlb_in_use; i++) {
50 tlb = &env->tlb[i]; 50 tlb = &env->tlb[i];
51 /* Check ASID, virtual page number & size */ 51 /* Check ASID, virtual page number & size */
52 if ((tlb->G == 1 || tlb->ASID == ASID) && 52 if ((tlb->G == 1 || tlb->ASID == ASID) &&
target-mips/mips-defs.h
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 /* Uses MIPS R4Kc TLB model */ 22 /* Uses MIPS R4Kc TLB model */
23 #define MIPS_USES_R4K_TLB 23 #define MIPS_USES_R4K_TLB
24 #define MIPS_TLB_NB 16 24 #define MIPS_TLB_NB 16
  25 +#define MIPS_TLB_MAX 128
25 /* basic FPU register support */ 26 /* basic FPU register support */
26 #define MIPS_USES_FPU 1 27 #define MIPS_USES_FPU 1
27 /* Define a implementation number of 1. 28 /* Define a implementation number of 1.
target-mips/op_helper.c
@@ -367,7 +367,7 @@ void do_mtc0 (int reg, int sel) @@ -367,7 +367,7 @@ void do_mtc0 (int reg, int sel)
367 env->CP0_EntryHi = val; 367 env->CP0_EntryHi = val;
368 /* If the ASID changes, flush qemu's TLB. */ 368 /* If the ASID changes, flush qemu's TLB. */
369 if ((old & 0xFF) != (val & 0xFF)) 369 if ((old & 0xFF) != (val & 0xFF))
370 - tlb_flush (env, 1); 370 + cpu_mips_tlb_flush (env, 1);
371 rn = "EntryHi"; 371 rn = "EntryHi";
372 break; 372 break;
373 case 11: 373 case 11:
@@ -568,7 +568,14 @@ void fpu_handle_exception(void) @@ -568,7 +568,14 @@ void fpu_handle_exception(void)
568 568
569 /* TLB management */ 569 /* TLB management */
570 #if defined(MIPS_USES_R4K_TLB) 570 #if defined(MIPS_USES_R4K_TLB)
571 -static void invalidate_tlb (int idx) 571 +void cpu_mips_tlb_flush (CPUState *env, int flush_global)
  572 +{
  573 + /* Flush qemu's TLB and discard all shadowed entries. */
  574 + tlb_flush (env, flush_global);
  575 + env->tlb_in_use = MIPS_TLB_NB;
  576 +}
  577 +
  578 +static void invalidate_tlb (int idx, int use_extra)
572 { 579 {
573 tlb_t *tlb; 580 tlb_t *tlb;
574 target_ulong addr; 581 target_ulong addr;
@@ -583,6 +590,15 @@ static void invalidate_tlb (int idx) @@ -583,6 +590,15 @@ static void invalidate_tlb (int idx)
583 return; 590 return;
584 } 591 }
585 592
  593 + if (use_extra && env->tlb_in_use < MIPS_TLB_MAX) {
  594 + /* For tlbwr, we can shadow the discarded entry into
  595 + a new (fake) TLB entry, as long as the guest can not
  596 + tell that it's there. */
  597 + env->tlb[env->tlb_in_use] = *tlb;
  598 + env->tlb_in_use++;
  599 + return;
  600 + }
  601 +
586 if (tlb->V0) { 602 if (tlb->V0) {
587 tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN); 603 tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
588 addr = tlb->VPN; 604 addr = tlb->VPN;
@@ -601,6 +617,14 @@ static void invalidate_tlb (int idx) @@ -601,6 +617,14 @@ static void invalidate_tlb (int idx)
601 } 617 }
602 } 618 }
603 619
  620 +static void mips_tlb_flush_extra (CPUState *env, int first)
  621 +{
  622 + /* Discard entries from env->tlb[first] onwards. */
  623 + while (env->tlb_in_use > first) {
  624 + invalidate_tlb(--env->tlb_in_use, 0);
  625 + }
  626 +}
  627 +
604 static void fill_tlb (int idx) 628 static void fill_tlb (int idx)
605 { 629 {
606 tlb_t *tlb; 630 tlb_t *tlb;
@@ -627,9 +651,14 @@ static void fill_tlb (int idx) @@ -627,9 +651,14 @@ static void fill_tlb (int idx)
627 651
628 void do_tlbwi (void) 652 void do_tlbwi (void)
629 { 653 {
  654 + /* Discard cached TLB entries. We could avoid doing this if the
  655 + tlbwi is just upgrading access permissions on the current entry;
  656 + that might be a further win. */
  657 + mips_tlb_flush_extra (env, MIPS_TLB_NB);
  658 +
630 /* Wildly undefined effects for CP0_index containing a too high value and 659 /* Wildly undefined effects for CP0_index containing a too high value and
631 MIPS_TLB_NB not being a power of two. But so does real silicon. */ 660 MIPS_TLB_NB not being a power of two. But so does real silicon. */
632 - invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1)); 661 + invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1), 0);
633 fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1)); 662 fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
634 } 663 }
635 664
@@ -637,7 +666,7 @@ void do_tlbwr (void) @@ -637,7 +666,7 @@ void do_tlbwr (void)
637 { 666 {
638 int r = cpu_mips_get_random(env); 667 int r = cpu_mips_get_random(env);
639 668
640 - invalidate_tlb(r); 669 + invalidate_tlb(r, 1);
641 fill_tlb(r); 670 fill_tlb(r);
642 } 671 }
643 672
@@ -660,6 +689,17 @@ void do_tlbp (void) @@ -660,6 +689,17 @@ void do_tlbp (void)
660 } 689 }
661 } 690 }
662 if (i == MIPS_TLB_NB) { 691 if (i == MIPS_TLB_NB) {
  692 + /* No match. Discard any shadow entries, if any of them match. */
  693 + for (i = MIPS_TLB_NB; i < env->tlb_in_use; i++) {
  694 + tlb = &env->tlb[i];
  695 +
  696 + /* Check ASID, virtual page number & size */
  697 + if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
  698 + mips_tlb_flush_extra (env, i);
  699 + break;
  700 + }
  701 + }
  702 +
663 env->CP0_index |= 0x80000000; 703 env->CP0_index |= 0x80000000;
664 } 704 }
665 } 705 }
@@ -674,8 +714,10 @@ void do_tlbr (void) @@ -674,8 +714,10 @@ void do_tlbr (void)
674 tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)]; 714 tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
675 715
676 /* If this will change the current ASID, flush qemu's TLB. */ 716 /* If this will change the current ASID, flush qemu's TLB. */
677 - if (ASID != tlb->ASID && tlb->G != 1)  
678 - tlb_flush (env, 1); 717 + if (ASID != tlb->ASID)
  718 + cpu_mips_tlb_flush (env, 1);
  719 +
  720 + mips_tlb_flush_extra(env, MIPS_TLB_NB);
679 721
680 env->CP0_EntryHi = tlb->VPN | tlb->ASID; 722 env->CP0_EntryHi = tlb->VPN | tlb->ASID;
681 size = (tlb->end - tlb->VPN) >> 12; 723 size = (tlb->end - tlb->VPN) >> 12;
target-mips/translate.c
@@ -2430,6 +2430,7 @@ CPUMIPSState *cpu_mips_init (void) @@ -2430,6 +2430,7 @@ CPUMIPSState *cpu_mips_init (void)
2430 env->PC = 0xBFC00000; 2430 env->PC = 0xBFC00000;
2431 #if defined (MIPS_USES_R4K_TLB) 2431 #if defined (MIPS_USES_R4K_TLB)
2432 env->CP0_random = MIPS_TLB_NB - 1; 2432 env->CP0_random = MIPS_TLB_NB - 1;
  2433 + env->tlb_in_use = MIPS_TLB_NB;
2433 #endif 2434 #endif
2434 env->CP0_Wired = 0; 2435 env->CP0_Wired = 0;
2435 env->CP0_Config0 = MIPS_CONFIG0; 2436 env->CP0_Config0 = MIPS_CONFIG0;