Permalink
Browse files

linear_scan can now works for NQP compilation

Fixed the seemingly last few bugs that prevented linear_scan from
working correctly. NQP now compiles and works, and I can start
implementing spilling.

NB: NQP commit a6c267fcd11cc3c69ec579345c8db959de78af1c, because it's
not fully up to date with master.
  • Loading branch information...
bdw committed Jan 4, 2017
1 parent 9a67811 commit ab077741ec1822740fff450e7754fcd46b98bf4f
Showing with 108 additions and 73 deletions.
  1. +5 −4 docs/jit/plan.org
  2. +1 −1 src/jit/compile.c
  3. +102 −68 src/jit/linear_scan.c
View
@@ -203,10 +203,11 @@ X64_GPR(MVM_JIT_REG)
int i = MVM_JIT_REQUIRE(RSP);
#+END_SRC
**** TODO Use register requirements on definition tiles
**** DONE Use register requirements on definition tiles
- reading of register requirements from tile
- look up table to detect that certain registers are non-volatile (callee-saved)
- in single-pass allocator, predefine non-volatile register value references
*** DONE tile editor code moves to tile.c
- keep it abstract?
@@ -305,7 +306,7 @@ MVMint32 has_local_location(MVMJitExprTree *tree, MVMint32 node) {
}
#+END_SRC
** TODO Implement linear scan
** DONE Implement linear scan
The basic idea of linear scan is:
- iterate over live ranges in order of first definition
@@ -339,7 +340,7 @@ the expire-register case.....
Maybe we should have the prefered-register thing per use/defintiion,
but that becomes very complicated fast.
*** TODO Use linked list for use/definition storage
*** DONE Use linked list for use/definition storage
The number of definition/use references is strictly limited to
4*num_tiles, because each tile can only define one value and use at
@@ -466,7 +467,7 @@ Comes down to:
- output-register must be copied off /or/ spilled
** TODO register assignment
** DONE register assignment
Register assignment should be inline with the register allocation
step, because otherwise we simply have to iterate twice in the same
View
@@ -205,7 +205,7 @@ void MVM_jit_compile_expr_tree(MVMThreadContext *tc, MVMJitCompiler *compiler, M
list = MVM_jit_tile_expr_tree(tc, compiler, tree);
/* Second stage, allocate registers */
MVM_jit_register_allocate(tc, compiler, list);
MVM_jit_linear_scan_allocate(tc, compiler, list);
/* Allocate sufficient space for the internal labels */
dasm_growpc(compiler, compiler->label_offset + tree->num_labels);
View
@@ -52,18 +52,6 @@ typedef struct {
MVMint32 reg_num;
} LiveRange;
/* quick accessors for common checks */
static inline MVMint32 first_ref(LiveRange *r) {
MVMint32 a = r->first == NULL ? INT32_MAX : r->first->tile_idx;
MVMint32 b = r->synthetic[0] == NULL ? INT32_MAX : r->synth_pos[0];
return MIN(a,b);
}
static inline MVMint32 last_ref(LiveRange *r) {
MVMint32 a = r->last == NULL ? -1 : r->last->tile_idx;
MVMint32 b = r->synthetic[1] == NULL ? -1 : r->synth_pos[1];
return MAX(a,b);
}
@@ -95,69 +83,43 @@ typedef struct {
} RegisterAllocator;
UnionFind * value_set_find(UnionFind *sets, MVMint32 key) {
while (sets[key].key != key) {
key = sets[key].key;
}
return sets + key;
}
/* quick accessors for common checks */
static inline MVMint32 first_ref(LiveRange *r) {
MVMint32 a = r->first == NULL ? INT32_MAX : r->first->tile_idx;
MVMint32 b = r->synthetic[0] == NULL ? INT32_MAX : r->synth_pos[0];
return MIN(a,b);
}
MVMint32 value_set_union(UnionFind *sets, LiveRange *values, MVMint32 a, MVMint32 b) {
LiveRange *ra = values + sets[a].idx, *rb = values + sets[b].idx;
ValueRef *head, *tail;
if (first_ref(rb) < first_ref(ra)) {
MVMint32 t = a; a = b; b = t;
}
sets[b].key = a; /* point b to a */
/* merge value ref sets */
if (first_ref(ra) <= first_ref(rb)) {
head = ra->first;
ra->first = ra->first->next;
} else {
head = rb->first;
rb->first = rb->first->next;
}
tail = head;
while (ra->first != NULL && rb->first != NULL) {
if (ra->first->tile_idx <= rb->first->tile_idx) {
tail->next = ra->first;
ra->first = ra->first->next;
} else {
tail->next = rb->first;
rb->first = rb->first->next;
}
}
while (ra->first != NULL) {
tail->next = ra->first;
ra->first = ra->first->next;
}
while (rb->first != NULL) {
tail->next = rb->first;
rb->first = rb->first->next;
}
values[sets[a].idx].first = head;
values[sets[a].idx].last = tail;
return a;
static inline MVMint32 last_ref(LiveRange *r) {
MVMint32 a = r->last == NULL ? -1 : r->last->tile_idx;
MVMint32 b = r->synthetic[1] == NULL ? -1 : r->synth_pos[1];
return MAX(a,b);
}
static inline MVMint32 is_definition(ValueRef *v) {
return (v->tile_idx == 0);
}
/* create a new live range object and return a reference */
/* allocate a new live range value by pointer-bumping */
MVMint32 live_range_init(RegisterAllocator *alc) {
LiveRange *range;
MVMint32 idx = alc->values_num++;
MVM_VECTOR_ENSURE_SIZE(alc->values, idx);
range = &alc->values[idx];
range->first = NULL;
range->last = NULL;
range->synthetic[0] = NULL;
range->synthetic[1] = NULL;
return idx;
}
static inline MVMint32 live_range_is_empty(LiveRange *range) {
return (range->first == NULL &&
range->synthetic[0] == NULL &&
range->synthetic[1] == NULL);
}
/* append ref to end of queue */
static void live_range_add_ref(RegisterAllocator *alc, LiveRange *range, MVMint32 tile_idx, MVMint32 value_idx) {
ValueRef *ref = alc->refs + alc->refs_num++;
ref->tile_idx = tile_idx;
ref->value_idx = value_idx;
@@ -171,12 +133,80 @@ static void live_range_add_ref(RegisterAllocator *alc, LiveRange *range, MVMint3
ref->next = NULL;
}
static inline MVMint32 live_range_is_empty(LiveRange *range) {
return (range->first == NULL &&
range->synthetic[0] == NULL &&
range->synthetic[1] == NULL);
/* merge value ref sets */
static void live_range_merge(LiveRange *a, LiveRange *b) {
ValueRef *head = NULL, *tail = NULL;
MVMint32 i;
if (first_ref(a) <= first_ref(b)) {
head = a->first;
a->first = a->first->next;
} else {
head = b->first;
b->first = b->first->next;
}
tail = head;
while (a->first != NULL && b->first != NULL) {
if (a->first->tile_idx <= b->first->tile_idx) {
tail->next = a->first;
a->first = a->first->next;
} else {
tail->next = b->first;
b->first = b->first->next;
}
tail = tail->next;
}
while (a->first != NULL) {
tail->next = a->first;
a->first = a->first->next;
tail = tail->next;
}
while (b->first != NULL) {
tail->next = b->first;
b->first = b->first->next;
tail = tail->next;
}
a->first = head;
a->last = tail;
for (i = 0; i < 2; i++) {
if (b->synthetic[i] == NULL) {
continue;
}
if (a->synthetic[i] != NULL) {
MVM_panic(1, "Can't merge the same synthetic!");
}
a->synthetic[i] = b->synthetic[i];
a->synth_pos[i] = b->synth_pos[i];
}
}
UnionFind * value_set_find(UnionFind *sets, MVMint32 key) {
while (sets[key].key != key) {
key = sets[key].key;
}
return sets + key;
}
MVMint32 value_set_union(UnionFind *sets, LiveRange *values, MVMint32 a, MVMint32 b) {
/* dereference the sets to their roots */
a = value_set_find(sets, a)->key;
b = value_set_find(sets, b)->key;
if (first_ref(values + sets[b].idx) < first_ref(values + sets[a].idx)) {
/* ensure we're picking the first one to start so that we maintain the
* heap order */
MVMint32 t = a; a = b; b = t;
}
sets[b].key = a; /* point b to a */
live_range_merge(values + sets[a].idx, values + sets[b].idx);
return a;
}
static inline void heap_swap(MVMint32 *heap, MVMint32 a, MVMint32 b) {
MVMint32 t = heap[a];
heap[a] = heap[b];
@@ -296,6 +326,7 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
alc->sets = MVM_calloc(tree->nodes_num, sizeof(UnionFind));
/* TODO: add count for ARGLIST refs, which can be > 3 per 'tile' */
alc->refs = MVM_calloc(list->items_num * 4, sizeof(ValueRef));
alc->refs_num = 0;
MVM_VECTOR_INIT(alc->values, list->items_num);
MVM_VECTOR_INIT(alc->worklist, list->items_num);
@@ -329,8 +360,8 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
if (MVM_JIT_REGISTER_HAS_REQUIREMENT(register_spec)) {
alc->values[idx].register_spec = register_spec;
}
MVM_VECTOR_PUSH(alc->worklist, idx);
}
/* account for uses */
for (j = 0; j < tile->num_refs; j++) {
MVMint8 register_spec = MVM_JIT_REGISTER_FETCH(tile->register_spec, j+1);
@@ -346,6 +377,7 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
}
}
}
}
/* The code below needs some thinking... */
@@ -365,7 +397,7 @@ static void active_set_add(MVMThreadContext *tc, RegisterAllocator *alc, MVMint3
if (last_ref(&alc->values[b]) > last_ref(&alc->values[a])) {
/* insert a before b */
memmove(alc->active + i + 1, alc->active + i, sizeof(MVMint32)*(alc->active_top - i));
alc->active[i] = b;
alc->active[i] = a;
alc->active_top++;
return;
}
@@ -435,14 +467,16 @@ static void split_live_range(MVMThreadContext *tc, RegisterAllocator *alc, MVMin
static void linear_scan(MVMThreadContext *tc, RegisterAllocator *alc, MVMJitTileList *list) {
MVMint32 i, j;
MVM_VECTOR_INIT(alc->retired, alc->worklist_num);
while (alc->worklist_num > 0) {
MVMint32 v = live_range_heap_pop(alc->values, alc->worklist, &alc->worklist_num);
MVMint32 pos = first_ref(alc->values + v);
MVMint8 reg;
/* NB: should i wrap this in a separate loop to remove these? */
/* NB: Should I have a compaction step to remove these? */
if (live_range_is_empty(alc->values + v))
continue;
/* assign registers in loop */
active_set_expire(tc, alc, pos);
if (MVM_JIT_REGISTER_HAS_REQUIREMENT(alc->values[v].register_spec)) {

0 comments on commit ab07774

Please sign in to comment.