-
-
Save MasterDuke17/a618b3e46db4f7ea9fac78d984af2d3e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/core/coerce.c b/src/core/coerce.c | |
index 813e913b2..85d990702 100644 | |
--- a/src/core/coerce.c | |
+++ b/src/core/coerce.c | |
@@ -285,146 +285,149 @@ void MVM_coerce_smart_stringify(MVMThreadContext *tc, MVMObject *obj, MVMRegiste | |
return; | |
} | |
/* If it can unbox as a string, that wins right off. */ | |
ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); | |
if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR && IS_CONCRETE(obj)) { | |
res_reg->s = REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
return; | |
} | |
/* Check if there is a Str method. */ | |
MVMROOT(tc, obj, { | |
strmeth = MVM_6model_find_method_cache_only(tc, obj, | |
tc->instance->str_consts.Str); | |
}); | |
if (!MVM_is_null(tc, strmeth)) { | |
/* We need to do the invocation; just set it up with our result reg as | |
* the one for the call. */ | |
MVMObject *code = MVM_frame_find_invokee(tc, strmeth, NULL); | |
MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); | |
MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_STR, inv_arg_callsite); | |
tc->cur_frame->args[0].o = obj; | |
STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); | |
+ //fprintf(stderr, ".Str method called on '%s'\n", MVM_6model_get_debug_name(tc, obj)); | |
return; | |
} | |
/* Otherwise, guess something appropriate. */ | |
if (!IS_CONCRETE(obj)) | |
res_reg->s = tc->instance->str_consts.empty; | |
else { | |
if (REPR(obj)->ID == MVM_REPR_ID_MVMException) | |
res_reg->s = ((MVMException *)obj)->body.message; | |
else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) | |
res_reg->s = MVM_coerce_i_s(tc, REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj))); | |
else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) | |
res_reg->s = MVM_coerce_n_s(tc, REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj))); | |
else | |
MVM_exception_throw_adhoc(tc, "cannot stringify this"); | |
} | |
} | |
MVMint64 MVM_coerce_s_i(MVMThreadContext *tc, MVMString *s) { | |
char *enc = MVM_string_ascii_encode(tc, s, NULL, 0); | |
MVMint64 i = strtoll(enc, NULL, 10); | |
MVM_free(enc); | |
return i; | |
} | |
void MVM_coerce_smart_numify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { | |
MVMObject *nummeth; | |
/* Handle null case. */ | |
if (MVM_is_null(tc, obj)) { | |
res_reg->n64 = 0.0; | |
return; | |
} | |
/* Check if there is a Num method. */ | |
MVMROOT(tc, obj, { | |
nummeth = MVM_6model_find_method_cache_only(tc, obj, | |
tc->instance->str_consts.Num); | |
}); | |
if (!MVM_is_null(tc, nummeth)) { | |
/* We need to do the invocation; just set it up with our result reg as | |
* the one for the call. */ | |
MVMObject *code = MVM_frame_find_invokee(tc, nummeth, NULL); | |
MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); | |
MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_NUM, inv_arg_callsite); | |
tc->cur_frame->args[0].o = obj; | |
STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); | |
+ //fprintf(stderr, ".Num method called on '%s'\n", MVM_6model_get_debug_name(tc, obj)); | |
return; | |
} | |
/* Otherwise, guess something appropriate. */ | |
if (!IS_CONCRETE(obj)) { | |
res_reg->n64 = 0.0; | |
} | |
else { | |
const MVMStorageSpec *ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); | |
if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) | |
res_reg->n64 = (MVMnum64)REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) | |
res_reg->n64 = REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) | |
res_reg->n64 = MVM_coerce_s_n(tc, REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj))); | |
else if (REPR(obj)->ID == MVM_REPR_ID_VMArray) | |
res_reg->n64 = (MVMnum64)REPR(obj)->elems(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
else if (REPR(obj)->ID == MVM_REPR_ID_MVMHash) | |
res_reg->n64 = (MVMnum64)REPR(obj)->elems(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
else | |
MVM_exception_throw_adhoc(tc, "cannot numify this"); | |
} | |
} | |
void MVM_coerce_smart_intify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { | |
MVMObject *intmeth; | |
/* Handle null case. */ | |
if (MVM_is_null(tc, obj)) { | |
res_reg->i64 = 0; | |
} | |
/* Check if there is an Int method. */ | |
MVMROOT(tc, obj, { | |
intmeth = MVM_6model_find_method_cache_only(tc, obj, | |
tc->instance->str_consts.Int); | |
}); | |
if (!MVM_is_null(tc, intmeth)) { | |
/* We need to do the invocation; just set it up with our result reg as | |
* the one for the call. */ | |
MVMObject *code = MVM_frame_find_invokee(tc, intmeth, NULL); | |
MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); | |
MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_INT, inv_arg_callsite); | |
tc->cur_frame->args[0].o = obj; | |
STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); | |
+ //fprintf(stderr, ".Int method called on '%s'\n", MVM_6model_get_debug_name(tc, obj)); | |
return; | |
} | |
else { | |
res_reg->i64 = MVM_coerce_simple_intify(tc, obj); | |
} | |
} | |
MVMint64 MVM_coerce_simple_intify(MVMThreadContext *tc, MVMObject *obj) { | |
/* Handle null and non-concrete case. */ | |
if (MVM_is_null(tc, obj) || !IS_CONCRETE(obj)) { | |
return 0; | |
} | |
/* Otherwise, guess something appropriate. */ | |
else { | |
const MVMStorageSpec *ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); | |
if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) | |
return REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) | |
return (MVMint64)REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) | |
return MVM_coerce_s_i(tc, REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj))); | |
else if (REPR(obj)->ID == MVM_REPR_ID_VMArray) | |
return REPR(obj)->elems(tc, STABLE(obj), obj, OBJECT_BODY(obj)); | |
else if (REPR(obj)->ID == MVM_REPR_ID_MVMHash) | |
diff --git a/src/spesh/optimize.c b/src/spesh/optimize.c | |
index 9f55fa598..1bac5406a 100644 | |
--- a/src/spesh/optimize.c | |
+++ b/src/spesh/optimize.c | |
@@ -907,87 +907,101 @@ static void optimize_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB * | |
ins->info = MVM_op_get_op(MVM_OP_const_n64); | |
ins->operands[1].lit_n64 = result; | |
result_facts->flags |= MVM_SPESH_FACT_KNOWN_VALUE; | |
result_facts->value.n = result; | |
} | |
} | |
/* If we know the type of a significant operand, we might try to specialize by | |
* representation. */ | |
static void optimize_repr_op(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, | |
MVMSpeshIns *ins, MVMint32 type_operand) { | |
/* Immediately mark guards as used, as the JIT would like to devirtualize | |
* repr ops later and we don't want guards to be thrown out before that */ | |
MVMSpeshFacts *facts = MVM_spesh_get_and_use_facts(tc, g, ins->operands[type_operand]); | |
if (facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && facts->type) | |
if (REPR(facts->type)->spesh) { | |
REPR(facts->type)->spesh(tc, STABLE(facts->type), g, bb, ins); | |
MVM_spesh_use_facts(tc, g, facts); | |
} | |
} | |
/* smrt_strify and smrt_numify can turn into unboxes, but at least | |
* for smrt_numify it's "complicated". Also, later when we know how | |
* to put new invocations into spesh'd code, we could make direct | |
- * invoke calls to the .Str and .Num methods. */ | |
+ * invoke calls to the .Str, .Num, and .Int methods. */ | |
static void optimize_smart_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { | |
MVMSpeshFacts *facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); | |
MVMuint16 is_strify = ins->info->opcode == MVM_OP_smrt_strify; | |
+ MVMuint16 is_numify = ins->info->opcode == MVM_OP_smrt_numify; | |
+ MVMuint16 is_intify = ins->info->opcode == MVM_OP_smrt_intify; | |
if (facts->flags & (MVM_SPESH_FACT_KNOWN_TYPE | MVM_SPESH_FACT_CONCRETE) && facts->type) { | |
const MVMStorageSpec *ss; | |
MVMint64 can_result; | |
ss = REPR(facts->type)->get_storage_spec(tc, STABLE(facts->type)); | |
if (is_strify && ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) { | |
MVM_spesh_use_facts(tc, g, facts); | |
ins->info = MVM_op_get_op(MVM_OP_unbox_s); | |
/* And now that we have a repr op, we can try to optimize | |
* it even further. */ | |
optimize_repr_op(tc, g, bb, ins, 1); | |
return; | |
} | |
+ else if (is_intify && ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) { | |
+ MVM_spesh_use_facts(tc, g, facts); | |
+ | |
+ ins->info = MVM_op_get_op(MVM_OP_unbox_i); | |
+ /* And now that we have a repr op, we can try to optimize | |
+ * it even further. */ | |
+ optimize_repr_op(tc, g, bb, ins, 1); | |
+ | |
+ return; | |
+ } | |
/* We cheat a little, since we have full control over the boot | |
* types. XXX is it a fair DIHWIDT if the user adds stuff to those? */ | |
if (facts->type == tc->instance->boot_types.BOOTInt | |
|| facts->type == tc->instance->boot_types.BOOTNum | |
|| facts->type == tc->instance->boot_types.BOOTStr) { | |
can_result = 0; | |
} | |
else { | |
can_result = MVM_spesh_try_can_method(tc, facts->type, | |
- is_strify ? tc->instance->str_consts.Str : tc->instance->str_consts.Num); | |
+ is_strify ? tc->instance->str_consts.Str : | |
+ is_intify ? tc->instance->str_consts.Int : | |
+ tc->instance->str_consts.Num); | |
} | |
if (can_result == -1) { | |
- /* Couldn't safely figure out if the type has a Str method or not. */ | |
- MVM_spesh_graph_add_comment(tc, g, ins, "Couldn't safely determine whether %s can .Str/.Num", | |
+ /* Couldn't safely figure out if the type has a Str/Num/Int method or not. */ | |
+ MVM_spesh_graph_add_comment(tc, g, ins, "Couldn't safely determine whether %s can .Str/.Num/.Int", | |
MVM_6model_get_debug_name(tc, facts->type)); | |
return; | |
} else if (can_result == 0) { | |
MVM_spesh_use_facts(tc, g, facts); | |
/* We can't .Str this object, so we'll duplicate the "guessing" | |
* logic from smrt_strify here to remove indirection. */ | |
if (is_strify && REPR(facts->type)->ID == MVM_REPR_ID_MVMException) { | |
MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 3); | |
MVMSpeshOperand *old_opers = ins->operands; | |
MVM_spesh_graph_add_comment(tc, g, ins, "specialized from %s", ins->info->name); | |
ins->info = MVM_op_get_op(MVM_OP_sp_get_s); | |
ins->operands = operands; | |
operands[0] = old_opers[0]; | |
operands[1] = old_opers[1]; | |
operands[2].lit_i16 = offsetof( MVMException, body.message ); | |
} else if(ss->can_box & (MVM_STORAGE_SPEC_CAN_BOX_NUM | MVM_STORAGE_SPEC_CAN_BOX_INT)) { | |
MVMuint16 register_type = | |
ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT ? MVM_reg_int64 : MVM_reg_num64; | |
MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); | |
MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2); | |
@@ -999,82 +1013,88 @@ static void optimize_smart_coerce(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpe | |
if (is_strify) | |
new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_coerce_ns : MVM_OP_coerce_is); | |
else | |
new_ins->info = MVM_op_get_op(register_type == MVM_reg_num64 ? MVM_OP_set : MVM_OP_coerce_in); | |
new_ins->operands = operands; | |
operands[0] = ins->operands[0]; | |
operands[1] = temp; | |
/* We can directly "eliminate" a set instruction here. */ | |
if (new_ins->info->opcode != MVM_OP_set) { | |
ins->operands[0] = temp; | |
MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins); | |
MVM_spesh_usages_add_by_reg(tc, g, temp, new_ins); | |
get_facts_direct(tc, g, temp)->writer = ins; | |
get_facts_direct(tc, g, operands[0])->writer = new_ins; | |
} | |
/* Finally, let's try to optimize the unboxing REPROp. */ | |
optimize_repr_op(tc, g, bb, ins, 1); | |
/* And as a last clean-up step, we release the temporary register. */ | |
MVM_spesh_manipulate_release_temp_reg(tc, g, temp); | |
return; | |
- } else if (!is_strify && (REPR(facts->type)->ID == MVM_REPR_ID_VMArray || | |
+ } else if (is_numify && (REPR(facts->type)->ID == MVM_REPR_ID_VMArray || | |
(REPR(facts->type)->ID == MVM_REPR_ID_MVMHash))) { | |
- /* A smrt_numify on an array or hash can be replaced by an | |
- * elems operation, that can then be optimized by our | |
- * versatile and dilligent friend optimize_repr_op. */ | |
+ /* A smrt_numify or smrt_intify on an array or hash can be | |
+ * replaced by an elems operation, that can then be optimized | |
+ * by our versatile and dilligent friend optimize_repr_op. */ | |
MVMSpeshIns *new_ins = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshIns )); | |
MVMSpeshOperand *operands = MVM_spesh_alloc(tc, g, sizeof( MVMSpeshOperand ) * 2); | |
MVMSpeshOperand temp = MVM_spesh_manipulate_get_temp_reg(tc, g, MVM_reg_int64); | |
MVMSpeshOperand orig_dst = ins->operands[0]; | |
- | |
+fprintf(stderr, "in numify branch\n"); | |
MVM_spesh_graph_add_comment(tc, g, ins, "specialized from %s", ins->info->name); | |
ins->info = MVM_op_get_op(MVM_OP_elems); | |
ins->operands[0] = temp; | |
MVM_spesh_get_facts(tc, g, temp)->writer = ins; | |
new_ins->info = MVM_op_get_op(MVM_OP_coerce_in); | |
new_ins->operands = operands; | |
operands[0] = orig_dst; | |
operands[1] = temp; | |
MVM_spesh_get_facts(tc, g, orig_dst)->writer = new_ins; | |
MVM_spesh_manipulate_insert_ins(tc, bb, ins, new_ins); | |
optimize_repr_op(tc, g, bb, ins, 1); | |
MVM_spesh_usages_add_by_reg(tc, g, temp, new_ins); | |
MVM_spesh_manipulate_release_temp_reg(tc, g, temp); | |
return; | |
+ } else if (is_intify && (REPR(facts->type)->ID == MVM_REPR_ID_VMArray || | |
+ (REPR(facts->type)->ID == MVM_REPR_ID_MVMHash))) { | |
+fprintf(stderr, "in intify branch\n"); | |
+ ins->info = MVM_op_get_op(MVM_OP_elems); | |
+ optimize_repr_op(tc, g, bb, ins, 1); | |
+ return; | |
} | |
} else if (can_result == 1) { | |
/* When we know how to generate additional callsites, we could | |
* make an invocation to .Str or .Num here and perhaps have it | |
* in-lined. */ | |
} | |
} | |
} | |
/* Optimize string equality if one param is the empty string */ | |
static void optimize_string_equality(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) { | |
MVMSpeshFacts *a_facts = MVM_spesh_get_facts(tc, g, ins->operands[1]); | |
MVMSpeshFacts *b_facts = MVM_spesh_get_facts(tc, g, ins->operands[2]); | |
MVMuint8 was_eq = 0; | |
return; | |
if (ins->info->opcode == MVM_OP_eq_s) | |
was_eq = 1; | |
if (a_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE && b_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE) { | |
/* Cool, we can constant-fold this. */ | |
MVMSpeshFacts *target_facts = MVM_spesh_get_facts(tc, g, ins->operands[0]); | |
MVM_spesh_usages_delete(tc, g, a_facts, ins); | |
@@ -2653,50 +2673,51 @@ static void optimize_bb_switch(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshB | |
case MVM_OP_arg_s: | |
case MVM_OP_arg_o: { | |
MVMint16 idx = ins->operands[0].lit_i16; | |
if (idx < MAX_ARGS_FOR_OPT) { | |
arg_info.arg_is_const[idx] = 0; | |
arg_info.arg_facts[idx] = MVM_spesh_get_and_use_facts(tc, g, ins->operands[1]); | |
arg_info.arg_ins[idx] = ins; | |
} | |
break; | |
} | |
case MVM_OP_argconst_i: | |
case MVM_OP_argconst_n: | |
case MVM_OP_argconst_s: { | |
MVMint16 idx = ins->operands[0].lit_i16; | |
if (idx < MAX_ARGS_FOR_OPT) { | |
arg_info.arg_is_const[idx] = 1; | |
arg_info.arg_ins[idx] = ins; | |
} | |
break; | |
} | |
case MVM_OP_coerce_in: | |
optimize_coerce(tc, g, bb, ins); | |
break; | |
case MVM_OP_smrt_numify: | |
case MVM_OP_smrt_strify: | |
+ case MVM_OP_smrt_intify: | |
optimize_smart_coerce(tc, g, bb, ins); | |
break; | |
case MVM_OP_invoke_v: | |
optimize_call(tc, g, bb, ins, p, 0, &arg_info); | |
break; | |
case MVM_OP_invoke_i: | |
case MVM_OP_invoke_n: | |
case MVM_OP_invoke_s: | |
case MVM_OP_invoke_o: | |
optimize_call(tc, g, bb, ins, p, 1, &arg_info); | |
break; | |
case MVM_OP_getarg_i: | |
optimize_getarg(tc, g, bb, ins); | |
break; | |
case MVM_OP_speshresolve: | |
if (p) { | |
/* Rewriting spesh plugins will insert a bunch of instructions | |
* in front of the generated code, which would be skipped by | |
* our loop, so we skip to before the prepargs that belongs to | |
* it so that we run across the new instructions, too. */ | |
MVMSpeshIns *prepargs = ins->prev; | |
do { | |
prepargs = prepargs->prev; | |
} while (prepargs && prepargs->info->opcode != MVM_OP_prepargs); | |
optimize_plugin(tc, g, bb, ins, p); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment