Mercurial > hg > Members > anatofuz > MoarVM
view src/jit/graph.c @ 0:2cf249471370
convert mercurial for git
author | Takahiro SHIMIZU <anatofuz@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 08 May 2018 16:09:12 +0900 |
parents | |
children |
line wrap: on
line source
#include "moar.h" #include "math.h" #include "platform/sys.h" #include "platform/time.h" static void jg_append_node(MVMJitGraph *jg, MVMJitNode *node) { if (jg->last_node) { jg->last_node->next = node; jg->last_node = node; } else { jg->first_node = node; jg->last_node = node; } node->next = NULL; } static void jg_append_primitive(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns * ins) { MVMJitNode * node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); node->type = MVM_JIT_NODE_PRIMITIVE; node->u.prim.ins = ins; jg_append_node(jg, node); } static void jg_append_call_c(MVMThreadContext *tc, MVMJitGraph *jg, void * func_ptr, MVMint16 num_args, MVMJitCallArg *call_args, MVMJitRVMode rv_mode, MVMint16 rv_idx) { MVMJitNode * node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); size_t args_size = num_args * sizeof(MVMJitCallArg); node->type = MVM_JIT_NODE_CALL_C; node->u.call.func_ptr = func_ptr; node->u.call.num_args = num_args; node->u.call.has_vargs = 0; /* don't support them yet */ /* Call argument array is typically stack allocated, * so they need to be copied */ node->u.call.args = MVM_spesh_alloc(tc, jg->sg, args_size); memcpy(node->u.call.args, call_args, args_size); node->u.call.rv_mode = rv_mode; node->u.call.rv_idx = rv_idx; jg_append_node(jg, node); } static void add_deopt_idx(MVMThreadContext *tc, MVMJitGraph *jg, MVMint32 label_name, MVMint32 deopt_idx) { MVMJitDeopt deopt; deopt.label = label_name; deopt.idx = deopt_idx; MVM_VECTOR_PUSH(jg->deopts, deopt); } static void jg_append_branch(MVMThreadContext *tc, MVMJitGraph *jg, MVMint32 name, MVMSpeshIns *ins) { MVMJitNode * node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); node->type = MVM_JIT_NODE_BRANCH; if (ins == NULL) { node->u.branch.ins = NULL; node->u.branch.dest = name; } else { MVMSpeshBB *bb; node->u.branch.ins = ins; if (ins->info->opcode == MVM_OP_goto) { bb = ins->operands[0].ins_bb; } else if (ins->info->opcode == MVM_OP_indexat || ins->info->opcode == MVM_OP_indexnat) { bb = ins->operands[3].ins_bb; } else { bb = ins->operands[1].ins_bb; } node->u.branch.dest = MVM_jit_label_before_bb(tc, jg, bb); } jg_append_node(jg, node); } static void jg_append_label(MVMThreadContext *tc, MVMJitGraph *jg, MVMint32 name) { MVMJitNode *node; /* does this label already exist? */ MVM_VECTOR_ENSURE_SIZE(jg->label_nodes, name); if (jg->label_nodes[name] != NULL) return; node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); node->type = MVM_JIT_NODE_LABEL; node->u.label.name = name; jg_append_node(jg, node); jg->label_nodes[name] = node; MVM_jit_log(tc, "append label: %d\n", node->u.label.name); } static void * op_to_func(MVMThreadContext *tc, MVMint16 opcode) { switch(opcode) { case MVM_OP_checkarity: return MVM_args_checkarity; case MVM_OP_say: return MVM_string_say; case MVM_OP_print: return MVM_string_print; case MVM_OP_isnull: return MVM_is_null; case MVM_OP_capturelex: return MVM_frame_capturelex; case MVM_OP_captureinnerlex: return MVM_frame_capture_inner; case MVM_OP_takeclosure: return MVM_frame_takeclosure; case MVM_OP_usecapture: return MVM_args_use_capture; case MVM_OP_savecapture: return MVM_args_save_capture; case MVM_OP_captureposprimspec: return MVM_capture_pos_primspec; case MVM_OP_return: return MVM_args_assert_void_return_ok; case MVM_OP_return_i: return MVM_args_set_result_int; case MVM_OP_return_s: return MVM_args_set_result_str; case MVM_OP_return_o: return MVM_args_set_result_obj; case MVM_OP_return_n: return MVM_args_set_result_num; case MVM_OP_coerce_is: return MVM_coerce_i_s; case MVM_OP_coerce_ns: return MVM_coerce_n_s; case MVM_OP_coerce_si: return MVM_coerce_s_i; case MVM_OP_coerce_sn: return MVM_coerce_s_n; case MVM_OP_coerce_In: return MVM_bigint_to_num; case MVM_OP_coerce_nI: return MVM_bigint_from_num; case MVM_OP_coerce_II: return MVM_bigint_from_bigint; case MVM_OP_iterkey_s: return MVM_iterkey_s; case MVM_OP_iter: return MVM_iter; case MVM_OP_iterval: return MVM_iterval; case MVM_OP_die: return MVM_exception_die; case MVM_OP_throwdyn: case MVM_OP_throwlex: case MVM_OP_throwlexotic: case MVM_OP_rethrow: return MVM_exception_throwobj; case MVM_OP_throwcatdyn: case MVM_OP_throwcatlex: case MVM_OP_throwcatlexotic: return MVM_exception_throwcat; case MVM_OP_throwpayloadlex: return MVM_exception_throwpayload; case MVM_OP_bindexpayload: return MVM_bind_exception_payload; case MVM_OP_getexpayload: return MVM_get_exception_payload; case MVM_OP_resume: return MVM_exception_resume; case MVM_OP_continuationreset: return MVM_continuation_reset; case MVM_OP_continuationcontrol: return MVM_continuation_control; case MVM_OP_continuationinvoke: return MVM_continuation_invoke; case MVM_OP_smrt_numify: return MVM_coerce_smart_numify; case MVM_OP_smrt_strify: return MVM_coerce_smart_stringify; case MVM_OP_gethow: return MVM_6model_get_how_obj; case MVM_OP_box_i: return MVM_box_int; case MVM_OP_box_s: return MVM_box_str; case MVM_OP_box_n: return MVM_box_num; case MVM_OP_unbox_i: return MVM_repr_get_int; case MVM_OP_unbox_s: return MVM_repr_get_str; case MVM_OP_unbox_n: return MVM_repr_get_num; case MVM_OP_istrue: case MVM_OP_isfalse: return MVM_coerce_istrue; case MVM_OP_istype: return MVM_6model_istype; case MVM_OP_isint: case MVM_OP_isnum: case MVM_OP_isstr: /* continued */ case MVM_OP_islist: case MVM_OP_ishash: return MVM_repr_compare_repr_id; case MVM_OP_wval: case MVM_OP_wval_wide: return MVM_sc_get_sc_object; case MVM_OP_scgetobjidx: return MVM_sc_find_object_idx_jit; case MVM_OP_getdynlex: return MVM_frame_getdynlex; case MVM_OP_binddynlex: return MVM_frame_binddynlex; case MVM_OP_getlexouter: return MVM_frame_find_lexical_by_name_outer; case MVM_OP_findmeth: case MVM_OP_findmeth_s: return MVM_6model_find_method; case MVM_OP_tryfindmeth: case MVM_OP_tryfindmeth_s: return MVM_6model_find_method; case MVM_OP_multicacheadd: return MVM_multi_cache_add; case MVM_OP_multicachefind: return MVM_multi_cache_find; case MVM_OP_can: case MVM_OP_can_s: return MVM_6model_can_method; case MVM_OP_push_i: return MVM_repr_push_i; case MVM_OP_push_n: return MVM_repr_push_n; case MVM_OP_push_s: return MVM_repr_push_s; case MVM_OP_push_o: return MVM_repr_push_o; case MVM_OP_unshift_i: return MVM_repr_unshift_i; case MVM_OP_unshift_n: return MVM_repr_unshift_n; case MVM_OP_unshift_s: return MVM_repr_unshift_s; case MVM_OP_unshift_o: return MVM_repr_unshift_o; case MVM_OP_pop_i: return MVM_repr_pop_i; case MVM_OP_pop_n: return MVM_repr_pop_n; case MVM_OP_pop_s: return MVM_repr_pop_s; case MVM_OP_pop_o: return MVM_repr_pop_o; case MVM_OP_shift_i: return MVM_repr_shift_i; case MVM_OP_shift_n: return MVM_repr_shift_n; case MVM_OP_shift_s: return MVM_repr_shift_s; case MVM_OP_shift_o: return MVM_repr_shift_o; case MVM_OP_setelemspos: return MVM_repr_pos_set_elems; case MVM_OP_splice: return MVM_repr_pos_splice; case MVM_OP_existskey: return MVM_repr_exists_key; case MVM_OP_deletekey: return MVM_repr_delete_key; case MVM_OP_atpos_i: return MVM_repr_at_pos_i; case MVM_OP_atpos_n: return MVM_repr_at_pos_n; case MVM_OP_atpos_s: return MVM_repr_at_pos_s; case MVM_OP_atpos_o: return MVM_repr_at_pos_o; case MVM_OP_existspos: return MVM_repr_exists_pos; case MVM_OP_atkey_i: return MVM_repr_at_key_i; case MVM_OP_atkey_n: return MVM_repr_at_key_n; case MVM_OP_atkey_s: return MVM_repr_at_key_s; case MVM_OP_atkey_o: return MVM_repr_at_key_o; case MVM_OP_bindpos_i: return MVM_repr_bind_pos_i; case MVM_OP_bindpos_n: return MVM_repr_bind_pos_n; case MVM_OP_bindpos_s: return MVM_repr_bind_pos_s; case MVM_OP_bindpos_o: return MVM_repr_bind_pos_o; case MVM_OP_bindkey_i: return MVM_repr_bind_key_i; case MVM_OP_bindkey_n: return MVM_repr_bind_key_n; case MVM_OP_bindkey_s: return MVM_repr_bind_key_s; case MVM_OP_bindkey_o: return MVM_repr_bind_key_o; case MVM_OP_getattr_s: return MVM_repr_get_attr_s; case MVM_OP_getattr_n: return MVM_repr_get_attr_n; case MVM_OP_getattr_i: return MVM_repr_get_attr_i; case MVM_OP_getattr_o: return MVM_repr_get_attr_o; case MVM_OP_getattrs_s: return MVM_repr_get_attr_s; case MVM_OP_getattrs_n: return MVM_repr_get_attr_n; case MVM_OP_getattrs_i: return MVM_repr_get_attr_i; case MVM_OP_getattrs_o: return MVM_repr_get_attr_o; case MVM_OP_attrinited: return MVM_repr_attribute_inited; case MVM_OP_bindattr_i: case MVM_OP_bindattr_n: case MVM_OP_bindattr_s: case MVM_OP_bindattr_o: return MVM_repr_bind_attr_inso; case MVM_OP_bindattrs_i: case MVM_OP_bindattrs_n: case MVM_OP_bindattrs_s: case MVM_OP_bindattrs_o: return MVM_repr_bind_attr_inso; case MVM_OP_hintfor: return MVM_repr_hint_for; case MVM_OP_gt_s: case MVM_OP_ge_s: case MVM_OP_lt_s: case MVM_OP_le_s: case MVM_OP_cmp_s: return MVM_string_compare; case MVM_OP_queuepoll: return MVM_concblockingqueue_jit_poll; case MVM_OP_open_fh: return MVM_file_open_fh; case MVM_OP_close_fh: return MVM_io_close; case MVM_OP_eof_fh: return MVM_io_eof; case MVM_OP_write_fhb: return MVM_io_write_bytes; case MVM_OP_read_fhb: return MVM_io_read_bytes; case MVM_OP_encode: return MVM_string_encode_to_buf; case MVM_OP_decoderaddbytes: return MVM_decoder_add_bytes; case MVM_OP_decodertakeline: return MVM_decoder_take_line; case MVM_OP_elems: return MVM_repr_elems; case MVM_OP_concat_s: return MVM_string_concatenate; case MVM_OP_repeat_s: return MVM_string_repeat; case MVM_OP_flip: return MVM_string_flip; case MVM_OP_split: return MVM_string_split; case MVM_OP_escape: return MVM_string_escape; case MVM_OP_uc: return MVM_string_uc; case MVM_OP_tc: return MVM_string_tc; case MVM_OP_lc: return MVM_string_lc; case MVM_OP_fc: return MVM_string_fc; case MVM_OP_eq_s: return MVM_string_equal; case MVM_OP_eqat_s: return MVM_string_equal_at; case MVM_OP_eqatic_s: return MVM_string_equal_at_ignore_case; case MVM_OP_eqatim_s: return MVM_string_equal_at_ignore_mark; case MVM_OP_eqaticim_s: return MVM_string_equal_at_ignore_case_ignore_mark; case MVM_OP_chars: case MVM_OP_graphs_s: return MVM_string_graphs; case MVM_OP_chr: return MVM_string_chr; case MVM_OP_codes_s: return MVM_string_codes; case MVM_OP_getcp_s: return MVM_string_get_grapheme_at; case MVM_OP_index_s: return MVM_string_index; case MVM_OP_substr_s: return MVM_string_substring; case MVM_OP_join: return MVM_string_join; case MVM_OP_replace: return MVM_string_replace; case MVM_OP_iscclass: return MVM_string_is_cclass; case MVM_OP_findcclass: return MVM_string_find_cclass; case MVM_OP_findnotcclass: return MVM_string_find_not_cclass; case MVM_OP_nfarunalt: return MVM_nfa_run_alt; case MVM_OP_nfarunproto: return MVM_nfa_run_proto; case MVM_OP_nfafromstatelist: return MVM_nfa_from_statelist; case MVM_OP_hllize: return MVM_hll_map; case MVM_OP_gethllsym: return MVM_hll_sym_get; case MVM_OP_clone: return MVM_repr_clone; case MVM_OP_create: return MVM_repr_alloc_init; case MVM_OP_getcodeobj: return MVM_frame_get_code_object; case MVM_OP_isbig_I: return MVM_bigint_is_big; case MVM_OP_cmp_I: return MVM_bigint_cmp; case MVM_OP_add_I: return MVM_bigint_add; case MVM_OP_sub_I: return MVM_bigint_sub; case MVM_OP_mul_I: return MVM_bigint_mul; case MVM_OP_div_I: return MVM_bigint_div; case MVM_OP_bor_I: return MVM_bigint_or; case MVM_OP_band_I: return MVM_bigint_and; case MVM_OP_bxor_I: return MVM_bigint_xor; case MVM_OP_mod_I: return MVM_bigint_mod; case MVM_OP_lcm_I: return MVM_bigint_lcm; case MVM_OP_gcd_I: return MVM_bigint_gcd; case MVM_OP_bool_I: return MVM_bigint_bool; case MVM_OP_isprime_I: return MVM_bigint_is_prime; case MVM_OP_brshift_I: return MVM_bigint_shr; case MVM_OP_blshift_I: return MVM_bigint_shl; case MVM_OP_bnot_I: return MVM_bigint_not; case MVM_OP_div_In: return MVM_bigint_div_num; case MVM_OP_coerce_Is: case MVM_OP_base_I: return MVM_bigint_to_str; case MVM_OP_radix: return MVM_radix; case MVM_OP_radix_I: return MVM_bigint_radix; case MVM_OP_sqrt_n: return sqrt; case MVM_OP_sin_n: return sin; case MVM_OP_cos_n: return cos; case MVM_OP_tan_n: return tan; case MVM_OP_asin_n: return asin; case MVM_OP_acos_n: return acos; case MVM_OP_atan_n: return atan; case MVM_OP_atan2_n: return atan2; case MVM_OP_floor_n: return floor; case MVM_OP_pow_I: return MVM_bigint_pow; case MVM_OP_rand_I: return MVM_bigint_rand; case MVM_OP_pow_n: return pow; case MVM_OP_time_n: return MVM_proc_time_n; case MVM_OP_randscale_n: return MVM_proc_randscale_n; case MVM_OP_isnanorinf: return MVM_num_isnanorinf; case MVM_OP_nativecallcast: return MVM_nativecall_cast; case MVM_OP_nativecallinvoke: return MVM_nativecall_invoke; case MVM_OP_nativeinvoke_o: return MVM_nativecall_invoke_jit; case MVM_OP_typeparameterized: return MVM_6model_parametric_type_parameterized; case MVM_OP_typeparameters: return MVM_6model_parametric_type_parameters; case MVM_OP_typeparameterat: return MVM_6model_parametric_type_parameter_at; case MVM_OP_objectid: return MVM_gc_object_id; case MVM_OP_iscont_i: return MVM_6model_container_iscont_i; case MVM_OP_iscont_n: return MVM_6model_container_iscont_n; case MVM_OP_iscont_s: return MVM_6model_container_iscont_s; case MVM_OP_isrwcont: return MVM_6model_container_iscont_rw; case MVM_OP_assign_i: return MVM_6model_container_assign_i; case MVM_OP_assign_n: return MVM_6model_container_assign_n; case MVM_OP_assign_s: return MVM_6model_container_assign_s; case MVM_OP_decont_i: return MVM_6model_container_decont_i; case MVM_OP_decont_n: return MVM_6model_container_decont_n; case MVM_OP_decont_s: return MVM_6model_container_decont_s; case MVM_OP_getrusage: return MVM_proc_getrusage; case MVM_OP_cpucores: return MVM_platform_cpu_count; case MVM_OP_sleep: return MVM_platform_sleep; case MVM_OP_getlexref_i32: case MVM_OP_getlexref_i16: case MVM_OP_getlexref_i8: case MVM_OP_getlexref_i: return MVM_nativeref_lex_i; case MVM_OP_getlexref_n32: case MVM_OP_getlexref_n: return MVM_nativeref_lex_n; case MVM_OP_getlexref_s: return MVM_nativeref_lex_s; case MVM_OP_getattrref_i: return MVM_nativeref_attr_i; case MVM_OP_getattrref_n: return MVM_nativeref_attr_n; case MVM_OP_getattrref_s: return MVM_nativeref_attr_s; case MVM_OP_getattrsref_i: return MVM_nativeref_attr_i; case MVM_OP_getattrsref_n: return MVM_nativeref_attr_n; case MVM_OP_getattrsref_s: return MVM_nativeref_attr_s; case MVM_OP_atposref_i: return MVM_nativeref_pos_i; case MVM_OP_atposref_n: return MVM_nativeref_pos_n; case MVM_OP_atposref_s: return MVM_nativeref_pos_s; case MVM_OP_indexingoptimized: return MVM_string_indexing_optimized; case MVM_OP_sp_boolify_iter: return MVM_iter_istrue; case MVM_OP_prof_allocated: return MVM_profile_log_allocated; case MVM_OP_prof_exit: return MVM_profile_log_exit; case MVM_OP_sp_resolvecode: return MVM_frame_resolve_invokee_spesh; case MVM_OP_cas_o: return MVM_6model_container_cas; case MVM_OP_cas_i: return MVM_6model_container_cas_i; case MVM_OP_atomicinc_i: return MVM_6model_container_atomic_inc; case MVM_OP_atomicdec_i: return MVM_6model_container_atomic_dec; case MVM_OP_atomicadd_i: return MVM_6model_container_atomic_add; case MVM_OP_atomicload_o: return MVM_6model_container_atomic_load; case MVM_OP_atomicload_i: return MVM_6model_container_atomic_load_i; case MVM_OP_atomicstore_o: return MVM_6model_container_atomic_store; case MVM_OP_atomicstore_i: return MVM_6model_container_atomic_store_i; case MVM_OP_lock: return MVM_reentrantmutex_lock_checked; case MVM_OP_unlock: return MVM_reentrantmutex_unlock_checked; case MVM_OP_getexcategory: return MVM_get_exception_category; case MVM_OP_bindexcategory: return MVM_bind_exception_category; case MVM_OP_exreturnafterunwind: return MVM_exception_returnafterunwind; case MVM_OP_breakpoint: return MVM_debugserver_breakpoint_check; default: MVM_oops(tc, "JIT: No function for op %d in op_to_func (%s)", opcode, MVM_op_get_op(opcode)->name); } } static void jg_append_guard(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns *ins) { MVMSpeshAnn *ann = ins->annotations; MVMJitNode *node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); MVMint32 deopt_idx; node->type = MVM_JIT_NODE_GUARD; node->u.guard.ins = ins; while (ann) { if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS || ann->type == MVM_SPESH_ANN_DEOPT_INLINE) { deopt_idx = ann->data.deopt_idx; break; } ann = ann->next; } if (!ann) { MVM_oops(tc, "Can't find deopt idx annotation on spesh ins <%s>", ins->info->name); } node->u.guard.deopt_target = ins->operands[2].lit_ui32; node->u.guard.deopt_offset = jg->sg->deopt_addrs[2 * deopt_idx + 1]; jg_append_node(jg, node); } static MVMint32 consume_invoke(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter, MVMSpeshIns *ins) { MVMCompUnit *cu = iter->graph->sf->body.cu; MVMint16 callsite_idx = ins->operands[0].callsite_idx; MVMCallsite *cs = cu->body.callsites[callsite_idx]; MVMSpeshIns **arg_ins = MVM_spesh_alloc(tc, iter->graph, sizeof(MVMSpeshIns*) * cs->arg_count); MVMint16 i = 0; MVMJitNode *node; MVMint32 reentry_label; MVMReturnType return_type; MVMint16 return_register; MVMint16 code_register; MVMint16 spesh_cand; MVMint16 is_fast; while ((ins = ins->next)) { switch(ins->info->opcode) { case MVM_OP_arg_i: case MVM_OP_arg_n: case MVM_OP_arg_s: case MVM_OP_arg_o: case MVM_OP_argconst_i: case MVM_OP_argconst_n: case MVM_OP_argconst_s: MVM_jit_log(tc, "Invoke arg: <%s>\n", ins->info->name); arg_ins[i++] = ins; break; case MVM_OP_invoke_v: return_type = MVM_RETURN_VOID; return_register = -1; code_register = ins->operands[0].reg.orig; spesh_cand = -1; is_fast = 0; goto checkargs; case MVM_OP_invoke_i: return_type = MVM_RETURN_INT; return_register = ins->operands[0].reg.orig; code_register = ins->operands[1].reg.orig; spesh_cand = -1; is_fast = 0; goto checkargs; case MVM_OP_invoke_n: return_type = MVM_RETURN_NUM; return_register = ins->operands[0].reg.orig; code_register = ins->operands[1].reg.orig; spesh_cand = -1; is_fast = 0; goto checkargs; case MVM_OP_invoke_s: return_type = MVM_RETURN_STR; return_register = ins->operands[0].reg.orig; code_register = ins->operands[1].reg.orig; spesh_cand = -1; is_fast = 0; goto checkargs; case MVM_OP_invoke_o: return_type = MVM_RETURN_OBJ; return_register = ins->operands[0].reg.orig; code_register = ins->operands[1].reg.orig; spesh_cand = -1; is_fast = 0; goto checkargs; case MVM_OP_nativeinvoke_o: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 site = ins->operands[1].reg.orig; MVMint16 restype = ins->operands[2].reg.orig; MVMNativeCallBody *body; MVMJitGraph *nc_jg; MVMObject *nc_site; MVMSpeshFacts *object_facts = MVM_spesh_get_facts(tc, iter->graph, ins->operands[1]); MVM_jit_log(tc, "Invoke instruction: <%s>\n", ins->info->name); if (!(object_facts->flags & MVM_SPESH_FACT_KNOWN_VALUE)) { MVM_jit_log(tc, "BAIL: op <%s> (Can't find nc_site value on spesh ins)\n", ins->info->name); return 0; } body = MVM_nativecall_get_nc_body(tc, object_facts->value.o); nc_jg = MVM_nativecall_jit_graph_for_caller_code(tc, iter->graph, body, restype, dst, arg_ins); if (nc_jg == NULL) return 0; jg->last_node->next = nc_jg->first_node; jg->last_node = nc_jg->last_node; goto success; } case MVM_OP_sp_fastinvoke_v: return_type = MVM_RETURN_VOID; return_register = -1; code_register = ins->operands[0].reg.orig; spesh_cand = ins->operands[1].lit_i16; is_fast = 1; goto checkargs; case MVM_OP_sp_fastinvoke_o: return_type = MVM_RETURN_OBJ; return_register = ins->operands[0].reg.orig;; code_register = ins->operands[1].reg.orig; spesh_cand = ins->operands[2].lit_i16; is_fast = 1; goto checkargs; case MVM_OP_sp_fastinvoke_s: return_type = MVM_RETURN_STR; return_register = ins->operands[0].reg.orig;; code_register = ins->operands[1].reg.orig; spesh_cand = ins->operands[2].lit_i16; is_fast = 1; goto checkargs; case MVM_OP_sp_fastinvoke_i: return_type = MVM_RETURN_INT; return_register = ins->operands[0].reg.orig;; code_register = ins->operands[1].reg.orig; spesh_cand = ins->operands[2].lit_i16; is_fast = 1; goto checkargs; case MVM_OP_sp_fastinvoke_n: return_type = MVM_RETURN_NUM; return_register = ins->operands[0].reg.orig;; code_register = ins->operands[1].reg.orig; spesh_cand = ins->operands[2].lit_i16; is_fast = 1; goto checkargs; default: MVM_jit_log(tc, "Unexpected opcode in invoke sequence: <%s>\n", ins->info->name); return 0; } } checkargs: if (!ins || i < cs->arg_count) { MVM_jit_log(tc, "Could not find invoke opcode or enough arguments\n" "BAIL: op <%s>, expected args: %d, num of args: %d\n", ins? ins->info->name : "NULL", i, cs->arg_count); return 0; } MVM_jit_log(tc, "Invoke instruction: <%s>\n", ins->info->name); /* get label /after/ current (invoke) ins, where we'll need to reenter the JIT */ reentry_label = MVM_jit_label_after_ins(tc, jg, iter->bb, ins); /* create invoke node */ node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); node->type = MVM_JIT_NODE_INVOKE; node->u.invoke.callsite_idx = callsite_idx; node->u.invoke.arg_count = cs->arg_count; node->u.invoke.arg_ins = arg_ins; node->u.invoke.return_type = return_type; node->u.invoke.return_register = return_register; node->u.invoke.code_register = code_register; node->u.invoke.spesh_cand = spesh_cand; node->u.invoke.reentry_label = reentry_label; node->u.invoke.is_fast = is_fast; jg_append_node(jg, node); /* append reentry label */ jg_append_label(tc, jg, reentry_label); success: /* move forward to invoke ins */ iter->ins = ins; return 1; } static void jg_append_control(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns *ins, MVMJitControlType ctrl) { MVMJitNode *node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); node->type = MVM_JIT_NODE_CONTROL; node->u.control.ins = ins; node->u.control.type = ctrl; jg_append_node(jg, node); } static MVMint32 consume_jumplist(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter, MVMSpeshIns *ins) { MVMint64 num_labels = ins->operands[0].lit_i64; MVMint16 idx_reg = ins->operands[1].reg.orig; MVMint32 *in_labels = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMint32) * num_labels); MVMint32 *out_labels = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMint32) * num_labels); MVMSpeshBB *bb = iter->bb; MVMJitNode *node; MVMint64 i; for (i = 0; i < num_labels; i++) { bb = bb->linear_next; /* take the next basic block */ if (!bb || bb->first_ins != bb->last_ins) return 0; /* which must exist */ ins = bb->first_ins; /* and it's first and only entry */ if (ins->info->opcode != MVM_OP_goto) /* which must be a goto */ return 0; in_labels[i] = MVM_jit_label_before_bb(tc, jg, bb); out_labels[i] = MVM_jit_label_before_bb(tc, jg, ins->operands[0].ins_bb); } /* build the node */ node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); node->type = MVM_JIT_NODE_JUMPLIST; node->u.jumplist.num_labels = num_labels; node->u.jumplist.reg = idx_reg; node->u.jumplist.in_labels = in_labels; node->u.jumplist.out_labels = out_labels; jg_append_node(jg, node); /* set iterator bb and ins to the end of our jumplist */ iter->bb = bb; iter->ins = ins; return 1; } static MVMint32 jg_add_data_node(MVMThreadContext *tc, MVMJitGraph *jg, void *data, size_t size) { MVMJitNode *node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); MVMint32 label = MVM_jit_label_for_obj(tc, jg, data); node->type = MVM_JIT_NODE_DATA; node->u.data.data = data; node->u.data.size = size; node->u.data.label = label; jg_append_node(jg, node); return label; } static MVMuint16 * try_fake_extop_regs(MVMThreadContext *tc, MVMSpeshGraph *sg, MVMSpeshIns *ins, size_t *bufsize) { MVMuint16 *regs = MVM_spesh_alloc(tc, sg, (*bufsize = (ins->info->num_operands * sizeof(MVMuint16)))); MVMuint16 i; for (i = 0; i < ins->info->num_operands; i++) { switch (ins->info->operands[i] & MVM_operand_rw_mask) { case MVM_operand_read_reg: case MVM_operand_write_reg: regs[i] = ins->operands[i].reg.orig; break; default: MVM_free(regs); return NULL; } } return regs; } static void log_inline(MVMThreadContext *tc, MVMSpeshGraph *sg, MVMint32 inline_idx, MVMint32 is_entry) { MVMStaticFrame *sf = sg->inlines[inline_idx].sf; char *name = MVM_string_utf8_encode_C_string(tc, sf->body.name); char *cuuid = MVM_string_utf8_encode_C_string(tc, sf->body.cuuid); MVM_jit_log(tc, "%s inline %d (name: %s, cuuid: %s)\n", is_entry ? "Entering" : "Leaving", inline_idx, name, cuuid); MVM_free(name); MVM_free(cuuid); } static void before_ins(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter, MVMSpeshIns *ins) { MVMSpeshBB *bb = iter->bb; MVMSpeshAnn *ann = ins->annotations; MVMint32 has_label = 0, has_dynamic_control = 0, label; /* Search annotations for stuff that may need a label. */ while (ann) { switch(ann->type) { case MVM_SPESH_ANN_DEOPT_OSR: { /* get label before our instruction */ label = MVM_jit_label_before_ins(tc, jg, bb, ins); add_deopt_idx(tc, jg, label, ann->data.deopt_idx); has_label = 1; break; } case MVM_SPESH_ANN_FH_START: { label = MVM_jit_label_before_ins(tc, jg, bb, ins); jg->handlers[ann->data.frame_handler_index].start_label = label; has_label = 1; has_dynamic_control = 1; /* Load the current position into the jit entry label, so that * when throwing we'll know which handler to use */ break; } case MVM_SPESH_ANN_FH_END: { label = MVM_jit_label_before_ins(tc, jg, bb, ins); jg->handlers[ann->data.frame_handler_index].end_label = label; /* Same as above. Note that the dynamic label control * actually loads a position a few bytes away from the * label appended above. This is in this case intentional * because the frame handler end is exclusive; once it is * passed we should not use the same handler again. If we * loaded the exact same position, we would not be able to * distinguish between the end of the basic block to which * the handler applies and the start of the basic block to * which it doesn't. */ has_label = 1; has_dynamic_control = 1; break; } case MVM_SPESH_ANN_FH_GOTO: { label = MVM_jit_label_before_ins(tc, jg, bb, ins); jg->handlers[ann->data.frame_handler_index].goto_label = label; has_label = 1; break; } case MVM_SPESH_ANN_INLINE_START: { label = MVM_jit_label_before_ins(tc, jg, bb, ins); jg->inlines[ann->data.inline_idx].start_label = label; if (tc->instance->jit_log_fh) log_inline(tc, jg->sg, ann->data.inline_idx, 1); has_label = 1; break; } } /* switch */ ann = ann->next; } if (has_label) { jg_append_label(tc, jg, label); } if (has_dynamic_control) { MVM_jit_log(tc, "Dynamic control label on ins %s\n", ins->info->name); jg_append_control(tc, jg, ins, MVM_JIT_CONTROL_DYNAMIC_LABEL); } if (ins->info->jittivity & (MVM_JIT_INFO_THROWISH | MVM_JIT_INFO_INVOKISH)) { jg_append_control(tc, jg, ins, MVM_JIT_CONTROL_THROWISH_PRE); } } static void after_ins(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter, MVMSpeshIns *ins) { MVMSpeshBB *bb = iter->bb; MVMSpeshAnn *ann = ins->annotations; /* If we've consumed an (or throwish) op, we should append a guard */ if (ins->info->jittivity & MVM_JIT_INFO_INVOKISH) { MVM_jit_log(tc, "append invokish control guard\n"); jg_append_control(tc, jg, ins, MVM_JIT_CONTROL_INVOKISH); } else if (ins->info->jittivity & MVM_JIT_INFO_THROWISH) { jg_append_control(tc, jg, ins, MVM_JIT_CONTROL_THROWISH_POST); } /* This order of processing is necessary to ensure that a label * calculated by one of the control guards as well as the labels * calculated below point to the exact same instruction. This is a * relatively fragile construction! One could argue that the * control guards should in fact use the same (dynamic) labels. */ while (ann) { if (ann->type == MVM_SPESH_ANN_INLINE_END) { MVMint32 label = MVM_jit_label_after_ins(tc, jg, bb, ins); jg_append_label(tc, jg, label); jg->inlines[ann->data.inline_idx].end_label = label; if (tc->instance->jit_log_fh) log_inline(tc, jg->sg, ann->data.inline_idx, 0); } else if (ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS) { /* An underlying assumption here is that this instruction * will in fact set the jit_entry_label to a correct * value. This is clearly true for invoking ops as well * as invokish ops, and in fact there is no other way * to get a deopt_all_ins annotation. Still, be warned. */ MVMint32 label = MVM_jit_label_after_ins(tc, jg, bb, ins); jg_append_label(tc, jg, label); add_deopt_idx(tc, jg, label, ann->data.deopt_idx); } ann = ann->next; } } static void jg_sc_wb(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshOperand check) { MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, check.reg.orig } }; jg_append_call_c(tc, jg, &MVM_SC_WB_OBJ, 2, args, MVM_JIT_RV_VOID, -1); } static MVMint32 consume_reprop(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iterator, MVMSpeshIns *ins) { MVMint16 op = ins->info->opcode; MVMSpeshOperand type_operand; MVMSpeshFacts *type_facts = 0; MVMint32 alternative = 0; switch (op) { case MVM_OP_unshift_i: case MVM_OP_unshift_n: case MVM_OP_unshift_s: case MVM_OP_unshift_o: case MVM_OP_bindkey_i: case MVM_OP_bindkey_n: case MVM_OP_bindkey_s: case MVM_OP_bindkey_o: case MVM_OP_bindpos_i: case MVM_OP_bindpos_n: case MVM_OP_bindpos_s: case MVM_OP_bindpos_o: case MVM_OP_bindattr_i: case MVM_OP_bindattr_n: case MVM_OP_bindattr_s: case MVM_OP_bindattr_o: case MVM_OP_bindattrs_i: case MVM_OP_bindattrs_n: case MVM_OP_bindattrs_s: case MVM_OP_bindattrs_o: case MVM_OP_push_i: case MVM_OP_push_n: case MVM_OP_push_s: case MVM_OP_push_o: case MVM_OP_deletekey: case MVM_OP_setelemspos: case MVM_OP_splice: type_operand = ins->operands[0]; break; case MVM_OP_atpos_i: case MVM_OP_atpos_n: case MVM_OP_atpos_s: case MVM_OP_atpos_o: case MVM_OP_atkey_i: case MVM_OP_atkey_n: case MVM_OP_atkey_s: case MVM_OP_atkey_o: case MVM_OP_elems: case MVM_OP_shift_i: case MVM_OP_shift_n: case MVM_OP_shift_s: case MVM_OP_shift_o: case MVM_OP_pop_i: case MVM_OP_pop_n: case MVM_OP_pop_s: case MVM_OP_pop_o: case MVM_OP_existskey: case MVM_OP_existspos: case MVM_OP_getattr_i: case MVM_OP_getattr_n: case MVM_OP_getattr_s: case MVM_OP_getattr_o: case MVM_OP_getattrs_i: case MVM_OP_getattrs_n: case MVM_OP_getattrs_s: case MVM_OP_getattrs_o: case MVM_OP_attrinited: case MVM_OP_hintfor: type_operand = ins->operands[1]; break; case MVM_OP_box_i: case MVM_OP_box_n: case MVM_OP_box_s: type_operand = ins->operands[2]; break; default: MVM_jit_log(tc, "devirt: couldn't figure out type operand for op %s\n", ins->info->name); return 0; } type_facts = MVM_spesh_get_facts(tc, jg->sg, type_operand); if (type_facts && type_facts->flags & MVM_SPESH_FACT_KNOWN_TYPE && type_facts->type && type_facts->flags & MVM_SPESH_FACT_CONCRETE) { switch(op) { case MVM_OP_atkey_i: case MVM_OP_atkey_n: case MVM_OP_atkey_s: case MVM_OP_atkey_o: alternative = 1; case MVM_OP_atpos_i: case MVM_OP_atpos_n: case MVM_OP_atpos_s: case MVM_OP_atpos_o: { /* atpos_i w(int64) r(obj) r(int64) */ /* atkey_i w(int64) r(obj) r(str)*/ /*void (*at_pos) (MVMThreadContext *tc, MVMSTable *st, * MVMObject *root, void *data, MVMint64 index, * MVMRegister *result, MVMuint16 kind);*/ /*REPR(obj)->pos_funcs.at_pos(tc, STABLE(obj), obj, OBJECT_BODY(obj), * idx, &value, MVM_reg_int64);*/ MVMint32 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 value = ins->operands[2].reg.orig; void *function = alternative ? (void *)((MVMObject*)type_facts->type)->st->REPR->ass_funcs.at_key : (void *)((MVMObject*)type_facts->type)->st->REPR->pos_funcs.at_pos; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, value }, { MVM_JIT_REG_ADDR, dst }, { MVM_JIT_LITERAL, op == MVM_OP_atpos_i || op == MVM_OP_atkey_i ? MVM_reg_int64 : op == MVM_OP_atpos_n || op == MVM_OP_atkey_n ? MVM_reg_num64 : op == MVM_OP_atpos_s || op == MVM_OP_atkey_s ? MVM_reg_str : MVM_reg_obj } }; jg_append_call_c(tc, jg, function, 7, args, MVM_JIT_RV_VOID, -1); MVM_jit_log(tc, "devirt: emitted an %s via consume_reprop\n", ins->info->name); return 1; } case MVM_OP_bindkey_i: case MVM_OP_bindkey_n: case MVM_OP_bindkey_s: case MVM_OP_bindkey_o: alternative = 1; case MVM_OP_bindpos_i: case MVM_OP_bindpos_n: case MVM_OP_bindpos_s: case MVM_OP_bindpos_o: { /*bindpos_i r(obj) r(int64) r(int64)*/ /*bindkey_i r(obj) r(str) r(int64)*/ /* void (*bind_pos) (MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMint64 index, MVMRegister value, MVMuint16 kind); */ /* void (*bind_key) (MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *key, MVMRegister value, MVMuint16 kind); */ MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 key = ins->operands[1].reg.orig; MVMint32 value = ins->operands[2].reg.orig; void *function = alternative ? (void *)((MVMObject*)type_facts->type)->st->REPR->ass_funcs.bind_key : (void *)((MVMObject*)type_facts->type)->st->REPR->pos_funcs.bind_pos; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, key }, { MVM_JIT_REG_VAL, value }, { MVM_JIT_LITERAL, op == MVM_OP_bindpos_i || op == MVM_OP_bindkey_i ? MVM_reg_int64 : op == MVM_OP_bindpos_n || op == MVM_OP_bindkey_n ? MVM_reg_num64 : op == MVM_OP_bindpos_s || op == MVM_OP_bindkey_s ? MVM_reg_str : MVM_reg_obj } }; jg_append_call_c(tc, jg, function, 7, args, MVM_JIT_RV_VOID, -1); MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); jg_sc_wb(tc, jg, ins->operands[0]); return 1; } case MVM_OP_elems: { /*elems w(int64) r(obj) :pure*/ MVMint32 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; void *function = ((MVMObject*)type_facts->type)->st->REPR->elems; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant } }; jg_append_call_c(tc, jg, function, 4, args, MVM_JIT_RV_INT, dst); MVM_jit_log(tc, "devirt: emitted an elems via consume_reprop\n"); return 1; } case MVM_OP_getattr_i: case MVM_OP_getattr_n: case MVM_OP_getattr_s: case MVM_OP_getattr_o: case MVM_OP_getattrs_i: case MVM_OP_getattrs_n: case MVM_OP_getattrs_s: case MVM_OP_getattrs_o: { /*getattr_i w(int64) r(obj) r(obj) str int16*/ /*getattrs_i w(int64) r(obj) r(obj) r(str)*/ /*static void get_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root,*/ /* void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint,*/ /* MVMRegister *result_reg, MVMuint16 kind) {*/ /* reprconv and interp.c check for concreteness, so we'd either * have to emit a bit of code to check and throw or just rely * on a concreteness fact */ MVMSpeshFacts *object_facts = MVM_spesh_get_facts(tc, jg->sg, ins->operands[1]); if (object_facts->flags & MVM_SPESH_FACT_CONCRETE) { MVMint32 is_name_direct = ins->info->num_operands == 5; MVMint32 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 type = ins->operands[2].reg.orig; MVMint32 attrname = is_name_direct ? ins->operands[3].lit_str_idx : ins->operands[3].reg.orig; MVMint32 attrhint = is_name_direct ? ins->operands[4].lit_i16 : -1; void *function = ((MVMObject*)type_facts->type)->st->REPR->attr_funcs.get_attribute; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, type }, { is_name_direct ? MVM_JIT_STR_IDX : MVM_JIT_REG_VAL, attrname }, { MVM_JIT_LITERAL, attrhint }, { MVM_JIT_REG_ADDR, dst }, { MVM_JIT_LITERAL, op == MVM_OP_getattr_i || op == MVM_OP_getattrs_i ? MVM_reg_int64 : op == MVM_OP_getattr_n || op == MVM_OP_getattrs_n ? MVM_reg_num64 : op == MVM_OP_getattr_s || op == MVM_OP_getattrs_s ? MVM_reg_str : MVM_reg_obj } }; MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); jg_append_call_c(tc, jg, function, 9, args, MVM_JIT_RV_VOID, -1); return 1; } else { MVM_jit_log(tc, "devirt: couldn't %s; concreteness not sure\n", ins->info->name); break; } } case MVM_OP_attrinited: { /*attrinited w(int64) r(obj) r(obj) r(str)*/ /*MVMint64 (*is_attribute_initialized) (MVMThreadContext *tc, MVMSTable *st,*/ /*void *data, MVMObject *class_handle, MVMString *name,*/ /*MVMint64 hint);*/ /* reprconv and interp.c check for concreteness, so we'd either * have to emit a bit of code to check and throw or just rely * on a concreteness fact */ MVMSpeshFacts *object_facts = MVM_spesh_get_facts(tc, jg->sg, ins->operands[1]); if (object_facts->flags & MVM_SPESH_FACT_CONCRETE) { MVMint32 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 type = ins->operands[2].reg.orig; MVMint32 attrname = ins->operands[3].reg.orig; MVMint32 attrhint = MVM_NO_HINT; void *function = ((MVMObject*)type_facts->type)->st->REPR->attr_funcs.is_attribute_initialized; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, type }, { MVM_JIT_REG_VAL, attrname }, { MVM_JIT_LITERAL, attrhint } }; MVM_jit_log(tc, "devirt: emitted a %s via jgb_consume_reprop\n", ins->info->name); jg_append_call_c(tc, jg, function, 6, args, MVM_JIT_RV_INT, dst); return 1; } else { MVM_jit_log(tc, "devirt: couldn't %s; concreteness not sure\n", ins->info->name); break; } } case MVM_OP_bindattr_i: case MVM_OP_bindattr_n: case MVM_OP_bindattr_s: case MVM_OP_bindattr_o: case MVM_OP_bindattrs_i: case MVM_OP_bindattrs_n: case MVM_OP_bindattrs_s: case MVM_OP_bindattrs_o: { /*bindattr_n r(obj) r(obj) str r(num64) int16*/ /*bindattrs_n r(obj) r(obj) r(str) r(num64)*/ /* static void bind_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, * void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint, * MVMRegister value_reg, MVMuint16 kind) */ /* reprconv and interp.c check for concreteness, so we'd either * have to emit a bit of code to check and throw or just rely * on a concreteness fact */ MVMSpeshFacts *object_facts = MVM_spesh_get_facts(tc, jg->sg, ins->operands[1]); if (object_facts->flags & MVM_SPESH_FACT_CONCRETE) { MVMint32 is_name_direct = ins->info->num_operands == 5; MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 type = ins->operands[1].reg.orig; MVMint32 attrname = is_name_direct ? ins->operands[2].lit_str_idx : ins->operands[2].reg.orig; MVMint32 attrhint = is_name_direct ? ins->operands[4].lit_i16 : -1; MVMint32 value = ins->operands[3].reg.orig; void *function = ((MVMObject*)type_facts->type)->st->REPR->attr_funcs.bind_attribute; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, type }, { is_name_direct ? MVM_JIT_STR_IDX : MVM_JIT_REG_VAL, attrname }, { MVM_JIT_LITERAL, attrhint }, { MVM_JIT_REG_VAL, value }, { MVM_JIT_LITERAL, op == MVM_OP_bindattr_i || op == MVM_OP_bindattrs_i ? MVM_reg_int64 : op == MVM_OP_bindattr_n || op == MVM_OP_bindattrs_n ? MVM_reg_num64 : op == MVM_OP_bindattr_s || op == MVM_OP_bindattrs_s ? MVM_reg_str : MVM_reg_obj } }; MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); jg_append_call_c(tc, jg, function, 9, args, MVM_JIT_RV_VOID, -1); jg_sc_wb(tc, jg, ins->operands[0]); return 1; } else { MVM_jit_log(tc, "devirt: couldn't %s; concreteness not sure\n", ins->info->name); break; } } case MVM_OP_hintfor: { /* * MVMint64 (*hint_for) (MVMThreadContext *tc, MVMSTable *st, * MVMObject *class_handle, MVMString *name); */ MVMint32 result = ins->operands[0].reg.orig; MVMint32 type = ins->operands[1].reg.orig; MVMint32 attrname = ins->operands[2].reg.orig; void *function = ((MVMObject*)type_facts->type)->st->REPR->attr_funcs.hint_for; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, type }, { MVM_JIT_REG_VAL, type }, { MVM_JIT_REG_VAL, attrname } }; MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); jg_append_call_c(tc, jg, function, 4, args, MVM_JIT_RV_INT, result); return 1; break; } case MVM_OP_push_i: case MVM_OP_push_n: case MVM_OP_push_s: case MVM_OP_push_o: alternative = 1; case MVM_OP_unshift_i: case MVM_OP_unshift_n: case MVM_OP_unshift_s: case MVM_OP_unshift_o: { MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 value = ins->operands[1].reg.orig; void *function = alternative ? (void *)((MVMObject*)type_facts->type)->st->REPR->pos_funcs.push : (void *)((MVMObject*)type_facts->type)->st->REPR->pos_funcs.unshift; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, value }, { MVM_JIT_LITERAL, op == MVM_OP_push_i || op == MVM_OP_unshift_i ? MVM_reg_int64 : op == MVM_OP_push_n || op == MVM_OP_unshift_n ? MVM_reg_num64 : op == MVM_OP_push_s || op == MVM_OP_unshift_s ? MVM_reg_str : MVM_reg_obj } }; jg_append_call_c(tc, jg, function, 6, args, MVM_JIT_RV_VOID, -1); MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); jg_sc_wb(tc, jg, ins->operands[0]); return 1; } case MVM_OP_pop_i: case MVM_OP_pop_n: case MVM_OP_pop_s: case MVM_OP_pop_o: alternative = 1; case MVM_OP_shift_i: case MVM_OP_shift_n: case MVM_OP_shift_s: case MVM_OP_shift_o: { MVMint32 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; void *function = alternative ? (void *)((MVMObject*)type_facts->type)->st->REPR->pos_funcs.pop : (void *)((MVMObject*)type_facts->type)->st->REPR->pos_funcs.shift; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_ADDR, dst }, { MVM_JIT_LITERAL, op == MVM_OP_pop_i || op == MVM_OP_shift_i ? MVM_reg_int64 : op == MVM_OP_pop_n || op == MVM_OP_shift_n ? MVM_reg_num64 : op == MVM_OP_pop_s || op == MVM_OP_shift_s ? MVM_reg_str : MVM_reg_obj } }; jg_append_call_c(tc, jg, function, 6, args, MVM_JIT_RV_VOID, -1); MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); return 1; } case MVM_OP_setelemspos: { MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 amount = ins->operands[1].reg.orig; void *function = ((MVMObject*)type_facts->type)->st->REPR->pos_funcs.set_elems; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, amount } }; jg_append_call_c(tc, jg, function, 5, args, MVM_JIT_RV_VOID, -1); MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); return 1; } case MVM_OP_existskey: { /*existskey w(int64) r(obj) r(str) :pure*/ MVMint32 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 keyidx = ins->operands[2].reg.orig; void *function = (void *)((MVMObject*)type_facts->type)->st->REPR->ass_funcs.exists_key; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, keyidx } }; jg_append_call_c(tc, jg, function, 5, args, MVM_JIT_RV_INT, dst); MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); return 1; } case MVM_OP_splice: { MVMint16 invocant = ins->operands[0].reg.orig; MVMint16 source = ins->operands[1].reg.orig; MVMint16 offset = ins->operands[2].reg.orig; MVMint16 count = ins->operands[3].reg.orig; void *function = ((MVMObject*)type_facts->type)->st->REPR->pos_funcs.splice; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_STABLE, invocant }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_OBJBODY, invocant }, { MVM_JIT_REG_VAL, source }, { MVM_JIT_REG_VAL, offset }, { MVM_JIT_REG_VAL, count } }; jg_append_call_c(tc, jg, function, 7, args, MVM_JIT_RV_VOID, -1); MVM_jit_log(tc, "devirt: emitted a %s via consume_reprop\n", ins->info->name); return 1; } default: MVM_jit_log(tc, "devirt: please implement emitting repr op %s\n", ins->info->name); } } else { MVM_jit_log(tc, "devirt: repr op %s couldn't be devirtualized: type unknown\n", ins->info->name); } skipdevirt: switch(op) { case MVM_OP_push_i: case MVM_OP_push_s: case MVM_OP_push_o: case MVM_OP_unshift_i: case MVM_OP_unshift_s: case MVM_OP_unshift_o: { MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 value = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, value } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); jg_sc_wb(tc, jg, ins->operands[0]); break; } case MVM_OP_unshift_n: case MVM_OP_push_n: { MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 value = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL_F, value } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_shift_s: case MVM_OP_pop_s: case MVM_OP_shift_o: case MVM_OP_pop_o: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_shift_i: case MVM_OP_pop_i: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_shift_n: case MVM_OP_pop_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_deletekey: case MVM_OP_setelemspos: { MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 key_or_val = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, key_or_val } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_existskey: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 key = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, key } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_splice: { MVMint16 invocant = ins->operands[0].reg.orig; MVMint16 source = ins->operands[1].reg.orig; MVMint16 offset = ins->operands[2].reg.orig; MVMint16 count = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, source }, { MVM_JIT_REG_VAL, offset }, { MVM_JIT_REG_VAL, count } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_existspos: case MVM_OP_atkey_i: case MVM_OP_atpos_i: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 position = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, position } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_atkey_n: case MVM_OP_atpos_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 position = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, position } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_atpos_o: case MVM_OP_atkey_o: case MVM_OP_atkey_s: case MVM_OP_atpos_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 position = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, position } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_bindkey_n: case MVM_OP_bindpos_n: { MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 key_pos = ins->operands[1].reg.orig; MVMint32 value = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, key_pos }, { MVM_JIT_REG_VAL_F, value } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); jg_sc_wb(tc, jg, ins->operands[0]); break; } case MVM_OP_bindpos_i: case MVM_OP_bindpos_s: case MVM_OP_bindpos_o: case MVM_OP_bindkey_i: case MVM_OP_bindkey_s: case MVM_OP_bindkey_o: { MVMint32 invocant = ins->operands[0].reg.orig; MVMint32 key_pos = ins->operands[1].reg.orig; MVMint32 value = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, key_pos }, { MVM_JIT_REG_VAL, value } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); jg_sc_wb(tc, jg, ins->operands[0]); break; } case MVM_OP_getattr_i: case MVM_OP_getattr_n: case MVM_OP_getattr_s: case MVM_OP_getattr_o: { MVMuint16 kind = op == MVM_OP_getattr_i ? MVM_JIT_RV_INT : op == MVM_OP_getattr_n ? MVM_JIT_RV_NUM : op == MVM_OP_getattr_s ? MVM_JIT_RV_PTR : /* MVM_OP_getattr_o ? */ MVM_JIT_RV_PTR; MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint16 typ = ins->operands[2].reg.orig; MVMuint32 str_idx = ins->operands[3].lit_str_idx; MVMint16 hint = ins->operands[4].lit_i16; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, obj }, { MVM_JIT_REG_VAL, typ }, { MVM_JIT_STR_IDX, str_idx }, { MVM_JIT_LITERAL, hint }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, kind, dst); break; } case MVM_OP_getattrs_i: case MVM_OP_getattrs_n: case MVM_OP_getattrs_s: case MVM_OP_getattrs_o: { MVMuint16 kind = op == MVM_OP_getattrs_i ? MVM_JIT_RV_INT : op == MVM_OP_getattrs_n ? MVM_JIT_RV_NUM : op == MVM_OP_getattrs_s ? MVM_JIT_RV_PTR : /* MVM_OP_getattrs_o ? */ MVM_JIT_RV_PTR; MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint16 typ = ins->operands[2].reg.orig; MVMint16 str = ins->operands[3].reg.orig; MVMint16 hint = -1; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, obj }, { MVM_JIT_REG_VAL, typ }, { MVM_JIT_REG_VAL, str }, { MVM_JIT_LITERAL, hint }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, kind, dst); break; } case MVM_OP_attrinited: { MVMint32 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 type = ins->operands[2].reg.orig; MVMint32 attrname = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, type }, { MVM_JIT_REG_VAL, attrname } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_bindattr_i: case MVM_OP_bindattr_n: case MVM_OP_bindattr_s: case MVM_OP_bindattr_o: { MVMint16 obj = ins->operands[0].reg.orig; MVMint16 typ = ins->operands[1].reg.orig; MVMuint32 str_idx = ins->operands[2].lit_str_idx; MVMint16 val = ins->operands[3].reg.orig; MVMint16 hint = ins->operands[4].lit_i16; MVMuint16 kind = op == MVM_OP_bindattr_i ? MVM_reg_int64 : op == MVM_OP_bindattr_n ? MVM_reg_num64 : op == MVM_OP_bindattr_s ? MVM_reg_str : /* MVM_OP_bindattr_o ? */ MVM_reg_obj; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, obj }, { MVM_JIT_REG_VAL, typ }, { MVM_JIT_STR_IDX, str_idx }, { MVM_JIT_LITERAL, hint }, { MVM_JIT_REG_VAL, val }, /* Takes MVMRegister, so no _F needed. */ { MVM_JIT_LITERAL, kind } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 7, args, MVM_JIT_RV_VOID, -1); jg_sc_wb(tc, jg, ins->operands[0]); break; } case MVM_OP_bindattrs_i: case MVM_OP_bindattrs_n: case MVM_OP_bindattrs_s: case MVM_OP_bindattrs_o: { MVMint16 obj = ins->operands[0].reg.orig; MVMint16 typ = ins->operands[1].reg.orig; MVMint16 str = ins->operands[2].reg.orig; MVMint16 val = ins->operands[3].reg.orig; MVMint16 hint = -1; MVMuint16 kind = op == MVM_OP_bindattrs_i ? MVM_reg_int64 : op == MVM_OP_bindattrs_n ? MVM_reg_num64 : op == MVM_OP_bindattrs_s ? MVM_reg_str : /* MVM_OP_bindattrs_o ? */ MVM_reg_obj; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, obj }, { MVM_JIT_REG_VAL, typ }, { MVM_JIT_REG_VAL, str }, { MVM_JIT_LITERAL, hint }, { MVM_JIT_REG_VAL, val }, /* Takes MVMRegister, so no _F needed. */ { MVM_JIT_LITERAL, kind } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 7, args, MVM_JIT_RV_VOID, -1); jg_sc_wb(tc, jg, ins->operands[0]); break; } case MVM_OP_hintfor: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 type = ins->operands[1].reg.orig; MVMint32 attrname = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, type }, { MVM_JIT_REG_VAL, attrname } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_elems: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } default: return 0; } return 1; } static MVMint32 consume_ins(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter, MVMSpeshIns *ins) { MVMint16 op = ins->info->opcode; MVM_jit_log(tc, "append_ins: <%s>\n", ins->info->name); switch(op) { case MVM_SSA_PHI: case MVM_OP_no_op: break; /* arithmetic */ case MVM_OP_add_i: case MVM_OP_sub_i: case MVM_OP_mul_i: case MVM_OP_div_i: case MVM_OP_mod_i: case MVM_OP_inc_i: case MVM_OP_dec_i: case MVM_OP_neg_i: case MVM_OP_band_i: case MVM_OP_bor_i: case MVM_OP_bxor_i: case MVM_OP_bnot_i: case MVM_OP_blshift_i: case MVM_OP_brshift_i: case MVM_OP_add_n: case MVM_OP_sub_n: case MVM_OP_mul_n: case MVM_OP_div_n: case MVM_OP_neg_n: /* number coercion */ case MVM_OP_coerce_ni: case MVM_OP_coerce_in: case MVM_OP_extend_i32: case MVM_OP_trunc_i16: case MVM_OP_trunc_i32: /* comparison (integer) */ case MVM_OP_eq_i: case MVM_OP_ne_i: case MVM_OP_lt_i: case MVM_OP_le_i: case MVM_OP_gt_i: case MVM_OP_ge_i: case MVM_OP_cmp_i: /* comparison (numbers) */ case MVM_OP_eq_n: case MVM_OP_ne_n: case MVM_OP_ge_n: case MVM_OP_gt_n: case MVM_OP_lt_n: case MVM_OP_le_n: case MVM_OP_cmp_n: /* comparison (objects) */ case MVM_OP_eqaddr: case MVM_OP_isconcrete: /* comparison (big integer) */ case MVM_OP_eq_I: case MVM_OP_ne_I: case MVM_OP_lt_I: case MVM_OP_le_I: case MVM_OP_gt_I: case MVM_OP_ge_I: /* constants */ case MVM_OP_const_i64_16: case MVM_OP_const_i64_32: case MVM_OP_const_i64: case MVM_OP_const_n64: case MVM_OP_nan: case MVM_OP_const_s: case MVM_OP_null: /* argument reading */ case MVM_OP_getarg_i: case MVM_OP_getarg_o: case MVM_OP_getarg_n: case MVM_OP_getarg_s: case MVM_OP_sp_getarg_i: case MVM_OP_sp_getarg_o: case MVM_OP_sp_getarg_n: case MVM_OP_sp_getarg_s: /* accessors */ case MVM_OP_sp_p6oget_o: case MVM_OP_sp_p6oget_s: case MVM_OP_sp_p6oget_i: case MVM_OP_sp_p6oget_n: case MVM_OP_sp_p6ogetvc_o: case MVM_OP_sp_p6ogetvt_o: case MVM_OP_sp_p6obind_i: case MVM_OP_sp_p6obind_n: case MVM_OP_sp_p6obind_s: case MVM_OP_sp_p6obind_o: case MVM_OP_sp_bind_i64: case MVM_OP_sp_bind_n: case MVM_OP_sp_bind_s: case MVM_OP_sp_bind_o: case MVM_OP_sp_get_i64: case MVM_OP_sp_get_n: case MVM_OP_sp_get_s: case MVM_OP_sp_get_o: case MVM_OP_sp_deref_bind_i64: case MVM_OP_sp_deref_bind_n: case MVM_OP_sp_deref_get_i64: case MVM_OP_sp_deref_get_n: case MVM_OP_set: case MVM_OP_getlex: case MVM_OP_sp_getlex_o: case MVM_OP_sp_getlex_ins: case MVM_OP_sp_getlexvia_o: case MVM_OP_sp_getlexvia_ins: case MVM_OP_getlex_no: case MVM_OP_sp_getlex_no: case MVM_OP_bindlex: case MVM_OP_getwhat: case MVM_OP_getwho: case MVM_OP_getwhere: case MVM_OP_sp_getspeshslot: case MVM_OP_takedispatcher: case MVM_OP_setdispatcher: case MVM_OP_ctx: case MVM_OP_curcode: case MVM_OP_getcode: case MVM_OP_callercode: case MVM_OP_sp_fastcreate: case MVM_OP_iscont: case MVM_OP_decont: case MVM_OP_sp_decont: case MVM_OP_sp_findmeth: case MVM_OP_hllboxtype_i: case MVM_OP_hllboxtype_n: case MVM_OP_hllboxtype_s: case MVM_OP_null_s: case MVM_OP_isnull_s: case MVM_OP_not_i: case MVM_OP_isnull: case MVM_OP_isnonnull: case MVM_OP_isint: case MVM_OP_isnum: case MVM_OP_isstr: case MVM_OP_islist: case MVM_OP_ishash: case MVM_OP_sp_boolify_iter_arr: case MVM_OP_sp_boolify_iter_hash: case MVM_OP_objprimspec: case MVM_OP_objprimbits: case MVM_OP_takehandlerresult: case MVM_OP_exception: case MVM_OP_scwbdisable: case MVM_OP_scwbenable: case MVM_OP_assign: case MVM_OP_assignunchecked: case MVM_OP_getlexstatic_o: case MVM_OP_getlexperinvtype_o: case MVM_OP_paramnamesused: case MVM_OP_assertparamcheck: case MVM_OP_getobjsc: case MVM_OP_getstderr: case MVM_OP_getstdout: case MVM_OP_getstdin: case MVM_OP_ordat: case MVM_OP_ordfirst: case MVM_OP_setcodeobj: /* Profiling */ case MVM_OP_prof_enterspesh: case MVM_OP_prof_enterinline: case MVM_OP_invokewithcapture: case MVM_OP_captureposelems: case MVM_OP_capturehasnameds: /* Exception handling */ case MVM_OP_lastexpayload: /* Parameters */ case MVM_OP_param_sp: case MVM_OP_param_sn: /* Specialized atomics */ case MVM_OP_sp_cas_o: case MVM_OP_sp_atomicload_o: case MVM_OP_sp_atomicstore_o: jg_append_primitive(tc, jg, ins); break; /* Unspecialized parameter access */ case MVM_OP_param_rp_i: { MVMint16 dst = ins->operands[0].reg.orig; MVMuint16 arg_idx = ins->operands[1].lit_ui16; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_PARAMS } }, { MVM_JIT_LITERAL, { arg_idx } } }; jg_append_call_c(tc, jg, MVM_args_get_required_pos_int, 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_param_rp_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMuint16 arg_idx = ins->operands[1].lit_ui16; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_PARAMS } }, { MVM_JIT_LITERAL, { arg_idx } } }; jg_append_call_c(tc, jg, MVM_args_get_required_pos_str, 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_param_rp_o: { MVMint16 dst = ins->operands[0].reg.orig; MVMuint16 arg_idx = ins->operands[1].lit_ui16; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_PARAMS } }, { MVM_JIT_LITERAL, { arg_idx } } }; jg_append_call_c(tc, jg, MVM_args_get_required_pos_obj, 3, args, MVM_JIT_RV_PTR, dst); break; } /* branches */ case MVM_OP_goto: case MVM_OP_if_i: case MVM_OP_unless_i: case MVM_OP_if_n: case MVM_OP_unless_n: case MVM_OP_ifnonnull: case MVM_OP_indexat: case MVM_OP_indexnat: case MVM_OP_if_s0: case MVM_OP_unless_s0: jg_append_branch(tc, jg, 0, ins); break; /* never any need to implement them anymore, since they're automatically lowered for us by spesh into istrue + if_i. We can't properly compile if_o / unless_o as-is because they're both invokish and branching. */ case MVM_OP_if_o: case MVM_OP_unless_o: MVM_oops(tc, "Trying to compile if_o/unless_o, should never happen"); break; /* some functions */ case MVM_OP_gethow: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_istype: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint16 type = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_VAL, { type } }, { MVM_JIT_REG_ADDR, { dst } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_gethllsym: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 hll = ins->operands[1].reg.orig; MVMint16 sym = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { hll } }, { MVM_JIT_REG_VAL, { sym } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_checkarity: { MVMuint16 min = ins->operands[0].lit_i16; MVMuint16 max = ins->operands[1].lit_i16; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_PARAMS } }, { MVM_JIT_LITERAL, { min } }, { MVM_JIT_LITERAL, { max } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_say: case MVM_OP_print: { MVMint32 reg = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { reg } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_wval: case MVM_OP_wval_wide: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 dep = ins->operands[1].lit_i16; MVMint64 idx = op == MVM_OP_wval ? ins->operands[2].lit_i16 : ins->operands[2].lit_i64; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_CU } }, { MVM_JIT_LITERAL, { dep } }, { MVM_JIT_LITERAL, { idx } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_scgetobjidx: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 sc = ins->operands[1].reg.orig; MVMint64 obj = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { sc } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_throwdyn: case MVM_OP_throwlex: case MVM_OP_throwlexotic: { MVMint16 regi = ins->operands[0].reg.orig; MVMint16 object = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_LITERAL, { op == MVM_OP_throwlexotic ? MVM_EX_THROW_LEXOTIC : op == MVM_OP_throwlex ? MVM_EX_THROW_LEX : MVM_EX_THROW_DYN } }, { MVM_JIT_REG_VAL, { object } }, { MVM_JIT_REG_ADDR, { regi } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_rethrow: { MVMint16 obj = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_LITERAL, { MVM_EX_THROW_DYN } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_LITERAL, { 0 } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_throwcatdyn: case MVM_OP_throwcatlex: case MVM_OP_throwcatlexotic: { MVMint16 regi = ins->operands[0].reg.orig; MVMint32 category = (MVMuint32)ins->operands[1].lit_i64; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_LITERAL, { op == MVM_OP_throwcatdyn ? MVM_EX_THROW_DYN : op == MVM_OP_throwcatlex ? MVM_EX_THROW_LEX : MVM_EX_THROW_LEXOTIC } }, { MVM_JIT_LITERAL, { category } }, { MVM_JIT_REG_ADDR, { regi } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_throwpayloadlex: { MVMint16 regi = ins->operands[0].reg.orig; MVMint32 category = (MVMuint32)ins->operands[1].lit_i64; MVMint16 payload = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_LITERAL, { MVM_EX_THROW_LEX } }, { MVM_JIT_LITERAL, { category } }, { MVM_JIT_REG_VAL, { payload } }, { MVM_JIT_REG_ADDR, { regi } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_getexpayload: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_bindexpayload: { MVMint16 obj = ins->operands[0].reg.orig; MVMint16 payload = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_VAL, { payload } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_resume: { MVMint16 exc = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { exc } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_die: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 str = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { str } }, { MVM_JIT_REG_ADDR, { dst } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_getdynlex: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 name = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { name } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_CALLER } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_binddynlex: { MVMint16 name = ins->operands[0].reg.orig; MVMint16 val = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { name } }, { MVM_JIT_REG_VAL, { val } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_CALLER } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_getlexouter: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 name = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { name } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_isfalse: case MVM_OP_istrue: { MVMint16 obj = ins->operands[1].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_ADDR, { dst } }, { MVM_JIT_LITERAL, { 0 } }, { MVM_JIT_LITERAL, { 0 } }, { MVM_JIT_LITERAL, { op == MVM_OP_isfalse } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 6, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_capturelex: { MVMint16 code = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { code } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_captureinnerlex: { MVMint16 code = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { code } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_takeclosure: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_usecapture: case MVM_OP_savecapture: { MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_FRAME } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_captureposprimspec: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 capture = ins->operands[1].reg.orig; MVMint16 index = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { capture } }, { MVM_JIT_REG_VAL, { index } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_gt_s: case MVM_OP_ge_s: case MVM_OP_lt_s: case MVM_OP_le_s: case MVM_OP_cmp_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 a = ins->operands[1].reg.orig; MVMint16 b = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { a } }, { MVM_JIT_REG_VAL, { b } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); /* We rely on an implementation of the comparisons against -1, 0 and 1 * in emit.dasc */ if (op != MVM_OP_cmp_s) { jg_append_primitive(tc, jg, ins); } break; } case MVM_OP_hllize: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMHLLConfig *hll_config = jg->sg->sf->body.cu->body.hll_config; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } }, { MVM_JIT_LITERAL_PTR, { (MVMint64)hll_config } }, { MVM_JIT_REG_ADDR, { dst } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_clone: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_create: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 type = ins->operands[1].reg.orig; MVMJitCallArg args_alloc[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { type } } }; MVMJitCallArg args_init[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { dst } } }; jg_append_call_c(tc, jg, MVM_repr_alloc, 2, args_alloc, MVM_JIT_RV_PTR, dst); jg_append_call_c(tc, jg, MVM_repr_init, 2, args_init, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_cas_o: { MVMint16 result = ins->operands[0].reg.orig; MVMint16 target = ins->operands[1].reg.orig; MVMint16 expected = ins->operands[2].reg.orig; MVMint16 value = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { expected } }, { MVM_JIT_REG_VAL, { value } }, { MVM_JIT_REG_ADDR, { result } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_cas_i: { MVMint16 result = ins->operands[0].reg.orig; MVMint16 target = ins->operands[1].reg.orig; MVMint16 expected = ins->operands[2].reg.orig; MVMint16 value = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { expected } }, { MVM_JIT_REG_VAL, { value } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_INT, result); break; } case MVM_OP_atomicinc_i: case MVM_OP_atomicdec_i: case MVM_OP_atomicload_i: { MVMint16 result = ins->operands[0].reg.orig; MVMint16 target = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { target } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, result); break; } case MVM_OP_atomicadd_i: { MVMint16 result = ins->operands[0].reg.orig; MVMint16 target = ins->operands[1].reg.orig; MVMint16 increment = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { increment } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, result); break; } case MVM_OP_atomicload_o: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 target = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { target } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_atomicstore_o: case MVM_OP_atomicstore_i: { MVMint16 target = ins->operands[0].reg.orig; MVMint16 value = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { value } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_lock: { MVMint16 lock = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { lock } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_unlock: { MVMint16 lock = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { lock } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } /* repr ops */ case MVM_OP_unshift_i: case MVM_OP_unshift_n: case MVM_OP_unshift_s: case MVM_OP_unshift_o: case MVM_OP_push_i: case MVM_OP_push_n: case MVM_OP_push_s: case MVM_OP_push_o: case MVM_OP_shift_i: case MVM_OP_shift_n: case MVM_OP_shift_s: case MVM_OP_shift_o: case MVM_OP_pop_i: case MVM_OP_pop_n: case MVM_OP_pop_s: case MVM_OP_pop_o: case MVM_OP_deletekey: case MVM_OP_existskey: case MVM_OP_existspos: case MVM_OP_setelemspos: case MVM_OP_splice: case MVM_OP_atpos_i: case MVM_OP_atpos_n: case MVM_OP_atpos_s: case MVM_OP_atpos_o: case MVM_OP_atkey_i: case MVM_OP_atkey_n: case MVM_OP_atkey_s: case MVM_OP_atkey_o: case MVM_OP_bindpos_i: case MVM_OP_bindpos_n: case MVM_OP_bindpos_s: case MVM_OP_bindpos_o: case MVM_OP_bindkey_i: case MVM_OP_bindkey_n: case MVM_OP_bindkey_s: case MVM_OP_bindkey_o: case MVM_OP_getattr_i: case MVM_OP_getattr_n: case MVM_OP_getattr_s: case MVM_OP_getattr_o: case MVM_OP_getattrs_i: case MVM_OP_getattrs_n: case MVM_OP_getattrs_s: case MVM_OP_getattrs_o: case MVM_OP_attrinited: case MVM_OP_bindattr_i: case MVM_OP_bindattr_n: case MVM_OP_bindattr_s: case MVM_OP_bindattr_o: case MVM_OP_bindattrs_i: case MVM_OP_bindattrs_n: case MVM_OP_bindattrs_s: case MVM_OP_bindattrs_o: case MVM_OP_hintfor: case MVM_OP_elems: if (!consume_reprop(tc, jg, iter, ins)) { MVM_jit_log(tc, "BAIL: op <%s> (devirt attempted)\n", ins->info->name); return 0; } break; case MVM_OP_iterkey_s: case MVM_OP_iterval: case MVM_OP_iter: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { invocant } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_continuationreset: { MVMint16 reg = ins->operands[0].reg.orig; MVMint16 tag = ins->operands[1].reg.orig; MVMint16 code = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { tag } }, { MVM_JIT_REG_VAL, { code } }, { MVM_JIT_REG_ADDR, { reg } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_continuationcontrol: { MVMint16 reg = ins->operands[0].reg.orig; MVMint16 protect = ins->operands[1].reg.orig; MVMint16 tag = ins->operands[2].reg.orig; MVMint16 code = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { protect } }, { MVM_JIT_REG_VAL, { tag } }, { MVM_JIT_REG_VAL, { code } }, { MVM_JIT_REG_ADDR, { reg } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_continuationinvoke: { MVMint16 reg = ins->operands[0].reg.orig; MVMint16 cont = ins->operands[1].reg.orig; MVMint16 code = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { cont } }, { MVM_JIT_REG_VAL, { code } }, { MVM_JIT_REG_ADDR, { reg } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_sp_boolify_iter: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_findmeth: case MVM_OP_findmeth_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint32 name = (op == MVM_OP_findmeth_s ? ins->operands[2].reg.orig : ins->operands[2].lit_str_idx); MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { (op == MVM_OP_findmeth_s ? MVM_JIT_REG_VAL : MVM_JIT_STR_IDX), { name } }, { MVM_JIT_REG_ADDR, { dst } }, { MVM_JIT_LITERAL, { 1 } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_tryfindmeth: case MVM_OP_tryfindmeth_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint32 name = (op == MVM_OP_tryfindmeth_s ? ins->operands[2].reg.orig : ins->operands[2].lit_str_idx); MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { (op == MVM_OP_tryfindmeth_s ? MVM_JIT_REG_VAL : MVM_JIT_STR_IDX), { name } }, { MVM_JIT_REG_ADDR, { dst } }, { MVM_JIT_LITERAL, { 0 } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_multicachefind: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 cache = ins->operands[1].reg.orig; MVMint16 capture = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { cache } }, { MVM_JIT_REG_VAL, { capture } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_multicacheadd: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 cache = ins->operands[1].reg.orig; MVMint16 capture = ins->operands[2].reg.orig; MVMint16 result = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { cache } }, { MVM_JIT_REG_VAL, { capture } }, { MVM_JIT_REG_VAL, { result } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_can: case MVM_OP_can_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint32 name = (op == MVM_OP_can_s ? ins->operands[2].reg.orig : ins->operands[2].lit_str_idx); MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { (op == MVM_OP_can_s ? MVM_JIT_REG_VAL : MVM_JIT_STR_IDX), { name } }, { MVM_JIT_REG_ADDR, { dst } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } /* coercion */ case MVM_OP_coerce_sn: case MVM_OP_coerce_ns: case MVM_OP_coerce_si: case MVM_OP_coerce_is: case MVM_OP_coerce_In: { MVMint16 src = ins->operands[1].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = {{ MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } } }; MVMJitRVMode rv_mode = ((op == MVM_OP_coerce_sn || op == MVM_OP_coerce_In) ? MVM_JIT_RV_NUM : op == MVM_OP_coerce_si ? MVM_JIT_RV_INT : MVM_JIT_RV_PTR); if (op == MVM_OP_coerce_ns) { args[1].type = MVM_JIT_REG_VAL_F; } jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, rv_mode, dst); break; } case MVM_OP_coerce_nI: { MVMint16 src = ins->operands[1].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMint16 typ = ins->operands[2].reg.orig; MVMJitCallArg args[] = {{ MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { typ } }, { MVM_JIT_REG_VAL_F, { src } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_coerce_II: { MVMint16 src = ins->operands[1].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMint16 typ = ins->operands[2].reg.orig; MVMJitCallArg args[] = {{ MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { typ } }, { MVM_JIT_REG_VAL, { src } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_smrt_strify: case MVM_OP_smrt_numify: { MVMint16 obj = ins->operands[1].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_ADDR, { dst } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_queuepoll: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_close_fh: { MVMint16 fho = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { fho } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_open_fh: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 path = ins->operands[1].reg.orig; MVMint16 mode = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { path } }, { MVM_JIT_REG_VAL, { mode } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_eof_fh: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 fho = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { fho } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_write_fhb: { MVMint16 fho = ins->operands[0].reg.orig; MVMint16 buf = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { fho } }, { MVM_JIT_REG_VAL, { buf } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_read_fhb: { MVMint16 fho = ins->operands[0].reg.orig; MVMint16 res = ins->operands[1].reg.orig; MVMint16 len = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { fho } }, { MVM_JIT_REG_VAL, { res } }, { MVM_JIT_REG_VAL, { len } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_box_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 val = ins->operands[1].reg.orig; MVMint16 type = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL_F, { val } }, { MVM_JIT_REG_VAL, { type } }, { MVM_JIT_REG_ADDR, { dst } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_box_s: case MVM_OP_box_i: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 val = ins->operands[1].reg.orig; MVMint16 type = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { val } }, { MVM_JIT_REG_VAL, { type } }, { MVM_JIT_REG_ADDR, { dst } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_unbox_i: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_unbox_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_unbox_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR , { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } /* string ops */ case MVM_OP_repeat_s: case MVM_OP_split: case MVM_OP_concat_s: { MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_escape: case MVM_OP_uc: case MVM_OP_lc: case MVM_OP_tc: case MVM_OP_fc: case MVM_OP_indexingoptimized: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 string = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { string } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_ne_s: case MVM_OP_eq_s: { MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } } }; jg_append_call_c(tc, jg, op_to_func(tc, MVM_OP_eq_s), 3, args, MVM_JIT_RV_INT, dst); if (op == MVM_OP_ne_s) { /* append not_i to negate ne_s */ MVMSpeshIns *not_i = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMSpeshIns)); not_i->info = MVM_op_get_op(MVM_OP_not_i); not_i->operands = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMSpeshOperand) * 2); not_i->operands[0].reg.orig = dst; not_i->operands[1].reg.orig = dst; jg_append_primitive(tc, jg, not_i); } break; } case MVM_OP_eqat_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } }, { MVM_JIT_REG_VAL, { offset } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_eqatic_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } }, { MVM_JIT_REG_VAL, { offset } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_eqatim_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } }, { MVM_JIT_REG_VAL, { offset } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_eqaticim_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } }, { MVM_JIT_REG_VAL, { offset } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_chars: case MVM_OP_graphs_s: case MVM_OP_codes_s: case MVM_OP_flip: { MVMint16 src = ins->operands[1].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } } }; MVMJitRVMode rv_mode = (op == MVM_OP_flip ? MVM_JIT_RV_PTR : MVM_JIT_RV_INT); jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, rv_mode, dst); break; } case MVM_OP_getcp_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMint16 idx = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } }, { MVM_JIT_REG_VAL, { idx } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_chr: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_join: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 sep = ins->operands[1].reg.orig; MVMint16 input = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { sep } }, { MVM_JIT_REG_VAL, { input } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_replace: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 a = ins->operands[1].reg.orig; MVMint16 start = ins->operands[2].reg.orig; MVMint16 length = ins->operands[3].reg.orig; MVMint16 replace = ins->operands[4].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { a } }, { MVM_JIT_REG_VAL, { start } }, { MVM_JIT_REG_VAL, { length } }, { MVM_JIT_REG_VAL, { replace } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_substr_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 string = ins->operands[1].reg.orig; MVMint16 start = ins->operands[2].reg.orig; MVMint16 length = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { string } }, { MVM_JIT_REG_VAL, { start } }, { MVM_JIT_REG_VAL, { length } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_index_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 haystack = ins->operands[1].reg.orig; MVMint16 needle = ins->operands[2].reg.orig; MVMint16 start = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { haystack } }, { MVM_JIT_REG_VAL, { needle } }, { MVM_JIT_REG_VAL, { start } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_iscclass: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 cclass = ins->operands[1].reg.orig; MVMint16 str = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { cclass } }, { MVM_JIT_REG_VAL, { str } }, { MVM_JIT_REG_VAL, { offset } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_findcclass: case MVM_OP_findnotcclass: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 cclass = ins->operands[1].reg.orig; MVMint16 target = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMint16 count = ins->operands[4].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { cclass } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { offset } }, { MVM_JIT_REG_VAL, { count } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_nfarunalt: { MVMint16 nfa = ins->operands[0].reg.orig; MVMint16 target = ins->operands[1].reg.orig; MVMint16 offset = ins->operands[2].reg.orig; MVMint16 bstack = ins->operands[3].reg.orig; MVMint16 cstack = ins->operands[4].reg.orig; MVMint16 labels = ins->operands[5].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { nfa } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { offset } }, { MVM_JIT_REG_VAL, { bstack } }, { MVM_JIT_REG_VAL, { cstack } }, { MVM_JIT_REG_VAL, { labels } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 7, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_nfarunproto: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 nfa = ins->operands[1].reg.orig; MVMint16 target = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { nfa } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { offset } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_nfafromstatelist: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 states = ins->operands[1].reg.orig; MVMint16 type = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { states } }, { MVM_JIT_REG_VAL, { type } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } /* encode/decode ops */ case MVM_OP_encode: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 str = ins->operands[1].reg.orig; MVMint16 enc = ins->operands[2].reg.orig; MVMint16 buf = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { str } }, { MVM_JIT_REG_VAL, { enc } }, { MVM_JIT_REG_VAL, { buf } }, { MVM_JIT_LITERAL_PTR, { 0 } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_decoderaddbytes: { MVMint16 decoder = ins->operands[0].reg.orig; MVMint16 bytes = ins->operands[1].reg.orig; MVMJitCallArg argc[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { decoder } }, { MVM_JIT_LITERAL_PTR, { (MVMint64)"decoderaddbytes" } } }; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { decoder } }, { MVM_JIT_REG_VAL, { bytes } } }; jg_append_call_c(tc, jg, &MVM_decoder_ensure_decoder, 3, argc, MVM_JIT_RV_VOID, -1); jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_decodertakeline: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 decoder = ins->operands[1].reg.orig; MVMint16 chomp = ins->operands[2].reg.orig; MVMint16 inc = ins->operands[3].reg.orig; MVMJitCallArg argc[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { decoder } }, { MVM_JIT_LITERAL_PTR, { (MVMint64)"decodertakeline" } } }; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { decoder } }, { MVM_JIT_REG_VAL, { chomp } }, { MVM_JIT_REG_VAL, { inc } } }; jg_append_call_c(tc, jg, &MVM_decoder_ensure_decoder, 3, argc, MVM_JIT_RV_VOID, -1); jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } /* bigint ops */ case MVM_OP_isbig_I: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_cmp_I: { MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_add_I: case MVM_OP_sub_I: case MVM_OP_mul_I: case MVM_OP_div_I: case MVM_OP_mod_I: case MVM_OP_bor_I: case MVM_OP_band_I: case MVM_OP_bxor_I: case MVM_OP_lcm_I: case MVM_OP_gcd_I: { MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 type = ins->operands[3].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { type } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_pow_I: { MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMint16 type_n = ins->operands[3].reg.orig; MVMint16 type_I = ins->operands[4].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } }, { MVM_JIT_REG_VAL, { type_n } }, { MVM_JIT_REG_VAL, { type_I } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_div_In: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src_a = ins->operands[1].reg.orig; MVMint16 src_b = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src_a } }, { MVM_JIT_REG_VAL, { src_b } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_brshift_I: case MVM_OP_blshift_I: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMint16 shift = ins->operands[2].reg.orig; MVMint16 type = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { type } }, { MVM_JIT_REG_VAL, { src } }, { MVM_JIT_REG_VAL, { shift } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_coerce_Is: { MVMint16 src = ins->operands[1].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } }, { MVM_JIT_LITERAL, { 10 } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_radix: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 radix = ins->operands[1].reg.orig; MVMint16 string = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMint16 flag = ins->operands[4].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { radix } }, { MVM_JIT_REG_VAL, { string } }, { MVM_JIT_REG_VAL, { offset } }, { MVM_JIT_REG_VAL, { flag } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 5, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_radix_I: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 radix = ins->operands[1].reg.orig; MVMint16 string = ins->operands[2].reg.orig; MVMint16 offset = ins->operands[3].reg.orig; MVMint16 flag = ins->operands[4].reg.orig; MVMint16 type = ins->operands[5].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { radix } }, { MVM_JIT_REG_VAL, { string } }, { MVM_JIT_REG_VAL, { offset } }, { MVM_JIT_REG_VAL, { flag } }, { MVM_JIT_REG_VAL, { type } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 6, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_base_I: { MVMint16 src = ins->operands[1].reg.orig; MVMint16 base = ins->operands[2].reg.orig; MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { src } }, { MVM_JIT_REG_VAL, { base } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_isprime_I: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 rounds = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant }, { MVM_JIT_REG_VAL, rounds } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_bool_I: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_rand_I: case MVM_OP_bnot_I: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMint32 type = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, type }, { MVM_JIT_REG_VAL, invocant } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_getcodeobj: { MVMint16 dst = ins->operands[0].reg.orig; MVMint32 invocant = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_REG_VAL, invocant } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_floor_n: case MVM_OP_sqrt_n: case MVM_OP_sin_n: case MVM_OP_cos_n: case MVM_OP_tan_n: case MVM_OP_asin_n: case MVM_OP_acos_n: case MVM_OP_atan_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_REG_VAL_F, { src } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 1, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_pow_n: case MVM_OP_atan2_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 a = ins->operands[1].reg.orig; MVMint16 b = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_REG_VAL_F, { a } }, { MVM_JIT_REG_VAL_F, { b } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_time_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 1, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_randscale_n: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 scale = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL_F, { scale } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_NUM, dst); break; } case MVM_OP_isnanorinf: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 src = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL_F, { src } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_nativecallcast: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 restype = ins->operands[1].reg.orig; MVMint16 site = ins->operands[2].reg.orig; MVMint16 cargs = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { restype } }, { MVM_JIT_REG_VAL, { site } }, { MVM_JIT_REG_VAL, { cargs } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_nativecallinvoke: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 restype = ins->operands[1].reg.orig; MVMint16 site = ins->operands[2].reg.orig; MVMint16 cargs = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { restype } }, { MVM_JIT_REG_VAL, { site } }, { MVM_JIT_REG_VAL, { cargs } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_typeparameters: case MVM_OP_typeparameterized: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_typeparameterat: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint16 idx = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_VAL, { idx } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_objectid: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } /* native references (as simple function calls for now) */ case MVM_OP_iscont_i: case MVM_OP_iscont_n: case MVM_OP_iscont_s: case MVM_OP_isrwcont: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_INT, dst); break; } case MVM_OP_assign_i: case MVM_OP_assign_n: case MVM_OP_assign_s: { MVMint16 target = ins->operands[0].reg.orig; MVMint16 value = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { target } }, { MVM_JIT_REG_VAL, { value } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_decont_i: case MVM_OP_decont_n: case MVM_OP_decont_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_ADDR, { dst } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_getrusage: { MVMint16 obj = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_cpucores: { MVMint16 dst = ins->operands[0].reg.orig; jg_append_call_c(tc, jg, op_to_func(tc, op), 0, NULL, MVM_JIT_RV_INT, dst); break; } case MVM_OP_sleep: { MVMint16 time = ins->operands[0].reg.orig; MVMJitCallArg block_args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } } }; MVMJitCallArg sleep_args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { time } } }; jg_append_call_c(tc, jg, MVM_gc_mark_thread_blocked, 1, block_args, MVM_JIT_RV_VOID, -1); jg_append_call_c(tc, jg, op_to_func(tc, op), 2, sleep_args, MVM_JIT_RV_VOID, -1); jg_append_call_c(tc, jg, MVM_gc_mark_thread_unblocked, 1, block_args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_getlexref_i: case MVM_OP_getlexref_i32: case MVM_OP_getlexref_i16: case MVM_OP_getlexref_i8: case MVM_OP_getlexref_n: case MVM_OP_getlexref_n32: case MVM_OP_getlexref_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMuint16 outers = ins->operands[1].lex.outers; MVMuint16 idx = ins->operands[1].lex.idx; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_LITERAL, { outers } }, { MVM_JIT_LITERAL, { idx } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_getattrref_i: case MVM_OP_getattrref_n: case MVM_OP_getattrref_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint16 class = ins->operands[2].reg.orig; MVMint16 name = ins->operands[3].lit_str_idx; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_VAL, { class } }, { MVM_JIT_STR_IDX, { name } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_getattrsref_i: case MVM_OP_getattrsref_n: case MVM_OP_getattrsref_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint16 class = ins->operands[2].reg.orig; MVMint16 name = ins->operands[3].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_VAL, { class } }, { MVM_JIT_REG_VAL, { name } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 4, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_atposref_i: case MVM_OP_atposref_n: case MVM_OP_atposref_s: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMint16 index = ins->operands[2].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_VAL, { index } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_PTR, dst); break; } /* profiling */ case MVM_OP_prof_allocated: { MVMint16 reg = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { reg } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_prof_exit: { MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 1, args, MVM_JIT_RV_VOID, -1); break; } /* special jumplist branch */ case MVM_OP_jumplist: { return consume_jumplist(tc, jg, iter, ins); } /* returning */ case MVM_OP_return: { MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_LITERAL, { 0 } }}; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); jg_append_call_c(tc, jg, &MVM_frame_try_return, 1, args, MVM_JIT_RV_VOID, -1); jg_append_branch(tc, jg, MVM_JIT_BRANCH_EXIT, NULL); break; } case MVM_OP_return_o: case MVM_OP_return_s: case MVM_OP_return_n: case MVM_OP_return_i: { MVMint16 reg = ins->operands[0].reg.orig; MVMJitCallArg args[] = {{ MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { reg } }, { MVM_JIT_LITERAL, { 0 } } }; if (op == MVM_OP_return_n) { args[1].type = MVM_JIT_REG_VAL_F; } jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); /* reuse args for tc arg */ jg_append_call_c(tc, jg, &MVM_frame_try_return, 1, args, MVM_JIT_RV_VOID, -1); jg_append_branch(tc, jg, MVM_JIT_BRANCH_EXIT, NULL); break; } case MVM_OP_sp_guard: case MVM_OP_sp_guardconc: case MVM_OP_sp_guardtype: case MVM_OP_sp_guardsf: jg_append_guard(tc, jg, ins); break; case MVM_OP_sp_resolvecode: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_prepargs: { return consume_invoke(tc, jg, iter, ins); } case MVM_OP_getexcategory: { MVMint16 dst = ins->operands[0].reg.orig; MVMint16 obj = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_PTR, dst); break; } case MVM_OP_bindexcategory: { MVMint16 obj = ins->operands[0].reg.orig; MVMint16 category = ins->operands[1].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } }, { MVM_JIT_REG_VAL, { category } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_exreturnafterunwind: { MVMint16 obj = ins->operands[0].reg.orig; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_REG_VAL, { obj } } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 2, args, MVM_JIT_RV_VOID, -1); break; } case MVM_OP_breakpoint: { MVMint32 file_idx = ins->operands[0].lit_i16; MVMint32 line_no = ins->operands[1].lit_i16; MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, MVM_JIT_INTERP_TC }, { MVM_JIT_LITERAL, file_idx }, { MVM_JIT_LITERAL, line_no } }; jg_append_call_c(tc, jg, op_to_func(tc, op), 3, args, MVM_JIT_RV_VOID, -1); break; } default: { /* Check if it's an extop. */ MVMint32 emitted_extop = 0; if (ins->info->opcode == (MVMuint16)-1) { MVMExtOpRecord *extops = jg->sg->sf->body.cu->body.extops; MVMuint16 num_extops = jg->sg->sf->body.cu->body.num_extops; MVMuint16 i; for (i = 0; i < num_extops; i++) { if (extops[i].info == ins->info && !extops[i].no_jit) { size_t fake_regs_size; MVMuint16 *fake_regs = try_fake_extop_regs(tc, jg->sg, ins, &fake_regs_size); if (fake_regs_size && fake_regs != NULL) { MVMint32 data_label = jg_add_data_node(tc, jg, fake_regs, fake_regs_size); MVMJitCallArg args[] = { { MVM_JIT_INTERP_VAR, { MVM_JIT_INTERP_TC } }, { MVM_JIT_DATA_LABEL, { data_label } }}; if (ins->info->jittivity & MVM_JIT_INFO_INVOKISH) jg_append_control(tc, jg, ins, MVM_JIT_CONTROL_THROWISH_PRE); jg_append_call_c(tc, jg, extops[i].func, 2, args, MVM_JIT_RV_VOID, -1); if (ins->info->jittivity & MVM_JIT_INFO_INVOKISH) jg_append_control(tc, jg, ins, MVM_JIT_CONTROL_INVOKISH); MVM_jit_log(tc, "append extop: <%s>\n", ins->info->name); emitted_extop = 1; } break; } } } if (!emitted_extop) { MVM_jit_log(tc, "BAIL: op <%s>\n", ins->info->name); return 0; } } } return 1; } static MVMint32 consume_bb(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter, MVMSpeshBB *bb) { MVMJitExprTree *tree = NULL; MVMint32 i; MVMint32 label = MVM_jit_label_before_bb(tc, jg, bb); jg_append_label(tc, jg, label); /* We always append a label update at the start of a basic block for now. * This may be more than is actually needed, but it's safe. The problem is * that a jump can move us out of the scope of an exception hander, and so * we need a location update. This came to light in the case that we left an * inline (which is a jump) and came back to a region where a handler should * be in force, and it failed to be. */ jg_append_control(tc, jg, bb->first_ins, MVM_JIT_CONTROL_DYNAMIC_LABEL); /* add a jit breakpoint if required */ for (i = 0; i < tc->instance->jit_breakpoints_num; i++) { if (tc->instance->jit_breakpoints[i].frame_nr == tc->instance->jit_seq_nr && tc->instance->jit_breakpoints[i].block_nr == iter->bb->idx) { jg_append_control(tc, jg, bb->first_ins, MVM_JIT_CONTROL_BREAKPOINT); break; /* one is enough though */ } } /* Try to create an expression tree */ if (tc->instance->jit_expr_enabled && (tc->instance->jit_expr_last_frame < 0 || tc->instance->jit_seq_nr < tc->instance->jit_expr_last_frame || (tc->instance->jit_seq_nr == tc->instance->jit_expr_last_frame && (tc->instance->jit_expr_last_bb < 0 || iter->bb->idx <= tc->instance->jit_expr_last_bb)))) { while (iter->ins) { /* consumes iterator */ tree = MVM_jit_expr_tree_build(tc, jg, iter); if (tree != NULL) { MVMJitNode *node = MVM_spesh_alloc(tc, jg->sg, sizeof(MVMJitNode)); node->type = MVM_JIT_NODE_EXPR_TREE; node->u.tree = tree; tree->seq_nr = jg->expr_seq_nr++; jg_append_node(jg, node); MVM_jit_log_expr_tree(tc, tree); } if (iter->ins) { /* something we can't compile yet, or simply an empty tree */ break; } } } /* Try to consume the (rest of the) basic block per instruction */ while (iter->ins) { before_ins(tc, jg, iter, iter->ins); if(!consume_ins(tc, jg, iter, iter->ins)) return 0; after_ins(tc, jg, iter, iter->ins); MVM_spesh_iterator_next_ins(tc, iter); } return 1; } MVMJitGraph * MVM_jit_try_make_graph(MVMThreadContext *tc, MVMSpeshGraph *sg) { MVMSpeshIterator iter; MVMJitGraph *graph; if (!MVM_jit_support()) { return NULL; } if (tc->instance->jit_log_fh) { char *cuuid = MVM_string_utf8_encode_C_string(tc, sg->sf->body.cuuid); char *name = MVM_string_utf8_encode_C_string(tc, sg->sf->body.name); MVM_jit_log(tc, "Constructing JIT graph (cuuid: %s, name: '%s')\n", cuuid, name); MVM_free(cuuid); MVM_free(name); } MVM_spesh_iterator_init(tc, &iter, sg); /* ignore first BB, which always contains a NOP */ MVM_spesh_iterator_next_bb(tc, &iter); graph = MVM_spesh_alloc(tc, sg, sizeof(MVMJitGraph)); graph->sg = sg; graph->first_node = NULL; graph->last_node = NULL; /* Set initial instruction label offset */ graph->obj_label_ofs = sg->num_bbs + 1; /* Labels for individual instructions (not basic blocks), for instance at * boundaries of exception handling frames */ MVM_VECTOR_INIT(graph->obj_labels, 16); /* Deoptimization labels */ MVM_VECTOR_INIT(graph->deopts, 8); /* Nodes for each label, used to ensure labels aren't added twice */ MVM_VECTOR_INIT(graph->label_nodes, 16 + sg->num_bbs); graph->expr_seq_nr = 0; /* JIT handlers are indexed by spesh graph handler index */ if (sg->num_handlers > 0) { MVM_VECTOR_INIT(graph->handlers, sg->num_handlers); graph->handlers_num = sg->num_handlers; } else { graph->handlers = NULL; graph->handlers_num = 0; } /* JIT inlines are indexed by spesh graph inline index */ if (sg->num_inlines > 0) { MVM_VECTOR_INIT(graph->inlines, sg->num_inlines); graph->inlines_num = sg->num_inlines; } else { graph->inlines = NULL; graph->inlines_num = 0; } /* Add start-of-graph label */ jg_append_label(tc, graph, MVM_jit_label_before_graph(tc, graph, sg)); /* Loop over basic blocks */ while (iter.bb) { if (!consume_bb(tc, graph, &iter, iter.bb)) goto bail; MVM_spesh_iterator_next_bb(tc, &iter); } /* Check if we've added a instruction at all */ if (!graph->first_node) goto bail; /* append the end-of-graph label */ jg_append_label(tc, graph, MVM_jit_label_after_graph(tc, graph, sg)); /* Calculate number of basic block + graph labels */ graph->num_labels = graph->obj_label_ofs + graph->obj_labels_num; return graph; bail: MVM_jit_graph_destroy(tc, graph); return NULL; } void MVM_jit_graph_destroy(MVMThreadContext *tc, MVMJitGraph *graph) { MVMJitNode *node; /* destroy all trees */ for (node = graph->first_node; node != NULL; node = node->next) { if (node->type == MVM_JIT_NODE_EXPR_TREE) { MVM_jit_expr_tree_destroy(tc, node->u.tree); } } MVM_free(graph->label_nodes); MVM_free(graph->obj_labels); MVM_free(graph->deopts); MVM_free(graph->handlers); MVM_free(graph->inlines); }