Commit 814b9a47490c4500fd105b524b8354764e6655e5
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
Showing
6 changed files
with
54 additions
and
8 deletions
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; |