--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include "gwion_util.h"
+#include "gwion_ast.h"
+#include "oo.h"
+#include "vm.h"
+#include "env.h"
+#include "type.h"
+#include "nspc.h"
+#include "value.h"
+#include "instr.h"
+#include "emit.h"
+#include "object.h"
+#include "array.h"
+#include "func.h"
+#include "traverse.h"
+#include "template.h"
+#include "escape.h"
+#include "parse.h"
+#include "memoize.h"
+#include "operator.h"
+#include "import.h"
+#include "switch.h"
+
+typedef struct Local_ {
+ m_uint size;
+ m_uint offset;
+ m_bool is_obj;
+} Local;
+
+static inline void emit_pop_type(const Emitter emit) { nspc_pop_type(emit->env->curr); }
+static inline void emit_pop(const Emitter emit, const m_uint scope) { env_pop(emit->env, scope); }
+static inline m_uint emit_push(const Emitter emit, const Type type, const Nspc nspc) {
+ return env_push(emit->env, type, nspc);
+}
+static inline m_uint emit_push_type(const Emitter emit, const Type type) {
+ return env_push_type(emit->env, type);
+}
+static inline m_uint emit_push_global(const Emitter emit) {
+ return env_push_global(emit->env);
+}
+
+ANEW static Frame* new_frame() {
+ Frame* frame = mp_alloc(Frame);
+ vector_init(&frame->stack);
+ vector_add(&frame->stack, (vtype)NULL);
+ return frame;
+}
+
+ANN static void free_frame(Frame* a) {
+ LOOP_OPTIM
+ for(vtype i = vector_size(&a->stack) + 1; --i;)
+ if(vector_at(&a->stack, i - 1))
+ mp_free(Local, (Local*)vector_at(&a->stack, i - 1));
+ vector_release(&a->stack);
+ mp_free(Frame, a);
+}
+
+ANN static Local* new_local(const m_uint size, const m_bool is_obj) {
+ Local* local = mp_alloc(Local);
+ local->size = size;
+ local->is_obj = is_obj;
+ return local;
+}
+
+ANN static m_uint frame_local(Frame* frame, const m_uint size, const m_bool is_obj) {
+ Local* local = new_local(size, is_obj);
+ local->offset = frame->curr_offset;
+ frame->curr_offset += size;
+ vector_add(&frame->stack, (vtype)local);
+ return local->offset;
+}
+
+ANN static inline void frame_push(Frame* frame) {
+ vector_add(&frame->stack, (vtype)NULL);
+}
+
+ANN static m_int frame_pop(Frame* frame) {
+ const Local* l = (Local*)vector_pop(&frame->stack);
+ CHECK_OB(l)
+ frame->curr_offset -= l->size;
+ return l->is_obj ? (m_int)l->offset : frame_pop(frame);
+}
+
+ANN static m_bool emit_exp(const Emitter emit, Exp exp, const m_bool add_ref);
+ANN static m_bool emit_stmt(const Emitter emit, const Stmt stmt, const m_bool pop);
+ANN static m_bool emit_stmt_list(const Emitter emit, Stmt_List list);
+ANN static m_bool emit_exp_dot(const Emitter emit, const Exp_Dot* member);
+ANN static m_bool emit_func_def(const Emitter emit, const Func_Def func_def);
+
+ANEW static Code* new_code(const Emitter emit, const m_str name) {
+ Code* code = mp_alloc(Code);
+ code->name = code_name_set(name, emit->env->name);
+ vector_init(&code->instr);
+ vector_init(&code->stack_break);
+ vector_init(&code->stack_cont);
+ vector_init(&code->stack_return);
+ code->frame = new_frame();
+ return code;
+}
+
+ANN static void free_code(Code* code) {
+ vector_release(&code->instr);
+ vector_release(&code->stack_break);
+ vector_release(&code->stack_cont);
+ vector_release(&code->stack_return);
+ free_frame(code->frame);
+ free(code->name);
+ mp_free(Code, code);
+}
+
+ANN static void emit_pop_scope(const Emitter emit) { GWDEBUG_EXE
+ m_int offset;
+ while((offset = frame_pop(emit->code->frame)) > -1) {
+ Instr instr = emit_add_instr(emit, ObjectRelease);
+ instr->m_val = (m_uint)offset;
+ }
+}
+
+ANN static inline void emit_push_code(const Emitter emit, const m_str name) {
+ vector_add(&emit->stack, (vtype)emit->code);
+ emit->code = new_code(emit, name);
+}
+
+ANN static inline void emit_pop_code(const Emitter emit) {
+ emit->code = (Code*)vector_pop(&emit->stack);
+}
+
+ANN static inline void emit_push_scope(const Emitter emit) {
+ frame_push(emit->code->frame);
+}
+
+ANN static inline m_uint emit_code_size(const Emitter emit) {
+ return vector_size(&emit->code->instr);
+}
+
+ANN static inline m_uint emit_code_offset(const Emitter emit) {
+ return emit->code->frame->curr_offset;
+}
+
+ANN static inline m_uint emit_local(const Emitter emit, const m_uint size, const m_bool is_obj) { GWDEBUG_EXE
+ return frame_local(emit->code->frame, size, is_obj);
+}
+
+ANN static void emit_pre_ctor(const Emitter emit, const Type type) { GWDEBUG_EXE
+ if(type->parent)
+ emit_pre_ctor(emit, type->parent);
+ if(type->nspc->pre_ctor)
+ emit_ext_ctor(emit, type->nspc->pre_ctor);
+ if(GET_FLAG(type, template) && GET_FLAG(type, builtin)) {
+ const Type t = template_parent(type);
+ if(t->nspc->pre_ctor)
+ emit_ext_ctor(emit, t->nspc->pre_ctor);
+ }
+}
+
+ANN static void emit_pre_constructor_array(const Emitter emit, const Type type) {
+ const m_uint start_index = emit_code_size(emit);
+ const Instr top = emit_add_instr(emit, ArrayTop);
+ top->m_val2 = (m_uint)type;
+ emit_pre_ctor(emit, type);
+ const Instr bottom = emit_add_instr(emit, ArrayBottom);
+ top->m_val = emit_code_size(emit);
+ bottom->m_val = start_index;
+ emit_add_instr(emit, ArrayPost);
+}
+
+ANN ArrayInfo* emit_array_extend_inner(const Emitter emit, const Type t, const Exp e) { GWDEBUG_EXE
+ CHECK_BO(emit_exp(emit, e, 0))
+ const Type base = array_base(t);
+ ArrayInfo* info = mp_alloc(ArrayInfo);
+ vector_init(&info->type);
+ for(m_uint i = 1; i < t->array_depth; ++i)
+ vector_add(&info->type, (vtype)array_type(base, i));
+ vector_add(&info->type, (vtype)t);
+ info->depth = (m_int)t->array_depth;
+ info->base = base;
+ const Instr alloc = emit_add_instr(emit, ArrayAlloc);
+ alloc->m_val = (m_uint)info;
+ if(isa(base, t_object) > 0) {
+ emit_pre_constructor_array(emit, base);
+ info->is_obj = 1;
+ }
+ return info;
+}
+
+ANN void emit_ext_ctor(const Emitter emit, const VM_Code code) { GWDEBUG_EXE
+ emit_add_instr(emit, RegDup);
+ const Instr push_f = emit_add_instr(emit, RegPushImm);
+ push_f->m_val = (m_uint)code;
+ const Instr offset = emit_add_instr(emit, RegSetImm);
+ offset->m_val = emit_code_offset(emit);
+ emit_add_instr(emit, !GET_FLAG(code, builtin) ? FuncUsr : FuncMember);
+}
+
+ANN m_bool emit_array_extend(const Emitter emit, const Type t, const Exp e) { GWDEBUG_EXE
+ CHECK_OB(emit_array_extend_inner(emit, t, e))
+ emit_add_instr(emit, PopArrayClass);
+ return 1;
+}
+
+ANN2(1,2) m_bool emit_instantiate_object(const Emitter emit, const Type type,
+ const Array_Sub array, const uint is_ref) {
+ if(type->array_depth) {
+ assert(array);
+ assert(array->exp);
+ ArrayInfo* info = emit_array_extend_inner(emit, type, array->exp);
+ CHECK_OB(info)
+ info->is_ref = !!is_ref;
+ } else if(!is_ref) {
+ const Instr instr = emit_add_instr(emit, ObjectInstantiate);
+ instr->m_val = (m_uint)type;
+ emit_pre_ctor(emit, type);
+ }
+ return GW_OK;
+}
+
+static inline enum Kind kindof(const m_uint size, const uint emit_var) {
+ if(emit_var)
+ return KIND_ADDR;
+ return size == SZ_INT ? KIND_INT : size == SZ_FLOAT ? KIND_FLOAT : KIND_OTHER;
+}
+
+ANN static Instr emit_kind(Emitter emit, const m_uint size, const uint addr, const f_instr func[]) {
+ const enum Kind kind = kindof(size, addr);
+ const Instr instr = emit_add_instr(emit, func[kind]);
+ instr->m_val2 = size;
+ return instr;
+}
+
+static const f_instr regpushimm[] = { RegPushImm, RegPushImm2, RegPushImm3, RegPushImm4 };
+static const f_instr regpushmem[] = { RegPushMem, RegPushMem2, RegPushMem3, RegPushMem4 };
+static const f_instr regpushbase[] = { RegPushBase, RegPushBase2, RegPushBase3, RegPushBase4 };
+static const f_instr dotstatic[] = { DotStatic, DotStatic2, DotStatic3, RegPushImm };
+static const f_instr dotmember[] = { DotMember, DotMember2, DotMember3, DotMember4 };
+static const f_instr allocmember[] = { RegPushImm, RegPushImm2, RegPushImm3, AllocMember4 };
+static const f_instr allocword[] = { AllocWord, AllocWord2, AllocWord3, AllocWord4 };
+
+ANN static m_bool emit_symbol_owned(const Emitter emit, const Exp_Primary* prim) { GWDEBUG_EXE
+ const Value v = prim->value;
+ const Exp exp = new_exp_prim_id(insert_symbol("this"), prim->self->pos);
+ const Exp dot = new_exp_dot(exp, prim->d.var);
+ exp->type = v->owner_class;
+ dot->d.exp_dot.t_base = v->owner_class;
+ dot->type = v->type;
+ dot->emit_var = prim->self->emit_var;
+ const m_bool ret = emit_exp_dot(emit, &dot->d.exp_dot);
+ free_exp(exp);
+ return ret;
+}
+
+ANN static m_bool emit_symbol_builtin(const Emitter emit, const Exp_Primary* prim) { GWDEBUG_EXE
+ const Value v = prim->value;
+ if(GET_FLAG(v, func)) {
+ const Instr instr = emit_add_instr(emit, RegPushImm);
+ instr->m_val = (m_uint)v->d.func_ref;
+ return GW_OK;
+ }
+ if(GET_FLAG(v, union)) {
+ const m_uint size = v->type->size;
+ const Instr instr = emit_kind(emit, size, prim->self->emit_var, dotstatic);
+ instr->m_val = (m_uint)v->d.ptr;
+ } else {
+ const m_uint size = v->type->size;
+ const Instr instr = emit_kind(emit, size, prim->self->emit_var, regpushimm);
+ if(!prim->self->emit_var && size == SZ_INT) {
+ if(isa(v->type, t_object) > 0) {
+ instr->execute = RegPushImm;
+ instr->m_val = (m_uint)v->d.ptr;
+ } else if(v->d.ptr)
+ instr->m_val = *(m_uint*)v->d.ptr;
+ } else if(v->d.ptr)
+ memcpy(&instr->m_val, v->d.ptr, v->type->size);
+ else
+ instr->m_val = (m_uint)v->d.ptr;
+ instr->m_val2 = size;
+ }
+ return GW_OK;
+}
+
+ANN static m_bool emit_symbol(const Emitter emit, const Exp_Primary* prim) { GWDEBUG_EXE
+ const Value v = prim->value;
+ if(v->owner_class)
+ return emit_symbol_owned(emit, prim);
+ if(GET_FLAG(v, builtin) || GET_FLAG(v, union))
+ return emit_symbol_builtin(emit, prim);
+ const m_uint size = v->type->size;
+ const Instr instr = emit_kind(emit, size, prim->self->emit_var, !GET_FLAG(v, global) ? regpushmem : regpushbase);
+ instr->m_val = v->offset;
+ return GW_OK;
+}
+
+ANEW ANN VM_Code emit_code(const Emitter emit) { GWDEBUG_EXE
+ Code* c = emit->code;
+ const VM_Code code = new_vm_code(&c->instr, c->stack_depth,
+ c->flag, c->name);
+ free_code(c);
+ return code;
+}
+
+ANN static VM_Code finalyze(const Emitter emit) {
+ emit_add_instr(emit, EOC);
+ const VM_Code code = emit_code(emit);
+ emit_pop_code(emit);
+ return code;
+}
+
+ANN static m_bool prim_array(const Emitter emit, const Exp_Primary * primary) {
+ const Array_Sub array = primary->d.array;
+ Exp e = array->exp;
+ CHECK_BB(emit_exp(emit, e, 0))
+ m_uint count = 0;
+ do ++count;
+ while((e = e->next));
+ const Type type = array->type;
+ const Type base = array_base(type);
+ const Instr push = emit_add_instr(emit, RegSetImm);
+ push->m_val = count;
+ const Instr instr = emit_add_instr(emit, ArrayInit);
+ instr->m_val = (m_uint)type;
+ instr->m_val2 = base->size;
+ emit_add_instr(emit, GcAdd);
+ return GW_OK;
+}
+
+ANN static m_uint get_depth(Type t) {
+ m_uint depth = 0;
+ do depth += t->array_depth;
+ while((t = t->parent));
+ return depth;
+}
+
+ANN static m_bool emit_exp_array(const Emitter emit, const Exp_Array* array) { GWDEBUG_EXE
+ const m_uint is_var = array->self->emit_var;
+ const m_uint depth = get_depth(array->base->type) - array->self->type->array_depth;
+ CHECK_BB(emit_exp(emit, array->base, 0))
+ CHECK_BB(emit_exp(emit, array->array->exp, 0))
+ if(depth == 1) {
+ const Instr instr = emit_add_instr(emit, ArrayAccess);
+ instr->m_val = is_var;
+ instr->m_val2 = is_var ? SZ_INT : array->self->type->size;
+ } else {
+ const Instr push = emit_add_instr(emit, RegSetImm);
+ push->m_val = depth;
+ const Instr instr = emit_add_instr(emit, ArrayAccessMulti);
+ instr->m_val = is_var || array->self->type->array_depth;
+ instr->m_val2 = (is_var || array->self->type->array_depth) ?
+ SZ_INT : array_base(array->base->type)->size;
+ }
+ return GW_OK;
+}
+
+ANN static m_bool prim_vec(const Emitter emit, const Exp_Primary * primary) { GWDEBUG_EXE
+ const Vec * vec = &primary->d.vec;
+ const ae_prim_t t = primary->primary_type;
+ CHECK_BB(emit_exp(emit, vec->exp, 0));
+ m_int n = (m_int)((t == ae_primary_vec ? 3 : 2) - vec->dim + 1);
+ while(--n > 0)
+ emit_add_instr(emit, RegPushImm2);
+ if(primary->self->emit_var) {
+ emit_local(emit, primary->self->type->size, 0);
+ const m_uint offset = emit_local(emit, SZ_INT, 0);
+ const Instr cpy = emit_add_instr(emit, VecCpy);
+ cpy->m_val = offset;
+ cpy->m_val2 = primary->self->type->size;
+ const Instr instr = emit_add_instr(emit, RegPushMem);
+ instr->m_val = offset;
+ }
+ return GW_OK;
+}
+
+ANN static m_bool prim_id(const Emitter emit, const Exp_Primary* prim) {
+ if(prim->d.var == insert_symbol("this"))
+ emit_add_instr(emit, RegPushMem);
+ else if(prim->d.var == insert_symbol("me"))
+ emit_add_instr(emit, RegPushMe);
+ else if(prim->d.var == insert_symbol("now"))
+ emit_add_instr(emit, RegPushNow);
+ else if(prim->d.var == insert_symbol("maybe"))
+ emit_add_instr(emit, RegPushMaybe);
+ else if(prim->d.var == insert_symbol("__func__")) {
+ const Instr instr = emit_add_instr(emit, RegPushStr);
+ instr->m_val = (m_uint)s_name(insert_symbol(emit->env->func ?
+ emit->env->func->name : emit->env->class_def ?
+ emit->env->class_def->name : emit->env->name));
+ } else
+ emit_symbol(emit, prim);
+ return GW_OK;
+}
+
+ANN static m_bool prim_num(const Emitter emit, const Exp_Primary * primary) {
+ const Instr instr = emit_add_instr(emit, RegPushImm);
+ instr->m_val = primary->d.num;
+ return GW_OK;
+}
+
+ANN static m_bool prim_float(const Emitter emit, const Exp_Primary* primary) {
+ const Instr instr = emit_add_instr(emit, RegPushImm2);
+ instr->f = primary->d.fnum;
+ return GW_OK;
+}
+
+ANN static m_bool prim_char(const Emitter emit, const Exp_Primary* prim) {
+ const m_int c = str2char(prim->d.chr, prim->self->pos);
+ CHECK_BB(c);
+ const Instr instr = emit_add_instr(emit, RegPushImm);
+ instr->m_val = (m_uint)c;
+ return GW_OK;
+}
+
+ANN static m_bool prim_str(const Emitter emit, const Exp_Primary* prim) { GWDEBUG_EXE
+ char c[strlen(prim->d.str)];
+ strcpy(c, prim->d.str);
+ CHECK_BB(escape_str(c, prim->self->pos));
+ const Instr instr = emit_add_instr(emit, RegPushStr);
+ instr->m_val = (m_uint)s_name(insert_symbol(c));
+ return GW_OK;
+}
+
+ANN static m_bool prim_gack(const Emitter emit, const Exp_Primary* primary) {
+ const Exp exp = primary->d.exp;
+ const Vector v = new_vector();
+ m_uint offset = 0;
+ Exp e = exp;
+ do {
+ vector_add(v, (vtype)e->type);
+ offset += e->type->size;
+ if(e->type != emit->env->class_def)
+ ADD_REF(e->type);
+ } while((e = e->next));
+ if(emit_exp(emit, exp, 0) < 0) {
+ free_vector(v);
+ ERR_B(exp->pos, "\t... in 'gack' expression.")
+ }
+ const Instr instr = emit_add_instr(emit, Gack);
+ instr->m_val = offset;
+ instr->m_val2 = (m_uint)v;
+ return GW_OK;
+}
+
+static const _exp_func prim_func[] = {
+ (_exp_func)prim_id, (_exp_func)prim_num, (_exp_func)prim_float, (_exp_func)prim_str,
+ (_exp_func)prim_array, (_exp_func)prim_gack, (_exp_func)prim_vec, (_exp_func)prim_vec,
+ (_exp_func)prim_vec, (_exp_func)prim_char, (_exp_func)dummy_func,
+};
+
+ANN static m_bool emit_exp_primary(const Emitter emit, const Exp_Primary* prim) { GWDEBUG_EXE
+ return prim_func[prim->primary_type](emit, prim);
+}
+
+ANN static m_bool emit_dot_static_data(const Emitter emit, const Value v, const uint emit_var) { GWDEBUG_EXE
+ const m_uint size = v->type->size;
+ const Instr instr = emit_kind(emit, size, emit_var, dotstatic);
+ instr->m_val = (m_uint)(v->owner_class->nspc->class_data + v->offset);
+ instr->m_val2 = size;
+ return GW_OK;
+}
+
+ANN static m_bool decl_static(const Emitter emit, const Var_Decl var_decl, const uint is_ref) {
+ const Value v = var_decl->value;
+ Code* code = emit->code;
+ emit->code = (Code*)vector_back(&emit->stack);
+ CHECK_BB(emit_instantiate_object(emit, v->type, var_decl->array, is_ref))
+ CHECK_BB(emit_dot_static_data(emit, v, 1))
+ emit_add_instr(emit, ObjectAssign);
+ emit->code = code;
+ return GW_OK;
+}
+
+ANN static m_bool emit_exp_decl_static(const Emitter emit, const Var_Decl var_decl, const uint is_ref) { GWDEBUG_EXE
+ const Value value = var_decl->value;
+ if(isa(value->type, t_object) > 0 && !is_ref)
+ CHECK_BB(decl_static(emit, var_decl, 0))
+ return emit_dot_static_data(emit, value, 1);
+}
+
+ANN static m_bool emit_exp_decl_non_static(const Emitter emit, const Var_Decl var_decl,
+ const uint is_ref, const uint emit_var) { GWDEBUG_EXE
+ const Value v = var_decl->value;
+ const Type type = v->type;
+ const Array_Sub array = var_decl->array;
+ const m_bool is_array = array && array->exp;
+ const m_bool is_obj = isa(type, t_object) > 0;
+ const uint emit_addr = ((is_ref && !array) || isa(type, t_object) < 0) ?
+ emit_var : 1;
+ if(is_obj && (is_array || !is_ref))
+ CHECK_BB(emit_instantiate_object(emit, type, array, is_ref))
+ f_instr *exec = (f_instr*)allocmember;
+ if(!GET_FLAG(v, member)) {
+ v->offset = emit_local(emit, v->type->size, is_obj);
+ exec = (f_instr*)(allocword);
+ }
+ const Instr instr = emit_kind(emit, v->type->size, emit_addr, exec);
+ instr->m_val = v->offset;
+ instr->m_val2 = v->type->size;
+ if(is_obj && (is_array || !is_ref)) {
+ const Instr assign = emit_add_instr(emit, ObjectAssign);
+ assign->m_val = (m_uint)emit_var;
+ if(is_array && !emit->env->scope)
+ ADD_REF(type)
+ }
+ return GW_OK;
+}
+
+ANN static m_bool emit_exp_decl_global(const Emitter emit, const Var_Decl var_decl,
+ const uint is_ref, const uint emit_var) { GWDEBUG_EXE
+ const Value v = var_decl->value;
+ const Type type = v->type;
+ const Array_Sub array = var_decl->array;
+ const m_bool is_array = array && array->exp;
+ const m_bool is_obj = isa(type, t_object) > 0;
+ const uint emit_addr = ((is_ref && !array) || isa(type, t_object) < 0) ?
+ emit_var : 1;
+ if(is_obj && (is_array || !is_ref))
+ CHECK_BB(emit_instantiate_object(emit, type, array, is_ref))
+ f_instr *exec = (f_instr*)dotstatic;
+ const Instr instr = emit_kind(emit, v->type->size, emit_addr, exec);
+ v->d.ptr = _mp_alloc(v->type->size);
+ SET_FLAG(v, union);
+ instr->m_val = (m_uint)v->d.ptr;
+ instr->m_val2 = v->type->size;
+ if(is_obj && (is_array || !is_ref)) {
+ const Instr assign = emit_add_instr(emit, ObjectAssign);
+ assign->m_val = (m_uint)emit_var;
+ if(is_array && !emit->env->scope)
+ ADD_REF(type)
+ const Instr instr = emit_add_instr(emit, RegAddRef);
+ instr->m_val = emit_var;
+ }
+ return GW_OK;
+}
+
+ANN static m_bool emit_class_def(const Emitter, const Class_Def);
+
+ANN static m_bool emit_exp_decl_template(const Emitter emit, const Exp_Decl* decl) { GWDEBUG_EXE
+ const Type t = typedef_base(decl->type);
+ if(!GET_FLAG(t, emit)) {
+ CHECK_BB(template_push_types(emit->env, t->def->tmpl->list.list, t->def->tmpl->base))
+ CHECK_BB(emit_class_def(emit, t->def))
+ emit_pop_type(emit);
+ }
+ return GW_OK;
+}
+
+ANN static m_bool emit_exp_decl(const Emitter emit, const Exp_Decl* decl) { GWDEBUG_EXE
+ Var_Decl_List list = decl->list;
+ const uint ref = GET_FLAG(decl->td, ref) || type_ref(decl->type);
+ const uint var = decl->self->emit_var;
+
+ if(GET_FLAG(decl->type, template))
+ CHECK_BB(emit_exp_decl_template(emit, decl))
+ m_uint scope;
+ const m_bool global = GET_FLAG(decl->td, global);
+ if(global)
+ scope = emit_push_global(emit);
+ do {
+ const uint r = (uint)(GET_FLAG(list->self->value, ref) + ref);
+ if(!GET_FLAG(list->self->value, used))
+ continue;
+ if(GET_FLAG(decl->td, static))
+ CHECK_BB(emit_exp_decl_static(emit, list->self, r))
+ else if(!global)
+ CHECK_BB(emit_exp_decl_non_static(emit, list->self, r, var))
+else
+ CHECK_BB(emit_exp_decl_global(emit, list->self, r, var))
+ } while((list = list->next));
+ if(global)
+ emit_pop(emit, scope);
+ return GW_OK;
+}
+
+ANN static m_uint vararg_size(const Exp_Call* exp_call, const Vector kinds) {
+ Exp e = exp_call->args;
+ Arg_List l = exp_call->m_func->def->arg_list;
+ m_uint size = 0;
+ while(e) {
+ if(!l) {
+ size += e->type->size;
+ vector_add(kinds, e->type->size);
+ } else
+ l = l->next;
+ e = e->next;
+ }
+ return size;
+}
+
+ANN static void emit_func_arg_vararg(const Emitter emit, const Exp_Call* exp_call) { GWDEBUG_EXE
+ const Instr instr = emit_add_instr(emit, VarargIni);
+ const Vector kinds = new_vector();
+ if((instr->m_val = vararg_size(exp_call, kinds)))
+ instr->m_val2 = (m_uint)kinds;
+ else {
+ instr->execute = VarargEmpty;
+ free_vector(kinds);
+ }
+}
+
+ANN static m_bool emit_func_args(const Emitter emit, const Exp_Call* exp_call) { GWDEBUG_EXE
+ if(exp_call->args)
+ CHECK_BB(emit_exp(emit, exp_call->args, 1))
+ if(GET_FLAG(exp_call->m_func->def, variadic))
+ emit_func_arg_vararg(emit, exp_call);
+ return GW_OK;
+}
+
+ANN static m_bool prepare_call(const Emitter emit, const Exp_Call* exp_call) { GWDEBUG_EXE
+ CHECK_BB(emit_func_args(emit, exp_call))
+ return emit_exp(emit, exp_call->func, 0);
+}
+
+ANN static inline m_int push_tmpl_func(const Emitter emit, const Func f,
+ const Type_List types) {
+ const Value v = f->value_ref;
+ const m_uint scope = emit_push(emit, v->owner_class, v->owner);
+ CHECK_BB(traverse_func_template(emit->env, f->def, types))
+ return (m_int)scope;
+}
+
+ANN static m_bool emit_exp_call_template(const Emitter emit, const Exp_Call* exp_call) {
+ if(emit->env->func && emit->env->func == exp_call->m_func)
+ return prepare_call(emit, exp_call);
+ const m_int scope = push_tmpl_func(emit, exp_call->m_func, exp_call->tmpl->types);
+ CHECK_BB(scope);
+ CHECK_BB(prepare_call(emit, exp_call))
+ emit_pop_type(emit);
+ emit_pop(emit, (m_uint)scope);
+ UNSET_FLAG(exp_call->m_func, checked);
+ return GW_OK;
+}
+
+ANN static m_bool emit_exp_call(const Emitter emit, const Exp_Call* exp_call) { GWDEBUG_EXE
+ if(!exp_call->tmpl)
+ CHECK_BB(prepare_call(emit, exp_call))
+ else
+ CHECK_BB(emit_exp_call_template(emit, exp_call))
+ return emit_exp_call1(emit, exp_call->m_func);
+}
+
+ANN static m_bool emit_binary_func(const Emitter emit, const Exp_Binary* bin) { GWDEBUG_EXE
+ const Exp_Call exp = { .func=bin->rhs, .args=bin->lhs, .m_func=bin->func, .tmpl=bin->tmpl, .self=bin->self };
+ return emit_exp_call(emit, &exp);
+}
+
+ANN static m_bool emit_exp_binary(const Emitter emit, const Exp_Binary* bin) { GWDEBUG_EXE
+ const Exp lhs = bin->lhs;
+ const Exp rhs = bin->rhs;
+ struct Op_Import opi = { .op=bin->op, .lhs=lhs->type, .rhs=rhs->type, .data = (uintptr_t)bin };
+ if(bin->op == op_chuck && isa(rhs->type, t_function) > 0)
+ return emit_binary_func(emit, bin);
+ CHECK_BB(emit_exp(emit, lhs, 1))
+ CHECK_BB(emit_exp(emit, rhs, 1))
+ return op_emit(emit, &opi);
+}
+
+ANN static m_bool emit_exp_cast(const Emitter emit, const Exp_Cast* cast) {
+ struct Op_Import opi = { .op=op_cast, .lhs=cast->exp->type, .rhs=cast->self->type, .data=(uintptr_t)cast};
+ CHECK_BB(emit_exp(emit, cast->exp, 0))
+ (void)op_emit(emit, &opi);
+ return GW_OK;
+}
+
+ANN static m_bool emit_exp_post(const Emitter emit, const Exp_Postfix* post) { GWDEBUG_EXE
+ struct Op_Import opi = { .op=post->op, .lhs=post->exp->type, .data=(uintptr_t)post };
+ CHECK_BB(emit_exp(emit, post->exp, 1))
+ return op_emit(emit, &opi);
+}
+
+ANN static m_bool emit_exp_dur(const Emitter emit, const Exp_Dur* dur) { GWDEBUG_EXE
+ CHECK_BB(emit_exp(emit, dur->base, 0))
+ CHECK_BB(emit_exp(emit, dur->unit, 0))
+ emit_add_instr(emit, FloatTimes);
+ return GW_OK;
+}
+
+ANN static m_bool is_special(const Type t) {
+ if(isa(t, t_complex) > 0 || isa(t, t_polar) > 0 ||
+ isa(t, t_vec3) > 0 || isa(t, t_vec4) > 0 ||
+ isa(t, t_vararg) > 0)
+ return GW_OK;
+ return GW_ERROR;
+}
+
+ANN static Type_List tmpl_tl(const Env env, const m_str name) {
+ const m_str start = strchr(name, '<');
+ const m_str end = strchr(name, '@');
+ char c[strlen(name)];
+ strcpy(c, start + 1);
+ c[strlen(start) - strlen(end) - 2] = '\0';
+ m_uint depth;
+ return str2tl(env, c, &depth);
+}
+
+ANN m_bool traverse_dot_tmpl(const Emitter emit, const struct dottmpl_ *dt) {
+ const m_uint scope = env_push_type(emit->env, dt->owner);
+ m_bool ret = GW_ERROR;
+ if(traverse_func_template(emit->env, dt->def, dt->tl) > 0) {
+ ret = emit_func_def(emit, dt->def);
+ nspc_pop_type(emit->env->curr);
+ }
+ env_pop(emit->env, scope);
+ return ret;
+}
+
+static inline m_bool push_func_code(const Emitter emit, const Func f) {
+ if(GET_FLAG(f, template) && f->value_ref->owner_class) {
+ const Instr _instr = (Instr)vector_back(&emit->code->instr);
+ assert(_instr->execute == DotTmpl);
+ size_t len = strlen(f->name);
+ size_t sz = len - strlen(f->value_ref->owner_class->name);
+ char c[sz + 1];
+ strncpy(c, f->name, sz);
+ c[sz] = '\0';
+ struct dottmpl_ *dt = mp_alloc(dottmpl);
+ dt->name = s_name(insert_symbol(c));
+ dt->overload = f->def->tmpl->base;
+ dt->tl = tmpl_tl(emit->env, c);
+ dt->base = f->def;
+ _instr->m_val = (m_uint)dt;
+ _instr->m_val2 = strlen(c);
+ return GW_OK;
+ }
+ const Instr _instr = (Instr)vector_back(&emit->code->instr);
+ if(_instr->opcode == (m_bit)(m_uint)RegPushImm)
+ return !!(_instr->m_val = (m_uint)f->code);
+ assert(_instr->opcode == (m_bit)(m_uint)RegPushBase);
+ _instr->m_val = (m_uint)f->code;
+ _instr->opcode = (m_bit)(m_uint)RegPushImm;
+ return GW_OK;
+}
+
+static m_bool emit_template_code(const Emitter emit, const Func f) {
+ if(GET_FLAG(f, ref))
+ CHECK_BB(traverse_template(emit->env, f->value_ref->owner_class->def))
+ const Value v = f->value_ref;
+ size_t scope = emit_push(emit, v->owner_class, v->owner);
+ CHECK_BB(emit_func_def(emit, f->def)) // orig
+ emit_pop(emit, scope);
+ return push_func_code(emit, f);
+}
+
+ ANN static Instr emit_call(const Emitter emit, const Func f) {
+ MEMOIZE_CALL
+ const Type t = actual_type(f->value_ref->type);
+ if(isa(t, t_fptr) < 0) {
+ const f_instr exec = GET_FLAG(f->def, builtin) ? GET_FLAG(f, member) ?
+ FuncMember : FuncStatic : FuncUsr;
+ return emit_add_instr(emit, exec);
+ }
+ /*const Instr ex = */emit_add_instr(emit, GWOP_EXCEPT);
+// ex->m_val = -SZ_INT;
+ return emit_add_instr(emit, FuncPtr);
+}
+
+ANN m_bool emit_exp_call1(const Emitter emit, const Func f) { GWDEBUG_EXE
+ if(!f->code || (GET_FLAG(f, ref) && !GET_FLAG(f, builtin))) {
+ if(GET_FLAG(f, template) && emit->env->func != f)
+ CHECK_BB(emit_template_code(emit, f))
+ } else if((f->value_ref->owner_class && is_special(f->value_ref->owner_class) > 0) ||
+ !f->value_ref->owner_class || GET_FLAG(f, template))
+ push_func_code(emit, f);
+ else {
+ const Instr back = (Instr)vector_back(&emit->code->instr);
+ if((f_instr)(m_uint)back->opcode == DotFunc)
+ back->m_val = f->vt_index;
+ }
+ if(GET_FLAG(f, member) && isa(actual_type(f->value_ref->type), t_fptr) > 0) {
+ const Instr back = (Instr)vector_back(&emit->code->instr);
+ m_bit exec = back->opcode;
+ m_uint val = back->m_val;
+ m_uint val2 = back->m_val2;
+ back->opcode = (m_bit)(m_uint)RegDup2;
+ back->m_val = f->def->stack_depth;
+ const Instr instr = emit_add_instr(emit, (f_instr)(m_uint)exec);
+ instr->m_val = val;
+ instr->m_val2 = val2;
+ }
+ const Instr offset = emit_add_instr(emit, RegSetImm);
+ offset->m_val = emit_code_offset(emit);
+ const Instr instr = emit_call(emit, f);
+ const m_uint size = instr->m_val = f->def->ret_type->size;
+ return (m_bool)(instr->m_val2 = kindof(size, !size));
+}
+
+ANN2(1,2) static m_bool emit_exp_spork_finish(const Emitter emit, const VM_Code code,
+ const m_uint depth, const m_bool f) {
+ const Instr ini = emit_add_instr(emit, SporkIni);
+ ini->m_val = (m_uint)code;
+ if(!f) {
+ const m_uint member = GET_FLAG(code, member) ? SZ_INT : 0;
+ const Instr pop = emit_add_instr(emit, RegPop);
+ pop->m_val = depth + member;
+ if(depth) {
+ const Instr spork = emit_add_instr(emit, SporkFunc);
+ spork->m_val = depth;
+ }
+ if(member) {
+ const Instr m = emit_add_instr(emit, SporkThis);
+ m->m_val = depth;
+ }
+ } else {
+ const Instr spork = emit_add_instr(emit, SporkExp);
+ spork->m_val = depth;
+ }
+ emit_add_instr(emit, SporkEnd);
+ return GW_OK;
+}
+
+static inline void stack_alloc(const Emitter emit) {
+ emit_local(emit, SZ_INT, 0);
+ emit->code->stack_depth += SZ_INT;
+}
+
+static inline void stack_alloc_this(const Emitter emit) {
+ SET_FLAG(emit->code, member);
+ stack_alloc(emit);
+}
+
+static m_bool scoped_stmt(const Emitter emit, const Stmt stmt, const m_bool pop) {
+ ++emit->env->scope;
+ emit_push_scope(emit);
+ const m_bool pure = SAFE_FLAG(emit->env->func, pure);
+ if(!pure)
+ emit_add_instr(emit, GcIni);
+ CHECK_BB(emit_stmt(emit, stmt, pop))
+ if(!pure)
+ emit_add_instr(emit, GcEnd);
+ emit_pop_scope(emit);
+ --emit->env->scope;
+ return GW_OK;
+}
+
+#define SPORK_FUNC_PREFIX "spork~func:%i"
+#define SPORK_CODE_PREFIX "spork~code:%i"
+
+static void push_spork_code(const Emitter emit, const m_str prefix, const int pos) {
+ char c[strlen(SPORK_FUNC_PREFIX) + num_digit(pos) + 1];
+ sprintf(c, prefix, pos);
+ emit_push_code(emit, c);
+}
+
+ANN static m_bool spork_func(const Emitter emit, const Exp_Call* exp) { GWDEBUG_EXE
+ CHECK_BB(prepare_call(emit, exp))
+ push_spork_code(emit, SPORK_FUNC_PREFIX, exp->self->pos);
+ if(GET_FLAG(exp->m_func, member))
+ SET_FLAG(emit->code, member);
+ const Instr p = emit_add_instr(emit, RegPushImm);
+ p->m_val = (m_uint)exp->m_func->code;
+ CHECK_BB(emit_exp_call1(emit, exp->m_func))
+ const VM_Code code = finalyze(emit);
+ const m_uint size = exp->m_func->def->stack_depth - (GET_FLAG(exp->m_func,
+ member) ? SZ_INT : 0);
+ return emit_exp_spork_finish(emit, code, size, 0);
+}
+
+ANN static m_bool spork_code(const Emitter emit, const Stmt stmt) { GWDEBUG_EXE
+ emit_add_instr(emit, RegPushImm);
+ push_spork_code(emit, SPORK_CODE_PREFIX, stmt->pos);
+ if(!SAFE_FLAG(emit->env->func, member))
+ stack_alloc_this(emit);
+ CHECK_BB(scoped_stmt(emit, stmt, 0))
+ const VM_Code code = finalyze(emit);
+ return emit_exp_spork_finish(emit, code, emit->code->stack_depth, 1);
+}
+
+ANN m_bool emit_exp_spork(const Emitter emit, const Exp_Unary* unary) {
+ return unary->code ? spork_code(emit, unary->code) :
+ spork_func(emit, &unary->exp->d.exp_call);
+}
+
+ANN static m_bool emit_exp_unary(const Emitter emit, const Exp_Unary* unary) { GWDEBUG_EXE
+ struct Op_Import opi = { .op=unary->op, .data=(uintptr_t)unary };
+ if(unary->op != op_spork && unary->exp) {
+ CHECK_BB(emit_exp(emit, unary->exp, 1))
+ opi.rhs = unary->exp->type;
+ }
+ return op_emit(emit, &opi);
+}
+
+ANN static m_bool emit_implicit_cast(const Emitter emit,
+ const restrict Type from, const restrict Type to) { GWDEBUG_EXE
+ struct Op_Import opi = { .op=op_impl, .lhs=from, .rhs=to, .data=(m_uint)from };
+ return op_emit(emit, &opi);
+}
+
+ANN static Instr emit_flow(const Emitter emit, const Type type,
+ const f_instr f1, const f_instr f2) { GWDEBUG_EXE
+ if(isa(type, t_float) > 0) {
+ return emit_add_instr(emit, f2);
+ }
+ return emit_add_instr(emit, f1);
+}
+
+ANN static m_bool emit_exp_if(const Emitter emit, const Exp_If* exp_if) { GWDEBUG_EXE
+ CHECK_BB(emit_exp(emit, exp_if->cond, 0))
+ const Instr op = emit_flow(emit, exp_if->cond->type, BranchEqInt, BranchEqFloat);
+ CHECK_BB(emit_exp(emit, exp_if->if_exp, 0))
+ const Instr op2 = emit_add_instr(emit, Goto);
+ op->m_val = emit_code_size(emit);
+ const m_bool ret = emit_exp(emit, exp_if->else_exp, 0);
+ op2->m_val = emit_code_size(emit);
+ return ret;
+}
+
+ANN static m_bool emit_exp_lambda(const Emitter emit, const Exp_Lambda * lambda) { GWDEBUG_EXE
+ if(lambda->def) {
+ const m_uint scope = !lambda->owner ?
+ emit->env->scope : emit_push_type(emit, lambda->owner);
+ CHECK_BB(emit_func_def(emit, lambda->def))
+ const Instr instr = emit_add_instr(emit, RegPushImm);
+ instr->m_val = (m_uint)lambda->def->func->code;
+ if(lambda->owner)
+ emit_pop(emit, scope);
+ } else
+ emit_add_instr(emit, RegPushImm);
+ return GW_OK;
+}
+
+DECL_EXP_FUNC(emit)
+
+ANN2(1) static m_bool emit_exp(const Emitter emit, Exp exp, const m_bool ref) { GWDEBUG_EXE
+ do {
+ CHECK_BB(exp_func[exp->exp_type](emit, &exp->d))
+ if(exp->cast_to)
+ CHECK_BB(emit_implicit_cast(emit, exp->type, exp->cast_to))
+ if(ref && isa(exp->type, t_object) > 0) {
+ const Instr instr = emit_add_instr(emit, RegAddRef);
+ instr->m_val = exp->emit_var;
+ }
+ if(emit->env->func && isa(exp->type, t_function) > 0 &&
+ !GET_FLAG(exp->type->d.func->value_ref->d.func_ref, pure))
+ UNSET_FLAG(emit->env->func, pure);
+ } while((exp = exp->next));
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_if(const Emitter emit, const Stmt_If stmt) { GWDEBUG_EXE
+ emit_push_scope(emit);
+ CHECK_BB(emit_exp(emit, stmt->cond, 0))
+ const Instr op = emit_flow(emit, isa(stmt->cond->type, t_object) > 0 ?
+ t_int : stmt->cond->type, BranchEqInt, BranchEqFloat);
+ CHECK_OB(op)
+ CHECK_BB(scoped_stmt(emit, stmt->if_body, 1))
+ const Instr op2 = emit_add_instr(emit, Goto);
+ op->m_val = emit_code_size(emit);
+ if(stmt->else_body)
+ CHECK_BB(scoped_stmt(emit, stmt->else_body, 1))
+ op2->m_val = emit_code_size(emit);
+ emit_pop_scope(emit);
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_code(const Emitter emit, const Stmt_Code stmt) { GWDEBUG_EXE
+ ++emit->env->scope;
+ const m_bool ret = stmt->stmt_list ? emit_stmt_list(emit, stmt->stmt_list) : 1;
+ --emit->env->scope;
+ return ret;
+}
+
+#ifdef OPTIMIZE
+ANN static m_bool optimize_taill_call(const Emitter emit, const Exp_Call* e) {
+ Exp arg = e->args;
+ if(arg)
+ CHECK_BB(emit_exp(emit, e->args, 0))
+ const Instr instr = emit_add_instr(emit, PutArgsInMem);
+ while(arg) {
+ instr->m_val += arg->type->size;
+ arg = arg->next;
+ }
+ emit_add_instr(emit, Goto);
+ return GW_OK;
+}
+#define OPTIMIZE_TCO\
+ if(stmt->val->exp_type == ae_exp_call && emit->env->func == stmt->val->d.exp_call.m_func)\
+ return optimize_taill_call(emit, &stmt->val->d.exp_call);
+#else
+#define OPTIMIZE_TCO
+#endif
+
+
+ANN static m_bool emit_stmt_return(const Emitter emit, const Stmt_Exp stmt) { GWDEBUG_EXE
+ if(stmt->val) {
+ OPTIMIZE_TCO
+ CHECK_BB(emit_exp(emit, stmt->val, 0))
+ if(isa(stmt->val->type, t_object) > 0)
+ emit_add_instr(emit, RegAddRef);
+ }
+ vector_add(&emit->code->stack_return, (vtype)emit_add_instr(emit, Goto));
+ return GW_OK;
+}
+
+ANN static inline m_bool emit_stmt_continue(const Emitter emit, const Stmt stmt __attribute__((unused))) { GWDEBUG_EXE
+ vector_add(&emit->code->stack_cont, (vtype)emit_add_instr(emit, Goto));
+ return GW_OK;
+}
+
+ANN static inline m_bool emit_stmt_break(const Emitter emit, const Stmt stmt __attribute__((unused))) { GWDEBUG_EXE
+ vector_add(&emit->code->stack_break, (vtype)emit_add_instr(emit, Goto));
+ return GW_OK;
+}
+
+ANN static inline void emit_push_stack(const Emitter emit) {
+ emit_push_scope(emit);
+ vector_add(&emit->code->stack_cont, (vtype)NULL);
+ vector_add(&emit->code->stack_break, (vtype)NULL);
+}
+
+ANN static void pop_vector(Vector v, const m_uint pc) {
+ Instr instr;
+ while((instr = (Instr)vector_pop(v)))
+ instr->m_val = pc;
+}
+
+ANN static void emit_pop_stack(const Emitter emit, const m_uint index) {
+ pop_vector(&emit->code->stack_cont, index);
+ pop_vector(&emit->code->stack_break, emit_code_size(emit));
+ emit_pop_scope(emit);
+}
+
+ANN static Instr _flow(const Emitter emit, const Exp e, const m_bool b) {
+ CHECK_BO(emit_exp(emit, e, 0))
+ const f_instr instr_i = b ? BranchEqInt : BranchNeqInt;
+ const f_instr instr_f = b ? BranchEqFloat : BranchNeqFloat;
+ return emit_flow(emit, e->type, instr_i, instr_f);
+}
+
+ANN static m_bool emit_stmt_flow(const Emitter emit, const Stmt_Flow stmt) { GWDEBUG_EXE
+ const m_uint index = emit_code_size(emit);
+ Instr op = NULL;
+ emit_push_stack(emit);
+ if(!stmt->is_do)
+ op = _flow(emit, stmt->cond, stmt->self->stmt_type == ae_stmt_while);
+ CHECK_BB(scoped_stmt(emit, stmt->body, 1))
+ if(stmt->is_do) {
+ CHECK_OB((op = _flow(emit, stmt->cond, stmt->self->stmt_type != ae_stmt_while)))
+ op->m_val = index;
+ } else {
+ const Instr goto_ = emit_add_instr(emit, Goto);
+ goto_->m_val = index;
+ op->m_val = emit_code_size(emit);
+ }
+ emit_pop_stack(emit, index);
+ return GW_OK;
+}
+
+ANN static m_uint get_decl_size(Var_Decl_List a) {
+ m_uint size = 0;
+ do if(GET_FLAG(a->self->value, used))
+ size += a->self->value->type->size;
+ while((a = a->next));
+ return size;
+}
+
+ANN static m_uint pop_exp_size(const Emitter emit, Exp e) {
+ m_uint size = 0;
+ do {
+ if(e->exp_type == ae_exp_primary &&
+ e->d.exp_primary.primary_type == ae_primary_hack) {
+ size += pop_exp_size(emit, e->d.exp_primary.d.exp);
+ continue;
+ }
+ size += (e->exp_type == ae_exp_decl ?
+ get_decl_size(e->d.exp_decl.list) : e->type->size);
+ } while((e = e->next));
+ return size;
+}
+
+ANN static void pop_exp(const Emitter emit, Exp e) {
+ const m_uint size = pop_exp_size(emit, e);
+ if(size) {
+ const Instr instr = emit_add_instr(emit, RegPop);
+ instr->m_val = size;
+ }
+}
+
+ANN static m_bool emit_stmt_for(const Emitter emit, const Stmt_For stmt) { GWDEBUG_EXE
+ emit_push_stack(emit);
+ CHECK_BB(emit_stmt(emit, stmt->c1, 1))
+ const m_uint index = emit_code_size(emit);
+ CHECK_BB(emit_stmt(emit, stmt->c2, 0))
+ const Instr op = emit_flow(emit, stmt->c2->d.stmt_exp.val->type,
+ BranchEqInt, BranchEqFloat);
+ CHECK_BB(scoped_stmt(emit, stmt->body, 1))
+ const m_uint action_index = emit_code_size(emit);
+ if(stmt->c3) {
+ CHECK_BB(emit_exp(emit, stmt->c3, 0))
+ pop_exp(emit, stmt->c3);
+ }
+ const Instr _goto = emit_add_instr(emit, Goto);
+ _goto->m_val = index;
+ op->m_val = emit_code_size(emit);
+ emit_pop_stack(emit, action_index);
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_auto(const Emitter emit, const Stmt_Auto stmt) { GWDEBUG_EXE
+ CHECK_BB(emit_exp(emit, stmt->exp, 0))
+ const Instr s1 = emit_add_instr(emit, MemSetImm);
+ const Instr s2 = emit_add_instr(emit, MemSetImm);
+ const m_uint ini_pc = emit_code_size(emit);
+ emit_push_stack(emit);
+ const Instr loop = emit_add_instr(emit, AutoLoopStart);
+ const m_uint offset = emit_local(emit, 2*SZ_INT, 0);
+ s2->m_val = stmt->v->offset = offset + SZ_INT;
+ CHECK_BB(emit_stmt(emit, stmt->body, 1))
+ const m_uint end_pc = emit_code_size(emit);
+ if(stmt->is_ptr) {
+ const Instr release = emit_add_instr(emit, ObjectRelease);
+ release->m_val = offset + SZ_INT;
+ }
+ const Instr end = emit_add_instr(emit, AutoLoopEnd);
+ const Instr tgt = emit_add_instr(emit, Goto);
+ end->m_val2 = emit_code_size(emit);
+ tgt->m_val = ini_pc;
+ s1->m_val = end->m_val = loop->m_val = offset;
+ if(stmt->is_ptr)
+ loop->m_val2 = (m_uint)stmt->v->type;
+ emit_pop_stack(emit, end_pc);
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_loop(const Emitter emit, const Stmt_Loop stmt) { GWDEBUG_EXE
+ emit_push_stack(emit);
+ CHECK_BB(emit_exp(emit, stmt->cond, 0))
+ m_int* counter = (m_int*)xcalloc(1, SZ_INT);
+ const Instr init = emit_add_instr(emit, InitLoopCounter);
+ init->m_val = (m_uint)counter;
+ const m_uint index = emit_code_size(emit);
+ const Instr deref = emit_add_instr(emit, DotStatic);
+ deref->m_val = (m_uint)counter;
+ deref->m_val2 = SZ_INT;
+ const Instr op = emit_add_instr(emit, BranchEqInt);
+ const Instr dec = emit_add_instr(emit, DecIntAddr);
+ dec->m_val = (m_uint)counter;
+ CHECK_BB(scoped_stmt(emit, stmt->body, 1))
+ const Instr _goto = emit_add_instr(emit, Goto);
+ _goto->m_val = index;
+ op->m_val = emit_code_size(emit);
+ emit_pop_stack(emit, index);
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_jump(const Emitter emit, const Stmt_Jump stmt) { GWDEBUG_EXE
+ if(!stmt->is_label)
+ stmt->data.instr = emit_add_instr(emit, Goto);
+ else {
+ if(switch_inside(emit->env, stmt->self->pos) && !strcmp(s_name(stmt->name), "default")) {
+ if(!strcmp(s_name(stmt->name), "default"))
+ vector_release(&stmt->data.v);
+ return switch_default(emit->env, emit_code_size(emit), stmt->self->pos);
+ }
+ if(!stmt->data.v.ptr)
+ ERR_B(stmt->self->pos, "illegal case")
+ const m_uint size = vector_size(&stmt->data.v);
+ if(!size) {
+ vector_release(&stmt->data.v);
+ ERR_B(stmt->self->pos, "label '%s' defined but not used.", s_name(stmt->name))
+ }
+ LOOP_OPTIM
+ for(m_uint i = size + 1; --i;) {
+ const Stmt_Jump label = (Stmt_Jump)vector_at(&stmt->data.v, i - 1);
+ if(!label->data.instr) {
+ vector_release(&stmt->data.v);
+ ERR_B(label->self->pos, "you are trying to use a upper label.")
+ }
+ label->data.instr->m_val = emit_code_size(emit);
+ }
+ vector_release(&stmt->data.v);
+ }
+ return GW_OK;
+}
+
+ANN static m_bool emit_switch_instr(const Emitter emit, Instr *instr) {
+ const m_uint dyn = switch_dyn(emit->env);
+ if(dyn) {
+ Exp e;
+ while((e = switch_expget(emit->env)))
+ CHECK_BB(emit_exp(emit, e, 0))
+ *instr = emit_add_instr(emit, SwitchIni);
+ } else {
+ const Instr instr = emit_add_instr(emit, RegSetImm);
+ instr->m_val = (m_uint)switch_map(emit->env);
+ }
+ return GW_OK;
+}
+
+ANN static void emit_switch_map(const Instr instr, const Map map) {
+ const Map m = new_map();
+ for(m_uint i = map_size(map) + 1; --i;)
+ map_set(m, VKEY(map, i-1), VVAL(map, i -1));
+ instr->m_val2 = (m_uint)m;
+}
+
+ANN static m_bool emit_stmt_switch(const Emitter emit, const Stmt_Switch stmt) { GWDEBUG_EXE
+ switch_get(emit->env, stmt);
+ Instr push = NULL;
+ CHECK_BB(emit_exp(emit, stmt->val, 0))
+ CHECK_BB(emit_switch_instr(emit, &push))
+ vector_add(&emit->code->stack_break, (vtype)NULL);
+ const Instr instr = emit_add_instr(emit, BranchSwitch);
+ instr->m_val2 = (m_uint)switch_map(emit->env);
+ CHECK_BB(emit_stmt(emit, stmt->stmt, 1))
+ instr->m_val = switch_idx(emit->env) ?: emit_code_size(emit);
+ if(push) {
+ emit_switch_map(push, (Map)instr->m_val2);
+ (push)->m_val = (m_uint)switch_vec(emit->env);
+ }
+ switch_end(emit->env);
+ pop_vector(&emit->code->stack_break, emit_code_size(emit));
+ return GW_OK;
+}
+
+ANN m_bool value_value(const Value v, m_int *value) {
+ if((!GET_FLAG(v, builtin) && !GET_FLAG(v, enum)) || GET_FLAG(v, member))
+ return 0;
+ *value = v->d.ptr ? *(m_int*)v->d.ptr : v->owner->class_data[v->offset];
+ return GW_OK;
+}
+
+ANN Value case_value(const Exp exp) {
+ if(exp->exp_type == ae_exp_primary) {
+ const Exp_Primary* prim = &exp->d.exp_primary;
+ if(prim->primary_type == ae_primary_num)
+ return NULL;
+ return prim->value;
+ }
+ const Exp_Dot* dot = &exp->d.exp_dot;
+ return find_value(actual_type(dot->t_base), dot->xid);
+}
+
+ANN static m_bool prim_value(const Exp exp, m_int *value) {
+ *value = (m_int)exp->d.exp_primary.d.num;
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_case(const Emitter emit, const Stmt_Exp stmt) { GWDEBUG_EXE
+ CHECK_BB(switch_inside(emit->env, stmt->self->pos))
+ m_int value = 0;
+ const Value v = case_value(stmt->val);
+ if((!v && prim_value(stmt->val, &value)) || value_value(v, &value)) {
+ CHECK_BB(switch_dup(emit->env, value, stmt->val->pos))
+ switch_dynpc(emit->env, value, emit_code_size(emit));
+ } else
+ switch_pc(emit->env, emit_code_size(emit));
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_type(const Emitter emit, const Stmt_Type stmt) { GWDEBUG_EXE
+ return stmt->type->def ? emit_class_def(emit, stmt->type->def) : 1;
+}
+
+ANN static m_bool emit_stmt_enum(const Emitter emit, const Stmt_Enum stmt) { GWDEBUG_EXE
+ LOOP_OPTIM
+ for(m_uint i = 0; i < vector_size(&stmt->values); ++i) {
+ const Value v = (Value)vector_at(&stmt->values, i);
+ if(!emit->env->class_def) {
+ ALLOC_PTR(addr, m_uint, i);
+ v->offset = emit_local(emit, SZ_INT, 0);
+ v->d.ptr = addr;
+ } else
+ *(m_uint*)(emit->env->class_def->nspc->class_data + v->offset) = i;
+ }
+ return GW_OK;
+}
+
+ANN void emit_union_offset(Decl_List l, const m_uint o) { GWDEBUG_EXE
+ do {
+ Var_Decl_List v = l->self->d.exp_decl.list;
+ do v->self->value->offset = o;
+ while((v = v->next));
+ } while((l = l->next));
+}
+
+ANN static m_bool emit_stmt_union(const Emitter emit, const Stmt_Union stmt) { GWDEBUG_EXE
+ Decl_List l = stmt->l;
+ m_uint scope = emit->env->scope;
+ const m_bool global = GET_FLAG(stmt, global);
+ if(stmt->xid) {
+ if(stmt->value->type->nspc->class_data_size && !stmt->value->type->nspc->class_data)
+ stmt->value->type->nspc->class_data =
+ (m_bit*)xcalloc(1, stmt->value->type->nspc->class_data_size);
+ stmt->value->type->nspc->offset = stmt->s;
+ if(!stmt->value->type->p)
+ stmt->value->type->p = mp_ini((uint32_t)stmt->value->type->size);
+ Type_Decl *type_decl = new_type_decl(new_id_list(stmt->xid, stmt->self->pos),
+ emit->env->class_def ? ae_flag_member : 0); // or zero ?
+ type_decl->flag = stmt->flag;
+ const Var_Decl var_decl = new_var_decl(stmt->xid, NULL, 0);
+ const Var_Decl_List var_decl_list = new_var_decl_list(var_decl, NULL);
+ const Exp exp = new_exp_decl(type_decl, var_decl_list);
+ exp->d.exp_decl.type = stmt->value->type;
+ var_decl->value = stmt->value;
+ CHECK_BB(emit_exp_decl(emit, &exp->d.exp_decl))
+ free_exp(exp);
+ if(global) {
+ const M_Object o = new_object(NULL, stmt->value->type);
+ stmt->value->d.ptr = (m_uint*)o;
+ SET_FLAG(stmt->value, builtin);
+ SET_FLAG(stmt->value, global);
+ }
+ scope = emit_push_type(emit, stmt->value->type);
+ } else if(stmt->type_xid) {
+ if(stmt->type->nspc->class_data_size && !stmt->type->nspc->class_data)
+ stmt->type->nspc->class_data =
+ (m_bit*)xcalloc(1, stmt->type->nspc->class_data_size);
+ stmt->type->nspc->offset = stmt->s;
+ if(!stmt->type->p)
+ stmt->type->p = mp_ini((uint32_t)stmt->type->size);
+ scope = emit_push_type(emit, stmt->type);
+ } else if(emit->env->class_def) {
+ if(!GET_FLAG(l->self->d.exp_decl.list->self->value, member))
+ stmt->o = emit_local(emit, stmt->s, 0);
+ } else if(global) {
+ void* ptr = (void*)xcalloc(1, stmt->s);
+ l = stmt->l;
+ do {
+ Var_Decl_List list = l->self->d.exp_decl.list;
+ list->self->value->d.ptr = ptr;
+ SET_FLAG(list->self->value, union);
+ } while((l = l->next));
+ SET_FLAG(stmt->l->self->d.exp_decl.list->self->value, enum);
+ }
+ emit_union_offset(stmt->l, stmt->o);
+ if(stmt->xid) {
+ const Instr instr = emit_add_instr(emit, RegPop);
+ instr->m_val = SZ_INT;
+ }
+ if(stmt->xid || stmt->type_xid || global)
+ emit_pop(emit, scope);
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_exp(const Emitter emit, const struct Stmt_Exp_* exp) {
+ return exp->val ? emit_exp(emit, exp->val, 0) : 1;
+}
+
+static const _exp_func stmt_func[] = {
+ (_exp_func)emit_stmt_exp, (_exp_func)emit_stmt_flow, (_exp_func)emit_stmt_flow,
+ (_exp_func)emit_stmt_for, (_exp_func)emit_stmt_auto, (_exp_func)emit_stmt_loop,
+ (_exp_func)emit_stmt_if, (_exp_func)emit_stmt_code, (_exp_func)emit_stmt_switch,
+ (_exp_func)emit_stmt_break, (_exp_func)emit_stmt_continue, (_exp_func)emit_stmt_return,
+ (_exp_func)emit_stmt_case, (_exp_func)emit_stmt_jump, (_exp_func)emit_stmt_enum,
+ (_exp_func)dummy_func, (_exp_func)emit_stmt_type, (_exp_func)emit_stmt_union,
+};
+
+ANN static m_bool emit_stmt(const Emitter emit, const Stmt stmt, const m_bool pop) { GWDEBUG_EXE
+ CHECK_BB(stmt_func[stmt->stmt_type](emit, &stmt->d))
+ if(pop && stmt->stmt_type == ae_stmt_exp && stmt->d.stmt_exp.val)
+ pop_exp(emit, stmt->d.stmt_exp.val);
+ return GW_OK;
+}
+
+ANN static m_bool emit_stmt_list(const Emitter emit, Stmt_List l) { GWDEBUG_EXE
+ do CHECK_BB(emit_stmt(emit, l->stmt, 1))
+ while((l = l->next));
+ return GW_OK;
+}
+
+ANN static m_bool emit_dot_static_import_data(const Emitter emit, const Value v, const uint emit_addr) { GWDEBUG_EXE
+ if(v->d.ptr && GET_FLAG(v, builtin)) { // from C
+ if(GET_FLAG(v, enum)) {
+ const Instr func_i = emit_add_instr(emit, RegPushImm);
+ func_i->m_val = (m_uint)v->d.ptr;
+ } else {
+ const m_uint size = v->type->size;
+ const Instr instr = emit_kind(emit, size, emit_addr, regpushimm);
+ instr->m_val = (isa(v->type, t_object) > 0 ?
+ (m_uint)&v->d.ptr : (m_uint)v->d.ptr);
+ instr->m_val2 = size;
+ }
+ return GW_OK;
+ }
+ return emit_dot_static_data(emit, v, emit_addr);
+}
+
+ANN static m_bool emit_complex_member(const Emitter emit, const Exp_Dot* member) { GWDEBUG_EXE
+ const Exp base = member->base;
+ base->emit_var = 1;
+ CHECK_BB(emit_exp(emit, base, 0))
+ const m_bool is_complex = !strcmp((isa(base->type, t_complex) > 0 ? "re" : "phase") ,
+ s_name(member->xid));
+ if(is_complex && member->self->emit_var) // skip
+ return GW_OK;
+ const Instr instr = emit_add_instr(emit, is_complex ? ComplexReal : ComplexImag);
+ instr->m_val = member->self->emit_var;
+ return GW_OK;
+}
+
+ANN static inline void emit_vec_func(const Emitter emit, const Value v) {
+ const Instr instr = emit_add_instr(emit, RegPushImm);
+ instr->m_val = (m_uint)v->d.func_ref->code;
+}
+
+ANN static m_bool emit_VecMember(const Emitter emit, const Exp_Dot* member) {
+ member->base->emit_var = 1;
+ CHECK_BB(emit_exp(emit, member->base, 0))
+ const Value v = find_value(member->base->type, member->xid);
+ if(GET_FLAG(v, func)) {
+ emit_vec_func(emit, v);
+ return GW_OK;
+ }
+ if(!v->offset && member->self->emit_var) // skip
+ return GW_OK;
+ const Instr instr = emit_add_instr(emit, VecMember);
+ instr->m_val2 = v->offset;
+ instr->m_val = member->self->emit_var;
+ return GW_OK;
+}
+
+ANN static m_bool emit_vararg_start(const Emitter emit, const m_uint offset) { GWDEBUG_EXE
+ if(emit->env->func->variadic)
+ ERR_B(0, "vararg.start already used. this is an error")
+ emit->env->func->variadic = emit_add_instr(emit, VarargTop);
+ emit->env->func->variadic->m_val = offset;
+ emit->env->func->variadic->m_val2 = emit_code_size(emit);
+ return GW_OK;
+}
+
+ANN static m_bool emit_vararg_end(const Emitter emit, const m_uint offset) { GWDEBUG_EXE
+ if(!emit->env->func->variadic)
+ ERR_B(0, "vararg.start not used before vararg.end. this is an error")
+ const Instr instr = emit_add_instr(emit, VarargEnd);
+ instr->m_val = offset;
+ instr->m_val2 = emit->env->func->variadic->m_val2;
+ emit->env->func->variadic->m_val2 = emit_code_size(emit);
+ return GW_OK;
+}
+
+ANN static m_bool emit_vararg(const Emitter emit, const Exp_Dot* member) { GWDEBUG_EXE
+ m_uint offset = emit->env->class_def ? SZ_INT : 0;
+ Arg_List l = emit->env->func->def->arg_list;
+ const m_str str = s_name(member->xid);
+ while(l) {
+ offset += l->type->size;
+ l = l->next;
+ }
+ if(!strcmp(str, "start"))
+ return emit_vararg_start(emit, offset);
+ if(!strcmp(str, "end"))
+ return emit_vararg_end(emit, offset);
+ const Instr instr = emit_add_instr(emit, VarargMember);
+ instr->m_val = offset;
+ instr->m_val2 = member->self->type->size;
+ return GW_OK;
+}
+
+ANN static m_bool emit_exp_dot_special(const Emitter emit, const Exp_Dot* member) { GWDEBUG_EXE
+ const Type t = member->t_base;
+ if(isa(t, t_complex) > 0 || isa(t, t_polar) > 0)
+ return emit_complex_member(emit, member);
+ else if(isa(t, t_vec3) > 0 || isa(t, t_vec4) > 0)
+ return emit_VecMember(emit, member);
+ return emit_vararg(emit, member);
+}
+
+ANN static m_bool emit_dot_static_func(const Emitter emit, const Func func) { GWDEBUG_EXE
+// if(func->code) {
+// const Instr func_i = emit_add_instr(emit, RegPushImm);
+// func_i->m_val = (m_uint)func->code;
+// } else {
+ // TODO: improve me
+ const Instr func_i = emit_add_instr(emit, PushStaticCode);
+ func_i->m_val = (m_uint)func;
+// }
+ return GW_OK;
+}
+
+ANN static m_bool emit_member_func(const Emitter emit, const Exp_Dot* member, const Func func) { GWDEBUG_EXE
+ if(emit_exp(emit, member->base, 0) < 0)
+ ERR_B(member->self->pos, "... in member function") // LCOV_EXCL_LINE
+ /*const Instr ex = */emit_add_instr(emit, GWOP_EXCEPT);
+ //ex->m_val = -SZ_INT;
+ if(GET_FLAG(member->base->type, force)) {
+ const Instr func_i = emit_add_instr(emit, RegPushImm);
+ func_i->m_val = (m_uint)func->code;
+ return GW_OK;
+ }
+ if(!func->def->tmpl) {
+ const Instr func_i = emit_add_instr(emit, DotFunc);
+ func_i->m_val = func->vt_index;
+ return GW_OK;
+ }
+ emit_add_instr(emit, DotTmpl);
+ return GW_OK;
+}
+
+ANN static inline void emit_member(const Emitter emit, const Value v, const uint emit_addr) {
+ /*const Instr ex = */emit_add_instr(emit, GWOP_EXCEPT);
+ //ex->m_val = -SZ_INT;
+ const m_uint size = v->type->size;
+ const Instr instr = emit_kind(emit, size, emit_addr, dotmember);
+ instr->m_val = v->offset;
+ instr->m_val2 = size;
+}
+
+ANN static m_bool emit_exp_dot_instance(const Emitter emit, const Exp_Dot* member) { GWDEBUG_EXE
+ const Type t_base = member->t_base;
+ const Value value = find_value(t_base, member->xid);
+ const uint emit_addr = member->self->emit_var;
+ if(isa(member->self->type, t_fptr) > 0) { // function pointer
+ if(GET_FLAG(value, member)) { // member
+ if(emit_exp(emit, member->base, 0) < 0)
+ ERR_B(member->self->pos, "... in member function") // LCOV_EXCL_LINE
+ emit_member(emit, value, emit_addr);
+ return GW_OK;
+ } else
+ return emit_dot_static_data(emit, value, emit_addr);
+ } else if(isa(member->self->type, t_function) > 0)
+ return emit_member_func(emit, member, value->d.func_ref);
+ else { // variable
+ if(GET_FLAG(value, member)) { // member
+ CHECK_BB(emit_exp(emit, member->base, 0))
+ emit_member(emit, value, emit_addr);
+ return GW_OK;
+ } else // static
+ CHECK_BB(emit_dot_static_import_data(emit, value, emit_addr))
+ }
+ return GW_OK;
+}
+
+ANN static m_bool emit_exp_dot_static(const Emitter emit, const Exp_Dot* member) { GWDEBUG_EXE
+ const Type t_base = member->t_base->d.base_type;
+ const Value value = find_value(t_base, member->xid);
+ if(isa(member->self->type, t_function) > 0 && isa(member->self->type, t_fptr) < 0)
+ return emit_dot_static_func(emit, value->d.func_ref);
+ return emit_dot_static_import_data(emit, value, member->self->emit_var);
+}
+
+ANN static m_bool emit_exp_dot(const Emitter emit, const Exp_Dot* member) { GWDEBUG_EXE
+ if(is_special(member->t_base) > 0)
+ return emit_exp_dot_special(emit, member);
+ if(isa(member->t_base, t_class) > 0)
+ return emit_exp_dot_static(emit, member);
+ return emit_exp_dot_instance(emit, member);
+}
+
+ANN static inline void emit_func_def_global(const Emitter emit, const Value value) { GWDEBUG_EXE
+ const Instr set_mem = emit_add_instr(emit, MemSetImm);
+ set_mem->m_val = value->offset = emit_local(emit, SZ_INT, 0);
+ set_mem->m_val2 = (m_uint)value->d.func_ref->code;
+}
+
+ANN static void emit_func_def_init(const Emitter emit, const Func func) { GWDEBUG_EXE
+ const Type t = emit->env->class_def;
+ char c[(t ? strlen(t->name) + 1 : 0) + strlen(func->name) + 6];
+ sprintf(c, "%s%s%s(...)", t ? t->name : "", t ? "." : "", func->name);
+ emit_push_code(emit, c);
+}
+
+ANN static void emit_func_def_args(const Emitter emit, Arg_List a) { GWDEBUG_EXE
+ do {
+ const Value value = a->var_decl->value;
+ const m_uint size = value->type->size;
+ const m_bool obj = isa(value->type, t_object) > 0;
+ emit->code->stack_depth += size;
+ value->offset = emit_local(emit, size, obj);
+ } while((a = a->next));
+}
+
+ANN static void emit_func_def_ensure(const Emitter emit, const Func_Def func_def) { GWDEBUG_EXE
+ const m_uint size = func_def->ret_type->size;
+ if(size) {
+ const Instr instr = emit_kind(emit, size, 0, regpushimm);
+ instr->m_val2 = size;
+ }
+ vector_add(&emit->code->stack_return, (vtype)emit_add_instr(emit, Goto));
+}
+
+ANN static void emit_func_def_return(const Emitter emit) { GWDEBUG_EXE
+ const m_uint val = emit_code_size(emit);
+ LOOP_OPTIM
+ for(m_uint i = vector_size(&emit->code->stack_return) + 1; --i; ) {
+ const Instr instr = (Instr)vector_at(&emit->code->stack_return, i-1);
+ instr->m_val = val;
+ }
+ vector_clear(&emit->code->stack_return);
+ emit_pop_scope(emit);
+ MEMOIZE_STORE
+ emit_add_instr(emit, FuncReturn);
+}
+
+ANN static void emit_func_def_code(const Emitter emit, const Func func) { GWDEBUG_EXE
+ func->code = emit_code(emit);
+ if(GET_FLAG(func->def, dtor)) {
+ emit->env->class_def->nspc->dtor = func->code;
+ Instr instr = (Instr)vector_back(func->code->instr);
+ instr->execute = DTOR_EOC;
+ ADD_REF(func->code)
+ }
+}
+
+ANN static m_bool emit_func_def_body(const Emitter emit, const Func_Def func_def) { GWDEBUG_EXE
+ if(func_def->arg_list)
+ emit_func_def_args(emit, func_def->arg_list);
+ if(GET_FLAG(func_def, variadic))
+ stack_alloc(emit);
+ if(func_def->d.code->d.stmt_code.stmt_list)
+ CHECK_BB(emit_stmt_code(emit, &func_def->d.code->d.stmt_code))
+ emit_func_def_ensure(emit, func_def);
+ return GW_OK;
+}
+
+ANN static m_bool emit_func_def(const Emitter emit, const Func_Def func_def) { GWDEBUG_EXE
+ const Func func = get_func(emit->env, func_def);
+ if(func->code)return GW_OK;
+ if(tmpl_list_base(func_def->tmpl)) { // don't check template definition
+ UNSET_FLAG(func_def, template);
+ return GW_OK;
+ }
+ if(SAFE_FLAG(emit->env->class_def, builtin) && GET_FLAG(emit->env->class_def, template))
+ return GW_OK;
+ emit_func_def_init(emit, func);
+ if(GET_FLAG(func, member))
+ stack_alloc_this(emit);
+ emit_push_scope(emit);
+ const Func former = emit->env->func;
+ emit->env->func = func;
+ CHECK_BB(emit_func_def_body(emit, func_def))
+ if(GET_FLAG(func_def, variadic) && !emit->env->func->variadic)
+ ERR_B(func_def->td->xid->pos, "invalid variadic use")
+ emit_func_def_return(emit);
+ emit_func_def_code(emit, func);
+ emit->env->func = former;
+ emit_pop_code(emit);
+ if(!emit->env->class_def && !GET_FLAG(func_def, global) && !func_def->tmpl)
+ emit_func_def_global(emit, func->value_ref);
+ MEMOIZE_INI
+ return GW_OK;
+}
+
+DECL_SECTION_FUNC(emit)
+
+ANN Code* emit_class_code(const Emitter emit, const m_str name) { GWDEBUG_EXE
+ const m_uint len = strlen(name) + 7;
+ char c[len];
+ snprintf(c, len, "class %s", name);
+ emit_push_code(emit, c);
+ stack_alloc_this(emit);
+ return emit->code;
+}
+
+ANN inline void emit_class_finish(const Emitter emit, const Nspc nspc) { GWDEBUG_EXE
+ emit_add_instr(emit, FuncReturn);
+ nspc->pre_ctor = emit_code(emit);
+}
+
+ANN static inline void emit_class_push(const Emitter emit, const Type type) { GWDEBUG_EXE
+ vector_add(&emit->env->class_stack, (vtype)emit->env->class_def);
+ emit->env->class_def = type;
+}
+
+ANN static inline void emit_class_pop(const Emitter emit) { GWDEBUG_EXE
+ emit->env->class_def = (Type)vector_pop(&emit->env->class_stack);
+ emit_pop_code(emit);
+}
+
+ANN static m_bool emit_class_def(const Emitter emit, const Class_Def class_def) { GWDEBUG_EXE
+ const Type type = class_def->type;
+ const Nspc nspc = type->nspc;
+ if(tmpl_class_base(class_def->tmpl))
+ return GW_OK;
+ if(class_def->ext && ((/*!GET_FLAG(type->parent, emit) &&*/
+ GET_FLAG(class_def->ext, typedef)) || class_def->ext->types)) {
+ const Type base = class_def->ext->array ?
+ array_base(type->parent) : type->parent;
+ if(!base->nspc->pre_ctor)
+ CHECK_BB(emit_class_def(emit, base->def))
+ }
+ if(nspc->class_data_size)
+ nspc->class_data = (m_bit*)xcalloc(1, nspc->class_data_size);
+ emit_class_push(emit, type);
+ emit_class_code(emit, type->name);
+ if(class_def->ext && class_def->ext->array)
+ CHECK_BB(emit_array_extend(emit, type->parent, class_def->ext->array->exp))
+ if(class_def->body) {
+ Class_Body body = class_def->body;
+ do CHECK_BB(emit_section(emit, body->section))
+ while((body = body->next));
+ }
+ emit_class_finish(emit, nspc);
+ emit_class_pop(emit);
+ SET_FLAG(type, emit);
+ return GW_OK;
+}
+
+ANN static void emit_free_code(Code* code) {
+ LOOP_OPTIM
+ for(m_uint j = vector_size(&code->instr) + 1; --j;)
+ mp_free(Instr, (Instr)vector_at(&code->instr, j - 1));
+ free_code(code);
+}
+
+ANN static void emit_free_stack(const Emitter emit) { GWDEBUG_EXE
+ LOOP_OPTIM
+ for(m_uint i = vector_size(&emit->stack) + 1; --i;)
+ emit_free_code((Code*)vector_at(&emit->stack, i - 1));
+ vector_clear(&emit->stack);
+ emit_free_code(emit->code);
+}
+
+ANN static inline m_bool emit_ast_inner(const Emitter emit, Ast ast) { GWDEBUG_EXE
+ do CHECK_BB(emit_section(emit, ast->section))
+ while((ast = ast->next));
+ return GW_OK;
+}
+
+ANN VM_Code emit_ast(const Emitter emit, Ast ast) { GWDEBUG_EXE
+ emit->code = new_code(emit, emit->env->name);
+ emit_push_scope(emit);
+ const m_bool ret = emit_ast_inner(emit, ast);
+ emit_pop_scope(emit);
+ if(ret > 0)
+ return finalyze(emit);
+ emit_free_stack(emit);
+ return NULL;
+}