Skip to content

Commit

Permalink
Attempt inlining so-far unspecialized callees
Browse files Browse the repository at this point in the history
Sometimes we know the callee, but there wasn't yet a specialization
(or an applicable specialization) performed. If the bytecode is small
enough, then we can produce a specialization on demand and try to do
an inlining of that.

This doesn't yet convey the types of the incoming arguments to the
specialization of the callee, so various possible optimizations are
still not performed yet.
  • Loading branch information
jnthn committed Jul 4, 2018
1 parent ca51c5e commit f59335e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 18 deletions.
10 changes: 10 additions & 0 deletions src/spesh/args.c
Expand Up @@ -791,3 +791,13 @@ void MVM_spesh_args(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCallsite *cs,
MVM_free(named_ins);
MVM_free(named_bb);
}

/* Performs argument instruction specialization with type info provided by a
* call_info object. */
void MVM_spesh_args_from_callinfo(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshCallInfo *call_info) {
/* Transform call info to a type tuple. */
MVMSpeshStatsType *tt = MVM_calloc(call_info->cs->flag_count, sizeof(MVMSpeshStatsType));
MVM_spesh_args(tc, g, call_info->cs, tt);
MVM_free(tt);
}
2 changes: 2 additions & 0 deletions src/spesh/args.h
@@ -1,2 +1,4 @@
void MVM_spesh_args(MVMThreadContext *tc, MVMSpeshGraph *g, MVMCallsite *cs,
MVMSpeshStatsType *type_tuple);
void MVM_spesh_args_from_callinfo(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshCallInfo *call_info);
32 changes: 32 additions & 0 deletions src/spesh/inline.c
Expand Up @@ -206,6 +206,36 @@ MVMSpeshGraph * MVM_spesh_inline_try_get_graph(MVMThreadContext *tc, MVMSpeshGra
}
}

/* Tries to get a spesh graph for a particular unspecialized candidate. */
MVMSpeshGraph * MVM_spesh_inline_try_get_graph_from_unspecialized(MVMThreadContext *tc,
MVMSpeshGraph *inliner, MVMStaticFrame *target_sf, MVMSpeshIns *invoke_ins,
MVMSpeshCallInfo *call_info, char **no_inline_reason) {
MVMSpeshGraph *ig;

/* Check the target is suitable for inlining. */
if (!is_static_frame_inlineable(tc, inliner, target_sf, no_inline_reason))
return NULL;

/* Build the spesh graph from bytecode, transform args, do facts discovery
* (setting usage counts) and optimize. We do this before checking if it
* is inlineable as we can get rid of many :noinline ops (especially in
* the args specialization). */
ig = MVM_spesh_graph_create(tc, target_sf, 0, 1);
MVM_spesh_args_from_callinfo(tc, ig, call_info);
MVM_spesh_facts_discover(tc, ig, NULL);
MVM_spesh_optimize(tc, ig, NULL);

/* See if it's inlineable; clean up if not. */
if (is_graph_inlineable(tc, inliner, target_sf, invoke_ins, ig, no_inline_reason)) {
return ig;
}
else {
MVM_free(ig->spesh_slots);
MVM_spesh_graph_destroy(tc, ig);
return NULL;
}
}

/* Finds the deopt index of the return. */
static MVMint32 return_deopt_idx(MVMThreadContext *tc, MVMSpeshIns *invoke_ins) {
MVMSpeshAnn *ann = invoke_ins->annotations;
Expand Down Expand Up @@ -363,6 +393,8 @@ MVMSpeshBB * merge_graph(MVMThreadContext *tc, MVMSpeshGraph *inliner,
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_INLINE:
ann->data.deopt_idx += inliner->num_deopt_addrs;
break;
Expand Down
3 changes: 3 additions & 0 deletions src/spesh/inline.h
Expand Up @@ -44,6 +44,9 @@ struct MVMSpeshInline {
MVMSpeshGraph * MVM_spesh_inline_try_get_graph(MVMThreadContext *tc,
MVMSpeshGraph *inliner, MVMStaticFrame *target_sf, MVMSpeshCandidate *cand,
MVMSpeshIns *invoke_ins, char **no_inline_reason);
MVMSpeshGraph * MVM_spesh_inline_try_get_graph_from_unspecialized(MVMThreadContext *tc,
MVMSpeshGraph *inliner, MVMStaticFrame *target_sf, MVMSpeshIns *invoke_ins,
MVMSpeshCallInfo *call_info, char **no_inline_reason);
void MVM_spesh_inline(MVMThreadContext *tc, MVMSpeshGraph *inliner,
MVMSpeshCallInfo *call_info, MVMSpeshBB *invoke_bb,
MVMSpeshIns *invoke, MVMSpeshGraph *inlinee, MVMStaticFrame *inlinee_sf,
Expand Down
56 changes: 38 additions & 18 deletions src/spesh/optimize.c
Expand Up @@ -1231,14 +1231,16 @@ static void optimize_getlex_known(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpe
if (ann) {
/* See if we can find a logged static value. */
MVMSpeshStats *ss = g->sf->body.spesh->body.spesh_stats;
MVMuint32 n = ss->num_static_values;
MVMuint32 i;
for (i = 0; i < n; i++) {
if (ss->static_values[i].bytecode_offset == ann->data.bytecode_offset) {
MVMObject *log_obj = ss->static_values[i].value;
if (log_obj)
lex_to_constant(tc, g, ins, log_obj);
return;
if (ss) {
MVMuint32 n = ss->num_static_values;
MVMuint32 i;
for (i = 0; i < n; i++) {
if (ss->static_values[i].bytecode_offset == ann->data.bytecode_offset) {
MVMObject *log_obj = ss->static_values[i].value;
if (log_obj)
lex_to_constant(tc, g, ins, log_obj);
return;
}
}
}
}
Expand All @@ -1251,8 +1253,9 @@ static void optimize_getlex_per_invocant(MVMThreadContext *tc, MVMSpeshGraph *g,
MVMSpeshPlanned *p) {
MVMSpeshAnn *ann;

/* Can only do this when we've specialized on the first argument type. */
if (!g->specialized_on_invocant)
/* Can only do this when we've specialized on the first argument type and
* we have a plan. */
if (!p || !g->specialized_on_invocant)
return;

/* Try to find logged offset. */
Expand Down Expand Up @@ -1594,7 +1597,7 @@ static void optimize_call(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb
* a static value. */
code = callee_facts->value.o;
}
else {
else if (p) {
/* See if there is a stable static frame at the callsite. If so, add
* the resolution and guard instruction. Note that we must keep the
* temporary alive throughout the whole guard and invocation sequence,
Expand All @@ -1614,11 +1617,16 @@ static void optimize_call(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb
* this if the callsite isn't too big for arg_info. */
num_arg_slots = arg_info->cs->num_pos +
2 * (arg_info->cs->flag_count - arg_info->cs->num_pos);
stable_type_tuple = num_arg_slots <= MAX_ARGS_FOR_OPT
? find_invokee_type_tuple(tc, g, bb, ins, p, arg_info->cs)
: NULL;
if (stable_type_tuple)
check_and_tweak_arg_guards(tc, g, stable_type_tuple, arg_info);
if (p) {
stable_type_tuple = num_arg_slots <= MAX_ARGS_FOR_OPT
? find_invokee_type_tuple(tc, g, bb, ins, p, arg_info->cs)
: NULL;
if (stable_type_tuple)
check_and_tweak_arg_guards(tc, g, stable_type_tuple, arg_info);
}
else {
stable_type_tuple = NULL;
}

/* If we don't have a target static frame from speculation, check on what
* we're going to be invoking and see if we can further resolve it. */
Expand Down Expand Up @@ -1788,8 +1796,20 @@ static void optimize_call(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb
/* We know what we're calling, but there's no specialization available
* to us. If it's small, then we could produce one and inline it. */
else if (target_sf->body.bytecode_size < MVM_SPESH_MAX_INLINE_SIZE) {
log_inline(tc, g, target_sf, NULL, target_sf->body.bytecode_size,
"missed inline opportunity");
char *no_inline_reason = NULL;
MVMSpeshGraph *inline_graph = MVM_spesh_inline_try_get_graph_from_unspecialized(
tc, g, target_sf, ins, arg_info, &no_inline_reason);
log_inline(tc, g, target_sf, inline_graph, target_sf->body.bytecode_size,
no_inline_reason);
if (inline_graph) {
MVMSpeshOperand code_ref_reg = ins->info->opcode == MVM_OP_invoke_v
? ins->operands[0]
: ins->operands[1];
MVM_spesh_get_facts(tc, g, code_ref_reg)->usages++;
MVM_spesh_inline(tc, g, arg_info, bb, ins, inline_graph, target_sf,
code_ref_reg);
MVM_free(inline_graph->spesh_slots);
}
}

/* Otherwise, nothing to be done. */
Expand Down

0 comments on commit f59335e

Please sign in to comment.