Skip to content

Commit

Permalink
Move label assignment to tiler
Browse files Browse the repository at this point in the history
Branches internal to the expression tree are implemented using
dynamically assigned labels that are allocated after all 'graph'
labels (for objects and instructions). Prior to this patch, labels
were assigned to nodes (using the info structure) during the tree
analysis phase of tree construction.

This has two problems - at that point, we do not know how many 'graph'
labels need to be allocated, so we can't allocate an absolute label
number. Furthermore, this sets up the possibility for the optimizer to
disrupt the label structure of the tree and break it for no good
reason at all. Finally, I wanted to get rid of the label number in
the info structure because I want the info structure to fit in 32 and
coexist with the nodes themselves (so we don't have to maintain two
arrays with equal sizes).

With this commit we move the assignment of node labels to the tiling
phase, which is safe from disruption by the optimizer, and stores the
node -> label association in the (temporary) tiler state structure. We
also give the tiles absolute label numbers so that we don't have to
add the (transient) current label offset.

There is some overlap between block numbers and label numbers so maybe
they can be unified further, but I'm not interested in that now. As a
further drawback, the MVM_jit_tile_expr_tree can now change the
compiler label offset as a side effect, which isn't very 'clean', but
not unreasonable either.
  • Loading branch information
bdw committed Jun 20, 2018
1 parent a31e6da commit a7baa03
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 156 deletions.
13 changes: 5 additions & 8 deletions src/jit/compile.c
Expand Up @@ -201,17 +201,17 @@ void MVM_jit_code_destroy(MVMThreadContext *tc, MVMJitCode *code) {
/* pseudotile emit functions */
void MVM_jit_compile_branch(MVMThreadContext *tc, MVMJitCompiler *compiler,
MVMJitTile *tile, MVMJitExprTree *tree) {
MVM_jit_emit_branch(tc, compiler, tile->args[0] + compiler->label_offset);
MVM_jit_emit_branch(tc, compiler, tile->args[0]);
}

void MVM_jit_compile_conditional_branch(MVMThreadContext *tc, MVMJitCompiler *compiler,
MVMJitTile *tile, MVMJitExprTree *tree) {
MVM_jit_emit_conditional_branch(tc, compiler, tile->args[0], tile->args[1] + compiler->label_offset);
MVM_jit_emit_conditional_branch(tc, compiler, tile->args[0], tile->args[1]);
}

void MVM_jit_compile_label(MVMThreadContext *tc, MVMJitCompiler *compiler,
MVMJitTile *tile, MVMJitExprTree *tree) {
MVM_jit_emit_label(tc, compiler, tree->graph, tile->args[0] + compiler->label_offset);
MVM_jit_emit_label(tc, compiler, tree->graph, tile->args[0]);
}

void MVM_jit_compile_store(MVMThreadContext *tc, MVMJitCompiler *compiler,
Expand Down Expand Up @@ -258,8 +258,8 @@ void MVM_jit_compile_expr_tree(MVMThreadContext *tc, MVMJitCompiler *compiler, M
/* Second stage, allocate registers */
MVM_jit_linear_scan_allocate(tc, compiler, list);

/* Allocate sufficient space for the internal labels */
dasm_growpc(compiler, compiler->label_offset + tree->num_labels);
/* Allocate sufficient space for the new internal labels */
dasm_growpc(compiler, compiler->label_offset);

/* Third stage, emit the code */
for (i = 0; i < list->items_num; i++) {
Expand All @@ -271,9 +271,6 @@ void MVM_jit_compile_expr_tree(MVMThreadContext *tc, MVMJitCompiler *compiler, M
}
/* Cleanup tile lits */
MVM_jit_tile_list_destroy(tc, list);

/* Make sure no other tree reuses the same labels */
compiler->label_offset += tree->num_labels;
}

MVM_STATIC_INLINE MVMint32 reg_type_bucket(MVMint8 reg_type) {
Expand Down
136 changes: 2 additions & 134 deletions src/jit/expr.c
Expand Up @@ -373,7 +373,7 @@ static void analyze_node(MVMThreadContext *tc, MVMJitTreeTraverser *traverser,
MVMJitExprNodeInfo *node_info = tree->info + node;
MVMint32 i;

/* propagate node sizes and assign labels */
/* propagate node sizes */
switch (tree->nodes[node]) {
case MVM_JIT_CONST:
/* node size is given */
Expand Down Expand Up @@ -482,136 +482,6 @@ static void analyze_node(MVMThreadContext *tc, MVMJitTreeTraverser *traverser,
}
}

static void assign_labels(MVMThreadContext *tc, MVMJitTreeTraverser *traverser,
MVMJitExprTree *tree, MVMint32 node) {
/* IF has two blocks, the first I call left, the second I call right.
Regular IF is implemented by the following sequence:
* test
* negated conditional jump to label 1
* left block
* unconditional jump to label 2
* label 1
* right block
* label 2
The 'short-circuiting' cases of IF ALL and IF ANY require special
treatment. IF ALL simply repeats the test+negated branch for each of the
ALL's children. IF ANY on the other hand must short circuit not into the
default (right) but into the (left) conditional block. So IF ANY must be
implemented as:
(* test
* conditional jump to label 3) - repeated n times
* unconditional jump to label 1
* label 3
* left block
* unconditional jump to label 2
* label 1
* right block
* label 2
NB - the label before the left block has been given the number
3 for consistency with the regular case.
Simpilar observations are applicable to WHEN and WHEN ANY/WHEN
ALL. Different altogether are the cases of ANY ALL and ALL
ANY.
ANY ALL can be implemented as:
( test
negated conditional jump to label 4) - repeated for all in ALL
* unconditional jump to label 3
* label 4 (continuing the ANY)
This way the 'short-circuit' jump of the ALL sequence implies
the continuation of the ANY sequence, whereas the finishing of
the ALL sequence implies it succeeded and hence the ANY needs
to short-circuit.
ALL ANY can be implemented analogously as:
( test
conditional jump to label 4) repeated for all children of ANY
* unconditional short-circuit jump to label 1
* label 4
Nested ALL in ALL and ANY in ANY all have the same
short-circuiting behaviour (i.e. a nested ALL in ALL is
indistinguishable from inserting all the children of the nested
ALL into the nesting ALL), so they don't require special
treatment.
All this goes to say in that the number of labels required and
the actual labels assigned to different children depends on the
structure of the tree, which is why labels are 'pushed down'
from parents to children, at least when those children are ANY
and ALL. */

switch (tree->nodes[node]) {
case MVM_JIT_WHEN:
{
/* WHEN just requires one label in the default case */
MVMint32 test = tree->nodes[node+1];
tree->info[node].label = tree->num_labels++;
if (tree->nodes[test] == MVM_JIT_ANY) {
/* ANY requires a pre-left-block label */
tree->info[test].label = tree->num_labels++;
} else if (tree->nodes[test] == MVM_JIT_ALL) {
/* ALL takes over the label of its parent */
tree->info[test].label = tree->info[node].label;
}
}
break;
case MVM_JIT_IF:
case MVM_JIT_IFV:
{
MVMint32 test = tree->nodes[node+1];
/* take two labels, one for the left block and one for the right block */
tree->info[node].label = tree->num_labels;
tree->num_labels += 2;
if (tree->nodes[test] == MVM_JIT_ANY) {
/* assign 'label 3' to the ANY */
tree->info[test].label = tree->num_labels++;
} else if (tree->nodes[test] == MVM_JIT_ALL) {
/* assign 'label 1' to the ALL */
tree->info[test].label = tree->info[node].label;
}
}
break;
case MVM_JIT_ALL:
{
MVMint32 nchild = tree->nodes[node+1];
MVMint32 i;
for (i = 0; i < nchild; i++) {
MVMint32 test = tree->nodes[node+2+i];
if (tree->nodes[test] == MVM_JIT_ALL) {
/* use same label for child as parent */
tree->info[test].label = tree->info[node].label;
} else if (tree->nodes[test] == MVM_JIT_ANY) {
/* assign an extra label for ANY to short-circuit into */
tree->info[test].label = tree->num_labels++;
}
}
}
break;
case MVM_JIT_ANY:
{
MVMint32 nchild = tree->nodes[node+1];
MVMint32 i;
for (i = 0; i < nchild; i++) {
MVMint32 test = tree->nodes[node+2+i];
if (tree->nodes[test] == MVM_JIT_ANY) {
tree->info[test].label = tree->info[node].label;
} else if (tree->nodes[test] == MVM_JIT_ALL) {
tree->info[test].label = tree->num_labels++;
}
}
}
break;
default:
break;
}
}


void MVM_jit_expr_tree_analyze(MVMThreadContext *tc, MVMJitExprTree *tree) {
Expand All @@ -620,7 +490,7 @@ void MVM_jit_expr_tree_analyze(MVMThreadContext *tc, MVMJitExprTree *tree) {
MVM_VECTOR_ENSURE_SIZE(tree->info, tree->nodes_num);
traverser.policy = MVM_JIT_TRAVERSER_ONCE;
traverser.data = NULL;
traverser.preorder = &assign_labels;
traverser.preorder = NULL;
traverser.inorder = NULL;
traverser.postorder = &analyze_node;
MVM_jit_expr_tree_traverse(tc, tree, &traverser);
Expand All @@ -646,7 +516,6 @@ static void active_values_flush(MVMThreadContext *tc, MVMJitExprTree *tree,
}


/* TODO add labels to the expression tree */
MVMJitExprTree * MVM_jit_expr_tree_build(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter) {
MVMSpeshGraph *sg = jg->sg;
MVMSpeshIns *entry = iter->ins;
Expand All @@ -670,7 +539,6 @@ MVMJitExprTree * MVM_jit_expr_tree_build(MVMThreadContext *tc, MVMJitGraph *jg,
MVM_VECTOR_PUSH(tree->nodes, MVM_JIT_NOOP);

tree->graph = jg;
tree->num_labels = 0;
/* Hold indices to the node that last computed a value belonging
* to a register. Initialized as -1 to indicate that these
* values are empty. */
Expand Down
4 changes: 0 additions & 4 deletions src/jit/expr.h
Expand Up @@ -50,8 +50,6 @@ struct MVMJitExprNodeInfo {
MVMint8 opr_type;
/* Size of computed value */
MVMint8 size;
/* internal label for IF/WHEN/ALL/ANY etc, relative to the tree label offset */
MVMint32 label;
};

struct MVMJitExprTree {
Expand All @@ -60,8 +58,6 @@ struct MVMJitExprTree {
MVM_VECTOR_DECL(MVMint32, roots);
MVM_VECTOR_DECL(MVMJitExprNodeInfo, info);

MVMint32 label_ofs;
MVMint32 num_labels;
MVMuint32 seq_nr;
};

Expand Down

0 comments on commit a7baa03

Please sign in to comment.