With function helper_tlbwr(), specified LoongArch TLB entry will be updated. There are two PTE pages in one TLB entry, it is not necessary to flush QEMU TLB when one PTE page keeps unchanged and ther other PTE page is newly added.
Here check whether PTE page is the same or not, TLB flush can be skipped if both are the same or newly added. Signed-off-by: Bibo Mao <maob...@loongson.cn> --- target/loongarch/tcg/tlb_helper.c | 36 ++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 1ed2471e0a..6ac102862b 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -327,16 +327,42 @@ void helper_tlbrd(CPULoongArchState *env) void helper_tlbwr(CPULoongArchState *env) { int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); + LoongArchTLB *old, new; + int skip_inv = 0; + uint8_t tlb_v; - invalidate_tlb(env, index); - + old = env->tlb + index; if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { - env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, - TLB_MISC, E, 0); + invalidate_tlb(env, index); + old->tlb_misc = FIELD_DP64(old->tlb_misc, TLB_MISC, E, 0); return; } - fill_tlb_entry(env, env->tlb + index); + new.tlb_misc = 0; + new.tlb_entry0 = 0; + new.tlb_entry1 = 0; + fill_tlb_entry(env, &new); + /* Check whether ASID/VPPN is the same */ + if (old->tlb_misc == new.tlb_misc) { + tlb_v = FIELD_EX64(old->tlb_entry0, TLBENTRY, V); + /* Check whether even pte the same or invalid */ + if (!tlb_v || new.tlb_entry0 == old->tlb_entry0) { + skip_inv = 1; + } + + /* Check whether odd pte the same or invalid */ + tlb_v = FIELD_EX64(old->tlb_entry1, TLBENTRY, V); + if (!tlb_v || new.tlb_entry1 == old->tlb_entry1) { + skip_inv &= 1; + } + } + + if (!skip_inv) { + invalidate_tlb(env, index); + } + old->tlb_misc = new.tlb_misc; + old->tlb_entry0 = new.tlb_entry0; + old->tlb_entry1 = new.tlb_entry1; } void helper_tlbfill(CPULoongArchState *env) -- 2.39.3