From 711f80e8dcac83d08c9a83ce7b8c3a1e8589779c Mon Sep 17 00:00:00 2001 From: ncannasse Date: Sun, 15 Mar 2026 11:19:05 +0100 Subject: [PATCH 1/4] finished first ir emit pass --- src/emit.c | 1216 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1216 insertions(+) create mode 100644 src/emit.c diff --git a/src/emit.c b/src/emit.c new file mode 100644 index 000000000..faa1a1d3d --- /dev/null +++ b/src/emit.c @@ -0,0 +1,1216 @@ +/* + * Copyright (C)2015-2016 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include + +typedef enum { + LOAD, + STORE, + LEA, + TEST, + CMP, + JCOND, + JUMP, + JUMP_TABLE, + BINOP, + UNOP, + CONV, + RET, + ALLOC_STACK, + FREE_STACK, + ALLOC_GLOBAL_STACK, + NATIVE_REG, + PREFETCH, +} emit_op; + +typedef enum { + REG_RBP, +} native_reg; + +typedef enum { + I8, + I16, + I32, + I64, + F32, + F64 +} emit_mode; + +typedef struct { + int id; + hl_type *t; +} vreg; + +typedef struct { + int index; +} ereg; + +typedef struct { + emit_op op; + emit_mode mode; + short offset_param; + union { + struct { + ereg a; + ereg b; + } args; + uint64 value; + }; +} einstr; + +#define MAX_TMP_ARGS 32 +#define MAX_TRAPS 32 + +typedef struct { + hl_module *mod; + hl_function *fun; + einstr *instrs; + vreg *vregs; + int max_instrs; + int max_regs; + int emit_pos; + int op_pos; + + ereg tmp_args[MAX_TMP_ARGS]; + ereg traps[MAX_TRAPS]; + int *pos_map; + int pos_map_size; + int trap_count; + + int *jump_regs; + int jump_count; + int max_jumps; + + void *closure_list; // TODO : patch with good addresses +} emit_ctx; + +#define R(i) (ctx->vregs + (i)) + +#define emit_error(msg) _emit_error(msg,__LINE__) +#define emit_assert() emit_error("") + +#define LOAD(r) emit_load_reg(ctx, r) +#define STORE(r, v) emit_store_reg(ctx, r, v) +#define LOAD_CONST(v, t) emit_load_const(ctx, (uint64)v, t) +#define LOAD_CONST_PTR(v) LOAD_CONST(v,&hlt_bytes) +#define LOAD_MEM(v, offs, t) emit_load_mem(ctx, v, offs, t) +#define LOAD_MEM_PTR(v, offs) LOAD_MEM(v, offs, &hlt_bytes) +#define STORE_MEM(to, offs, v) emit_gen(ctx, STORE, to, v, offs) +#define LOAD_OBJ_METHOD(obj,id) LOAD_MEM_PTR(LOAD_MEM_PTR(LOAD_MEM_PTR((obj),0),HL_WSIZE*2),HL_WSIZE*(id)) +#define OFFSET(base,index,mult,offset) emit_gen(ctx, LEA, base, index, (mult) | ((offset) << 8)) + +#define CUR_REG() __current_reg(ctx) + +#define HDYN_VALUE 8 + +#define IS_FLOAT(t) (t->kind == HF64 || t->kind == HF32) + +static hl_type hlt_ui8 = { HUI8, 0 }; +static hl_type hlt_ui16 = { HUI16, 0 }; +static ereg ENULL = {-1}; + +static void _emit_error( const char *msg, int line ) { + printf("*** EMIT ERROR line %d (%s) ****\n", line, msg); + hl_debug_break(); + exit(-1); +} + +static void hl_stub_null_field_access() { emit_assert(); } +static void hl_stub_null_access() { emit_assert(); } +static void hl_stub_assert() { emit_assert(); } + +static ereg __current_reg( emit_ctx *ctx ) { + ereg r = {ctx->emit_pos-1}; + return r; +} + +static einstr *emit_instr( emit_ctx *ctx ) { + if( ctx->emit_pos == ctx->max_instrs ) { + int next_size = ctx->max_instrs ? (ctx->max_instrs * 3) >> 1 : 256; + einstr *instrs = (einstr*)malloc(sizeof(einstr) * next_size); + if( instrs == NULL ) emit_error("Out of memory"); + memcpy(instrs, ctx->instrs, ctx->emit_pos * sizeof(einstr)); + free(ctx->instrs); + ctx->instrs = instrs; + ctx->max_instrs = next_size; + } + return ctx->instrs + ctx->emit_pos++; +} + +static ereg emit_gen( emit_ctx *ctx, emit_op op, ereg a, ereg b, int mode ) { + einstr *e = emit_instr(ctx); + if( (short)mode != mode ) emit_assert(); + e->op = op; + e->mode = mode; + e->args.a = a; + e->args.b = b; + return CUR_REG(); +} + +static int emit_jump( emit_ctx *ctx, bool cond ) { + int p = ctx->emit_pos; + emit_gen(ctx, cond ? JCOND : JUMP, ENULL, ENULL, 0); + return p; +} + +static void patch_jump( emit_ctx *ctx, int jpos ) { + ctx->instrs[jpos].value = ctx->emit_pos; +} + +static void register_jump( emit_ctx *ctx, int jpos, int offs ) { + if( ctx->jump_count == ctx->max_jumps ) { + int next_size = ctx->max_jumps ? ctx->max_jumps << 1 : 64; + int *jumps = (int*)malloc(sizeof(int) * next_size); + if( jumps == NULL ) emit_error("Out of memory"); + memcpy(jumps, ctx->jump_regs, ctx->jump_count * sizeof(int)); + free(ctx->jump_regs); + ctx->jump_regs = jumps; + ctx->max_jumps = next_size; + } + ctx->jump_regs[ctx->jump_count++] = jpos; + ctx->jump_regs[ctx->jump_count++] = offs + ctx->op_pos + 1; +} + +static ereg emit_load_reg( emit_ctx *ctx, vreg *r ) { + return ENULL; +} + +static ereg emit_load_const( emit_ctx *ctx, uint64 value, hl_type *size_t ) { + return ENULL; +} + +static ereg emit_load_mem( emit_ctx *ctx, ereg v, int offset, hl_type *size_t ) { + return ENULL; +} + +static void emit_store_reg( emit_ctx *ctx, vreg *to, ereg v ) { + if( to->t->kind == HVOID ) return; +} + +static ereg emit_binop( emit_ctx *ctx, hl_op op, ereg a, ereg b ) { + return ENULL; +} + +static ereg emit_unop( emit_ctx *ctx, hl_op op, ereg v ) { + return ENULL; +} + +static ereg emit_native_call( emit_ctx *ctx, void *native_ptr, ereg args[], int nargs, hl_type *ret ) { + return ENULL; +} + +static ereg emit_dyn_call( emit_ctx *ctx, ereg f, ereg args[], int nargs, hl_type *ret ) { + return ENULL; +} + +static ereg emit_dyn_cast( emit_ctx *ctx, ereg v, hl_type *t ) { + return ENULL; +} + +static void emit_call_fun( emit_ctx *ctx, vreg *dst, int findex, int count, int *args ) { + emit_error("TODO"); +} + +static vclosure *alloc_static_closure( emit_ctx *ctx, int fid ) { + hl_module *m = ctx->mod; + vclosure *c = hl_malloc(&m->ctx.alloc,sizeof(vclosure)); + int fidx = m->functions_indexes[fid]; + c->hasValue = 0; + if( fidx >= m->code->nfunctions ) { + // native + c->t = m->code->natives[fidx - m->code->nfunctions].t; + c->fun = m->functions_ptrs[fid]; + c->value = NULL; + } else { + c->t = m->code->functions[fidx].type; + c->fun = (void*)(int_val)fid; + c->value = ctx->closure_list; + ctx->closure_list = c; + } + return c; +} + +static void *get_dynget( hl_type *t ) { + switch( t->kind ) { + case HF32: + return hl_dyn_getf; + case HF64: + return hl_dyn_getd; + case HI64: + case HGUID: + return hl_dyn_geti64; + case HI32: + case HUI16: + case HUI8: + case HBOOL: + return hl_dyn_geti; + default: + return hl_dyn_getp; + } +} + +static void *get_dynset( hl_type *t ) { + switch( t->kind ) { + case HF32: + return hl_dyn_setf; + case HF64: + return hl_dyn_setd; + case HI64: + case HGUID: + return hl_dyn_seti64; + case HI32: + case HUI16: + case HUI8: + case HBOOL: + return hl_dyn_seti; + default: + return hl_dyn_setp; + } +} + +static void emit_store_size( emit_ctx *ctx, ereg dst, int dst_offset, ereg src, int src_offset, int total_size ) { + int offset = 0; + while( offset < total_size) { + int remain = total_size - offset; + hl_type *ct = remain >= HL_WSIZE ? &hlt_bytes : (remain >= 4 ? &hlt_i32 : &hlt_ui8); + STORE_MEM(dst, dst_offset+offset, LOAD_MEM(src,src_offset+offset,ct)); + offset += hl_type_size(ct); + } +} + +static ereg *get_tmp_args( emit_ctx *ctx, int count ) { + if( count > MAX_TMP_ARGS ) emit_error("Too many arguments"); + return ctx->tmp_args; +} + +static ereg emit_phy( emit_ctx *ctx, ereg v1, ereg v2 ) { + return ENULL; +} + +static ereg emit_conv( emit_ctx *ctx, ereg v, hl_op op ) { + return ENULL; +} + +static bool dyn_need_type( hl_type *t ) { + return !(IS_FLOAT(t) || t->kind == HI64 || t->kind == HGUID); +} + +static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { + vreg *dst = R(o->p1); + vreg *ra = R(o->p2); + vreg *rb = R(o->p3); + hl_module *m = ctx->mod; + switch( o->op ) { + case OMov: + case OUnsafeCast: + STORE(dst, LOAD(ra)); + break; + case OInt: + STORE(dst, LOAD_CONST(m->code->ints[o->p2], dst->t)); + break; + case OBool: + STORE(dst, LOAD_CONST(o->p2, &hlt_bool)); + break; + case ONull: + STORE(dst, LOAD_CONST(0, dst->t)); + break; + case OFloat: + { + union { + float f; + double d; + uint64 i; + } v; + if( dst->t->kind == HF32 ) + v.f = (float)m->code->floats[o->p2]; + else + v.d = m->code->floats[o->p2]; + STORE(dst, LOAD_CONST(v.i, dst->t)); + } + break; + case OString: + STORE(dst, LOAD_CONST_PTR(hl_get_ustring(m->code,o->p2))); + break; + case OBytes: + { + char *b = m->code->version >= 5 ? m->code->bytes + m->code->bytes_pos[o->p2] : m->code->strings[o->p2]; + STORE(dst,LOAD_CONST_PTR(b)); + } + break; + case OGetGlobal: + { + void *addr = m->globals_data + m->globals_indexes[o->p2]; + STORE(dst, LOAD_MEM_PTR(LOAD_CONST_PTR(addr),0)); + } + break; + case OSetGlobal: + { + void *addr = m->globals_data + m->globals_indexes[o->p1]; + STORE_MEM(LOAD_CONST_PTR(addr),0,LOAD(ra)); + } + break; + case OCall0: + emit_call_fun(ctx, dst, o->p2, 0, NULL); + break; + case OCall1: + emit_call_fun(ctx, dst, o->p2, 1, &o->p3); + break; + case OCall2: + { + int args[2] = { o->p3, (int)(int_val)o->extra }; + emit_call_fun(ctx, dst, o->p2, 2, args); + } + break; + case OCall3: + { + int args[3] = { o->p3, o->extra[0], o->extra[1] }; + emit_call_fun(ctx, dst, o->p2, 3, args); + } + break; + case OCall4: + { + int args[4] = { o->p3, o->extra[0], o->extra[1], o->extra[2] }; + emit_call_fun(ctx, dst, o->p2, 4, args); + } + break; + case OCallN: + emit_call_fun(ctx, dst, o->p2, o->p3, o->extra); + break; + case OSub: + case OAdd: + case OMul: + case OSDiv: + case OUDiv: + case OShl: + case OSShr: + case OUShr: + case OAnd: + case OOr: + case OXor: + case OSMod: + case OUMod: + { + ereg va = LOAD(ra); + ereg vb = LOAD(rb); + STORE(dst, emit_gen(ctx, BINOP, va, vb, o->op)); + } + break; + case ONeg: + case ONot: + STORE(dst, emit_gen(ctx, UNOP, LOAD(ra), ENULL, o->op)); + break; + case OJFalse: + case OJTrue: + case OJNotNull: + case OJNull: + { + emit_gen(ctx, TEST, LOAD(dst), ENULL, o->op); + int jidx = emit_jump(ctx, true); + register_jump(ctx, jidx, o->p2); + } + break; + case OJEq: + case OJNotEq: + case OJSLt: + case OJSGte: + case OJSLte: + case OJSGt: + case OJULt: + case OJUGte: + case OJNotLt: + case OJNotGte: + { + emit_gen(ctx, CMP, LOAD(dst), LOAD(ra), o->op); + int jidx = emit_jump(ctx, true); + register_jump(ctx, jidx, o->p3); + } + break; + case OJAlways: + { + int jidx = emit_jump(ctx, false); + register_jump(ctx, jidx, o->p1); + } + break; + case OToDyn: + if( ra->t->kind == HBOOL ) { + ereg arg = LOAD(ra); + STORE(dst, emit_native_call(ctx,hl_alloc_dynbool,&arg,1,&hlt_dyn)); + } else { + ereg arg = LOAD_CONST_PTR(ra->t); + ereg ret = emit_native_call(ctx,hl_alloc_dynamic,&arg,1,&hlt_dyn); + STORE_MEM(ret,HDYN_VALUE,LOAD(ra)); + } + break; + case OToSFloat: + case OToInt: + case OToUFloat: + STORE(dst, emit_conv(ctx,LOAD(ra),o->op)); + break; + case ORet: + emit_gen(ctx, RET, LOAD(dst), ENULL, 0); + break; + case OIncr: + case ODecr: + { + if( IS_FLOAT(dst->t) ) { + emit_assert(); + } else { + STORE(dst, emit_unop(ctx,o->op, LOAD(dst))); + } + } + break; + case ONew: + { + ereg arg = ENULL; + void *allocFun = NULL; + int nargs = 1; + switch( dst->t->kind ) { + case HOBJ: + case HSTRUCT: + allocFun = hl_alloc_obj; + break; + case HDYNOBJ: + allocFun = hl_alloc_dynobj; + nargs = 0; + break; + case HVIRTUAL: + allocFun = hl_alloc_virtual; + break; + default: + emit_assert(); + } + if( nargs ) arg = LOAD_CONST_PTR(dst->t); + STORE(dst, emit_native_call(ctx,allocFun,&arg,nargs,dst->t)); + } + break; + case OInstanceClosure: + { + ereg args[3]; + args[0] = LOAD_CONST_PTR(m->code->functions[m->functions_indexes[o->p2]].type); + // WRITE (emit_pos + op_count) to process later and replace address ! + args[1] = LOAD_CONST_PTR(0); + args[2] = LOAD(rb); + STORE(dst, emit_native_call(ctx,hl_alloc_closure_ptr,args,3,dst->t)); + } + break; + case OVirtualClosure: + { + hl_type *t = NULL; + hl_type *ot = ra->t; + while( t == NULL ) { + int i; + for(i=0;iobj->nproto;i++) { + hl_obj_proto *pp = ot->obj->proto + i; + if( pp->pindex == o->p3 ) { + t = m->code->functions[m->functions_indexes[pp->findex]].type; + break; + } + } + ot = ot->obj->super; + } + ereg args[3]; + ereg obj = LOAD(ra); + args[0] = LOAD_CONST_PTR(t); + args[1] = LOAD_OBJ_METHOD(obj,o->p3); + args[2] = obj; + STORE(dst, emit_native_call(ctx,hl_alloc_closure_ptr,args,3,dst->t)); + } + break; + case OCallClosure: + if( ra->t->kind == HDYN ) { + int i; + ereg st = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, o->p3); + for(i=0;ip3;i++) { + vreg *r = R(o->extra[i]); + if( !hl_is_dynamic(r->t) ) emit_assert(); + STORE_MEM(st,i*HL_WSIZE,LOAD(r)); + } + ereg args[3]; + args[0] = LOAD(ra); + args[1] = st; + args[2] = LOAD_CONST(o->p3,&hlt_i32); + STORE(dst, emit_dyn_cast(ctx,emit_native_call(ctx,hl_dyn_call,args,3,dst->t),dst->t)); + emit_gen(ctx, FREE_STACK, ENULL, ENULL, o->p3); + } else { + ereg r = LOAD(ra); + ereg *args = get_tmp_args(ctx,o->p3+1); + // Code for if( c->hasValue ) c->fun(c->value,args) else c->fun(args) + ereg has = LOAD_MEM(r,HL_WSIZE*2,&hlt_i32); + emit_gen(ctx, TEST, has, ENULL, OJNull); + int jidx = emit_jump(ctx, true); + int i; + args[0] = LOAD_MEM_PTR(r,HL_WSIZE * 3); + for(i=0;ip3;i++) + args[i+1] = LOAD(R(o->extra[i])); + ereg v1 = emit_dyn_call(ctx,LOAD_MEM_PTR(r,HL_WSIZE),args,o->p3 + 1,dst->t); + int jend = emit_jump(ctx, false); + patch_jump(ctx, jidx); + for(i=0;ip3;i++) + args[i] = LOAD(R(o->extra[i])); + ereg v2 = emit_dyn_call(ctx,LOAD_MEM_PTR(r,HL_WSIZE),args,o->p3,dst->t); + patch_jump(ctx, jend); + STORE(dst, emit_phy(ctx,v1,v2)); + } + break; + case OStaticClosure: + { + vclosure *c = alloc_static_closure(ctx,o->p2); + STORE(dst, LOAD_CONST_PTR(c)); + } + break; + case OField: + { + switch( ra->t->kind ) { + case HOBJ: + case HSTRUCT: + { + hl_runtime_obj *rt = hl_get_obj_rt(ra->t); + ereg r = LOAD(ra); + if( dst->t->kind == HSTRUCT ) { + hl_type *ft = hl_obj_field_fetch(ra->t,o->p3)->t; + if( ft->kind == HPACKED ) { + STORE(dst,emit_gen(ctx, LEA, r, ENULL, rt->fields_indexes[o->p3])); + break; + } + } + STORE(dst, LOAD_MEM(r,rt->fields_indexes[o->p3],dst->t)); + } + break; + case HVIRTUAL: + // code for : if( hl_vfields(o)[f] ) r = *hl_vfields(o)[f]; else r = hl_dyn_get(o,hash(field),vt) + { + ereg obj = LOAD(ra); + ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p3); + emit_gen(ctx, TEST, field, ENULL, OJNull); + int jidx = emit_jump(ctx, true); + ereg v1 = LOAD_MEM(field,0,dst->t); + int jend = emit_jump(ctx, false); + patch_jump(ctx, jidx); + bool need_type = dyn_need_type(dst->t); + ereg args[3]; + args[0] = obj; + args[1] = LOAD_CONST(ra->t->virt->fields[o->p3].hashed_name,&hlt_i32); + if( need_type ) args[2] = LOAD_CONST_PTR(dst->t); + ereg v2 = emit_native_call(ctx,get_dynget(dst->t),args,need_type?3:2,dst->t); + patch_jump(ctx, jend); + STORE(dst, emit_phy(ctx, v1, v2)); + } + break; + default: + emit_assert(); + break; + } + } + break; + case OSetField: + { + switch( dst->t->kind ) { + case HOBJ: + case HSTRUCT: + { + ereg obj = LOAD(dst); + ereg val = LOAD(rb); + hl_runtime_obj *rt = hl_get_obj_rt(dst->t); + int field_pos = rt->fields_indexes[o->p2]; + if( rb->t->kind == HSTRUCT ) { + hl_type *ft = hl_obj_field_fetch(dst->t,o->p2)->t; + if( ft->kind == HPACKED ) { + emit_store_size(ctx,obj,field_pos,val,0,hl_get_obj_rt(ft->tparam)->size); + break; + } + } + STORE_MEM(obj,field_pos, val); + } + break; + case HVIRTUAL: + // code for : if( hl_vfields(o)[f] ) *hl_vfields(o)[f] = v; else hl_dyn_set(o,hash(field),vt,v) + { + ereg obj = LOAD(ra); + ereg val = LOAD(rb); + ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p3); + emit_gen(ctx, TEST, field, ENULL, OJNull); + int jidx = emit_jump(ctx, true); + STORE_MEM(field, 0, val); + int jend = emit_jump(ctx, false); + patch_jump(ctx, jidx); + bool need_type = dyn_need_type(dst->t); + ereg args[4]; + args[0] = obj; + args[1] = LOAD_CONST(ra->t->virt->fields[o->p3].hashed_name,&hlt_i32); + if( need_type ) { + args[2] = LOAD_CONST_PTR(rb->t); + args[3] = val; + } else { + args[2] = val; + } + emit_native_call(ctx,get_dynset(dst->t),args,need_type?4:3,dst->t); + patch_jump(ctx, jend); + } + break; + default: + emit_assert(); + break; + } + } + break; + case OGetThis: + { + vreg *r = R(0); + ereg obj = LOAD(r); + hl_runtime_obj *rt = hl_get_obj_rt(r->t); + int field_pos = rt->fields_indexes[o->p2]; + if( dst->t->kind == HSTRUCT ) { + hl_type *ft = hl_obj_field_fetch(r->t,o->p2)->t; + if( ft->kind == HPACKED ) { + STORE(dst, emit_gen(ctx, LEA, obj, ENULL, field_pos)); + break; + } + } + STORE(dst, LOAD_MEM(obj, field_pos, dst->t)); + } + break; + case OSetThis: + { + vreg *r = R(0); + ereg obj = LOAD(r); + ereg val = LOAD(ra); + hl_runtime_obj *rt = hl_get_obj_rt(r->t); + int field_pos = rt->fields_indexes[o->p1]; + if( ra->t->kind == HSTRUCT ) { + hl_type *ft = hl_obj_field_fetch(r->t,o->p1)->t; + if( ft->kind == HPACKED ) { + emit_store_size(ctx, obj, field_pos, val, 0, hl_get_obj_rt(ft->tparam)->size); + break; + } + } + STORE_MEM(obj,field_pos,val); + } + break; + case OCallThis: + { + int i; + int nargs = o->p3 + 1; + ereg obj = LOAD(R(0)); + ereg *args = get_tmp_args(ctx, nargs); + args[0] = obj; + for(i=1;iextra[i-1])); + ereg fun = LOAD_OBJ_METHOD(obj, o->p2); + STORE(dst, emit_dyn_call(ctx,fun,args,nargs,dst->t)); + } + break; + case OCallMethod: + { + vreg *r = R(o->extra[0]); + ereg obj = LOAD(r); + switch( r->t->kind ) { + case HOBJ: + { + int i; + int nargs = o->p3; + ereg *args = get_tmp_args(ctx, nargs); + for(i=0;iextra[i])); + ereg fun = LOAD_OBJ_METHOD(obj, o->p2); + STORE(dst, emit_dyn_call(ctx,fun,args,nargs,dst->t)); + } + break; + case HVIRTUAL: + // code for : if( hl_vfields(o)[f] ) dst = *hl_vfields(o)[f](o->value,args...); else dst = hl_dyn_call_obj(o->value,field,args,&ret) + { + vreg *_o = R(o->extra[0]); + ereg obj = LOAD(_o); + ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p2); + emit_gen(ctx, TEST, field, ENULL, OJNull); + int jidx = emit_jump(ctx, true); + + int nargs = o->p3 + 1; + ereg *args = get_tmp_args(ctx, nargs); + int i; + args[0] = LOAD_MEM_PTR(obj,HL_WSIZE); + for(i=1;iextra[i])); + ereg v1 = emit_dyn_call(ctx,LOAD_MEM_PTR(field,0),args,nargs,dst->t); + + int jend = emit_jump(ctx, false); + patch_jump(ctx, jidx); + + ereg eargs = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, o->p3); + for(i=0;ip3;i++) + STORE_MEM(eargs,i*HL_WSIZE,LOAD(R(o->extra[i+1]))); + bool need_dyn = !hl_is_ptr(dst->t) && dst->t->kind != HVOID; + int dyn_size = sizeof(vdynamic)/HL_WSIZE; + ereg edyn = need_dyn ? emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, dyn_size) : LOAD_CONST_PTR(NULL); + + args = get_tmp_args(ctx, 4); + args[0] = LOAD_MEM_PTR(obj,HL_WSIZE); + args[1] = LOAD_CONST(_o->t->virt->fields[o->p2].hashed_name,&hlt_i32); + args[2] = eargs; + args[3] = edyn; + + ereg v2 = emit_native_call(ctx, hl_dyn_call_obj, args, 4, dst->t); + + emit_gen(ctx, FREE_STACK, ENULL, ENULL, o->p3 + (need_dyn ? dyn_size : 0)); + patch_jump(ctx, jend); + + STORE(dst, emit_phy(ctx, v1, v2)); + } + break; + default: + emit_assert(); + break; + } + } + break; + case OThrow: + case ORethrow: + { + ereg arg = LOAD(dst); + emit_native_call(ctx, o->op == OThrow ? hl_throw : hl_rethrow, &arg, 1, &hlt_void); + } + break; + case OLabel: + // NOP + break; + case OGetI8: + case OGetI16: + case OGetMem: + { + ereg offs = OFFSET(LOAD(ra),LOAD(rb),1,0); + ereg val = LOAD_MEM(offs, 0, dst->t); + if( o->op != OGetMem ) val = emit_conv(ctx, val, o->op); + STORE(dst, val); + } + break; + case OSetI8: + case OSetI16: + case OSetMem: + { + ereg offs = OFFSET(LOAD(dst), LOAD(ra),1,0); + ereg val = LOAD(rb); + if( o->op != OSetMem ) val = emit_conv(ctx, val, o->op); + STORE_MEM(offs, 0, val); + } + break; + case OType: + STORE(dst, LOAD_CONST_PTR(m->code->types + o->p2)); + break; + case OGetType: + { + ereg r = LOAD(ra); + emit_gen(ctx, TEST, r, ENULL, OJNotNull); + int jidx = emit_jump(ctx, true); + ereg v1 = LOAD_CONST_PTR(&hlt_void); + int jend = emit_jump(ctx, false); + patch_jump(ctx, jidx); + ereg v2 = LOAD_MEM_PTR(r,0); + patch_jump(ctx, jend); + STORE(dst, emit_phy(ctx, v1, v2)); + } + break; + case OGetArray: + { + if( ra->t->kind == HABSTRACT ) { + int osize; + bool isPtr = dst->t->kind != HOBJ && dst->t->kind != HSTRUCT; + if( isPtr ) + osize = HL_WSIZE; // a pointer into the carray + else { + hl_runtime_obj *rt = hl_get_obj_rt(dst->t); + osize = rt->size; // a mem offset into it + } + ereg pos = OFFSET(LOAD(ra), LOAD(rb), osize, 0); + ereg val = isPtr ? LOAD_MEM_PTR(pos,0) : pos; + STORE(dst, val); + } else { + ereg pos = OFFSET(LOAD(ra), LOAD(rb), hl_type_size(dst->t), sizeof(varray)); + STORE(dst, LOAD_MEM_PTR(pos,0)); + } + } + break; + case OSetArray: + { + if( dst->t->kind == HABSTRACT ) { + int osize; + bool isPtr = rb->t->kind != HOBJ && rb->t->kind != HSTRUCT; + if( isPtr) { + osize = HL_WSIZE; + } else { + hl_runtime_obj *rt = hl_get_obj_rt(rb->t); + osize = rt->size; + } + ereg pos = OFFSET(LOAD(dst), LOAD(ra), osize, 0); + emit_store_size(ctx, pos, 0, LOAD(rb), 0, osize); + } else { + ereg pos = OFFSET(LOAD(dst), LOAD(ra), hl_type_size(dst->t), sizeof(varray)); + STORE_MEM(pos, 0, LOAD(rb)); + } + } + break; + case OArraySize: + STORE(dst, LOAD_MEM_PTR(LOAD(ra),HL_WSIZE*2)); + break; + case ORef: + { + ereg addr = emit_gen(ctx, ALLOC_GLOBAL_STACK, ENULL, ENULL, hl_type_size(ra->t)); + STORE_MEM(addr, 0, LOAD(ra)); + STORE(dst, addr); + } + break; + case OUnref: + STORE(dst, LOAD_MEM(LOAD(ra),0,dst->t)); + break; + case OSetref: + STORE_MEM(LOAD(dst),0,LOAD(ra)); + break; + case ORefData: + switch( ra->t->kind ) { + case HARRAY: + STORE(dst, OFFSET(LOAD(ra),ENULL,0,sizeof(varray))); + break; + default: + emit_assert(); + } + break; + case ORefOffset: + STORE(dst, OFFSET(LOAD(ra),LOAD(rb), hl_type_size(dst->t->tparam),0)); + break; + case OToVirtual: + { + ereg args[2]; + args[0] = LOAD(ra); + args[1] = LOAD_CONST_PTR(dst->t); + STORE(dst, emit_native_call(ctx,hl_to_virtual,args,2, dst->t)); + } + break; + case OMakeEnum: + { + ereg args[2]; + args[0] = LOAD_CONST_PTR(dst->t); + args[1] = LOAD_CONST(o->p2,&hlt_i32); + ereg en = emit_native_call(ctx, hl_alloc_enum, args, 2, dst->t); + STORE(dst, en); + hl_enum_construct *c = &dst->t->tenum->constructs[o->p2]; + for(int i=0;inparams;i++) + STORE_MEM(en, c->offsets[i], LOAD(R(o->extra[i]))); + } + break; + case OEnumAlloc: + { + ereg args[2]; + args[0] = LOAD_CONST_PTR(dst->t); + args[1] = LOAD_CONST(o->p2,&hlt_i32); + STORE(dst, emit_native_call(ctx, hl_alloc_enum, args, 2, dst->t)); + } + break; + case OEnumField: + { + hl_enum_construct *c = &ra->t->tenum->constructs[o->p3]; + int slot = (int)(int_val)o->extra; + STORE(dst, LOAD_MEM(LOAD(ra),c->offsets[slot], dst->t)); + } + break; + case OEnumIndex: + STORE(dst, LOAD_MEM(LOAD(ra),HL_WSIZE,dst->t)); + break; + case OSetEnumField: + { + hl_enum_construct *c = &ra->t->tenum->constructs[0]; + STORE_MEM(LOAD(dst), c->offsets[o->p2], LOAD(rb)); + } + break; + case ONullCheck: + { + emit_gen(ctx, TEST, LOAD(dst), ENULL, OJNotNull); + int jok = emit_jump(ctx, true); + + // ----- DETECT FIELD ACCESS ---------------- + hl_function *f = ctx->fun; + hl_opcode *next = f->ops + ctx->op_pos + 1; + bool null_field_access = false; + int hashed_name = 0; + // skip const and operation between nullcheck and access + while( (next < f->ops + f->nops - 1) && (next->op >= OInt && next->op <= ODecr) ) { + next++; + } + if( (next->op == OField && next->p2 == o->p1) || (next->op == OSetField && next->p1 == o->p1) ) { + int fid = next->op == OField ? next->p3 : next->p2; + hl_obj_field *f = NULL; + if( dst->t->kind == HOBJ || dst->t->kind == HSTRUCT ) + f = hl_obj_field_fetch(dst->t, fid); + else if( dst->t->kind == HVIRTUAL ) + f = dst->t->virt->fields + fid; + if( f == NULL ) emit_assert(); + null_field_access = true; + hashed_name = f->hashed_name; + } else if( (next->op >= OCall1 && next->op <= OCallN) && next->p3 == o->p1 ) { + int fid = next->p2 < 0 ? -1 : m->functions_indexes[next->p2]; + hl_function *cf = m->code->functions + fid; + const uchar *name = fun_field_name(cf); + null_field_access = true; + hashed_name = hl_hash_gen(name, true); + } + // ----------------------------------------- + ereg arg = null_field_access ? LOAD_CONST(hashed_name,&hlt_i32) : ENULL; + emit_native_call(ctx, null_field_access ? hl_stub_null_field_access : hl_stub_null_access, &arg, null_field_access ? 1 : 0, &hlt_void); + patch_jump(ctx, jok); + } + break; + case OSafeCast: + STORE(dst, emit_dyn_cast(ctx, LOAD(ra), dst->t)); + break; + case ODynGet: + { + bool need_type = dyn_need_type(dst->t); + ereg args[3]; + args[0] = LOAD(ra); + args[1] = LOAD_CONST(hl_hash_utf8(m->code->strings[o->p3]),&hlt_i32); + if( need_type ) args[2] = LOAD_CONST_PTR(dst->t); + STORE(dst, emit_native_call(ctx, get_dynget(dst->t), args, need_type ? 3 : 2, dst->t)); + } + break; + case ODynSet: + { + bool need_type = dyn_need_type(dst->t); + ereg args[4]; + args[0] = LOAD(dst); + args[1] = LOAD_CONST(hl_hash_utf8(m->code->strings[o->p2]),&hlt_i32); + if( need_type ) { + args[2] = LOAD_CONST_PTR(rb->t); + args[3] = LOAD(rb); + } else + args[2] = LOAD(rb); + emit_native_call(ctx, get_dynset(rb->t), args, need_type ? 4 : 3, &hlt_void); + } + break; + case OTrap: + { + ereg st = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, sizeof(hl_trap_ctx)); + + ereg thread, current_addr; + static hl_thread_info *tinf = NULL; + static hl_trap_ctx *trap = NULL; +# ifndef HL_THREADS + if( tinf == NULL ) tinf = hl_get_thread(); + current_addr = LOAD_CONST_PTR(&tinf->trap_current); +# else + thread = emit_native_call(ctx, hl_get_thread, NULL, 0, &hlt_bytes); + current_addr = OFFSET(thread, ENULL, 0, (int)(int_val)&tinf->trap_current); +# endif + STORE_MEM(st, (int)(int_val)&trap->prev, LOAD_MEM_PTR(current_addr,0)); + STORE_MEM(current_addr, 0, st); + + + /* + trap E,@catch + catch g + catch g2 + ... + @:catch + + // Before haxe 5 + This is a bit hackshish : we want to detect the type of exception filtered by the catch so we check the following + sequence of HL opcodes: + + trap E,@catch + ... + @catch: + global R, _ + call _, ???(R,E) + + ??? is expected to be hl.BaseType.check + */ + hl_function *f = ctx->fun; + hl_opcode *cat = f->ops + ctx->op_pos + 1; + hl_opcode *next = f->ops + ctx->op_pos + 1 + o->p2; + hl_opcode *next2 = f->ops + ctx->op_pos + 2 + o->p2; + void *addr = NULL; + if( cat->op == OCatch || (next->op == OGetGlobal && next2->op == OCall2 && next2->p3 == next->p1 && dst->id == (int)(int_val)next2->extra) ) { + int gindex = cat->op == OCatch ? cat->p1 : next->p2; + hl_type *gt = m->code->globals[gindex]; + while( gt->kind == HOBJ && gt->obj->super ) gt = gt->obj->super; + if( gt->kind == HOBJ && gt->obj->nfields && gt->obj->fields[0].t->kind == HTYPE ) + addr = m->globals_data + m->globals_indexes[gindex]; + } + STORE_MEM(st, (int)(int_val)&trap->tcheck, addr ? LOAD_MEM_PTR(LOAD_CONST_PTR(addr),0) : LOAD_CONST_PTR(NULL)); + + void *fun = setjmp; + ereg args[2]; + int nargs = 1; + args[0] = OFFSET(st, ENULL, 0, (int)(int_val)&trap->buf); +#if defined(HL_WIN) && defined(HL_64) + // On Win64 setjmp actually takes two arguments + // the jump buffer and the frame pointer (or the stack pointer if there is no FP) + nargs = 2; + args[1] = emit_gen(ctx, NATIVE_REG, ENULL, ENULL, REG_RBP); +#endif +#ifdef HL_MINGW + fun = _setjmp; +#endif + ereg ret = emit_native_call(ctx, fun, args, nargs, &hlt_i32); + emit_gen(ctx, TEST, ret, ENULL, OJNull); + int jskip = emit_jump(ctx, true); + emit_gen(ctx, FREE_STACK, ENULL, ENULL, sizeof(hl_trap_ctx)); + STORE(dst, tinf ? LOAD_CONST_PTR(&tinf->exc_value) : LOAD_MEM_PTR(thread,(int)(int_val)&tinf->exc_value)); + + int jtrap = emit_jump(ctx, false); + register_jump(ctx, jtrap, o->p2); + patch_jump(ctx, jskip); + + if( ctx->trap_count == MAX_TRAPS ) emit_error("Too many try/catch depth"); + ctx->traps[ctx->trap_count++] = st; + } + break; + case OEndTrap: + { + if( ctx->trap_count == 0 ) emit_assert(); + ereg st = ctx->traps[--ctx->trap_count]; + + ereg thread, current_addr; + static hl_thread_info *tinf = NULL; + static hl_trap_ctx *trap = NULL; +# ifndef HL_THREADS + if( tinf == NULL ) tinf = hl_get_thread(); + current_addr = LOAD_CONST_PTR(&tinf->trap_current); +# else + thread = emit_native_call(ctx, hl_get_thread, NULL, 0, &hlt_bytes); + current_addr = OFFSET(thread, ENULL, 0, (int)(int_val)&tinf->trap_current); +# endif + + STORE_MEM(current_addr, 0, LOAD_MEM_PTR(st,(int)(int_val)&trap->prev)); + +# ifdef HL_WIN + // erase eip (prevent false positive in exception stack) + { + _JUMP_BUFFER *b = NULL; +# ifdef HL_64 + int offset = (int)(int_val)&(b->Rip); +# else + int offset = (int)(int_val)&(b->Eip); +# endif + STORE_MEM(st, offset, LOAD_CONST_PTR(NULL)); + } +# endif + + emit_gen(ctx, FREE_STACK, ENULL, ENULL, sizeof(hl_trap_ctx)); + } + break; + case OSwitch: + { + ereg v = LOAD(dst); + int count = o->p2; + emit_gen(ctx, CMP, v, LOAD_CONST(count,&hlt_i32), OJUGte); + int jdefault = emit_jump(ctx, true); + emit_gen(ctx, JUMP_TABLE, v, ENULL, count); + for(int i=0; iextra[i]); + } + patch_jump(ctx, jdefault); + } + break; + case OGetTID: + STORE(dst, LOAD_MEM(LOAD(ra),0,&hlt_i32)); + break; + case OAssert: + emit_native_call(ctx, hl_stub_assert, NULL, 0, &hlt_void); + break; + case ONop: + break; + case OPrefetch: + { + ereg r = LOAD(dst); + if( o->p2 > 0 ) { + switch( dst->t->kind ) { + case HOBJ: + case HSTRUCT: + { + hl_runtime_obj *rt = hl_get_obj_rt(dst->t); + r = OFFSET(r, ENULL, 0, rt->fields_indexes[o->p2-1]); + } + break; + default: + emit_assert(); + break; + } + } + emit_gen(ctx, PREFETCH, r, ENULL, o->p3); + } + break; + case OAsm: + emit_assert(); + break; + case OCatch: + // Only used by OTrap typing + break; + default: + emit_error(hl_op_name(o->op)); + break; + } +} + +bool emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { + int i; + ctx->mod = m; + ctx->fun = f; + + if( f->nregs > ctx->max_regs ) { + free(ctx->vregs); + ctx->vregs = (vreg*)malloc(sizeof(vreg) * (f->nregs + 1)); + if( ctx->vregs == NULL ) + return false; + for(i=ctx->max_regs;inregs;i++) + R(i)->id = i; + ctx->max_regs = f->nregs; + } + + for(i=0;inregs;i++) { + vreg *r = R(i); + r->t = f->regs[i]; + } + + if( f->nops > ctx->pos_map_size ) { + free(ctx->pos_map); + ctx->pos_map = (int*)malloc(sizeof(int) * f->nops); + if( ctx->pos_map == NULL ) + return false; + ctx->pos_map_size = f->nops; + } + + for(int op_pos=0;op_posnops;op_pos++) { + ctx->op_pos = op_pos; + ctx->pos_map[op_pos] = ctx->emit_pos; + emit_opcode(ctx,f->ops + op_pos); + } + return true; +} + +emit_ctx *hl_emit_alloc() { + emit_ctx *ctx = (emit_ctx*)malloc(sizeof(emit_ctx)); + if( ctx == NULL ) return NULL; + memset(ctx,0,sizeof(emit_ctx)); + return ctx; +} + +void hl_emit_free( emit_ctx *ctx ) { + free(ctx->vregs); + free(ctx); +} From bf5620ba566f1b897a255bce255e934b16807b51 Mon Sep 17 00:00:00 2001 From: ncannasse Date: Sun, 15 Mar 2026 14:54:53 +0100 Subject: [PATCH 2/4] call emitter instead of jit, started ir emit & dump --- src/emit.c | 366 ++++++++++++++++++++++++++++++++++++++++++------- src/hlmodule.h | 17 +-- src/module.c | 35 ++--- src/opcodes.h | 20 +-- 4 files changed, 351 insertions(+), 87 deletions(-) diff --git a/src/emit.c b/src/emit.c index faa1a1d3d..44de4f59f 100644 --- a/src/emit.c +++ b/src/emit.c @@ -22,7 +22,9 @@ #include typedef enum { - LOAD, + LOAD_ADDR, + LOAD_IMM, + LOAD_ARG, STORE, LEA, TEST, @@ -33,40 +35,77 @@ typedef enum { BINOP, UNOP, CONV, + CONV_UNSIGNED, RET, + CALL_PTR, + CALL_REG, + CALL_FUN, + PHY, ALLOC_STACK, FREE_STACK, ALLOC_GLOBAL_STACK, NATIVE_REG, PREFETCH, + DEBUG_BREAK, } emit_op; +static const char *op_names[] = { + "load-addr", + "load-imm", + "load-arg", + "store", + "lea", + "test", + "cmp", + "jcond", + "jump", + "jump-table", + "binop", + "unop", + "conv", + "conv-unsigned", + "ret", + "call-ptr", + "call-reg", + "call-fun", + "phy", + "alloc-stack", + "free-stack", + "alloc-global-stack", + "native-reg", + "prefetch", + "debug-break", +}; + typedef enum { REG_RBP, } native_reg; typedef enum { + NONE, I8, I16, I32, I64, F32, - F64 + F64, + PTR, } emit_mode; -typedef struct { - int id; - hl_type *t; -} vreg; typedef struct { int index; } ereg; +typedef struct { + int id; + hl_type *t; + ereg current; +} vreg; + typedef struct { emit_op op; emit_mode mode; - short offset_param; union { struct { ereg a; @@ -79,7 +118,7 @@ typedef struct { #define MAX_TMP_ARGS 32 #define MAX_TRAPS 32 -typedef struct { +struct _emit_ctx { hl_module *mod; hl_function *fun; einstr *instrs; @@ -100,7 +139,7 @@ typedef struct { int max_jumps; void *closure_list; // TODO : patch with good addresses -} emit_ctx; +}; #define R(i) (ctx->vregs + (i)) @@ -113,10 +152,10 @@ typedef struct { #define LOAD_CONST_PTR(v) LOAD_CONST(v,&hlt_bytes) #define LOAD_MEM(v, offs, t) emit_load_mem(ctx, v, offs, t) #define LOAD_MEM_PTR(v, offs) LOAD_MEM(v, offs, &hlt_bytes) -#define STORE_MEM(to, offs, v) emit_gen(ctx, STORE, to, v, offs) +#define STORE_MEM(to, offs, v) emit_store_mem(ctx, to, offs, v) #define LOAD_OBJ_METHOD(obj,id) LOAD_MEM_PTR(LOAD_MEM_PTR(LOAD_MEM_PTR((obj),0),HL_WSIZE*2),HL_WSIZE*(id)) #define OFFSET(base,index,mult,offset) emit_gen(ctx, LEA, base, index, (mult) | ((offset) << 8)) - +#define BREAK() emit_gen(ctx, DEBUG_BREAK, ENULL, ENULL, 0) #define CUR_REG() __current_reg(ctx) #define HDYN_VALUE 8 @@ -137,28 +176,59 @@ static void hl_stub_null_field_access() { emit_assert(); } static void hl_stub_null_access() { emit_assert(); } static void hl_stub_assert() { emit_assert(); } +static emit_mode hl_type_mode( hl_type *t ) { + if( t->kind < HBOOL ) + return (emit_mode)t->kind; + if( t->kind == HBOOL ) + return sizeof(bool) == 1 ? I8 : I32; + if( t->kind == HGUID ) + return I64; + return PTR; +} + +void hl_jit_patch_method( void*fun, void**newt ) { + emit_assert(); +} + static ereg __current_reg( emit_ctx *ctx ) { ereg r = {ctx->emit_pos-1}; return r; } -static einstr *emit_instr( emit_ctx *ctx ) { +static ereg *get_tmp_args( emit_ctx *ctx, int count ) { + if( count > MAX_TMP_ARGS ) emit_error("Too many arguments"); + return ctx->tmp_args; +} + +static einstr *emit_instr( emit_ctx *ctx, emit_op op ) { if( ctx->emit_pos == ctx->max_instrs ) { + int pos = ctx->emit_pos; int next_size = ctx->max_instrs ? (ctx->max_instrs * 3) >> 1 : 256; einstr *instrs = (einstr*)malloc(sizeof(einstr) * next_size); if( instrs == NULL ) emit_error("Out of memory"); - memcpy(instrs, ctx->instrs, ctx->emit_pos * sizeof(einstr)); + memcpy(instrs, ctx->instrs, pos * sizeof(einstr)); + memset(instrs + pos, 0, (next_size - pos) * sizeof(einstr)); free(ctx->instrs); ctx->instrs = instrs; ctx->max_instrs = next_size; } - return ctx->instrs + ctx->emit_pos++; + einstr *e = ctx->instrs + ctx->emit_pos++; + e->op = op; + return e; +} + +static void emit_store_mem( emit_ctx *ctx, ereg to, int offs, ereg from ) { + einstr *e = emit_instr(ctx, STORE); + e->mode = (ctx->instrs[from.index].mode) | (offs << 8); + e->args.a = to; + e->args.b = from; +} + +static void store_args( emit_ctx *ctx, einstr *e, ereg *args, int count ) { } static ereg emit_gen( emit_ctx *ctx, emit_op op, ereg a, ereg b, int mode ) { - einstr *e = emit_instr(ctx); - if( (short)mode != mode ) emit_assert(); - e->op = op; + einstr *e = emit_instr(ctx, op); e->mode = mode; e->args.a = a; e->args.b = b; @@ -172,7 +242,7 @@ static int emit_jump( emit_ctx *ctx, bool cond ) { } static void patch_jump( emit_ctx *ctx, int jpos ) { - ctx->instrs[jpos].value = ctx->emit_pos; + ctx->instrs[jpos].mode = ctx->emit_pos - (jpos + 1); } static void register_jump( emit_ctx *ctx, int jpos, int offs ) { @@ -190,43 +260,74 @@ static void register_jump( emit_ctx *ctx, int jpos, int offs ) { } static ereg emit_load_reg( emit_ctx *ctx, vreg *r ) { - return ENULL; + //if( r->current.index < 0 ) emit_assert(); + return r->current; } static ereg emit_load_const( emit_ctx *ctx, uint64 value, hl_type *size_t ) { - return ENULL; + einstr *e = emit_instr(ctx, LOAD_IMM); + e->mode = hl_type_mode(size_t); + e->value = value; + return CUR_REG(); } static ereg emit_load_mem( emit_ctx *ctx, ereg v, int offset, hl_type *size_t ) { - return ENULL; + einstr *e = emit_instr(ctx, LOAD_ADDR); + e->mode = hl_type_mode(size_t); + e->args.a = v; + e->args.b.index = offset; + return CUR_REG(); } static void emit_store_reg( emit_ctx *ctx, vreg *to, ereg v ) { if( to->t->kind == HVOID ) return; + to->current = v; } static ereg emit_binop( emit_ctx *ctx, hl_op op, ereg a, ereg b ) { - return ENULL; + return emit_gen(ctx, BINOP, a, b, op); } static ereg emit_unop( emit_ctx *ctx, hl_op op, ereg v ) { - return ENULL; + return emit_gen(ctx, UNOP, v, ENULL, op); } static ereg emit_native_call( emit_ctx *ctx, void *native_ptr, ereg args[], int nargs, hl_type *ret ) { - return ENULL; + einstr *e = emit_instr(ctx, CALL_PTR); + e->mode = hl_type_mode(ret); + e->value = (int64)native_ptr; + store_args(ctx, e, args, nargs); + return CUR_REG(); } static ereg emit_dyn_call( emit_ctx *ctx, ereg f, ereg args[], int nargs, hl_type *ret ) { - return ENULL; + einstr *e = emit_instr(ctx, CALL_REG); + e->mode = hl_type_mode(ret); + e->args.a = f; + store_args(ctx, e, args, nargs); + return CUR_REG(); } static ereg emit_dyn_cast( emit_ctx *ctx, ereg v, hl_type *t ) { - return ENULL; + BREAK(); + return v; } -static void emit_call_fun( emit_ctx *ctx, vreg *dst, int findex, int count, int *args ) { - emit_error("TODO"); +static void emit_call_fun( emit_ctx *ctx, vreg *dst, int findex, int count, int *args_regs ) { + hl_module *m = ctx->mod; + int fid = m->functions_indexes[findex]; + bool isNative = fid >= m->code->nfunctions; + ereg *args = get_tmp_args(ctx, count); + for(int i=0;ifunctions_ptrs[findex], args, count, dst->t)); + else { + einstr *e = emit_instr(ctx, CALL_FUN); + e->mode = findex; + store_args(ctx, e, args, count); + STORE(dst, CUR_REG()); + } } static vclosure *alloc_static_closure( emit_ctx *ctx, int fid ) { @@ -296,17 +397,12 @@ static void emit_store_size( emit_ctx *ctx, ereg dst, int dst_offset, ereg src, } } -static ereg *get_tmp_args( emit_ctx *ctx, int count ) { - if( count > MAX_TMP_ARGS ) emit_error("Too many arguments"); - return ctx->tmp_args; -} - static ereg emit_phy( emit_ctx *ctx, ereg v1, ereg v2 ) { - return ENULL; + return emit_gen(ctx, PHY, v1, v2, 0); } -static ereg emit_conv( emit_ctx *ctx, ereg v, hl_op op ) { - return ENULL; +static ereg emit_conv( emit_ctx *ctx, ereg v, emit_mode mode, bool _unsigned ) { + return emit_gen(ctx, _unsigned ? CONV_UNSIGNED : CONV, v, ENULL, mode); } static bool dyn_need_type( hl_type *t ) { @@ -462,7 +558,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { case OToSFloat: case OToInt: case OToUFloat: - STORE(dst, emit_conv(ctx,LOAD(ra),o->op)); + STORE(dst, emit_conv(ctx,LOAD(ra),hl_type_mode(dst->t), o->op == OToUFloat)); break; case ORet: emit_gen(ctx, RET, LOAD(dst), ENULL, 0); @@ -505,7 +601,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { { ereg args[3]; args[0] = LOAD_CONST_PTR(m->code->functions[m->functions_indexes[o->p2]].type); - // WRITE (emit_pos + op_count) to process later and replace address ! + // TODO : WRITE (emit_pos + op_count) to process later and replace address ! args[1] = LOAD_CONST_PTR(0); args[2] = LOAD(rb); STORE(dst, emit_native_call(ctx,hl_alloc_closure_ptr,args,3,dst->t)); @@ -643,9 +739,9 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { case HVIRTUAL: // code for : if( hl_vfields(o)[f] ) *hl_vfields(o)[f] = v; else hl_dyn_set(o,hash(field),vt,v) { - ereg obj = LOAD(ra); + ereg obj = LOAD(dst); ereg val = LOAD(rb); - ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p3); + ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p2); emit_gen(ctx, TEST, field, ENULL, OJNull); int jidx = emit_jump(ctx, true); STORE_MEM(field, 0, val); @@ -654,7 +750,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { bool need_type = dyn_need_type(dst->t); ereg args[4]; args[0] = obj; - args[1] = LOAD_CONST(ra->t->virt->fields[o->p3].hashed_name,&hlt_i32); + args[1] = LOAD_CONST(dst->t->virt->fields[o->p2].hashed_name,&hlt_i32); if( need_type ) { args[2] = LOAD_CONST_PTR(rb->t); args[3] = val; @@ -742,7 +838,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { emit_gen(ctx, TEST, field, ENULL, OJNull); int jidx = emit_jump(ctx, true); - int nargs = o->p3 + 1; + int nargs = o->p3; ereg *args = get_tmp_args(ctx, nargs); int i; args[0] = LOAD_MEM_PTR(obj,HL_WSIZE); @@ -753,8 +849,9 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { int jend = emit_jump(ctx, false); patch_jump(ctx, jidx); - ereg eargs = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, o->p3); - for(i=0;ip3;i++) + nargs = o->p3 - 1; + ereg eargs = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, nargs); + for(i=0;iextra[i+1]))); bool need_dyn = !hl_is_ptr(dst->t) && dst->t->kind != HVOID; int dyn_size = sizeof(vdynamic)/HL_WSIZE; @@ -796,7 +893,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { { ereg offs = OFFSET(LOAD(ra),LOAD(rb),1,0); ereg val = LOAD_MEM(offs, 0, dst->t); - if( o->op != OGetMem ) val = emit_conv(ctx, val, o->op); + if( o->op != OGetMem ) val = emit_conv(ctx, val, I32, false); STORE(dst, val); } break; @@ -806,7 +903,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { { ereg offs = OFFSET(LOAD(dst), LOAD(ra),1,0); ereg val = LOAD(rb); - if( o->op != OSetMem ) val = emit_conv(ctx, val, o->op); + if( o->op != OSetMem ) val = emit_conv(ctx, val, I32, false); STORE_MEM(offs, 0, val); } break; @@ -933,7 +1030,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { break; case OSetEnumField: { - hl_enum_construct *c = &ra->t->tenum->constructs[0]; + hl_enum_construct *c = &dst->t->tenum->constructs[0]; STORE_MEM(LOAD(dst), c->offsets[o->p2], LOAD(rb)); } break; @@ -1082,7 +1179,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { case OEndTrap: { if( ctx->trap_count == 0 ) emit_assert(); - ereg st = ctx->traps[--ctx->trap_count]; + ereg st = ctx->traps[ctx->trap_count - 1]; ereg thread, current_addr; static hl_thread_info *tinf = NULL; @@ -1167,10 +1264,12 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { } } -bool emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { +int hl_emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { int i; ctx->mod = m; ctx->fun = f; + ctx->emit_pos = 0; + ctx->trap_count = 0; if( f->nregs > ctx->max_regs ) { free(ctx->vregs); @@ -1185,11 +1284,16 @@ bool emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { for(i=0;inregs;i++) { vreg *r = R(i); r->t = f->regs[i]; + r->current = ENULL; + } + + for(i=0;itype->fun->nargs;i++) { + STORE(R(i), emit_gen(ctx, LOAD_ARG, ENULL, ENULL, hl_type_mode(f->type->fun->args[i]))); } - if( f->nops > ctx->pos_map_size ) { + if( f->nops >= ctx->pos_map_size ) { free(ctx->pos_map); - ctx->pos_map = (int*)malloc(sizeof(int) * f->nops); + ctx->pos_map = (int*)malloc(sizeof(int) * (f->nops+1)); if( ctx->pos_map == NULL ) return false; ctx->pos_map_size = f->nops; @@ -1200,6 +1304,18 @@ bool emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { ctx->pos_map[op_pos] = ctx->emit_pos; emit_opcode(ctx,f->ops + op_pos); } + + // patch jumps + i = 0; + while( i < ctx->jump_count ) { + int pos = ctx->jump_regs[i++]; + einstr *e = ctx->instrs + pos; + int target = ctx->jump_regs[i++]; + e->mode = ctx->pos_map[target] - (pos + 1); + } + ctx->jump_count = 0; + + ctx->pos_map[f->nops] = -1; return true; } @@ -1210,7 +1326,153 @@ emit_ctx *hl_emit_alloc() { return ctx; } -void hl_emit_free( emit_ctx *ctx ) { +void hl_emit_free( emit_ctx *ctx, h_bool can_reset ) { free(ctx->vregs); + free(ctx->instrs); + free(ctx->pos_map); + free(ctx->jump_regs); free(ctx); } + +void hl_emit_init( emit_ctx *ctx, hl_module *m ) { +} + +void hl_emit_reset( emit_ctx *ctx, hl_module *m ) { +} + +void *hl_emit_code( emit_ctx *ctx, hl_module *m, int *codesize, hl_debug_infos **debug, hl_module *previous ) { + printf("TODO:emit_code\n"); + exit(0); + return NULL; +} + +static void hl_dump_arg( emit_ctx *ctx, int fmt, int val, char sep ) { + if( fmt == 0 ) return; + printf("%c", sep); + switch( fmt ) { + case 1: + case 2: + printf("R%d", val); + if( val < 0 || val >= ctx->fun->nregs ) printf("?"); + break; + case 3: + printf("%d", val); + break; + case 4: + printf("[%d]", val); + break; + case 5: + case 6: + printf("@%X", val + ctx->op_pos + 1); + break; + default: + printf("?#%d", fmt); + break; + } +} + +#define OP(_,_a,_b,_c) ((_a) | (((_b)&0xFF) << 8) | (((_c)&0xFF) << 16)), +#define OP_BEGIN static int hl_op_fmt[] = { +#define OP_END }; +#undef R +#include "opcodes.h" + +static void hl_dump_op( emit_ctx *ctx, hl_opcode *op ) { + printf("%s", hl_op_name(op->op) + 1); + int fmt = hl_op_fmt[op->op]; + hl_dump_arg(ctx, fmt & 0xFF, op->p1, ' '); + if( ((fmt >> 8) & 0xFF) == 5 ) { + int count = (fmt >> 16) & 0xFF; + printf(" ["); + if( count == 4 ) { + printf("%d", op->p2); + printf(",%d", op->p3); + printf(",%d", (int)(int_val)op->extra); + } else { + if( count == 0xFF ) count = op->p3; + for(int i=0;iextra[i]); + } + } + printf("]"); + } else { + hl_dump_arg(ctx, (fmt >> 8) & 0xFF, op->p2,','); + hl_dump_arg(ctx, fmt >> 16, op->p3,','); + } +} + +static const char *emit_mode_str( emit_mode mode ) { + switch( mode ) { + case NONE: return "-void"; + case I8: return "-i8"; + case I16: return "-i16"; + case I32: return "-i32"; + case I64: return "-i64"; + case F32: return "-f32"; + case F64: return "-f64"; + case PTR: return ""; + default: + static char buf[50]; + sprintf(buf,"?%d",mode); + return buf; + } +} + +void hl_emit_dump( emit_ctx *ctx ) { + int i; + int cur_op = 0; + hl_function *f = ctx->fun; + int nargs = f->type->fun->nargs; + printf("function %X(", f->findex); + for(i=0;i 0 ) printf(","); + uprintf(USTR("R%d"), i); + } + printf(")\n"); + for(i=0;inregs;i++) + uprintf(USTR("\tR%d : %s\n"),i, hl_type_str(f->regs[i])); + for(i=0;iemit_pos;i++) { + while( ctx->pos_map[cur_op] == i ) { + printf("@%X ", cur_op); + ctx->op_pos = cur_op; + hl_dump_op(ctx, f->ops + cur_op); + printf("\n"); + cur_op++; + } + einstr *e = ctx->instrs + i; + printf("\t\t@%X %s", i, op_names[e->op]); + switch( e->op ) { + case LOAD_ADDR: + case LOAD_IMM: + case LOAD_ARG: + printf("%s", emit_mode_str(e->mode)); + break; + default: + break; + } + switch( e->op ) { + case JUMP: + case JCOND: + printf(" @%X", i + 1 + e->mode); + break; + case STORE: + { + int offs = e->mode >> 8; + printf("%s", emit_mode_str(e->mode&0xFF)); + if( offs == 0 ) + printf(" [@%X] := @%X", e->args.a.index, e->args.b.index); + else + printf(" @%X[%d] := @%X", e->args.a.index, offs, e->args.b.index); + } + break; + default: + if( e->args.a.index >= 0 ) printf(" @%X", e->args.a.index); + if( e->args.b.index >= 0 ) printf(", @%X", e->args.b.index); + break; + } + printf("\n"); + } + printf("\n\n"); + fflush(stdout); +} \ No newline at end of file diff --git a/src/hlmodule.h b/src/hlmodule.h index d8ea8c912..e82a3ff9f 100644 --- a/src/hlmodule.h +++ b/src/hlmodule.h @@ -104,7 +104,7 @@ typedef struct { bool large; } hl_debug_infos; -typedef struct _jit_ctx jit_ctx; +typedef struct _emit_ctx emit_ctx; typedef struct { @@ -131,7 +131,7 @@ typedef struct { void *jit_code; hl_code_hash *hash; hl_debug_infos *jit_debug; - jit_ctx *jit_ctx; + emit_ctx *emit_ctx; hl_module_context ctx; #ifdef WIN64_UNWIND_TABLES PRUNTIME_FUNCTION unwind_table; @@ -161,10 +161,11 @@ hl_type *hl_module_resolve_type( hl_module *m, hl_type *t, bool err ); void hl_profile_setup( int sample_count ); void hl_profile_end(); -jit_ctx *hl_jit_alloc(); -void hl_jit_free( jit_ctx *ctx, h_bool can_reset ); -void hl_jit_reset( jit_ctx *ctx, hl_module *m ); -void hl_jit_init( jit_ctx *ctx, hl_module *m ); -int hl_jit_function( jit_ctx *ctx, hl_module *m, hl_function *f ); -void *hl_jit_code( jit_ctx *ctx, hl_module *m, int *codesize, hl_debug_infos **debug, hl_module *previous ); +emit_ctx *hl_emit_alloc(); +void hl_emit_free( emit_ctx *ctx, h_bool can_reset ); +void hl_emit_reset( emit_ctx *ctx, hl_module *m ); +void hl_emit_init( emit_ctx *ctx, hl_module *m ); +void hl_emit_dump( emit_ctx *ctx ); +int hl_emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ); +void *hl_emit_code( emit_ctx *ctx, hl_module *m, int *codesize, hl_debug_infos **debug, hl_module *previous ); void hl_jit_patch_method( void *old_fun, void **new_fun_table ); diff --git a/src/module.c b/src/module.c index e668b1064..6e0b6ef8b 100644 --- a/src/module.c +++ b/src/module.c @@ -680,7 +680,7 @@ static void hl_module_add( hl_module *m ) { int hl_module_init( hl_module *m, h_bool hot_reload ) { int i; - jit_ctx *ctx; + emit_ctx *ctx; // expand globals if( hot_reload ) { int nsize = m->globals_size + HOT_RELOAD_EXTRA_GLOBALS * sizeof(void*); @@ -706,20 +706,21 @@ int hl_module_init( hl_module *m, h_bool hot_reload ) { hl_module_init_natives(m); hl_module_init_indexes(m); // JIT - ctx = hl_jit_alloc(); + ctx = hl_emit_alloc(); if( ctx == NULL ) return 0; - hl_jit_init(ctx, m); + hl_emit_init(ctx, m); for(i=0;icode->nfunctions;i++) { hl_function *f = m->code->functions + i; - int fpos = hl_jit_function(ctx, m, f); + int fpos = hl_emit_function(ctx, m, f); if( fpos < 0 ) { - hl_jit_free(ctx, false); + hl_emit_free(ctx, false); return 0; } + hl_emit_dump(ctx); m->functions_ptrs[f->findex] = (void*)(int_val)fpos; } - m->jit_code = hl_jit_code(ctx, m, &m->codesize, &m->jit_debug, NULL); + m->jit_code = hl_emit_code(ctx, m, &m->codesize, &m->jit_debug, NULL); for(i=0;icode->nfunctions;i++) { hl_function *f = m->code->functions + i; m->functions_ptrs[f->findex] = ((unsigned char*)m->jit_code) + ((int_val)m->functions_ptrs[f->findex]); @@ -736,10 +737,10 @@ int hl_module_init( hl_module *m, h_bool hot_reload ) { # ifdef HL_VTUNE hl_setup.vtune_init = modules_init_vtune; # endif - hl_jit_free(ctx, hot_reload); + hl_emit_free(ctx, hot_reload); if( hot_reload ) { hl_code_hash_finalize(m->hash); - m->jit_ctx = ctx; + m->emit_ctx = ctx; } return 1; } @@ -824,7 +825,7 @@ h_bool hl_module_patch( hl_module *m1, hl_code *c ) { int i,i1,i2; bool has_changes = false; int changes_count = 0; - jit_ctx *ctx = m1->jit_ctx; + emit_ctx *ctx = m1->emit_ctx; hl_module *m2 = hl_module_alloc(c); m2->hash = hl_code_hash_alloc(c); @@ -849,7 +850,7 @@ h_bool hl_module_patch( hl_module *m1, hl_code *c ) { hl_module_init_natives(m2); hl_module_init_indexes(m2); - hl_jit_reset(ctx, m2); + hl_emit_reset(ctx, m2); hl_code_hash_finalize(m2->hash); for(i=0;icode->nconstants;i++) { @@ -893,7 +894,7 @@ h_bool hl_module_patch( hl_module *m1, hl_code *c ) { changes_count++; m1->hash->functions_hashes[i1] = hash2; // update hash - int fpos = hl_jit_function(ctx, m2, f2); + int fpos = hl_emit_function(ctx, m2, f2); if( fpos < 0 ) return false; m2->functions_ptrs[f2->findex] = (void*)(int_val)fpos; has_changes = true; @@ -902,7 +903,7 @@ h_bool hl_module_patch( hl_module *m1, hl_code *c ) { } if( i1 == m1->code->nfunctions ) { // not found (signature changed or new method) : inject new method! - int fpos = hl_jit_function(ctx, m2, f2); + int fpos = hl_emit_function(ctx, m2, f2); if( fpos < 0 ) return false; m2->hash->functions_hashes[i2] = -1; m2->functions_ptrs[f2->findex] = (void*)(int_val)fpos; @@ -921,7 +922,7 @@ h_bool hl_module_patch( hl_module *m1, hl_code *c ) { if( !has_changes ) { printf("[HotReload] No changes found\n"); fflush(stdout); - hl_jit_free(ctx, true); + hl_emit_free(ctx, true); return false; } @@ -969,7 +970,7 @@ h_bool hl_module_patch( hl_module *m1, hl_code *c ) { } } - m2->jit_code = hl_jit_code(ctx, m2, &m2->codesize, &m2->jit_debug, m1); + m2->jit_code = hl_emit_code(ctx, m2, &m2->codesize, &m2->jit_debug, m1); // patch missing debug info int start = -1; @@ -984,7 +985,7 @@ h_bool hl_module_patch( hl_module *m1, hl_code *c ) { } } - hl_jit_free(ctx,true); + hl_emit_free(ctx,true); if( m2->jit_code == NULL ) { printf("[HotReload] Couldn't JIT result\n"); @@ -1054,7 +1055,7 @@ void hl_module_free( hl_module *m ) { free(m->jit_debug[i].offsets); free(m->jit_debug); } - if( m->jit_ctx ) - hl_jit_free(m->jit_ctx,false); + if( m->emit_ctx ) + hl_emit_free(m->emit_ctx,false); free(m); } diff --git a/src/opcodes.h b/src/opcodes.h index ab9b1fa51..9e4df7f60 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -67,8 +67,8 @@ OP_BEGIN OP(OIncr,R,X,X) OP(ODecr,R,X,X) - OP(OCall0,R,R,X) - OP(OCall1,R,R,R) + OP(OCall0,R,C,X) + OP(OCall1,R,C,R) OP(OCall2,R,AR,4) OP(OCall3,R,AR,5) OP(OCall4,R,AR,6) @@ -78,17 +78,17 @@ OP_BEGIN OP(OCallClosure,R,AR,VAR_ARGS) OP(OStaticClosure,R,G,X) - OP(OInstanceClosure,R,R,G) + OP(OInstanceClosure,R,C,R) OP(OVirtualClosure,R,R,G) OP(OGetGlobal,R,G,X) - OP(OSetGlobal,R_NW,G,X) - OP(OField,R,R,C) - OP(OSetField,R_NW,R,C) - OP(OGetThis,R,C,X) - OP(OSetThis,R_NW,R,X) + OP(OSetGlobal,G,R,X) + OP(OField,R,R,G) + OP(OSetField,R_NW,G,R) + OP(OGetThis,R,G,X) + OP(OSetThis,G,R,X) OP(ODynGet,R,R,C) - OP(ODynSet,R_NW,R,C) + OP(ODynSet,R_NW,C,R) OP(OJTrue,R_NW,J,X) OP(OJFalse,R_NW,J,X) @@ -134,7 +134,7 @@ OP_BEGIN OP(ONew,R,X,X) OP(OArraySize,R,R,X) - OP(OType,R,R,X) + OP(OType,R,G,X) OP(OGetType,R,R,X) OP(OGetTID,R,R,X) From d8306a942fdcf1833abcb2b086dc3c063cab78a0 Mon Sep 17 00:00:00 2001 From: ncannasse Date: Sun, 15 Mar 2026 19:50:58 +0100 Subject: [PATCH 3/4] finalized IR and completed dump --- src/emit.c | 533 +++++++++++++++++++++++++++++++++++++++------------ src/module.c | 1 - 2 files changed, 412 insertions(+), 122 deletions(-) diff --git a/src/emit.c b/src/emit.c index 44de4f59f..8777c4f6b 100644 --- a/src/emit.c +++ b/src/emit.c @@ -40,10 +40,9 @@ typedef enum { CALL_PTR, CALL_REG, CALL_FUN, - PHY, + PHI, ALLOC_STACK, FREE_STACK, - ALLOC_GLOBAL_STACK, NATIVE_REG, PREFETCH, DEBUG_BREAK, @@ -65,13 +64,12 @@ static const char *op_names[] = { "conv", "conv-unsigned", "ret", - "call-ptr", - "call-reg", - "call-fun", - "phy", + "call", + "call", + "call", + "phi", "alloc-stack", "free-stack", - "alloc-global-stack", "native-reg", "prefetch", "debug-break", @@ -82,14 +80,15 @@ typedef enum { } native_reg; typedef enum { - NONE, - I8, - I16, - I32, - I64, - F32, - F64, - PTR, + M_NONE, + M_UI8, + M_UI16, + M_I32, + M_I64, + M_F32, + M_F64, + M_PTR, + M_VOID, } emit_mode; @@ -98,25 +97,28 @@ typedef struct { } ereg; typedef struct { - int id; hl_type *t; + int id; ereg current; } vreg; typedef struct { - emit_op op; - emit_mode mode; + unsigned char op; + unsigned char mode; + unsigned short nargs; + int size_offs; union { struct { ereg a; ereg b; - } args; + }; uint64 value; }; } einstr; #define MAX_TMP_ARGS 32 #define MAX_TRAPS 32 +#define MAX_REFS 512 // TODO : different impl struct _emit_ctx { hl_module *mod; @@ -130,9 +132,19 @@ struct _emit_ctx { ereg tmp_args[MAX_TMP_ARGS]; ereg traps[MAX_TRAPS]; + struct { + ereg r; + int reg; + } refs[MAX_REFS]; int *pos_map; int pos_map_size; int trap_count; + int ref_count; + + int *args_data; + int args_data_size; + int args_data_pos; + int *jump_regs; int jump_count; @@ -154,7 +166,7 @@ struct _emit_ctx { #define LOAD_MEM_PTR(v, offs) LOAD_MEM(v, offs, &hlt_bytes) #define STORE_MEM(to, offs, v) emit_store_mem(ctx, to, offs, v) #define LOAD_OBJ_METHOD(obj,id) LOAD_MEM_PTR(LOAD_MEM_PTR(LOAD_MEM_PTR((obj),0),HL_WSIZE*2),HL_WSIZE*(id)) -#define OFFSET(base,index,mult,offset) emit_gen(ctx, LEA, base, index, (mult) | ((offset) << 8)) +#define OFFSET(base,index,mult,offset) emit_gen_ext(ctx, LEA, base, index, 0, (mult) | ((offset) << 8)) #define BREAK() emit_gen(ctx, DEBUG_BREAK, ENULL, ENULL, 0) #define CUR_REG() __current_reg(ctx) @@ -165,10 +177,13 @@ struct _emit_ctx { static hl_type hlt_ui8 = { HUI8, 0 }; static hl_type hlt_ui16 = { HUI16, 0 }; static ereg ENULL = {-1}; +static emit_ctx *current_ctx = NULL; static void _emit_error( const char *msg, int line ) { printf("*** EMIT ERROR line %d (%s) ****\n", line, msg); + if( current_ctx ) hl_emit_dump(current_ctx); hl_debug_break(); + fflush(stdout); exit(-1); } @@ -177,13 +192,15 @@ static void hl_stub_null_access() { emit_assert(); } static void hl_stub_assert() { emit_assert(); } static emit_mode hl_type_mode( hl_type *t ) { + if( t->kind == HVOID ) + return M_VOID; if( t->kind < HBOOL ) return (emit_mode)t->kind; if( t->kind == HBOOL ) - return sizeof(bool) == 1 ? I8 : I32; + return sizeof(bool) == 1 ? M_UI8 : M_I32; if( t->kind == HGUID ) - return I64; - return PTR; + return M_I64; + return M_PTR; } void hl_jit_patch_method( void*fun, void**newt ) { @@ -200,6 +217,14 @@ static ereg *get_tmp_args( emit_ctx *ctx, int count ) { return ctx->tmp_args; } +static ereg resolve_ref( emit_ctx *ctx, int reg ) { + for(int i=0;iref_count;i++) { + if( ctx->refs[i].reg == reg ) + return ctx->refs[i].r; + } + return ENULL; +} + static einstr *emit_instr( emit_ctx *ctx, emit_op op ) { if( ctx->emit_pos == ctx->max_instrs ) { int pos = ctx->emit_pos; @@ -219,22 +244,60 @@ static einstr *emit_instr( emit_ctx *ctx, emit_op op ) { static void emit_store_mem( emit_ctx *ctx, ereg to, int offs, ereg from ) { einstr *e = emit_instr(ctx, STORE); - e->mode = (ctx->instrs[from.index].mode) | (offs << 8); - e->args.a = to; - e->args.b = from; + e->mode = ctx->instrs[from.index].mode; + e->size_offs = offs; + e->a = to; + e->b = from; } static void store_args( emit_ctx *ctx, einstr *e, ereg *args, int count ) { + if( count < 0 || count > 64 ) emit_assert(); + e->nargs = (unsigned short)count; + if( count == 0 ) return; + if( count == 1 ) { + e->size_offs = args[0].index; + return; + } + if( ctx->args_data_pos + count > ctx->args_data_size ) { + int next_size = ctx->args_data_size ? ctx->args_data_size << 1 : 128; + int *args = (int*)malloc(sizeof(int) * next_size); + if( args == NULL ) emit_error("Out of memory"); + memcpy(args, ctx->args_data, sizeof(int) * ctx->args_data_pos); + free(ctx->args_data); + ctx->args_data = args; + ctx->args_data_size = next_size; + } + e->size_offs = ctx->args_data_pos; + memcpy(ctx->args_data + ctx->args_data_pos, args, sizeof(int) * count); + ctx->args_data_pos += count; } -static ereg emit_gen( emit_ctx *ctx, emit_op op, ereg a, ereg b, int mode ) { +ereg *hl_emit_get_args( emit_ctx *ctx, einstr *e ) { + if( e->nargs == 0 ) + return NULL; + if( e->nargs == 1 ) + return (ereg*)&e->size_offs; + return (ereg*)(ctx->args_data + e->size_offs); +} + +static ereg emit_gen_ext( emit_ctx *ctx, emit_op op, ereg a, ereg b, int mode, int size_offs ) { einstr *e = emit_instr(ctx, op); - e->mode = mode; - e->args.a = a; - e->args.b = b; + if( (unsigned char)mode != mode ) emit_assert(); + e->mode = (unsigned char)mode; + e->size_offs = size_offs; + e->a = a; + e->b = b; return CUR_REG(); } +static ereg emit_gen( emit_ctx *ctx, emit_op op, ereg a, ereg b, int mode ) { + return emit_gen_ext(ctx,op,a,b,mode,0); +} + +static ereg emit_gen_size( emit_ctx *ctx, emit_op op, int size_offs ) { + return emit_gen_ext(ctx,op,ENULL,ENULL,0,size_offs); +} + static int emit_jump( emit_ctx *ctx, bool cond ) { int p = ctx->emit_pos; emit_gen(ctx, cond ? JCOND : JUMP, ENULL, ENULL, 0); @@ -242,7 +305,7 @@ static int emit_jump( emit_ctx *ctx, bool cond ) { } static void patch_jump( emit_ctx *ctx, int jpos ) { - ctx->instrs[jpos].mode = ctx->emit_pos - (jpos + 1); + ctx->instrs[jpos].size_offs = ctx->emit_pos - (jpos + 1); } static void register_jump( emit_ctx *ctx, int jpos, int offs ) { @@ -259,11 +322,6 @@ static void register_jump( emit_ctx *ctx, int jpos, int offs ) { ctx->jump_regs[ctx->jump_count++] = offs + ctx->op_pos + 1; } -static ereg emit_load_reg( emit_ctx *ctx, vreg *r ) { - //if( r->current.index < 0 ) emit_assert(); - return r->current; -} - static ereg emit_load_const( emit_ctx *ctx, uint64 value, hl_type *size_t ) { einstr *e = emit_instr(ctx, LOAD_IMM); e->mode = hl_type_mode(size_t); @@ -274,8 +332,8 @@ static ereg emit_load_const( emit_ctx *ctx, uint64 value, hl_type *size_t ) { static ereg emit_load_mem( emit_ctx *ctx, ereg v, int offset, hl_type *size_t ) { einstr *e = emit_instr(ctx, LOAD_ADDR); e->mode = hl_type_mode(size_t); - e->args.a = v; - e->args.b.index = offset; + e->a = v; + e->b.index = offset; return CUR_REG(); } @@ -284,14 +342,6 @@ static void emit_store_reg( emit_ctx *ctx, vreg *to, ereg v ) { to->current = v; } -static ereg emit_binop( emit_ctx *ctx, hl_op op, ereg a, ereg b ) { - return emit_gen(ctx, BINOP, a, b, op); -} - -static ereg emit_unop( emit_ctx *ctx, hl_op op, ereg v ) { - return emit_gen(ctx, UNOP, v, ENULL, op); -} - static ereg emit_native_call( emit_ctx *ctx, void *native_ptr, ereg args[], int nargs, hl_type *ret ) { einstr *e = emit_instr(ctx, CALL_PTR); e->mode = hl_type_mode(ret); @@ -303,14 +353,23 @@ static ereg emit_native_call( emit_ctx *ctx, void *native_ptr, ereg args[], int static ereg emit_dyn_call( emit_ctx *ctx, ereg f, ereg args[], int nargs, hl_type *ret ) { einstr *e = emit_instr(ctx, CALL_REG); e->mode = hl_type_mode(ret); - e->args.a = f; + e->a = f; store_args(ctx, e, args, nargs); return CUR_REG(); } -static ereg emit_dyn_cast( emit_ctx *ctx, ereg v, hl_type *t ) { - BREAK(); - return v; +static void emit_test( emit_ctx *ctx, ereg v, hl_op o ) { + emit_gen_ext(ctx, TEST, v, ENULL, ctx->instrs[v.index].mode, o); +} + +static ereg emit_load_reg( emit_ctx *ctx, vreg *r ) { + if( r->current.index < 0 ) { + ereg ref = resolve_ref(ctx, r->id); + if( ref.index < 0 ) emit_assert(); + // reload from ref + return LOAD_MEM(ref,0,r->t); + } + return r->current; } static void emit_call_fun( emit_ctx *ctx, vreg *dst, int findex, int count, int *args_regs ) { @@ -324,7 +383,8 @@ static void emit_call_fun( emit_ctx *ctx, vreg *dst, int findex, int count, int STORE(dst, emit_native_call(ctx, m->functions_ptrs[findex], args, count, dst->t)); else { einstr *e = emit_instr(ctx, CALL_FUN); - e->mode = findex; + e->mode = hl_type_mode(dst->t); + e->a.index = findex; store_args(ctx, e, args, count); STORE(dst, CUR_REG()); } @@ -387,6 +447,25 @@ static void *get_dynset( hl_type *t ) { } } +static void *get_dyncast( hl_type *t ) { + switch( t->kind ) { + case HF32: + return hl_dyn_castf; + case HF64: + return hl_dyn_castd; + case HI64: + case HGUID: + return hl_dyn_casti64; + case HI32: + case HUI16: + case HUI8: + case HBOOL: + return hl_dyn_casti; + default: + return hl_dyn_castp; + } +} + static void emit_store_size( emit_ctx *ctx, ereg dst, int dst_offset, ereg src, int src_offset, int total_size ) { int offset = 0; while( offset < total_size) { @@ -397,8 +476,8 @@ static void emit_store_size( emit_ctx *ctx, ereg dst, int dst_offset, ereg src, } } -static ereg emit_phy( emit_ctx *ctx, ereg v1, ereg v2 ) { - return emit_gen(ctx, PHY, v1, v2, 0); +static ereg emit_phi( emit_ctx *ctx, ereg v1, ereg v2 ) { + return emit_gen(ctx, PHI, v1, v2, 0); } static ereg emit_conv( emit_ctx *ctx, ereg v, emit_mode mode, bool _unsigned ) { @@ -409,11 +488,37 @@ static bool dyn_need_type( hl_type *t ) { return !(IS_FLOAT(t) || t->kind == HI64 || t->kind == HGUID); } +static ereg emit_dyn_cast( emit_ctx *ctx, ereg v, hl_type *t, hl_type *dt ) { + if( t->kind == HNULL && t->tparam->kind == dt->kind ) { + emit_test(ctx, v, OJNotNull); + int jnot = emit_jump(ctx, false); + ereg v1 = LOAD_CONST(0,dt); + int jend = emit_jump(ctx, true); + patch_jump(ctx, jnot); + ereg v2 = LOAD_MEM(v,0,dt); + patch_jump(ctx, jend); + return emit_phi(ctx, v1, v2); + } + bool need_dyn = dyn_need_type(dt); + ereg st = emit_gen_size(ctx, ALLOC_STACK, 1); + STORE_MEM(st, 0, v); + ereg args[3]; + args[0] = st; + args[1] = LOAD_CONST_PTR(t); + if( need_dyn ) args[2] = LOAD_CONST_PTR(dt); + ereg r = emit_native_call(ctx, get_dyncast(dt), args, need_dyn ? 3 : 2, dt); + emit_gen_size(ctx, FREE_STACK, 1); + return r; +} + static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { vreg *dst = R(o->p1); vreg *ra = R(o->p2); vreg *rb = R(o->p3); hl_module *m = ctx->mod; +#ifdef HL_DEBUG + int uid = (ctx->fun->findex << 16) | ctx->op_pos; +#endif switch( o->op ) { case OMov: case OUnsafeCast: @@ -506,19 +611,19 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { { ereg va = LOAD(ra); ereg vb = LOAD(rb); - STORE(dst, emit_gen(ctx, BINOP, va, vb, o->op)); + STORE(dst, emit_gen_ext(ctx, BINOP, va, vb, hl_type_mode(dst->t), o->op)); } break; case ONeg: case ONot: - STORE(dst, emit_gen(ctx, UNOP, LOAD(ra), ENULL, o->op)); + STORE(dst, emit_gen_ext(ctx, UNOP, LOAD(ra), ENULL, hl_type_mode(dst->t), o->op)); break; case OJFalse: case OJTrue: case OJNotNull: case OJNull: { - emit_gen(ctx, TEST, LOAD(dst), ENULL, o->op); + emit_test(ctx, LOAD(dst), o->op); int jidx = emit_jump(ctx, true); register_jump(ctx, jidx, o->p2); } @@ -534,7 +639,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { case OJNotLt: case OJNotGte: { - emit_gen(ctx, CMP, LOAD(dst), LOAD(ra), o->op); + emit_gen_ext(ctx, CMP, LOAD(dst), LOAD(ra), hl_type_mode(dst->t), o->op); int jidx = emit_jump(ctx, true); register_jump(ctx, jidx, o->p3); } @@ -553,6 +658,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { ereg arg = LOAD_CONST_PTR(ra->t); ereg ret = emit_native_call(ctx,hl_alloc_dynamic,&arg,1,&hlt_dyn); STORE_MEM(ret,HDYN_VALUE,LOAD(ra)); + STORE(dst, ret); } break; case OToSFloat: @@ -561,7 +667,10 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { STORE(dst, emit_conv(ctx,LOAD(ra),hl_type_mode(dst->t), o->op == OToUFloat)); break; case ORet: - emit_gen(ctx, RET, LOAD(dst), ENULL, 0); + if( dst->t->kind == HVOID ) + emit_gen(ctx,RET,ENULL,ENULL,M_VOID); + else + emit_gen(ctx, RET, LOAD(dst), ENULL, hl_type_mode(dst->t)); break; case OIncr: case ODecr: @@ -569,7 +678,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { if( IS_FLOAT(dst->t) ) { emit_assert(); } else { - STORE(dst, emit_unop(ctx,o->op, LOAD(dst))); + STORE(dst, emit_gen_ext(ctx,UNOP,LOAD(dst),ENULL,hl_type_mode(dst->t),o->op)); } } break; @@ -633,7 +742,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { case OCallClosure: if( ra->t->kind == HDYN ) { int i; - ereg st = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, o->p3); + ereg st = emit_gen_size(ctx, ALLOC_STACK, o->p3); for(i=0;ip3;i++) { vreg *r = R(o->extra[i]); if( !hl_is_dynamic(r->t) ) emit_assert(); @@ -643,14 +752,14 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { args[0] = LOAD(ra); args[1] = st; args[2] = LOAD_CONST(o->p3,&hlt_i32); - STORE(dst, emit_dyn_cast(ctx,emit_native_call(ctx,hl_dyn_call,args,3,dst->t),dst->t)); - emit_gen(ctx, FREE_STACK, ENULL, ENULL, o->p3); + STORE(dst, emit_dyn_cast(ctx,emit_native_call(ctx,hl_dyn_call,args,3,dst->t),ra->t,dst->t)); + emit_gen_size(ctx, FREE_STACK, o->p3); } else { ereg r = LOAD(ra); ereg *args = get_tmp_args(ctx,o->p3+1); // Code for if( c->hasValue ) c->fun(c->value,args) else c->fun(args) ereg has = LOAD_MEM(r,HL_WSIZE*2,&hlt_i32); - emit_gen(ctx, TEST, has, ENULL, OJNull); + emit_test(ctx, has, OJNull); int jidx = emit_jump(ctx, true); int i; args[0] = LOAD_MEM_PTR(r,HL_WSIZE * 3); @@ -663,7 +772,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { args[i] = LOAD(R(o->extra[i])); ereg v2 = emit_dyn_call(ctx,LOAD_MEM_PTR(r,HL_WSIZE),args,o->p3,dst->t); patch_jump(ctx, jend); - STORE(dst, emit_phy(ctx,v1,v2)); + STORE(dst, emit_phi(ctx,v1,v2)); } break; case OStaticClosure: @@ -683,7 +792,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { if( dst->t->kind == HSTRUCT ) { hl_type *ft = hl_obj_field_fetch(ra->t,o->p3)->t; if( ft->kind == HPACKED ) { - STORE(dst,emit_gen(ctx, LEA, r, ENULL, rt->fields_indexes[o->p3])); + STORE(dst,OFFSET(r, ENULL, 0, rt->fields_indexes[o->p3])); break; } } @@ -695,7 +804,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { { ereg obj = LOAD(ra); ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p3); - emit_gen(ctx, TEST, field, ENULL, OJNull); + emit_test(ctx, field, OJNull); int jidx = emit_jump(ctx, true); ereg v1 = LOAD_MEM(field,0,dst->t); int jend = emit_jump(ctx, false); @@ -707,7 +816,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { if( need_type ) args[2] = LOAD_CONST_PTR(dst->t); ereg v2 = emit_native_call(ctx,get_dynget(dst->t),args,need_type?3:2,dst->t); patch_jump(ctx, jend); - STORE(dst, emit_phy(ctx, v1, v2)); + STORE(dst, emit_phi(ctx, v1, v2)); } break; default: @@ -742,7 +851,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { ereg obj = LOAD(dst); ereg val = LOAD(rb); ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p2); - emit_gen(ctx, TEST, field, ENULL, OJNull); + emit_test(ctx, field, OJNull); int jidx = emit_jump(ctx, true); STORE_MEM(field, 0, val); int jend = emit_jump(ctx, false); @@ -776,7 +885,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { if( dst->t->kind == HSTRUCT ) { hl_type *ft = hl_obj_field_fetch(r->t,o->p2)->t; if( ft->kind == HPACKED ) { - STORE(dst, emit_gen(ctx, LEA, obj, ENULL, field_pos)); + STORE(dst, OFFSET(obj, ENULL, 0, field_pos)); break; } } @@ -835,7 +944,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { vreg *_o = R(o->extra[0]); ereg obj = LOAD(_o); ereg field = LOAD_MEM_PTR(obj,sizeof(vvirtual)+HL_WSIZE*o->p2); - emit_gen(ctx, TEST, field, ENULL, OJNull); + emit_test(ctx, field, OJNull); int jidx = emit_jump(ctx, true); int nargs = o->p3; @@ -850,12 +959,12 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { patch_jump(ctx, jidx); nargs = o->p3 - 1; - ereg eargs = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, nargs); + ereg eargs = emit_gen_size(ctx, ALLOC_STACK, nargs); for(i=0;iextra[i+1]))); bool need_dyn = !hl_is_ptr(dst->t) && dst->t->kind != HVOID; int dyn_size = sizeof(vdynamic)/HL_WSIZE; - ereg edyn = need_dyn ? emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, dyn_size) : LOAD_CONST_PTR(NULL); + ereg edyn = need_dyn ? emit_gen_size(ctx, ALLOC_STACK, dyn_size) : LOAD_CONST_PTR(NULL); args = get_tmp_args(ctx, 4); args[0] = LOAD_MEM_PTR(obj,HL_WSIZE); @@ -865,10 +974,10 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { ereg v2 = emit_native_call(ctx, hl_dyn_call_obj, args, 4, dst->t); - emit_gen(ctx, FREE_STACK, ENULL, ENULL, o->p3 + (need_dyn ? dyn_size : 0)); + emit_gen_size(ctx, FREE_STACK, o->p3 + (need_dyn ? dyn_size : 0)); patch_jump(ctx, jend); - STORE(dst, emit_phy(ctx, v1, v2)); + STORE(dst, emit_phi(ctx, v1, v2)); } break; default: @@ -893,7 +1002,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { { ereg offs = OFFSET(LOAD(ra),LOAD(rb),1,0); ereg val = LOAD_MEM(offs, 0, dst->t); - if( o->op != OGetMem ) val = emit_conv(ctx, val, I32, false); + if( o->op != OGetMem ) val = emit_conv(ctx, val, M_I32, false); STORE(dst, val); } break; @@ -903,7 +1012,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { { ereg offs = OFFSET(LOAD(dst), LOAD(ra),1,0); ereg val = LOAD(rb); - if( o->op != OSetMem ) val = emit_conv(ctx, val, I32, false); + if( o->op != OSetMem ) val = emit_conv(ctx, val, M_I32, false); STORE_MEM(offs, 0, val); } break; @@ -913,14 +1022,14 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { case OGetType: { ereg r = LOAD(ra); - emit_gen(ctx, TEST, r, ENULL, OJNotNull); + emit_test(ctx, r, OJNotNull); int jidx = emit_jump(ctx, true); ereg v1 = LOAD_CONST_PTR(&hlt_void); int jend = emit_jump(ctx, false); patch_jump(ctx, jidx); ereg v2 = LOAD_MEM_PTR(r,0); patch_jump(ctx, jend); - STORE(dst, emit_phy(ctx, v1, v2)); + STORE(dst, emit_phi(ctx, v1, v2)); } break; case OGetArray: @@ -967,9 +1076,11 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { break; case ORef: { - ereg addr = emit_gen(ctx, ALLOC_GLOBAL_STACK, ENULL, ENULL, hl_type_size(ra->t)); - STORE_MEM(addr, 0, LOAD(ra)); - STORE(dst, addr); + ereg ref = resolve_ref(ctx, ra->id); + if( ref.index < 0 ) emit_assert(); + if( ra->current.index >= 0 ) STORE_MEM(ref, 0, LOAD(ra)); + ra->current.index = -1; // ref will be modified + STORE(dst, ref); } break; case OUnref: @@ -1036,7 +1147,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { break; case ONullCheck: { - emit_gen(ctx, TEST, LOAD(dst), ENULL, OJNotNull); + emit_test(ctx, LOAD(dst), OJNotNull); int jok = emit_jump(ctx, true); // ----- DETECT FIELD ACCESS ---------------- @@ -1072,7 +1183,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { } break; case OSafeCast: - STORE(dst, emit_dyn_cast(ctx, LOAD(ra), dst->t)); + STORE(dst, emit_dyn_cast(ctx, LOAD(ra), ra->t, dst->t)); break; case ODynGet: { @@ -1100,7 +1211,7 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { break; case OTrap: { - ereg st = emit_gen(ctx, ALLOC_STACK, ENULL, ENULL, sizeof(hl_trap_ctx)); + ereg st = emit_gen_size(ctx, ALLOC_STACK, sizeof(hl_trap_ctx)); ereg thread, current_addr; static hl_thread_info *tinf = NULL; @@ -1163,9 +1274,9 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { fun = _setjmp; #endif ereg ret = emit_native_call(ctx, fun, args, nargs, &hlt_i32); - emit_gen(ctx, TEST, ret, ENULL, OJNull); + emit_test(ctx, ret, OJNull); int jskip = emit_jump(ctx, true); - emit_gen(ctx, FREE_STACK, ENULL, ENULL, sizeof(hl_trap_ctx)); + emit_gen_size(ctx, FREE_STACK, sizeof(hl_trap_ctx)); STORE(dst, tinf ? LOAD_CONST_PTR(&tinf->exc_value) : LOAD_MEM_PTR(thread,(int)(int_val)&tinf->exc_value)); int jtrap = emit_jump(ctx, false); @@ -1207,16 +1318,16 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { } # endif - emit_gen(ctx, FREE_STACK, ENULL, ENULL, sizeof(hl_trap_ctx)); + emit_gen_size(ctx, FREE_STACK, sizeof(hl_trap_ctx)); } break; case OSwitch: { ereg v = LOAD(dst); int count = o->p2; - emit_gen(ctx, CMP, v, LOAD_CONST(count,&hlt_i32), OJUGte); + emit_gen_ext(ctx, CMP, v, LOAD_CONST(count,&hlt_i32), M_I32, OJUGte); int jdefault = emit_jump(ctx, true); - emit_gen(ctx, JUMP_TABLE, v, ENULL, count); + emit_gen_ext(ctx, JUMP_TABLE, v, ENULL, 0, count); for(int i=0; iextra[i]); @@ -1264,19 +1375,33 @@ static void emit_opcode( emit_ctx *ctx, hl_opcode *o ) { } } +static void hl_emit_flush( emit_ctx *ctx ) { + int i = 0; + while( i < ctx->jump_count ) { + int pos = ctx->jump_regs[i++]; + einstr *e = ctx->instrs + pos; + int target = ctx->jump_regs[i++]; + e->size_offs = ctx->pos_map[target] - (pos + 1); + } + ctx->jump_count = 0; + ctx->pos_map[ctx->fun->nops] = -1; +} + int hl_emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { int i; ctx->mod = m; ctx->fun = f; ctx->emit_pos = 0; ctx->trap_count = 0; + ctx->ref_count = 0; + current_ctx = ctx; if( f->nregs > ctx->max_regs ) { free(ctx->vregs); ctx->vregs = (vreg*)malloc(sizeof(vreg) * (f->nregs + 1)); if( ctx->vregs == NULL ) return false; - for(i=ctx->max_regs;inregs;i++) + for(i=0;inregs;i++) R(i)->id = i; ctx->max_regs = f->nregs; } @@ -1291,6 +1416,18 @@ int hl_emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { STORE(R(i), emit_gen(ctx, LOAD_ARG, ENULL, ENULL, hl_type_mode(f->type->fun->args[i]))); } + for(i=f->nops-1;i>=0;i--) { + hl_opcode *o = f->ops + i; + if( o->op == ORef ) { + ereg ref = resolve_ref(ctx, o->p2); + if( ref.index >= 0 ) continue; + if( ctx->ref_count == MAX_REFS ) emit_error("Too many refs"); + ctx->refs[ctx->ref_count].r = emit_gen_size(ctx, ALLOC_STACK, hl_type_size(R(o->p2)->t)); + ctx->refs[ctx->ref_count].reg = o->p2; + ctx->ref_count++; + } + } + if( f->nops >= ctx->pos_map_size ) { free(ctx->pos_map); ctx->pos_map = (int*)malloc(sizeof(int) * (f->nops+1)); @@ -1305,17 +1442,8 @@ int hl_emit_function( emit_ctx *ctx, hl_module *m, hl_function *f ) { emit_opcode(ctx,f->ops + op_pos); } - // patch jumps - i = 0; - while( i < ctx->jump_count ) { - int pos = ctx->jump_regs[i++]; - einstr *e = ctx->instrs + pos; - int target = ctx->jump_regs[i++]; - e->mode = ctx->pos_map[target] - (pos + 1); - } - ctx->jump_count = 0; - - ctx->pos_map[f->nops] = -1; + hl_emit_flush(ctx); + current_ctx = NULL; return true; } @@ -1323,6 +1451,7 @@ emit_ctx *hl_emit_alloc() { emit_ctx *ctx = (emit_ctx*)malloc(sizeof(emit_ctx)); if( ctx == NULL ) return NULL; memset(ctx,0,sizeof(emit_ctx)); + if( sizeof(einstr) != 16 ) emit_assert(); return ctx; } @@ -1331,6 +1460,7 @@ void hl_emit_free( emit_ctx *ctx, h_bool can_reset ) { free(ctx->instrs); free(ctx->pos_map); free(ctx->jump_regs); + free(ctx->args_data); free(ctx); } @@ -1388,6 +1518,12 @@ static void hl_dump_op( emit_ctx *ctx, hl_opcode *op ) { printf("%d", op->p2); printf(",%d", op->p3); printf(",%d", (int)(int_val)op->extra); + } else if( op->op == OSwitch ) { + for(int i=0;ip2;i++) { + if( i != 0 ) printf(","); + printf("@%X", (op->extra[i] + ctx->op_pos + 1)); + } + printf(",def=@%X", op->p3 + ctx->op_pos + 1); } else { if( count == 0xFF ) count = op->p3; for(int i=0;i= -0x10000 && (int)value <= 0x10000 ) + printf("%d",(int)value); + else + printf("0x%X",(int)value); + break; + case M_F32: + tmp.v = value; + printf("%f",tmp.f); + break; + case M_F64: + tmp.v = value; + printf("%g",tmp.d); + break; + default: + if( value == 0 ) + printf("NULL"); + else + printf("0x%llX",value); + break; + } +} + +static void hl_dump_fun_name( hl_function *f ) { + if( f->obj ) + uprintf(USTR("%s.%s"),f->obj->name,f->field.name); + else if( f->field.ref ) + uprintf(USTR("%s.~%s.%d"),f->field.ref->obj->name, f->field.ref->field.name, f->ref); + printf("[%X]", f->findex); +} + +static void hl_dump_args( emit_ctx *ctx, einstr *e ) { + ereg *v = hl_emit_get_args(ctx, e); + printf("("); + for(int i=0;inargs;i++) { + if( i != 0 ) printf(","); + printf("@%X", v[i].index); + } + printf(")"); +} + + +typedef struct { const char *name; void *ptr; } named_ptr; +static void hl_dump_ptr_name( emit_ctx *ctx, void *ptr ) { +# define N(v) ptr_names[i].name = #v; ptr_names[i].ptr = v; i++ +# define N2(n,v) ptr_names[i].name = n; ptr_names[i].ptr = v; i++ + static named_ptr ptr_names[256] = { NULL }; + int i = 0; + if( !ptr_names[0].ptr ) { + i = 0; + N(hl_alloc_dynbool); + N(hl_alloc_dynamic); + N(hl_alloc_obj); + N(hl_alloc_dynobj); + N(hl_alloc_virtual); + N(hl_alloc_closure_ptr); + N(hl_dyn_call); + N(hl_dyn_call_obj); + N(hl_throw); + N(hl_rethrow); + N(hl_to_virtual); + N(hl_alloc_enum); + N(hl_dyn_castf); + N(hl_dyn_castd); + N(hl_dyn_casti64); + N(hl_dyn_casti); + N(hl_dyn_castp); + N2("null_field",hl_stub_null_field_access); + N2("null_access",hl_stub_null_access); + N(hl_get_thread); + N(setjmp); + N(_setjmp); + N2("assert",hl_stub_assert); + } +# undef N +# undef N2 + while( true ) { + named_ptr p = ptr_names[i++]; + if( !p.ptr ) break; + if( p.ptr == ptr ) { + printf("<%s>",p.name); + return; + } + } + for(i=0;imod->code->nnatives;i++) { + hl_native *n = ctx->mod->code->natives + i; + if( ctx->mod->functions_ptrs[n->findex] == ptr ) { + printf("<%s.%s>",n->lib,n->name); + return; + } + } + printf("",(uint64)ptr); +} + void hl_emit_dump( emit_ctx *ctx ) { int i; int cur_op = 0; hl_function *f = ctx->fun; int nargs = f->type->fun->nargs; - printf("function %X(", f->findex); + hl_emit_flush(ctx); // if it not was not before (in case of dump during emit) + printf("function "); + hl_dump_fun_name(f); + printf("("); for(i=0;i 0 ) printf(","); uprintf(USTR("R%d"), i); @@ -1443,36 +1688,82 @@ void hl_emit_dump( emit_ctx *ctx ) { einstr *e = ctx->instrs + i; printf("\t\t@%X %s", i, op_names[e->op]); switch( e->op ) { - case LOAD_ADDR: - case LOAD_IMM: - case LOAD_ARG: - printf("%s", emit_mode_str(e->mode)); + case TEST: + case CMP: + case BINOP: + case UNOP: + printf("-%s", hl_op_name(e->size_offs)+2); break; default: break; } + if( e->mode ) + printf("%s", emit_mode_str(e->mode)); switch( e->op ) { + case CALL_FUN: + printf(" "); + { + int fid = ctx->mod->functions_indexes[e->a.index]; + hl_code *code = ctx->mod->code; + if( fid < code->nfunctions ) { + hl_dump_fun_name(&code->functions[fid]); + } else { + printf("???"); + } + } + hl_dump_args(ctx,e); + break; + case CALL_REG: + printf(" @%X", e->a.index); + hl_dump_args(ctx,e); + break; + case CALL_PTR: + printf(" "); + hl_dump_ptr_name(ctx, (void*)e->value); + hl_dump_args(ctx,e); + break; case JUMP: case JCOND: - printf(" @%X", i + 1 + e->mode); + printf(" @%X", i + 1 + e->size_offs); + break; + case LOAD_IMM: + printf(" "); + dump_value(e->value, e->mode); + break; + case ALLOC_STACK: + printf(" %d", e->size_offs); + break; + case LOAD_ADDR: + if( (e->b.index>>8) ) + printf(" @%X[%Xh]", e->a.index, e->b.index); + else + printf(" @%X[%d]", e->a.index, e->b.index); break; case STORE: { - int offs = e->mode >> 8; - printf("%s", emit_mode_str(e->mode&0xFF)); + int offs = e->size_offs; if( offs == 0 ) - printf(" [@%X] := @%X", e->args.a.index, e->args.b.index); + printf(" [@%X] = @%X", e->a.index, e->b.index); else - printf(" @%X[%d] := @%X", e->args.a.index, offs, e->args.b.index); + printf(" @%X[%d] = @%X", e->a.index, offs, e->b.index); } break; default: - if( e->args.a.index >= 0 ) printf(" @%X", e->args.a.index); - if( e->args.b.index >= 0 ) printf(", @%X", e->args.b.index); + if( e->a.index >= 0 ) { + printf(" @%X", e->a.index); + if( e->b.index >= 0 ) printf(", @%X", e->b.index); + } break; } printf("\n"); } + // interrupted + if( cur_op < f->nops ) { + printf("@%X ", cur_op); + ctx->op_pos = cur_op; + hl_dump_op(ctx, f->ops + cur_op); + printf("\n\t\t...\n"); + } printf("\n\n"); fflush(stdout); } \ No newline at end of file diff --git a/src/module.c b/src/module.c index 6e0b6ef8b..1dbd9f2cf 100644 --- a/src/module.c +++ b/src/module.c @@ -717,7 +717,6 @@ int hl_module_init( hl_module *m, h_bool hot_reload ) { hl_emit_free(ctx, false); return 0; } - hl_emit_dump(ctx); m->functions_ptrs[f->findex] = (void*)(int_val)fpos; } m->jit_code = hl_emit_code(ctx, m, &m->codesize, &m->jit_debug, NULL); From 42995f1a8612c09a3840b8438c756402c846855d Mon Sep 17 00:00:00 2001 From: ncannasse Date: Sun, 15 Mar 2026 22:27:00 +0100 Subject: [PATCH 4/4] minor --- src/emit.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/emit.c b/src/emit.c index 8777c4f6b..6f58733a7 100644 --- a/src/emit.c +++ b/src/emit.c @@ -295,7 +295,7 @@ static ereg emit_gen( emit_ctx *ctx, emit_op op, ereg a, ereg b, int mode ) { } static ereg emit_gen_size( emit_ctx *ctx, emit_op op, int size_offs ) { - return emit_gen_ext(ctx,op,ENULL,ENULL,0,size_offs); + return emit_gen_ext(ctx,op,ENULL,ENULL,op==ALLOC_STACK ? M_PTR : 0,size_offs); } static int emit_jump( emit_ctx *ctx, bool cond ) { @@ -477,7 +477,9 @@ static void emit_store_size( emit_ctx *ctx, ereg dst, int dst_offset, ereg src, } static ereg emit_phi( emit_ctx *ctx, ereg v1, ereg v2 ) { - return emit_gen(ctx, PHI, v1, v2, 0); + int mode = ctx->instrs[v1.index].mode; + if( mode != ctx->instrs[v2.index].mode ) emit_assert(); + return emit_gen(ctx, PHI, v1, v2, mode); } static ereg emit_conv( emit_ctx *ctx, ereg v, emit_mode mode, bool _unsigned ) { @@ -1613,6 +1615,7 @@ typedef struct { const char *name; void *ptr; } named_ptr; static void hl_dump_ptr_name( emit_ctx *ctx, void *ptr ) { # define N(v) ptr_names[i].name = #v; ptr_names[i].ptr = v; i++ # define N2(n,v) ptr_names[i].name = n; ptr_names[i].ptr = v; i++ +# define DYN(p) N2("dyn_get" #p, hl_dyn_get##p); N2("dyn_set" #p, hl_dyn_set##p); N2("dyn_cast" #p, hl_dyn_cast##p) static named_ptr ptr_names[256] = { NULL }; int i = 0; if( !ptr_names[0].ptr ) { @@ -1629,11 +1632,11 @@ static void hl_dump_ptr_name( emit_ctx *ctx, void *ptr ) { N(hl_rethrow); N(hl_to_virtual); N(hl_alloc_enum); - N(hl_dyn_castf); - N(hl_dyn_castd); - N(hl_dyn_casti64); - N(hl_dyn_casti); - N(hl_dyn_castp); + DYN(f); + DYN(d); + DYN(i64); + DYN(i); + DYN(p); N2("null_field",hl_stub_null_field_access); N2("null_access",hl_stub_null_access); N(hl_get_thread); @@ -1746,6 +1749,8 @@ void hl_emit_dump( emit_ctx *ctx ) { printf(" [@%X] = @%X", e->a.index, e->b.index); else printf(" @%X[%d] = @%X", e->a.index, offs, e->b.index); + if( e->mode == 0 || e->mode != ctx->instrs[e->b.index].mode ) + printf(" ???"); } break; default: