Skip to content

Commit

Permalink
Implement box/unbox elimination
Browse files Browse the repository at this point in the history
This happens in the second phase, after inlinings, thus giving us more
potential pairings to work with. When we see a box, we look at its
usages and find matching unbox/decont instructions. We also look down
`set` chains in case the unbox/decont is a `set` away. If we see a pair,
and the original unboxed value is still available, we rewrite the unbox
to a `set` of the original value, removing a usage of the boxed value.
  • Loading branch information
jnthn committed Jul 11, 2018
1 parent 4543137 commit 3727d18
Showing 1 changed file with 53 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/spesh/optimize.c
Expand Up @@ -2559,6 +2559,47 @@ static void try_eliminate_set(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB
}
}

/* Find box_* that are used by a matching unbox_*. This can happen in part due
* to imperfect code-gen, but also because the box is in an inlinee and the
* unbox on the outside, or vice versa. */
static void try_elimiante_one_box_unbox(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
MVMSpeshIns *box_ins, MVMSpeshIns *unbox_ins) {
if (conflict_free(tc, g, bb, box_ins, unbox_ins, box_ins->operands[1].reg.orig, 1, 1)) {
/* Make unbox insturction no longer use the boxed value. */
MVM_spesh_usages_delete_by_reg(tc, g, unbox_ins->operands[1], unbox_ins);

/* Use the unboxed version instead, rewriting to a set. */
unbox_ins->info = MVM_op_get_op(MVM_OP_set);
unbox_ins->operands[1] = box_ins->operands[1];
MVM_spesh_usages_add_by_reg(tc, g, unbox_ins->operands[1], unbox_ins);
}
}
static void walk_set_looking_for_unbox(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
MVMSpeshIns *box_ins, MVMuint16 unbox_op, MVMuint16 decont_op,
MVMSpeshIns *set_ins) {
MVMSpeshUseChainEntry *user_entry = MVM_spesh_get_facts(tc, g, set_ins->operands[0])->usage.users;
while (user_entry) {
MVMSpeshIns *user = user_entry->user;
if (user->info->opcode == unbox_op || user->info->opcode == decont_op)
try_elimiante_one_box_unbox(tc, g, bb, box_ins, user);
else if (user->info->opcode == MVM_OP_set)
walk_set_looking_for_unbox(tc, g, bb, box_ins, unbox_op, decont_op, user);
user_entry = user_entry->next;
}
}
static void try_eliminate_box_unbox_pair(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
MVMSpeshIns *ins, MVMuint16 unbox_op, MVMuint16 decont_op) {
MVMSpeshUseChainEntry *user_entry = MVM_spesh_get_facts(tc, g, ins->operands[0])->usage.users;
while (user_entry) {
MVMSpeshIns *user = user_entry->user;
if (user->info->opcode == unbox_op || user->info->opcode == decont_op)
try_elimiante_one_box_unbox(tc, g, bb, ins, user);
else if (user->info->opcode == MVM_OP_set)
walk_set_looking_for_unbox(tc, g, bb, ins, unbox_op, decont_op, user);
user_entry = user_entry->next;
}
}

/* Drives the second, post-inline, optimization pass. */
static void second_pass(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb) {
MVMint32 i;
Expand All @@ -2570,6 +2611,18 @@ static void second_pass(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb)
case MVM_OP_set:
try_eliminate_set(tc, g, bb, ins);
break;
case MVM_OP_box_i:
try_eliminate_box_unbox_pair(tc, g, bb, ins, MVM_OP_unbox_i, MVM_OP_decont_i);
break;
case MVM_OP_box_n:
try_eliminate_box_unbox_pair(tc, g, bb, ins, MVM_OP_unbox_n, MVM_OP_decont_n);
break;
case MVM_OP_box_s:
try_eliminate_box_unbox_pair(tc, g, bb, ins, MVM_OP_unbox_s, MVM_OP_decont_s);
break;
case MVM_OP_box_u:
try_eliminate_box_unbox_pair(tc, g, bb, ins, MVM_OP_unbox_u, MVM_OP_decont_u);
break;
case MVM_OP_sp_getspeshslot:
/* Sometimes we emit two getspeshslots in a row that write into the
* exact same register. That's clearly wasteful and we can save a
Expand Down

0 comments on commit 3727d18

Please sign in to comment.