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 94  
95 95 #endif
96 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 99 #endif
99 100 uint32_t CP0_index;
100 101 uint32_t CP0_random;
... ...
target-mips/exec.h
... ... @@ -115,5 +115,6 @@ uint32_t cpu_mips_get_count (CPUState *env);
115 115 void cpu_mips_store_count (CPUState *env, uint32_t value);
116 116 void cpu_mips_store_compare (CPUState *env, uint32_t value);
117 117 void cpu_mips_clock_init (CPUState *env);
  118 +void cpu_mips_tlb_flush (CPUState *env, int flush_global);
118 119  
119 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 46 tlb_t *tlb;
47 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 50 tlb = &env->tlb[i];
51 51 /* Check ASID, virtual page number & size */
52 52 if ((tlb->G == 1 || tlb->ASID == ASID) &&
... ...
target-mips/mips-defs.h
... ... @@ -22,6 +22,7 @@
22 22 /* Uses MIPS R4Kc TLB model */
23 23 #define MIPS_USES_R4K_TLB
24 24 #define MIPS_TLB_NB 16
  25 +#define MIPS_TLB_MAX 128
25 26 /* basic FPU register support */
26 27 #define MIPS_USES_FPU 1
27 28 /* Define a implementation number of 1.
... ...
target-mips/op_helper.c
... ... @@ -367,7 +367,7 @@ void do_mtc0 (int reg, int sel)
367 367 env->CP0_EntryHi = val;
368 368 /* If the ASID changes, flush qemu's TLB. */
369 369 if ((old & 0xFF) != (val & 0xFF))
370   - tlb_flush (env, 1);
  370 + cpu_mips_tlb_flush (env, 1);
371 371 rn = "EntryHi";
372 372 break;
373 373 case 11:
... ... @@ -568,7 +568,14 @@ void fpu_handle_exception(void)
568 568  
569 569 /* TLB management */
570 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 580 tlb_t *tlb;
574 581 target_ulong addr;
... ... @@ -583,6 +590,15 @@ static void invalidate_tlb (int idx)
583 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 602 if (tlb->V0) {
587 603 tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
588 604 addr = tlb->VPN;
... ... @@ -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 628 static void fill_tlb (int idx)
605 629 {
606 630 tlb_t *tlb;
... ... @@ -627,9 +651,14 @@ static void fill_tlb (int idx)
627 651  
628 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 659 /* Wildly undefined effects for CP0_index containing a too high value and
631 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 662 fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
634 663 }
635 664  
... ... @@ -637,7 +666,7 @@ void do_tlbwr (void)
637 666 {
638 667 int r = cpu_mips_get_random(env);
639 668  
640   - invalidate_tlb(r);
  669 + invalidate_tlb(r, 1);
641 670 fill_tlb(r);
642 671 }
643 672  
... ... @@ -660,6 +689,17 @@ void do_tlbp (void)
660 689 }
661 690 }
662 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 703 env->CP0_index |= 0x80000000;
664 704 }
665 705 }
... ... @@ -674,8 +714,10 @@ void do_tlbr (void)
674 714 tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
675 715  
676 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 722 env->CP0_EntryHi = tlb->VPN | tlb->ASID;
681 723 size = (tlb->end - tlb->VPN) >> 12;
... ...
target-mips/translate.c
... ... @@ -2430,6 +2430,7 @@ CPUMIPSState *cpu_mips_init (void)
2430 2430 env->PC = 0xBFC00000;
2431 2431 #if defined (MIPS_USES_R4K_TLB)
2432 2432 env->CP0_random = MIPS_TLB_NB - 1;
  2433 + env->tlb_in_use = MIPS_TLB_NB;
2433 2434 #endif
2434 2435 env->CP0_Wired = 0;
2435 2436 env->CP0_Config0 = MIPS_CONFIG0;
... ...