Commit 814b9a47 authored by Thiemo Seufer's avatar Thiemo Seufer
Browse files

MIPS TLB performance improvements, by Daniel Jacobowitz.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2220 c046a42c-6fe2-441c-8c8c-71466251a162
parent ec230928
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -94,7 +94,8 @@ struct CPUMIPSState {
		
#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;
+1 −0
Original line number Diff line number Diff line
@@ -115,5 +115,6 @@ uint32_t cpu_mips_get_count (CPUState *env);
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__) */
+1 −1
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ static int map_address (CPUState *env, target_ulong *physical, int *prot,
    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) &&
+1 −0
Original line number Diff line number Diff line
@@ -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.
+48 −6
Original line number Diff line number Diff line
@@ -367,7 +367,7 @@ void do_mtc0 (int reg, int sel)
        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 +568,14 @@ void fpu_handle_exception(void)

/* 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 +590,15 @@ static void invalidate_tlb (int idx)
        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.  */
        env->tlb[env->tlb_in_use] = *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 +617,14 @@ static void invalidate_tlb (int idx)
    }
}

static void mips_tlb_flush_extra (CPUState *env, int first)
{
    /* Discard entries from env->tlb[first] onwards.  */
    while (env->tlb_in_use > first) {
        invalidate_tlb(--env->tlb_in_use, 0);
    }
}

static void fill_tlb (int idx)
{
    tlb_t *tlb;
@@ -627,9 +651,14 @@ static void fill_tlb (int idx)

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, MIPS_TLB_NB);

    /* 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 @@ void do_tlbwr (void)
{
    int r = cpu_mips_get_random(env);

    invalidate_tlb(r);
    invalidate_tlb(r, 1);
    fill_tlb(r);
}

@@ -660,6 +689,17 @@ void do_tlbp (void)
        }
    }
    if (i == MIPS_TLB_NB) {
        /* No match.  Discard any shadow entries, if any of them match.  */
        for (i = MIPS_TLB_NB; i < env->tlb_in_use; i++) {
	    tlb = &env->tlb[i];

	    /* Check ASID, virtual page number & size */
	    if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
                mips_tlb_flush_extra (env, i);
	        break;
	    }
	}

        env->CP0_index |= 0x80000000;
    }
}
@@ -674,8 +714,10 @@ void do_tlbr (void)
    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, MIPS_TLB_NB);

    env->CP0_EntryHi = tlb->VPN | tlb->ASID;
    size = (tlb->end - tlb->VPN) >> 12;
Loading