Skip to content

Commit

Permalink
Fix handling of added deopt points
Browse files Browse the repository at this point in the history
During call optimization, we might choose to insert extra deopting
instructions, each which get their own annotation. However, we were
leaving the original annotation then on a prepargs, which is not a
deopt-causing instruction, so we would wrongly drop use of the deopt
point. Fix by establishing a link between those added "synthetic"
deopt points and the original one they stole the target address from.
  • Loading branch information
jnthn committed Jul 13, 2018
1 parent 787b7bb commit 3345ec3
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 13 deletions.
4 changes: 4 additions & 0 deletions src/spesh/dump.c
Expand Up @@ -185,6 +185,10 @@ static void dump_bb(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g, MVMSpes
appendf(ds, " [Annotation: Logged (bytecode offset %d)]\n",
ann->data.bytecode_offset);
break;
case MVM_SPESH_ANN_DEOPT_SYNTH:
appendf(ds, " [Annotation: INS Deopt Synth (idx %d)]\n",
ann->data.deopt_idx);
break;
default:
appendf(ds, " [Annotation: %d (unknown)]\n", ann->type);
}
Expand Down
1 change: 1 addition & 0 deletions src/spesh/graph.h
Expand Up @@ -252,6 +252,7 @@ struct MVMSpeshAnn {
#define MVM_SPESH_ANN_DEOPT_OSR 9
#define MVM_SPESH_ANN_LINENO 10
#define MVM_SPESH_ANN_LOGGED 11
#define MVM_SPESH_ANN_DEOPT_SYNTH 12

/* Functions to create/destroy the spesh graph. */
MVMSpeshGraph * MVM_spesh_graph_create(MVMThreadContext *tc, MVMStaticFrame *sf,
Expand Down
50 changes: 37 additions & 13 deletions src/spesh/optimize.c
Expand Up @@ -1312,12 +1312,15 @@ MVMuint32 find_invoke_offset(MVMThreadContext *tc, MVMSpeshIns *ins) {

/* Given an instruction, finds the deopt target on it. Panics if there is not
* one there. */
MVMuint32 find_deopt_target(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMuint32 deopt_target;
void find_deopt_target_and_index(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins,
MVMint32 *deopt_target_out, MVMint32 *deopt_index_out) {
MVMSpeshAnn *deopt_ann = ins->annotations;
while (deopt_ann) {
if (deopt_ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS)
return g->deopt_addrs[2 * deopt_ann->data.deopt_idx];
if (deopt_ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS) {
*deopt_target_out = g->deopt_addrs[2 * deopt_ann->data.deopt_idx];
*deopt_index_out = deopt_ann->data.deopt_idx;
return;
}
deopt_ann = deopt_ann->next;
}
MVM_panic(1, "Spesh: unexpectedly missing deopt annotation on prepargs");
Expand Down Expand Up @@ -1378,12 +1381,24 @@ static MVMSpeshStatsType * find_invokee_type_tuple(MVMThreadContext *tc, MVMSpes
: NULL;
}

/* Adds an annotation relating a synthetic (added during optimization) deopt
* point back to the original one whose usages will have been recorded in the
* facts. */
static void add_synthetic_deopt_annotation(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshIns *ins, MVMuint32 deopt_index) {
MVMSpeshAnn *ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn));
ann->type = MVM_SPESH_ANN_DEOPT_SYNTH;
ann->data.deopt_idx = deopt_index;
ann->next = ins->annotations;
ins->annotations = ann;
}

/* Inserts an argument type guard as suggested by a logged type tuple. */
static void insert_arg_type_guard(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshStatsType *type_info,
MVMSpeshCallInfo *arg_info, MVMuint32 arg_idx) {
/* Insert guard before prepargs (this means they stack up in order). */
MVMuint32 deopt_target = find_deopt_target(tc, g, arg_info->prepargs_ins);
MVMuint32 deopt_target, deopt_index;
MVMSpeshIns *guard = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
guard->info = MVM_op_get_op(type_info->type_concrete
? MVM_OP_sp_guardconc
Expand All @@ -1392,22 +1407,25 @@ static void insert_arg_type_guard(MVMThreadContext *tc, MVMSpeshGraph *g,
guard->operands[0] = arg_info->arg_ins[arg_idx]->operands[1];
guard->operands[1].lit_i16 = MVM_spesh_add_spesh_slot_try_reuse(tc, g,
(MVMCollectable *)type_info->type->st);
find_deopt_target_and_index(tc, g, arg_info->prepargs_ins, &deopt_target, &deopt_index);
guard->operands[2].lit_ui32 = deopt_target;
MVM_spesh_manipulate_insert_ins(tc, arg_info->prepargs_bb,
arg_info->prepargs_ins->prev, guard);
MVM_spesh_usages_add_by_reg(tc, g, arg_info->arg_ins[arg_idx]->operands[1], guard);

/* Also give the instruction a deopt annotation. */
/* Also give the instruction a deopt annotation, and related it to the
* one on the prepargs. */
MVM_spesh_graph_add_deopt_annotation(tc, g, guard, deopt_target,
MVM_SPESH_ANN_DEOPT_ONE_INS);
add_synthetic_deopt_annotation(tc, g, guard, deopt_index);
}

/* Inserts an argument decont type guard as suggested by a logged type tuple. */
static void insert_arg_decont_type_guard(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshStatsType *type_info,
MVMSpeshCallInfo *arg_info, MVMuint32 arg_idx) {
MVMSpeshIns *decont, *guard;
MVMuint32 deopt_target;
MVMuint32 deopt_target, deopt_index;

/* We need a temporary register to decont into. */
MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_obj);
Expand All @@ -1425,7 +1443,7 @@ static void insert_arg_decont_type_guard(MVMThreadContext *tc, MVMSpeshGraph *g,
optimize_decont(tc, g, arg_info->prepargs_bb, decont);

/* Guard the decontainerized value. */
deopt_target = find_deopt_target(tc, g, arg_info->prepargs_ins);
find_deopt_target_and_index(tc, g, arg_info->prepargs_ins, &deopt_target, &deopt_index);
guard = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
guard->info = MVM_op_get_op(type_info->decont_type_concrete
? MVM_OP_sp_guardconc
Expand All @@ -1439,9 +1457,11 @@ static void insert_arg_decont_type_guard(MVMThreadContext *tc, MVMSpeshGraph *g,
arg_info->prepargs_ins->prev, guard);
MVM_spesh_usages_add_by_reg(tc, g, temp, guard);

/* Also give the instruction a deopt annotation. */
/* Also give the instruction a deopt annotation and reference prepargs
* deopt index. */
MVM_spesh_graph_add_deopt_annotation(tc, g, guard, deopt_target,
MVM_SPESH_ANN_DEOPT_ONE_INS);
add_synthetic_deopt_annotation(tc, g, guard, deopt_index);

/* Release the temp register. */
MVM_spesh_manipulate_release_temp_reg(tc, g, temp);
Expand Down Expand Up @@ -1545,7 +1565,7 @@ static void tweak_for_target_sf(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMStaticFrame *target_sf, MVMSpeshIns *ins,
MVMSpeshCallInfo *arg_info, MVMSpeshOperand temp) {
MVMSpeshIns *guard, *resolve;
MVMuint32 deopt_target;
MVMuint32 deopt_target, deopt_index;

/* Work out which operand of the invoke instruction has the invokee. */
MVMuint32 inv_code_index = ins->info->opcode == MVM_OP_invoke_v ? 0 : 1;
Expand All @@ -1563,7 +1583,7 @@ static void tweak_for_target_sf(MVMThreadContext *tc, MVMSpeshGraph *g,
MVM_spesh_usages_add_by_reg(tc, g, resolve->operands[1], resolve);

/* Insert guard instruction before the prepargs. */
deopt_target = find_deopt_target(tc, g, arg_info->prepargs_ins);
find_deopt_target_and_index(tc, g, arg_info->prepargs_ins, &deopt_target, &deopt_index);
guard = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
guard->info = MVM_op_get_op(MVM_OP_sp_guardsf);
guard->operands = MVM_spesh_alloc(tc, g, 3 * sizeof(MVMSpeshOperand));
Expand All @@ -1575,9 +1595,11 @@ static void tweak_for_target_sf(MVMThreadContext *tc, MVMSpeshGraph *g,
MVM_spesh_manipulate_insert_ins(tc, arg_info->prepargs_bb,
arg_info->prepargs_ins->prev, guard);

/* Also give the guard instruction a deopt annotation. */
/* Also give the guard instruction a deopt annotation and reference the
* prepargs annotation. */
MVM_spesh_graph_add_deopt_annotation(tc, g, guard, deopt_target,
MVM_SPESH_ANN_DEOPT_ONE_INS);
add_synthetic_deopt_annotation(tc, g, guard, deopt_index);

/* Make the invoke instruction call the resolved result. */
MVM_spesh_usages_delete_by_reg(tc, g, ins->operands[inv_code_index], ins);
Expand Down Expand Up @@ -1985,11 +2007,12 @@ static void optimize_throwcat(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB

/* Updates rebless with rebless_sp, which will deopt from the current code. */
static void tweak_rebless(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshIns *ins) {
MVMuint32 deopt_target = find_deopt_target(tc, g, ins);
MVMuint32 deopt_target, deopt_index;
MVMSpeshOperand *new_operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));
new_operands[0] = ins->operands[0];
new_operands[1] = ins->operands[1];
new_operands[2] = ins->operands[2];
find_deopt_target_and_index(tc, g, ins, &deopt_target, &deopt_index);
new_operands[3].lit_ui32 = deopt_target;
ins->info = MVM_op_get_op(MVM_OP_sp_rebless);
ins->operands = new_operands;
Expand Down Expand Up @@ -2751,6 +2774,7 @@ void MVM_spesh_optimize(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshPlanned
MVM_spesh_graph_recompute_dominance(tc, g);
eliminate_unused_log_guards(tc, g);
eliminate_pointless_gotos(tc, g);
MVM_spesh_usages_remove_unused_deopt(tc, g);
eliminate_dead_ins(tc, g);

merge_bbs(tc, g);
Expand Down
53 changes: 53 additions & 0 deletions src/spesh/usages.c
Expand Up @@ -268,6 +268,59 @@ void MVM_spesh_usages_add_unconditional_deopt_usage_by_reg(MVMThreadContext *tc,
MVM_spesh_usages_add_unconditional_deopt_usage(tc, g, MVM_spesh_get_facts(tc, g, operand));
}

/* Remove usages of deopt points that won't casue deopt. */
void MVM_spesh_usages_remove_unused_deopt(MVMThreadContext *tc, MVMSpeshGraph *g) {
MVMuint32 i, j;

/* First, walk graph to find which deopt points are actually used. */
MVMuint8 *deopt_used = MVM_spesh_alloc(tc, g, g->num_deopt_addrs);
MVMSpeshBB *bb = g->entry;
while (bb) {
if (!bb->inlined) {
MVMSpeshIns *ins = bb->first_ins;
while (ins) {
MVMSpeshAnn *ann = ins->annotations;
while (ann) {
switch (ann->type) {
case MVM_SPESH_ANN_DEOPT_ONE_INS:
case MVM_SPESH_ANN_DEOPT_ALL_INS:
case MVM_SPESH_ANN_DEOPT_SYNTH:
if (ins->info->may_cause_deopt)
deopt_used[ann->data.deopt_idx] = 1;
/* else
printf("Instruction %s cannot cause deopt\n", ins->info->name);*/
break;
}
ann = ann->next;
}
ins = ins->next;
}
}
bb = bb->linear_next;
}

/* Now delete deopt usages that are of unused deopt indices. */
for (i = 0; i < g->sf->body.num_locals; i++) {
for (j = 0; j < g->fact_counts[i]; j++) {
MVMSpeshFacts *facts = &(g->facts[i][j]);
MVMSpeshDeoptUseEntry *du_entry = facts->usage.deopt_users;
MVMSpeshDeoptUseEntry *prev_du_entry = NULL;
while (du_entry) {
if (du_entry->deopt_idx >= 0 && !deopt_used[du_entry->deopt_idx]) {
if (prev_du_entry)
prev_du_entry->next = du_entry->next;
else
facts->usage.deopt_users = du_entry->next;
}
else {
prev_du_entry = du_entry;
}
du_entry = du_entry->next;
}
}
}
}

/* Checks if the value is used, either by another instruction in the graph or
* by being needed for deopt. */
MVMuint32 MVM_spesh_usages_is_used(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand check) {
Expand Down
1 change: 1 addition & 0 deletions src/spesh/usages.h
Expand Up @@ -58,6 +58,7 @@ void MVM_spesh_usages_create_deopt_usage(MVMThreadContext *tc, MVMSpeshGraph *g)
void MVM_spesh_usages_add_unconditional_deopt_usage(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshFacts *facts);
void MVM_spesh_usages_add_unconditional_deopt_usage_by_reg(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshOperand operand);
void MVM_spesh_usages_remove_unused_deopt(MVMThreadContext *tc, MVMSpeshGraph *g);
MVMuint32 MVM_spesh_usages_is_used(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand check);
MVMuint32 MVM_spesh_usages_is_used_by_deopt(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand check);
MVMuint32 MVM_spesh_usages_is_used_by_handler(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshOperand check);
Expand Down

0 comments on commit 3345ec3

Please sign in to comment.