Skip to content

Commit

Permalink
Bindlex needs a write barrier
Browse files Browse the repository at this point in the history
I recall this being a bug for the lego JIT as well, a long time
ago. This unbreaks building rakudo with templates for box_s /
box_i (but at the cost of building somewhat smaller blocks,
unfortunately).
  • Loading branch information
bdw committed Mar 2, 2018
1 parent 9233d85 commit bfccd35
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 13 deletions.
12 changes: 11 additions & 1 deletion docs/jit/todo.org
Expand Up @@ -203,6 +203,17 @@ should allow for some control on the iteration order


* Register Allocator
** Switch to storing register numbers in a stack rather than in a ring

Using a ring has the disadvantage that register values are
'continuously moving', even when they do not need to be.

** Dump register allocator graph

I think it should be possible to dump the (result) of register
allocation. That is to say, create a graph that displays all tiles,
their basic block structure, the live range structure, and their
spills.

** Support multiple register classes

Expand All @@ -212,7 +223,6 @@ I want to distinguish register classes using ranges, i.e. on x86-64,
*** Find out if register selection for FPRs is supported
*** Support register buffers per class


** Generalized 3-operand to 2-operand conversion

Already implemented for direct-memory binary ops, but needs to be
Expand Down
32 changes: 22 additions & 10 deletions src/jit/expr.c
Expand Up @@ -228,22 +228,33 @@ static MVMint32 MVM_jit_expr_add_const(MVMThreadContext *tc, MVMJitExprTree *tre
return num;
}

static MVMint32 can_getlex(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns *ins) {
MVMint32 outers = ins->operands[1].lex.outers;
MVMint32 idx = ins->operands[1].lex.idx;
static MVMuint16 get_lexical_type(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshOperand opr) {
MVMStaticFrame *sf = jg->sg->sf;
MVMuint16 *lexical_types;
MVMint32 i;
for (i = 0; i < outers; i++) {
MVMuint16 *lexical_types;
for (i = 0; i < opr.lex.outers; i++) {
sf = sf->body.outer;
}
/* Use speshed lexical types, if necessary */
lexical_types = (outers == 0 && jg->sg->lexical_types != NULL ?
jg->sg->lexical_types : sf->body.lexical_types);
/* can't do getlex yet, if we have an object register */
return lexical_types[idx] != MVM_reg_obj;
lexical_types = (opr.lex.outers == 0 && jg->sg->lexical_types != NULL ?
jg->sg->lexical_types :
sf->body.lexical_types);
return lexical_types[opr.lex.idx];
}


static MVMint32 getlex_needs_autoviv(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns *ins) {
MVMuint16 lexical_type = get_lexical_type(tc, jg, ins->operands[1]);
return lexical_type == MVM_reg_obj;
}

static MVMint32 bindlex_needs_write_barrier(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns *ins) {
MVMuint16 lexical_type = get_lexical_type(tc, jg, ins->operands[0]);
/* need to hit a write barrier if we bindlex to a string */
return lexical_type == MVM_reg_obj || lexical_type == MVM_reg_str;
}


static MVMint32 ins_has_single_input_output_operand(MVMSpeshIns *ins) {
switch (ins->info->opcode) {
case MVM_OP_inc_i:
Expand Down Expand Up @@ -717,7 +728,8 @@ MVMJitExprTree * MVM_jit_expr_tree_build(MVMThreadContext *tc, MVMJitGraph *jg,
struct ValueDefinition *defined_value = NULL;

/* check if this is a getlex and if we can handle it */
BAIL(opcode == MVM_OP_getlex && !can_getlex(tc, jg, ins), "Can't compile object getlex");
BAIL(opcode == MVM_OP_getlex && getlex_needs_autoviv(tc, jg, ins), "Can't compile object getlex");
BAIL(opcode == MVM_OP_bindlex && bindlex_needs_write_barrier(tc, jg, ins), "Can't compile write-barrier bindlex");

/* Check annotations that may require handling or wrapping the expression */
for (ann = ins->annotations; ann != NULL; ann = ann->next) {
Expand Down
13 changes: 11 additions & 2 deletions src/jit/linear_scan.c
Expand Up @@ -521,10 +521,12 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
* of IF), and thus these are not actual definitions */
if (tile->op == MVM_JIT_COPY) {
MVMint32 ref = tree->nodes[tile->node + 1];
_DEBUG("Unify COPY node (%d -> %d)", tile->node, ref);
alc->sets[node].key = ref; /* point directly to actual definition */
} else if (tile->op == MVM_JIT_DO) {
MVMint32 nchild = tree->nodes[tile->node + 1];
MVMint32 ref = tree->nodes[tile->node + 1 + nchild];
_DEBUG("Unify COPY DO (%d -> %d)", tile->node, ref);
alc->sets[node].key = ref;
} else if (tile->op == MVM_JIT_IF) {
MVMint32 left_cond = tree->nodes[tile->node + 2];
Expand All @@ -533,14 +535,17 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
* case we should resolve it by creating a new live range or inserting
* a copy */
alc->sets[node].key = value_set_union(alc->sets, alc->values, left_cond, right_cond);
_DEBUG("Merging nodes %d and %d to %d (result key = %d)", left_cond, right_cond, node, alc->sets[node].key);
num_phi++;
} else if (tile->op == MVM_JIT_ARGLIST) {
MVMint32 num_args = tree->nodes[tile->node + 1];
MVMJitExprNode *refs = tree->nodes + tile->node + 2;
_DEBUG("Adding %d references to ARGLIST node", num_args);
for (j = 0; j < num_args; j++) {
MVMint32 carg = refs[j];
MVMint32 value = list->tree->nodes[carg+1];
MVMint32 idx = value_set_find(alc->sets, value)->idx;
_DEBUG(" Reference %d", idx);
live_range_add_ref(alc, alc->values + idx, i, j + 1);
/* include the CALL node into the arglist child range, so we
* don't release them too early */
Expand All @@ -553,6 +558,7 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
MVMint32 idx = live_range_init(alc);
alc->sets[node].key = node;
alc->sets[node].idx = idx;
_DEBUG("Create live range %d (tile=%d, node=%d)", idx,i, node);
live_range_add_ref(alc, alc->values + idx, i, 0);
if (MVM_JIT_REGISTER_HAS_REQUIREMENT(register_spec)) {
alc->values[idx].register_spec = register_spec;
Expand All @@ -565,6 +571,7 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
/* any 'use' register requirements are handled in the allocation step */
if (MVM_JIT_REGISTER_IS_USED(register_spec)) {
MVMint32 idx = value_set_find(alc->sets, tile->refs[j])->idx;
_DEBUG("Adding reference to live range %d from tile %d", idx, i);
live_range_add_ref(alc, alc->values + idx, i, j + 1);
}
}
Expand All @@ -575,7 +582,7 @@ static void determine_live_ranges(MVMThreadContext *tc, RegisterAllocator *alc,
"Register types do not match between value and node");
/* shift to match MVM_reg_types. should arguably be a macro maybe */
range->reg_type = tree->info[node].opr_type >> 3;
_DEBUG( "Assigned type: %d\n", range->reg_type);
_DEBUG( "Assigned type: %d to live range %d\n", range->reg_type, range - alc->values);
}
}
if (num_phi > 0) {
Expand Down Expand Up @@ -637,7 +644,7 @@ static void active_set_expire(MVMThreadContext *tc, RegisterAllocator *alc, MVMi
break;
} else {
_DEBUG("Live range %d is out of scope (last ref %d, %d) and releasing register %d",
v, values[v].end, order_nr, reg_num);
v, alc->values[v].end, order_nr, reg_num);
free_register(tc, alc, MVM_JIT_STORAGE_GPR, reg_num);
}
}
Expand Down Expand Up @@ -718,6 +725,7 @@ static void live_range_spill(MVMThreadContext *tc, RegisterAllocator *alc, MVMJi

MVMint8 reg_spilled = alc->values[to_spill].reg_num;
/* loop over all value refs */
_DEBUG("Spilling live range value %d to memory position %d at %d", to_spill, spill_pos, code_pos);

while (alc->values[to_spill].first != NULL) {
/* make a new live range */
Expand Down Expand Up @@ -989,6 +997,7 @@ static void prepare_arglist_and_call(MVMThreadContext *tc, RegisterAllocator *al
MVMint32 arg = spilled_args[i];
LiveRange *v = alc->values + arg_values[arg];
if (storage_refs[arg]._cls == MVM_JIT_STORAGE_GPR) {
_DEBUG("Loading spilled value to Rq(%d) from [rbx+%d]", storage_refs[arg]._pos, v->spill_pos);
INSERT_LOAD_LOCAL(storage_refs[arg]._pos, v->spill_pos);
} else if (storage_refs[arg]._cls == MVM_JIT_STORAGE_STACK) {
INSERT_LOCAL_STACK_COPY(storage_refs[arg]._pos, v->spill_pos);
Expand Down

0 comments on commit bfccd35

Please sign in to comment.