Skip to content

Commit

Permalink
parisc: TLB optimization patch by Sven Schnelle
Browse files Browse the repository at this point in the history
This is a temporary patch to speed up TLBs - by Sven Schnelle

Signed-off-by: Helge Deller <deller@gmx.de>
  • Loading branch information
hdeller committed Aug 31, 2020
1 parent d097630 commit 6447901
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 128 deletions.
28 changes: 28 additions & 0 deletions target/hppa/cpu.c
Expand Up @@ -111,6 +111,33 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
#endif
}

static gint hppa_tlb_cmp(gconstpointer _a, gconstpointer _b, gpointer data)
{
const hppa_tlb_entry *a = (const hppa_tlb_entry *)_a;
const hppa_tlb_entry *b = (const hppa_tlb_entry *)_b;

if (a->page == b->page)
return 0;

if (a->page > b->page)
return 1;
return -1;
}

static void hppa_tlb_destroy(gpointer _a)
{
hppa_tlb_entry *ent = _a;
// qemu_log("free tree %p\n", ent->tlb_list_entry);
g_slice_free(hppa_tlb_entry, ent);
}

void alloc_tlb(CPUHPPAState *env)
{
if (env->tlb)
g_tree_destroy(env->tlb);
env->tlb = g_tree_new_full(hppa_tlb_cmp, env, NULL, hppa_tlb_destroy);
}

static void hppa_cpu_initfn(Object *obj)
{
CPUState *cs = CPU(obj);
Expand All @@ -121,6 +148,7 @@ static void hppa_cpu_initfn(Object *obj)
cs->exception_index = -1;
cpu_hppa_loaded_fr0(env);
cpu_hppa_put_psw(env, PSW_W);
alloc_tlb(env);
}

static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model)
Expand Down
19 changes: 11 additions & 8 deletions target/hppa/cpu.h
Expand Up @@ -23,6 +23,7 @@
#include "cpu-qom.h"
#include "exec/cpu-defs.h"
#include "exec/memory.h"
#include <gmodule.h>

/* PA-RISC 1.x processors have a strong memory model. */
/* ??? While we do not yet implement PA-RISC 2.0, those processors have
Expand Down Expand Up @@ -149,8 +150,8 @@ typedef int64_t target_sreg;
#endif

typedef struct {
uint64_t va_b;
uint64_t va_e;
uint64_t page;
uint64_t cycle;
target_ureg pa;
unsigned u : 1;
unsigned t : 1;
Expand All @@ -162,12 +163,11 @@ typedef struct {
unsigned ar_pl2 : 2;
unsigned entry_valid : 1;
unsigned access_id : 16;
GList *tlb_list_entry;
} hppa_tlb_entry;

struct CPUHPPAState {
target_ureg gr[32];
uint64_t fr[32];
uint64_t sr[8]; /* stored shifted into place for gva */

target_ureg psw; /* All psw bits except the following: */
target_ureg psw_n; /* boolean */
Expand All @@ -188,6 +188,8 @@ struct CPUHPPAState {
uint64_t iasq_f;
uint64_t iasq_b;

uint64_t fr[32];
uint64_t sr[8]; /* stored shifted into place for gva */
uint32_t fr0_shadow; /* flags, c, ca/cq, rm, d, enables */
float_status fp_status;

Expand All @@ -196,13 +198,14 @@ struct CPUHPPAState {
target_ureg shadow[7]; /* shadow registers */

/* ??? The number of entries isn't specified by the architecture. */
#define HPPA_TLB_ENTRIES 256
#define HPPA_TLB_ENTRIES 2048
#define HPPA_BTLB_ENTRIES 0

/* ??? Implement a unified itlb/dtlb for the moment. */
/* ??? We should use a more intelligent data structure. */
hppa_tlb_entry tlb[HPPA_TLB_ENTRIES];
uint32_t tlb_last;
GTree *tlb;
GList *tlb_list;
int tlb_entries;
};

/**
Expand Down Expand Up @@ -341,5 +344,5 @@ void hppa_cpu_alarm_timer(void *);
int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr);
#endif
void QEMU_NORETURN hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra);

void alloc_tlb(CPUHPPAState *env);
#endif /* HPPA_CPU_H */
60 changes: 0 additions & 60 deletions target/hppa/machine.c
Expand Up @@ -65,63 +65,6 @@ static const VMStateInfo vmstate_psw = {
.put = put_psw,
};

/* FIXME: Use the PA2.0 format, which is a superset of the PA1.1 format. */
static int get_tlb(QEMUFile *f, void *opaque, size_t size,
const VMStateField *field)
{
hppa_tlb_entry *ent = opaque;
uint32_t val;

memset(ent, 0, sizeof(*ent));

ent->va_b = qemu_get_be64(f);
ent->pa = qemu_get_betr(f);
val = qemu_get_be32(f);

ent->entry_valid = extract32(val, 0, 1);
ent->access_id = extract32(val, 1, 18);
ent->u = extract32(val, 19, 1);
ent->ar_pl2 = extract32(val, 20, 2);
ent->ar_pl1 = extract32(val, 22, 2);
ent->ar_type = extract32(val, 24, 3);
ent->b = extract32(val, 27, 1);
ent->d = extract32(val, 28, 1);
ent->t = extract32(val, 29, 1);

ent->va_e = ent->va_b + TARGET_PAGE_SIZE - 1;
return 0;
}

static int put_tlb(QEMUFile *f, void *opaque, size_t size,
const VMStateField *field, QJSON *vmdesc)
{
hppa_tlb_entry *ent = opaque;
uint32_t val = 0;

if (ent->entry_valid) {
val = 1;
val = deposit32(val, 1, 18, ent->access_id);
val = deposit32(val, 19, 1, ent->u);
val = deposit32(val, 20, 2, ent->ar_pl2);
val = deposit32(val, 22, 2, ent->ar_pl1);
val = deposit32(val, 24, 3, ent->ar_type);
val = deposit32(val, 27, 1, ent->b);
val = deposit32(val, 28, 1, ent->d);
val = deposit32(val, 29, 1, ent->t);
}

qemu_put_be64(f, ent->va_b);
qemu_put_betr(f, ent->pa);
qemu_put_be32(f, val);
return 0;
}

static const VMStateInfo vmstate_tlb = {
.name = "tlb entry",
.get = get_tlb,
.put = put_tlb,
};

static VMStateField vmstate_env_fields[] = {
VMSTATE_UINTTR_ARRAY(gr, CPUHPPAState, 32),
VMSTATE_UINT64_ARRAY(fr, CPUHPPAState, 32),
Expand Down Expand Up @@ -152,9 +95,6 @@ static VMStateField vmstate_env_fields[] = {

VMSTATE_UINT32(fr0_shadow, CPUHPPAState),

VMSTATE_ARRAY(tlb, CPUHPPAState, ARRAY_SIZE(((CPUHPPAState *)0)->tlb),
0, vmstate_tlb, hppa_tlb_entry),
VMSTATE_UINT32(tlb_last, CPUHPPAState),

VMSTATE_END_OF_LIST()
};
Expand Down
120 changes: 63 additions & 57 deletions target/hppa/mem_helper.c
Expand Up @@ -17,12 +17,17 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#include <stddef.h>
#include "exec/target_page.h"
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "hw/core/cpu.h"
#include "qemu/timer.h"
#include "trace.h"
#include <gmodule.h>


#ifdef CONFIG_USER_ONLY
bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
Expand All @@ -38,49 +43,30 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
cpu_loop_exit_restore(cs, retaddr);
}
#else

static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr)
{
int i;

for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
hppa_tlb_entry *ent = &env->tlb[i];
if (ent->va_b <= addr && addr <= ent->va_e) {
trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid,
ent->va_b, ent->va_e, ent->pa);
return ent;
}
}
trace_hppa_tlb_find_entry_not_found(env, addr);
return NULL;
hppa_tlb_entry key;

key.page = addr >> TARGET_PAGE_BITS;
hppa_tlb_entry *ent = g_tree_lookup(env->tlb, &key);
if (ent)
trace_hppa_tlb_find_entry(env, ent, ent->entry_valid, ent->page << TARGET_PAGE_BITS, ent->pa);
else
trace_hppa_tlb_find_entry_not_found(env, addr);
return ent;
}

static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent)
{
CPUState *cs = env_cpu(env);
unsigned i, n = 1 << (2 * ent->page_size);
uint64_t addr = ent->va_b;
trace_hppa_tlb_flush_ent(env, ent, ent->page, ent->pa);

trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa);

for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) {
/* Do not flush MMU_PHYS_IDX. */
tlb_flush_page_by_mmuidx(cs, addr, 0xf);
}

memset(ent, 0, sizeof(*ent));
ent->va_b = -1;
}

static hppa_tlb_entry *hppa_alloc_tlb_ent(CPUHPPAState *env)
{
hppa_tlb_entry *ent;
uint32_t i = env->tlb_last;

env->tlb_last = (i == ARRAY_SIZE(env->tlb) - 1 ? 0 : i + 1);
ent = &env->tlb[i];

hppa_flush_tlb_ent(env, ent);
return ent;
if (ent->entry_valid)
tlb_flush_page_by_mmuidx(cs, ent->page << TARGET_PAGE_BITS, 0xf);
env->tlb_list = g_list_delete_link(env->tlb_list, ent->tlb_list_entry);
g_tree_remove(env->tlb, ent);
env->tlb_entries--;
}

int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
Expand All @@ -107,6 +93,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
goto egress;
}

ent->cycle++;
/* We now know the physical address. */
phys = ent->pa + (addr & ~TARGET_PAGE_MASK);

Expand Down Expand Up @@ -260,35 +247,51 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
return true;
}

static gint hppa_expire_cmp(gconstpointer _a, gconstpointer _b)
{
hppa_tlb_entry *a = (hppa_tlb_entry *)_a;
hppa_tlb_entry *b = (hppa_tlb_entry *)_b;
return a->cycle - b->cycle;
}

/* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */
void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
{
hppa_tlb_entry *empty = NULL;
unsigned long long page = addr >> TARGET_PAGE_BITS;
hppa_tlb_entry *entry = NULL, key;
GList *it, *itn;
int i;

/* Zap any old entries covering ADDR; notice empty entries on the way. */
for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
hppa_tlb_entry *ent = &env->tlb[i];
if (ent->va_b <= addr && addr <= ent->va_e) {
if (ent->entry_valid) {
hppa_flush_tlb_ent(env, ent);
}
if (!empty) {
empty = ent;
}
if (env->tlb_entries > HPPA_TLB_ENTRIES) {
env->tlb_list = g_list_sort(env->tlb_list, hppa_expire_cmp);
for (it = env->tlb_list, i = 0; it && i < HPPA_TLB_ENTRIES/2; i++) {
itn = g_list_next(it);
hppa_flush_tlb_ent(env, it->data);
it = itn;
}
}

/* If we didn't see an empty entry, evict one. */
if (empty == NULL) {
empty = hppa_alloc_tlb_ent(env);
key.page = page;
entry = g_tree_lookup(env->tlb, &key);
if (entry) {
if (entry->entry_valid) {
entry->entry_valid = 0;
CPUState *cs = env_cpu(env);
tlb_flush_page_by_mmuidx(cs, page, 0xf);
}
entry->page = page;
entry->cycle = 0;
} else {
entry = g_slice_new0(hppa_tlb_entry);
env->tlb_list = g_list_prepend(env->tlb_list, entry);
entry->tlb_list_entry = env->tlb_list;
entry->page = page;
g_tree_insert(env->tlb, entry, entry);
env->tlb_entries++;
}
entry->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS;
trace_hppa_tlb_itlba(env, entry, page << TARGET_PAGE_BITS, entry->pa);

/* Note that empty->entry_valid == 0 already. */
empty->va_b = addr & TARGET_PAGE_MASK;
empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1;
empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS;
trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa);
}

/* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */
Expand Down Expand Up @@ -322,9 +325,8 @@ static void ptlb_work(CPUState *cpu, run_on_cpu_data data)
target_ulong addr = (target_ulong) data.target_ptr;
hppa_tlb_entry *ent = hppa_find_tlb(env, addr);

if (ent && ent->entry_valid) {
if (ent)
hppa_flush_tlb_ent(env, ent);
}
}

void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
Expand All @@ -342,12 +344,16 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
async_safe_run_on_cpu(src, ptlb_work, data);
}


/* Purge (Insn/Data) TLB entry. This affects an implementation-defined
number of pages/entries (we choose all), and is local to the cpu. */
void HELPER(ptlbe)(CPUHPPAState *env)
{
trace_hppa_tlb_ptlbe(env);
memset(env->tlb, 0, sizeof(env->tlb));
g_list_free(env->tlb_list);
env->tlb_list = NULL;
env->tlb_entries = 0;
alloc_tlb(env);
tlb_flush_by_mmuidx(env_cpu(env), 0xf);
}

Expand Down
6 changes: 3 additions & 3 deletions target/hppa/trace-events
@@ -1,13 +1,13 @@
# See docs/devel/tracing.txt for syntax documentation.

# mem_helper.c
disable hppa_tlb_flush_ent(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx"
disable hppa_tlb_find_entry(void *env, void *ent, int valid, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p valid=%d va_b=0x%lx va_e=0x%lx pa=0x%lx"
disable hppa_tlb_flush_ent(void *env, void *ent, uint64_t page, uint64_t pa) "env=%p ent=%p page=0x%lx pa=0x%lx"
disable hppa_tlb_find_entry(void *env, void *ent, int valid, uint64_t page, uint64_t pa) "env=%p ent=%p valid=%d page=0x%lx pa=0x%lx"
disable hppa_tlb_find_entry_not_found(void *env, uint64_t addr) "env=%p addr=%08lx"
disable hppa_tlb_get_physical_address(void *env, int ret, int prot, uint64_t addr, uint64_t phys) "env=%p ret=%d prot=%d addr=0x%lx phys=0x%lx"
disable hppa_tlb_fill_excp(void *env, uint64_t addr, int size, int type, int mmu_idx) "env=%p addr=0x%lx size=%d type=%d mmu_idx=%d"
disable hppa_tlb_fill_success(void *env, uint64_t addr, uint64_t phys, int size, int type, int mmu_idx) "env=%p addr=0x%lx phys=0x%lx size=%d type=%d mmu_idx=%d"
disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx"
disable hppa_tlb_itlba(void *env, void *ent, uint64_t page, uint64_t pa) "env=%p ent=%p page=0x%lx pa=0x%lx"
disable hppa_tlb_itlbp(void *env, void *ent, int access_id, int u, int pl2, int pl1, int type, int b, int d, int t) "env=%p ent=%p access_id=%x u=%d pl2=%d pl1=%d type=%d b=%d d=%d t=%d"
disable hppa_tlb_ptlb(void *env) "env=%p"
disable hppa_tlb_ptlbe(void *env) "env=%p"
Expand Down

0 comments on commit 6447901

Please sign in to comment.