Ver Fonte

split files for HL C output

Nicolas Cannasse há 8 anos atrás
pai
commit
f98a945804
1 ficheiros alterados com 1111 adições e 948 exclusões
  1. 1111 948
      src/generators/hl2c.ml

+ 1111 - 948
src/generators/hl2c.ml

@@ -38,19 +38,47 @@ type output_options =
 	| OOBeginBlock
 	| OOEndBlock
 
-let c_kwds = [
-"auto";"break";"case";"char";"const";"continue";"default";"do";"double";"else";"enum";"extern";"float";"for";"goto";
-"if";"int";"long";"register";"return";"short";"signed";"sizeof";"static";"struct";"switch";"typedef";"union";"unsigned";
-"void";"volatile";"while";
-(* MS specific *)
-"__asm";"dllimport2";"__int8";"naked2";"__based1";"__except";"__int16";"__stdcall";"__cdecl";"__fastcall";"__int32";
-"thread2";"__declspec";"__finally";"__int64";"__try";"dllexport2";"__inline";"__leave";"asm";
-(* reserved by HLC *)
-"t";
-(* C11 *)
-"_Alignas";"_Alignof";"_Atomic";"_Bool";"_Complex";"_Generic";"_Imaginary";"_Noreturn";"_Static_assert";"_Thread_local";"_Pragma";
-"inline";"restrict"
-]
+type function_entry = {
+	mutable fe_name : string;
+	mutable fe_decl : fundecl option;
+	mutable fe_args : ttype list;
+	mutable fe_ret : ttype;
+}
+
+type context = {
+	version : int;
+	out : Buffer.t;
+	mutable tabs : string;
+	hash_cache : (int, int32) Hashtbl.t;
+	hlcode : code;
+	dir : string;
+	mutable curfile : string;
+	mutable cfiles : string list;
+	ftable : function_entry array;
+	htypes : (ttype, int) PMap.t;
+}
+
+let sprintf = Printf.sprintf
+
+let keywords =
+	let c_kwds = [
+	"auto";"break";"case";"char";"const";"continue";"default";"do";"double";"else";"enum";"extern";"float";"for";"goto";
+	"if";"int";"long";"register";"return";"short";"signed";"sizeof";"static";"struct";"switch";"typedef";"union";"unsigned";
+	"void";"volatile";"while";
+	(* MS specific *)
+	"__asm";"dllimport2";"__int8";"naked2";"__based1";"__except";"__int16";"__stdcall";"__cdecl";"__fastcall";"__int32";
+	"thread2";"__declspec";"__finally";"__int64";"__try";"dllexport2";"__inline";"__leave";"asm";
+	(* reserved by HLC *)
+	"t";
+	(* C11 *)
+	"_Alignas";"_Alignof";"_Atomic";"_Bool";"_Complex";"_Generic";"_Imaginary";"_Noreturn";"_Static_assert";"_Thread_local";"_Pragma";
+	"inline";"restrict"
+	] in
+	let h = Hashtbl.create 0 in
+	List.iter (fun i -> Hashtbl.add h i ()) c_kwds;
+	h
+
+let ident i = if Hashtbl.mem keywords i then "_" ^ i else i
 
 let s_comp = function
 	| CLt -> "<"
@@ -65,400 +93,171 @@ let core_types =
 	let ep = { ename = ""; eid = 0; eglobal = None; efields = [||] } in
 	[HVoid;HUI8;HUI16;HI32;HF32;HF64;HBool;HBytes;HDyn;HFun ([],HVoid);HObj null_proto;HArray;HType;HRef HVoid;HVirtual vp;HDynObj;HAbstract ("",0);HEnum ep;HNull HVoid]
 
-let write_c version file (code:code) =
-	let tabs = ref "" in
-	let file_count = ref 1 in
-	let line_count = ref 0 in
-	let main_ch = IO.output_channel (open_out_bin file) in
-	let ch = ref main_ch in
-	let end_ch = ref [(fun() -> IO.close_out main_ch)] in
-	let block() = tabs := !tabs ^ "\t" in
-	let unblock() = tabs := String.sub (!tabs) 0 (String.length (!tabs) - 1) in
-	let line str =
-		incr line_count;
-		IO.write_line !ch (!tabs ^ str)
-	in
-	let expr str = line (str ^ ";") in
-	let sexpr fmt = Printf.ksprintf expr fmt in
-	let sline fmt = Printf.ksprintf line fmt in
-	let sprintf = Printf.sprintf in
-
-	let flush_file() =
-		if !line_count > 60000 then begin
-			incr file_count;
-			let nfile = String.sub file 0 (String.length file - 2) ^ string_of_int !file_count ^ ".c" in
-			ch := main_ch;
-			sline "#include \"%s\"" (match List.rev (ExtString.String.nsplit (String.concat "/" (ExtString.String.nsplit nfile "\\")) "/") with file :: _ -> file | _ -> assert false);
-			let nch = IO.output_channel (open_out_bin nfile) in
- 			ch := nch;
-			sline "#ifdef HLC_H";
-			end_ch := (fun() -> IO.write_line nch "#endif"; IO.close_out nch) :: !end_ch;
-			line_count := 0;
-		end
-	in
-
-	let hash_cache = Hashtbl.create 0 in
-	let hash sid =
-		try
-			Hashtbl.find hash_cache sid
-		with Not_found ->
-			let h = hl_hash code.strings.(sid) in
-			Hashtbl.add hash_cache sid h;
-			h
-	in
+let tname str =
+	let n = String.concat "__" (ExtString.String.nsplit str ".") in
+	if Hashtbl.mem keywords ("_" ^ n) then "__" ^ n else n
 
-	let keywords =
-		let h = Hashtbl.create 0 in
-		List.iter (fun i -> Hashtbl.add h i ()) c_kwds;
-		h
-	in
+let is_gc_ptr = function
+	| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HType | HRef _ -> false
+	| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> true
 
-	let ident i = if Hashtbl.mem keywords i then "_" ^ i else i in
+let is_ptr = function
+	| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool -> false
+	| _ -> true
 
-	let tname str =
-		let n = String.concat "__" (ExtString.String.nsplit str ".") in
-		if Hashtbl.mem keywords ("_" ^ n) then "__" ^ n else n
-	in
+let rec ctype_no_ptr = function
+	| HVoid -> "void",0
+	| HUI8 -> "unsigned char",0
+	| HUI16 -> "unsigned short",0
+	| HI32 -> "int",0
+	| HF32 -> "float",0
+	| HF64 -> "double",0
+	| HBool -> "bool",0
+	| HBytes -> "vbyte",1
+	| HDyn -> "vdynamic",1
+	| HFun _ -> "vclosure",1
+	| HObj p -> tname p.pname,0
+	| HArray -> "varray",1
+	| HType -> "hl_type",1
+	| HRef t -> let s,i = ctype_no_ptr t in s,i + 1
+	| HVirtual _ -> "vvirtual",1
+	| HDynObj -> "vdynobj",1
+	| HAbstract (name,_) -> name,1
+	| HEnum _ -> "venum",1
+	| HNull _ -> "vdynamic",1
 
-	let is_gc_ptr = function
-		| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HType | HRef _ -> false
-		| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> true
-	in
+let ctype t =
+	let t, nptr = ctype_no_ptr t in
+	if nptr = 0 then t else t ^ String.make nptr '*'
 
-	let is_ptr = function
-		| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool -> false
-		| _ -> true
-	in
+let cast_fun s args t =
+	sprintf "((%s (*)(%s))%s)" (ctype t) (String.concat "," (List.map ctype args)) s
 
-	let rec ctype_no_ptr = function
-		| HVoid -> "void",0
-		| HUI8 -> "unsigned char",0
-		| HUI16 -> "unsigned short",0
-		| HI32 -> "int",0
-		| HF32 -> "float",0
-		| HF64 -> "double",0
-		| HBool -> "bool",0
-		| HBytes -> "vbyte",1
-		| HDyn -> "vdynamic",1
-		| HFun _ -> "vclosure",1
-		| HObj p -> tname p.pname,0
-		| HArray -> "varray",1
-		| HType -> "hl_type",1
-		| HRef t -> let s,i = ctype_no_ptr t in s,i + 1
-		| HVirtual _ -> "vvirtual",1
-		| HDynObj -> "vdynobj",1
-		| HAbstract (name,_) -> name,1
-		| HEnum _ -> "venum",1
-		| HNull _ -> "vdynamic",1
-	in
+let dyn_value_field t =
+	"->v." ^ match t with
+	| HUI8 -> "ui8"
+	| HUI16 -> "ui16"
+	| HI32 -> "i"
+	| HF32 -> "f"
+	| HF64 -> "d"
+	| HBool -> "b"
+	| _ -> "ptr"
 
-	let ctype t =
-		let t, nptr = ctype_no_ptr t in
-		if nptr = 0 then t else t ^ String.make nptr '*'
-	in
+let type_id t =
+	match t with
+	| HVoid -> "HVOID"
+	| HUI8 -> "HUI8"
+	| HUI16 -> "HUI16"
+	| HI32 -> "HI32"
+	| HF32 -> "HF32"
+	| HF64 -> "HF64"
+	| HBool -> "HBOOL"
+	| HBytes -> "HBYTES"
+	| HDyn -> "HDYN"
+	| HFun _ -> "HFUN"
+	| HObj _ -> "HOBJ"
+	| HArray -> "HARRAY"
+	| HType -> "HTYPE"
+	| HRef _ -> "HREF"
+	| HVirtual _ -> "HVIRTUAL"
+	| HDynObj -> "HDYNOBJ"
+	| HAbstract _ -> "HABSTRACT"
+	| HEnum _ -> "HENUM"
+	| HNull _ -> "HNULL"
 
-	let type_id t =
-		match t with
-		| HVoid -> "HVOID"
-		| HUI8 -> "HUI8"
-		| HUI16 -> "HUI16"
-		| HI32 -> "HI32"
-		| HF32 -> "HF32"
-		| HF64 -> "HF64"
-		| HBool -> "HBOOL"
-		| HBytes -> "HBYTES"
-		| HDyn -> "HDYN"
-		| HFun _ -> "HFUN"
-		| HObj _ -> "HOBJ"
-		| HArray -> "HARRAY"
-		| HType -> "HTYPE"
-		| HRef _ -> "HREF"
-		| HVirtual _ -> "HVIRTUAL"
-		| HDynObj -> "HDYNOBJ"
-		| HAbstract _ -> "HABSTRACT"
-		| HEnum _ -> "HENUM"
-		| HNull _ -> "HNULL"
-	in
+let var_type n t =
+	ctype t ^ " " ^ ident n
 
-	let var_type n t =
-		ctype t ^ " " ^ ident n
-	in
+let block ctx =
+	ctx.tabs <- ctx.tabs ^ "\t"
 
-	let version_major = version / 1000 in
-	let version_minor = (version mod 1000) / 100 in
-	let version_revision = (version mod 100) in
-	let ver_str = Printf.sprintf "%d.%d.%d" version_major version_minor version_revision in
-	line ("// Generated by HLC " ^ ver_str ^ " (HL v" ^ string_of_int code.version ^")");
-	line "#define HLC_BOOT";
-	line "#include <hlc.h>";
-	let all_types, htypes = gather_types code in
-	let tfuns = Array.create (Array.length code.functions + Array.length code.natives) ([],HVoid) in
-	let funnames = Array.create (Array.length code.functions + Array.length code.natives) "" in
+let unblock ctx =
+	ctx.tabs <- String.sub ctx.tabs 0 (String.length ctx.tabs - 1)
 
-	let cast_fun s args t =
-		sprintf "((%s (*)(%s))%s)" (ctype t) (String.concat "," (List.map ctype args)) s
-	in
+let hash ctx sid =
+	try
+		Hashtbl.find ctx.hash_cache sid
+	with Not_found ->
+		let h = hl_hash ctx.hlcode.strings.(sid) in
+		Hashtbl.add ctx.hash_cache sid h;
+		h
 
-	let enum_constr_type e i =
-		let cname,_, tl = e.efields.(i) in
-		if Array.length tl = 0 then
-			"venum"
-		else
-		let name = if e.eid = 0 then
-			let index = (try PMap.find (HEnum e) htypes with Not_found -> assert false) in
-			"Enum$" ^ string_of_int index
-		else
-			String.concat "_" (ExtString.String.nsplit e.ename ".")
-		in
-		if cname = "" then
-			name
-		else
-			name ^ "_" ^ cname
-	in
+let type_value ctx t =
+	let index = (try PMap.find t ctx.htypes with Not_found -> assert false) in
+	"&type$" ^ string_of_int index
 
-	let dyn_value_field t =
-		"->v." ^ match t with
-		| HUI8 -> "ui8"
-		| HUI16 -> "ui16"
-		| HI32 -> "i"
-		| HF32 -> "f"
-		| HF64 -> "d"
-		| HBool -> "b"
-		| _ -> "ptr"
+let enum_constr_type ctx e i =
+	let cname,_, tl = e.efields.(i) in
+	if Array.length tl = 0 then
+		"venum"
+	else
+	let name = if e.eid = 0 then
+		let index = (try PMap.find (HEnum e) ctx.htypes with Not_found -> assert false) in
+		"Enum$" ^ string_of_int index
+	else
+		String.concat "_" (ExtString.String.nsplit e.ename ".")
 	in
+	if cname = "" then
+		name
+	else
+		name ^ "_" ^ cname
 
-	let used_closures = Hashtbl.create 0 in
-	let bytes_strings = Hashtbl.create 0 in
-	Array.iter (fun f ->
-		Array.iteri (fun i op ->
-			match op with
-			| OStaticClosure (_,fid) | OSetMethod (_,_,fid) ->
-				Hashtbl.replace used_closures fid ()
-			| OBytes (_,sid) ->
-				Hashtbl.replace bytes_strings sid ()
-			| _ ->
-				()
-		) f.code
-	) code.functions;
-
-
-	line "";
-	line "// Types definitions";
-	Array.iter (fun t ->
-		match t with
-		| HObj o ->
-			let name = tname o.pname in
-			expr ("typedef struct _" ^ name ^ " *" ^ name);
-		| HAbstract (name,_) ->
-			expr ("typedef struct _" ^ name ^ " "  ^ name);
-		| _ ->
-			()
-	) all_types;
-
-	line "";
-	line "// Types implementation";
-
-	let unamed_field fid = "$_f" ^ string_of_int fid in
+let output ctx str =
+	Buffer.add_string ctx.out str
 
-	let obj_field fid name =
-		if name = "" then unamed_field fid else ident name
-	in
+let output_char ctx c =
+	Buffer.add_char ctx.out c
 
-	Array.iter (fun t ->
-		match t with
-		| HObj o ->
-			let name = tname o.pname in
-			line ("struct _" ^ name ^ " {");
-			block();
-			let rec loop o =
-				(match o.psuper with
-				| None -> expr ("hl_type *$type");
-				| Some c -> loop c);
-				Array.iteri (fun i (n,_,t) ->
-					let rec abs_index p v =
-						match p with
-						| None -> v
-						| Some o -> abs_index o.psuper (Array.length o.pfields + v)
-					in
-					expr (var_type (if n = "" then unamed_field (abs_index o.psuper i) else n) t)
-				) o.pfields;
-			in
-			loop o;
-			unblock();
-			expr "}";
-		| HEnum e ->
-			Array.iteri (fun i (_,_,pl) ->
-				if Array.length pl <> 0 then begin
-					line ("typedef struct {");
-					block();
-					expr "int index";
-					Array.iteri (fun i t ->
-						expr (var_type ("p" ^ string_of_int i) t)
-					) pl;
-					unblock();
-					sexpr "} %s" (enum_constr_type e i);
-				end;
-			) e.efields
-		| _ ->
-			()
-	) all_types;
+let line ctx str =
+	output ctx ctx.tabs;
+	output ctx str;
+	output_char ctx '\n'
 
-	line "";
-	line "// Types values declaration";
-	Array.iteri (fun i t ->
-		sexpr "static hl_type type$%d = { %s } /* %s */" i (type_id t) (tstr t);
-		match t with
-		| HObj o ->
-			sline "#define %s__val &type$%d" (tname o.pname) i
-		| _ ->
-			()
-	) all_types;
+let expr ctx str =
+	output ctx ctx.tabs;
+	output ctx str;
+	output ctx ";\n"
 
-	line "";
-	line "// Globals";
-	Array.iteri (fun i t ->
-		let name = "global$" ^ string_of_int i in
-		sexpr "static %s = 0" (var_type name t)
-	) code.globals;
+let unamed_field fid = "$_f" ^ string_of_int fid
 
-	line "";
-	line "// Natives functions";
-	Array.iter (fun (lib,name,t,idx) ->
-		match t with
-		| HFun (args,t) ->
-			let fname =
-				let lib = code.strings.(lib) in
-				let lib = if lib = "std" then "hl" else lib in
-				lib ^ "_" ^ code.strings.(name)
-			in
-			sexpr "HL_API %s %s(%s)" (ctype t) fname (String.concat "," (List.map ctype args));
-			funnames.(idx) <- fname;
-			Array.set tfuns idx (args,t)
-		| _ ->
-			assert false
-	) code.natives;
+let obj_field fid name =
+	if name = "" then unamed_field fid else ident name
 
-	line "";
-	line "// Functions declaration";
-	Array.iter (fun f ->
-		match f.ftype with
-		| HFun (args,t) ->
-			let fname = String.concat "_" (ExtString.String.nsplit (fundecl_name f) ".") in
-			sexpr "static %s %s(%s)" (ctype t) fname (String.concat "," (List.map ctype args));
-			Array.set tfuns f.findex (args,t);
-			funnames.(f.findex) <- fname;
-		| _ ->
-			assert false
-	) code.functions;
+let close_file ctx =
+	let str = Buffer.contents ctx.out in
+	Buffer.reset ctx.out;
+	let fpath = ctx.dir ^ "/" ^ ctx.curfile in
+	if String.sub ctx.curfile (String.length ctx.curfile - 2) 2 = ".c" then ctx.cfiles <- ctx.curfile :: ctx.cfiles;
+	ctx.curfile <- "";
+	let fcontent = (try Std.input_file ~bin:true fpath with _ -> "") in
+	if fcontent <> str then begin
+		Common.mkdir_recursive "" (ExtString.String.nsplit (Filename.dirname fpath) "/");
+		let ch = open_out_bin fpath in
+		output_string ch str;
+		close_out ch;
+	end
 
-	line "";
-	line "// Strings";
-	Array.iteri (fun i str ->
-		let rec loop s i =
-			if i = String.length s then [] else
-			let c = String.get s i in
-			string_of_int (int_of_char c) :: loop s (i+1)
-		in
-		if Hashtbl.mem bytes_strings i then
-			sexpr "static vbyte bytes$%d[] = {%s}" i (String.concat "," (loop str 0))
-		else
-			let s = utf8_to_utf16 str in
-			sexpr "static vbyte string$%d[] = {%s} /* %s */" i (String.concat "," (loop s 0)) (String.escaped (String.concat "* /" (ExtString.String.nsplit str "*/")))
-	) code.strings;
+let open_file ctx file =
+	if ctx.curfile <> "" then close_file ctx;
+	let version_major = ctx.version / 1000 in
+	let version_minor = (ctx.version mod 1000) / 100 in
+	let version_revision = (ctx.version mod 100) in
+	line ctx (sprintf "%s Generated by HLC %d.%d.%d (HL v%d)" (if file = "Makefile" then "#" else "//") version_major version_minor version_revision ctx.hlcode.version);
+	ctx.curfile <- file
 
-	let type_value t =
-		let index = (try PMap.find t htypes with Not_found -> assert false) in
-		"&type$" ^ string_of_int index
-	in
+let string_data_limit = 64
 
-	line "";
-	line "// Types values data";
-	Array.iteri (fun i t ->
-		let field_value (name,name_id,t) =
-			sprintf "{(const uchar*)string$%d, %s, %ld}" name_id (type_value t) (hash name_id)
-		in
-		match t with
-		| HObj o ->
-			let proto_value p =
-				sprintf "{(const uchar*)string$%d, %d, %d, %ld}" p.fid p.fmethod (match p.fvirtual with None -> -1 | Some i -> i) (hash p.fid)
-			in
-			let fields =
-				if Array.length o.pfields = 0 then "NULL" else
-				let name = sprintf "fields$%d" i in
-				sexpr "static hl_obj_field %s[] = {%s}" name (String.concat "," (List.map field_value (Array.to_list o.pfields)));
-				name
-			in
-			let proto =
-				if Array.length o.pproto = 0 then "NULL" else
-				let name = sprintf "proto$%d" i in
-				sexpr "static hl_obj_proto %s[] = {%s}" name (String.concat "," (List.map proto_value (Array.to_list o.pproto)));
-				name
-			in
-			let ofields = [
-				string_of_int (Array.length o.pfields);
-				string_of_int (Array.length o.pproto);
-				sprintf "(const uchar*)string$%d" o.pid;
-				(match o.psuper with None -> "NULL" | Some c -> sprintf "%s__val" (tname c.pname));
-				fields;
-				proto
-			] in
-			sexpr "static hl_type_obj obj$%d = {%s}" i (String.concat "," ofields);
-		| HEnum e ->
-			let constr_name = sprintf "econstructs$%d" i in
-			let constr_value cid (_,nid,tl) =
-				let tval = if Array.length tl = 0 then "NULL" else
-					let name = sprintf "econstruct$%d_%d" i cid in
-					sexpr "static hl_type *%s[] = {%s}" name (String.concat "," (List.map type_value (Array.to_list tl)));
-					name
-				in
-				let size = if Array.length tl = 0 then "0" else sprintf "sizeof(%s)" (enum_constr_type e cid) in
-				let offsets = if Array.length tl = 0 then "NULL" else
-					let name = sprintf "eoffsets$%d_%d" i cid in
-					sexpr "static int %s[] = {%s}" name (String.concat "," (List.map (fun _ -> "0") (Array.to_list tl)));
-					name
-				in
-				let has_ptr = List.exists is_gc_ptr (Array.to_list tl) in
-				sprintf "{(const uchar*)string$%d, %d, %s, %s, %s, %s}" nid (Array.length tl) tval size (if has_ptr then "true" else "false") offsets
-			in
-			sexpr "static hl_enum_construct %s[] = {%s}" constr_name (String.concat "," (Array.to_list (Array.mapi constr_value e.efields)));
-			let efields = [
-				if e.eid = 0 then "NULL" else sprintf "(const uchar*)string$%d" e.eid;
-				string_of_int (Array.length e.efields);
-				constr_name
-			] in
-			sexpr "static hl_type_enum enum$%d = {%s}" i (String.concat "," efields);
-		| HVirtual v ->
-			let fields_name =
-				if Array.length v.vfields = 0 then "NULL" else
-				let name = sprintf "vfields$%d" i in
-				sexpr "static hl_obj_field %s[] = {%s}" name (String.concat "," (List.map field_value (Array.to_list v.vfields)));
-				name
-			in
-			let vfields = [
-				fields_name;
-				string_of_int (Array.length v.vfields)
-			] in
-			sexpr "static hl_type_virtual virt$%d = {%s}" i (String.concat "," vfields);
-		| HFun (args,t) ->
-			let aname = if args = [] then "NULL" else
-				let name = sprintf "fargs$%d" i in
-				sexpr "static hl_type *%s[] = {%s}" name (String.concat "," (List.map type_value args));
-				name
-			in
-			sexpr "static hl_type_fun tfun$%d = {%s,%s,%d}" i aname (type_value t) (List.length args)
-		| _ ->
-			()
-	) all_types;
+let string ctx sid =
+	let s = ctx.hlcode.strings.(sid) in
+	if String.length s < string_data_limit then
+		sprintf "USTR(\"%s\")" (Ast.s_escape ~hex:false s)
+	else
+		sprintf "string$%d" sid
 
-	line "";
-	line "// Static data";
-	Hashtbl.iter (fun fid _ ->
-		let args, t = tfuns.(fid) in
-		sexpr "static vclosure cl$%d = { %s, %s, 0 }" fid (type_value (HFun (args,t))) funnames.(fid);
-	) used_closures;
+let generate_reflection ctx =
+	let line = line ctx and expr = expr ctx in
+	let sline fmt = Printf.ksprintf line fmt and sexpr fmt = Printf.ksprintf expr fmt in
 
-	line "";
-	line "// Reflection helpers";
 	let funByArgs = Hashtbl.create 0 in
 	let type_kind t =
 		match t with
@@ -490,19 +289,19 @@ let write_c version file (code:code) =
 				| _ -> ())
 			| _ -> ()
 		) f.code
-	) code.functions;
-	Array.iter (fun (args,t) -> add_fun args t) tfuns;
+	) ctx.hlcode.functions;
+	Array.iter (fun f -> add_fun f.fe_args f.fe_ret) ctx.ftable;
 	let argsCounts = List.sort compare (Hashtbl.fold (fun i _ acc -> i :: acc) funByArgs []) in
 	sexpr "static int TKIND[] = {%s}" (String.concat "," (List.map (fun t -> string_of_int (type_kind_id (type_kind t))) core_types));
 	line "";
 	line "void *hlc_static_call( void *fun, hl_type *t, void **args, vdynamic *out ) {";
-	block();
+	block ctx;
 	sexpr "int chk = TKIND[t->fun->ret->kind]";
 	sexpr "vdynamic *d";
 	line "switch( t->fun->nargs ) {";
 	List.iter (fun nargs ->
 		sline "case %d:" nargs;
-		block();
+		block ctx;
 		if nargs > 9 then sexpr "hl_fatal(\"Too many arguments, TODO:use more bits\")" else begin
 		for i = 0 to nargs-1 do
 			sexpr "chk |= TKIND[t->fun->args[%d]->kind] << %d" i ((i + 1) * 3);
@@ -512,7 +311,7 @@ let write_c version file (code:code) =
 			let s = ref (-1) in
 			let chk = List.fold_left (fun chk t -> incr s; chk lor ((type_kind_id t) lsl (!s * 3))) 0 (t :: args) in
 			sline "case %d:" chk;
-			block();
+			block ctx;
 			let idx = ref (-1) in
 			let vargs = List.map (fun t ->
 				incr idx;
@@ -531,17 +330,17 @@ let write_c version file (code:code) =
 				sexpr "out%s = %s" (dyn_value_field t) call;
 				sexpr "return &out%s" (dyn_value_field t);
 			end;
-			unblock();
+			unblock ctx;
 		) (Hashtbl.find funByArgs nargs);
 		sline "}";
 		expr "break";
 		end;
-		unblock();
+		unblock ctx;
 	) argsCounts;
 	line "}";
 	sexpr "hl_fatal(\"Unsupported dynamic call\")";
 	sexpr "return NULL";
-	unblock();
+	unblock ctx;
 	line "}";
 	line "";
 	let wrap_char = function
@@ -558,7 +357,7 @@ let write_c version file (code:code) =
 		Hashtbl.iter (fun (args,t) _ ->
 			let name = make_wrap_name args t in
 			sline "static %s wrap_%s(void *value%s) {" (ctype t) name (String.concat "" (list_mapi (fun i t -> "," ^ var_type ("p" ^ string_of_int i) t) args));
-			block();
+			block ctx;
 			if args <> [] then sexpr "void *args[] = {%s}" (String.concat "," (list_mapi (fun i t ->
 				if not (is_ptr t) then
 					sprintf "&p%d" i
@@ -575,18 +374,18 @@ let write_c version file (code:code) =
 				sexpr "hl_wrapper_call(value,%s,&ret)" vargs;
 				sexpr "return ret.v.%s" (wrap_char t);
 			end;
-			unblock();
+			unblock ctx;
 			line "}";
 		) (Hashtbl.find funByArgs nargs);
 	) argsCounts;
 	line "";
 	line "void *hlc_get_wrapper( hl_type *t ) {";
-	block();
+	block ctx;
 	sexpr "int chk = TKIND[t->fun->ret->kind]";
 	line "switch( t->fun->nargs ) {";
 	List.iter (fun nargs ->
 		sline "case %d:" nargs;
-		block();
+		block ctx;
 		if nargs > 9 then sexpr "hl_fatal(\"Too many arguments, TODO:use more bits\")" else begin
 		for i = 0 to nargs-1 do
 			sexpr "chk |= TKIND[t->fun->args[%d]->kind] << %d" i ((i + 1) * 3);
@@ -600,619 +399,983 @@ let write_c version file (code:code) =
 		sline "}";
 		expr "break";
 		end;
-		unblock();
+		unblock ctx;
 	) argsCounts;
 	line "}";
 	sexpr "return NULL";
-	unblock();
+	unblock ctx;
 	line "}";
-	line "";
-	line "// Functions code";
-	Array.iter (fun f ->
-
-		flush_file();
+	line ""
 
-		let rid = ref (-1) in
-		let reg id = "r" ^ string_of_int id in
+let generate_function ctx f =
+	let line = line ctx and expr = expr ctx in
+	let sline fmt = Printf.ksprintf line fmt and sexpr fmt = Printf.ksprintf expr fmt in
+	let block() = block ctx and unblock() = unblock ctx in
+	let type_value = type_value ctx in
+	let code = ctx.hlcode in
 
-		let label id = "label$" ^ string_of_int f.findex ^ "$" ^ string_of_int id in
+	let rid = ref (-1) in
+	let reg id = "r" ^ string_of_int id in
 
-		let rtype r = f.regs.(r) in
+	let label id = "label$" ^ string_of_int f.findex ^ "$" ^ string_of_int id in
 
-		let rcast r t =
-			if tsame (rtype r) t then (reg r)
-			else Printf.sprintf "((%s)%s)" (ctype t) (reg r)
-		in
+	let rtype r = f.regs.(r) in
 
-		let rfun r args t =
-			cast_fun (reg r ^ "->fun") args t
-		in
+	let funname fid = ctx.ftable.(fid).fe_name in
 
-		let rassign r t =
-			let rt = rtype r in
-			if t = HVoid then "" else
-			let assign = reg r ^ " = " in
-			if tsame t rt then assign else
-			if not (safe_cast t rt) then assert false
-			else assign ^ "(" ^ ctype rt ^ ")"
-		in
+	let rcast r t =
+		if tsame (rtype r) t then (reg r)
+		else Printf.sprintf "((%s)%s)" (ctype t) (reg r)
+	in
 
-		let ocall r fid args =
-			let targs, rt = tfuns.(fid) in
-			let rstr = rassign r rt in
-			sexpr "%s%s(%s)" rstr funnames.(fid) (String.concat "," (List.map2 rcast args targs))
-		in
+	let rfun r args t =
+		cast_fun (reg r ^ "->fun") args t
+	in
 
+	let rassign r t =
+		let rt = rtype r in
+		if t = HVoid then "" else
+		let assign = reg r ^ " = " in
+		if tsame t rt then assign else
+		if not (safe_cast t rt) then assert false
+		else assign ^ "(" ^ ctype rt ^ ")"
+	in
 
-		let dyn_prefix = function
-			| HUI8 | HUI16 | HI32 | HBool -> "i"
-			| HF32 -> "f"
-			| HF64 -> "d"
-			| _ -> "p"
-		in
+	let ocall r fid args =
+		let ft = ctx.ftable.(fid) in
+		let rstr = rassign r ft.fe_ret in
+		sexpr "%s%s(%s)" rstr ft.fe_name (String.concat "," (List.map2 rcast args ft.fe_args))
+	in
 
-		let type_value_opt t =
-			match t with HF32 | HF64 -> "" | _ -> "," ^ type_value t
-		in
 
-		let dyn_call r f pl =
-			line "{";
-			block();
-			if pl <> [] then sexpr "vdynamic *args[] = {%s}" (String.concat "," (List.map (fun p ->
-				match rtype p with
-				| HDyn ->
-					reg p
-				| t ->
-					if is_dynamic t then
-						sprintf "(vdynamic*)%s" (reg p)
-					else
-						sprintf "hl_make_dyn(&%s,%s)" (reg p) (type_value t)
-			) pl));
-			let rt = rtype r in
-			let ret = if rt = HVoid then "" else if is_dynamic rt then sprintf "%s = (%s)" (reg r) (ctype rt) else "vdynamic *ret = " in
-			sexpr "%shl_dyn_call((vclosure*)%s,%s,%d)" ret (reg f) (if pl = [] then "NULL" else "args") (List.length pl);
-			if rt <> HVoid && not (is_dynamic rt) then sexpr "%s = (%s)hl_dyn_cast%s(&ret,&hlt_dyn%s)" (reg r) (ctype rt) (dyn_prefix rt) (type_value_opt rt);
-			unblock();
-			line "}";
-		in
+	let dyn_prefix = function
+		| HUI8 | HUI16 | HI32 | HBool -> "i"
+		| HF32 -> "f"
+		| HF64 -> "d"
+		| _ -> "p"
+	in
 
-		let mcall r fid = function
-			| [] -> assert false
-			| o :: args ->
-				match rtype o with
-				| HObj _ ->
-					let vfun = cast_fun (sprintf "%s->$type->vobj_proto[%d]" (reg o) fid) (rtype o :: List.map rtype args) (rtype r) in
-					sexpr "%s%s(%s)" (rassign r (rtype r)) vfun (String.concat "," (List.map reg (o::args)))
-				| HVirtual vp ->
-					let rt = rtype r in
-					let meth = sprintf "hl_vfields(%s)[%d]" (reg o) fid in
-					let meth = cast_fun meth (HDyn :: List.map rtype args) rt in
-					sline "if( hl_vfields(%s)[%d] ) %s%s(%s); else {" (reg o) fid (rassign r rt) meth (String.concat "," ((reg o ^ "->value") :: List.map reg args));
-					block();
-					if args <> [] then sexpr "void *args[] = {%s}" (String.concat "," (List.map (fun p ->
-						let t = rtype p in
-						if is_ptr t then
-							reg p
-						else
-							sprintf "&%s" (reg p)
-					) args));
-					let rt = rtype r in
-					let ret = if rt = HVoid then "" else if is_ptr rt then sprintf "%s = (%s)" (reg r) (ctype rt) else begin sexpr "vdynamic ret"; ""; end in
-					let fname, fid, ft = vp.vfields.(fid) in
-					sexpr "%shl_dyn_call_obj(%s->value,%s,%ld/*%s*/,%s,%s)" ret (reg o) (type_value ft) (hash fid) fname (if args = [] then "NULL" else "args") (if is_ptr rt || rt == HVoid then "NULL" else "&ret");
-					if rt <> HVoid && not (is_ptr rt) then sexpr "%s = (%s)ret.v.%s" (reg r) (ctype rt) (dyn_prefix rt);
-					unblock();
-					sline "}"
-				| _ ->
-					assert false
-		in
+	let type_value_opt t =
+		match t with HF32 | HF64 -> "" | _ -> "," ^ type_value t
+	in
 
-		let set_field obj fid v =
-			match rtype obj with
-			| HObj o ->
-				let name, t = resolve_field o fid in
-				sexpr "%s->%s = %s" (reg obj) (obj_field fid name) (rcast v t)
-			| HVirtual vp ->
-				let name, nid, t = vp.vfields.(fid) in
-				let dset = sprintf "hl_dyn_set%s(%s->value,%ld/*%s*/%s,%s)" (dyn_prefix t) (reg obj) (hash nid) name (type_value_opt (rtype v)) (reg v) in
-				(match t with
-				| HFun _ -> expr dset
-				| _ -> sexpr "if( hl_vfields(%s)[%d] ) *(%s*)(hl_vfields(%s)[%d]) = (%s)%s; else %s" (reg obj) fid (ctype t) (reg obj) fid (ctype t) (reg v) dset)
-			| _ ->
-				assert false
-		in
+	let dyn_call r f pl =
+		line "{";
+		block();
+		if pl <> [] then sexpr "vdynamic *args[] = {%s}" (String.concat "," (List.map (fun p ->
+			match rtype p with
+			| HDyn ->
+				reg p
+			| t ->
+				if is_dynamic t then
+					sprintf "(vdynamic*)%s" (reg p)
+				else
+					sprintf "hl_make_dyn(&%s,%s)" (reg p) (type_value t)
+		) pl));
+		let rt = rtype r in
+		let ret = if rt = HVoid then "" else if is_dynamic rt then sprintf "%s = (%s)" (reg r) (ctype rt) else "vdynamic *ret = " in
+		sexpr "%shl_dyn_call((vclosure*)%s,%s,%d)" ret (reg f) (if pl = [] then "NULL" else "args") (List.length pl);
+		if rt <> HVoid && not (is_dynamic rt) then sexpr "%s = (%s)hl_dyn_cast%s(&ret,&hlt_dyn%s)" (reg r) (ctype rt) (dyn_prefix rt) (type_value_opt rt);
+		unblock();
+		line "}";
+	in
 
-		let get_field r obj fid =
-			match rtype obj with
-			| HObj o ->
-				let name, t = resolve_field o fid in
-				sexpr "%s%s->%s" (rassign r t) (reg obj) (obj_field fid name)
-			| HVirtual v ->
-				let name, nid, t = v.vfields.(fid) in
-				let dget = sprintf "(%s)hl_dyn_get%s(%s->value,%ld/*%s*/%s)" (ctype t) (dyn_prefix t) (reg obj) (hash nid) name (type_value_opt t) in
-				(match t with
-				| HFun _ -> sexpr "%s%s" (rassign r t) dget
-				| _ -> sexpr "%shl_vfields(%s)[%d] ? (*(%s*)(hl_vfields(%s)[%d])) : %s" (rassign r t) (reg obj) fid (ctype t) (reg obj) fid dget)
+	let mcall r fid = function
+		| [] -> assert false
+		| o :: args ->
+			match rtype o with
+			| HObj _ ->
+				let vfun = cast_fun (sprintf "%s->$type->vobj_proto[%d]" (reg o) fid) (rtype o :: List.map rtype args) (rtype r) in
+				sexpr "%s%s(%s)" (rassign r (rtype r)) vfun (String.concat "," (List.map reg (o::args)))
+			| HVirtual vp ->
+				let rt = rtype r in
+				let meth = sprintf "hl_vfields(%s)[%d]" (reg o) fid in
+				let meth = cast_fun meth (HDyn :: List.map rtype args) rt in
+				sline "if( hl_vfields(%s)[%d] ) %s%s(%s); else {" (reg o) fid (rassign r rt) meth (String.concat "," ((reg o ^ "->value") :: List.map reg args));
+				block();
+				if args <> [] then sexpr "void *args[] = {%s}" (String.concat "," (List.map (fun p ->
+					let t = rtype p in
+					if is_ptr t then
+						reg p
+					else
+						sprintf "&%s" (reg p)
+				) args));
+				let rt = rtype r in
+				let ret = if rt = HVoid then "" else if is_ptr rt then sprintf "%s = (%s)" (reg r) (ctype rt) else begin sexpr "vdynamic ret"; ""; end in
+				let fname, fid, ft = vp.vfields.(fid) in
+				sexpr "%shl_dyn_call_obj(%s->value,%s,%ld/*%s*/,%s,%s)" ret (reg o) (type_value ft) (hash ctx fid) fname (if args = [] then "NULL" else "args") (if is_ptr rt || rt == HVoid then "NULL" else "&ret");
+				if rt <> HVoid && not (is_ptr rt) then sexpr "%s = (%s)ret.v.%s" (reg r) (ctype rt) (dyn_prefix rt);
+				unblock();
+				sline "}"
 			| _ ->
 				assert false
-		in
+	in
 
-		let fret = (match f.ftype with
-		| HFun (args,t) ->
-			sline "static %s %s(%s) {" (ctype t) funnames.(f.findex) (String.concat "," (List.map (fun t -> incr rid; var_type (reg !rid) t) args));
-			t
+	let set_field obj fid v =
+		match rtype obj with
+		| HObj o ->
+			let name, t = resolve_field o fid in
+			sexpr "%s->%s = %s" (reg obj) (obj_field fid name) (rcast v t)
+		| HVirtual vp ->
+			let name, nid, t = vp.vfields.(fid) in
+			let dset = sprintf "hl_dyn_set%s(%s->value,%ld/*%s*/%s,%s)" (dyn_prefix t) (reg obj) (hash ctx nid) name (type_value_opt (rtype v)) (reg v) in
+			(match t with
+			| HFun _ -> expr dset
+			| _ -> sexpr "if( hl_vfields(%s)[%d] ) *(%s*)(hl_vfields(%s)[%d]) = (%s)%s; else %s" (reg obj) fid (ctype t) (reg obj) fid (ctype t) (reg v) dset)
 		| _ ->
 			assert false
-		) in
-		block();
-		let var_map = Hashtbl.create 0 in
-		Array.iteri (fun i t ->
-			if i <= !rid || t = HVoid then ()
-			else
-				let key = ctype_no_ptr t in
-				Hashtbl.replace var_map key (try (reg i) :: Hashtbl.find var_map key with Not_found -> [reg i])
-		) f.regs;
-		Hashtbl.iter (fun (s,i) il ->
-			let prefix = String.make i '*' in
-			let il = List.rev_map (fun s -> prefix ^ s) il in
-			sexpr "%s %s" s (String.concat ", " il)
-		) var_map;
-		let output_options = Array.make (Array.length f.code + 1) [] in
-		let output_at i oo = output_options.(i) <- oo :: output_options.(i) in
-		let output_at2 i ool = List.iter (output_at i) ool in
-		let has_label i = List.exists (function OOLabel -> true | _ -> false) output_options.(i) in
-
-		let trap_depth = ref 0 in
-		let max_trap_depth = ref 0 in
-		Array.iter (fun op ->
-			match op with
-			| OTrap _ ->
-				incr trap_depth;
-				if !trap_depth > !max_trap_depth then max_trap_depth := !trap_depth
-			| OEndTrap true ->
-				decr trap_depth
-			| _ ->
-				()
-		) f.code;
-		for i = 0 to !max_trap_depth - 1 do
-			sexpr "hl_trap_ctx trap$%d" i;
-		done;
+	in
 
-		let flush_options i =
-			match output_options.(i) with
-			| [] -> ()
-			| opts ->
-				(* put label after } *)
-				let opts = if has_label i && List.mem OOEndBlock opts then OOLabel :: List.filter (fun i -> i <> OOLabel) opts else opts in
-				let opts = List.rev opts in
-				List.iter (function
-					| OOLabel -> sline "%s:" (label i)
-					| OOCase i -> sline "case %i:" i
-					| OODefault -> line "default:"
-					| OOIncreaseIndent -> block()
-					| OODecreaseIndent -> unblock()
-					| OOBeginBlock ->  line "{"
-					| OOEndBlock -> line "}"
-				) opts
-		in
+	let get_field r obj fid =
+		match rtype obj with
+		| HObj o ->
+			let name, t = resolve_field o fid in
+			sexpr "%s%s->%s" (rassign r t) (reg obj) (obj_field fid name)
+		| HVirtual v ->
+			let name, nid, t = v.vfields.(fid) in
+			let dget = sprintf "(%s)hl_dyn_get%s(%s->value,%ld/*%s*/%s)" (ctype t) (dyn_prefix t) (reg obj) (hash ctx nid) name (type_value_opt t) in
+			(match t with
+			| HFun _ -> sexpr "%s%s" (rassign r t) dget
+			| _ -> sexpr "%shl_vfields(%s)[%d] ? (*(%s*)(hl_vfields(%s)[%d])) : %s" (rassign r t) (reg obj) fid (ctype t) (reg obj) fid dget)
+		| _ ->
+			assert false
+	in
 
-		Array.iteri (fun i op ->
-			flush_options i;
-			let label delta =
-				let addr = delta + i + 1 in
-				let label = label addr in
-				if not (has_label addr) then output_at addr OOLabel;
-				label
-			in
-			let todo() =
-				sexpr "hl_fatal(\"%s\")" (ostr (fun id -> "f" ^ string_of_int id) op)
+	let fret = (match f.ftype with
+	| HFun (args,t) ->
+		sline "%s %s(%s) {" (ctype t) (funname f.findex) (String.concat "," (List.map (fun t -> incr rid; var_type (reg !rid) t) args));
+		t
+	| _ ->
+		assert false
+	) in
+	block();
+	let var_map = Hashtbl.create 0 in
+	Array.iteri (fun i t ->
+		if i <= !rid || t = HVoid then ()
+		else
+			let key = ctype_no_ptr t in
+			Hashtbl.replace var_map key (try (reg i) :: Hashtbl.find var_map key with Not_found -> [reg i])
+	) f.regs;
+	Hashtbl.iter (fun (s,i) il ->
+		let prefix = String.make i '*' in
+		let il = List.rev_map (fun s -> prefix ^ s) il in
+		sexpr "%s %s" s (String.concat ", " il)
+	) var_map;
+	let output_options = Array.make (Array.length f.code + 1) [] in
+	let output_at i oo = output_options.(i) <- oo :: output_options.(i) in
+	let output_at2 i ool = List.iter (output_at i) ool in
+	let has_label i = List.exists (function OOLabel -> true | _ -> false) output_options.(i) in
+
+	let trap_depth = ref 0 in
+	let max_trap_depth = ref 0 in
+	Array.iter (fun op ->
+		match op with
+		| OTrap _ ->
+			incr trap_depth;
+			if !trap_depth > !max_trap_depth then max_trap_depth := !trap_depth
+		| OEndTrap true ->
+			decr trap_depth
+		| _ ->
+			()
+	) f.code;
+	for i = 0 to !max_trap_depth - 1 do
+		sexpr "hl_trap_ctx trap$%d" i;
+	done;
+
+	let flush_options i =
+		match output_options.(i) with
+		| [] -> ()
+		| opts ->
+			(* put label after } *)
+			let opts = if has_label i && List.mem OOEndBlock opts then OOLabel :: List.filter (fun i -> i <> OOLabel) opts else opts in
+			let opts = List.rev opts in
+			List.iter (function
+				| OOLabel -> sline "%s:" (label i)
+				| OOCase i -> sline "case %i:" i
+				| OODefault -> line "default:"
+				| OOIncreaseIndent -> block()
+				| OODecreaseIndent -> unblock()
+				| OOBeginBlock ->  line "{"
+				| OOEndBlock -> line "}"
+			) opts
+	in
+
+	Array.iteri (fun i op ->
+		flush_options i;
+		let label delta =
+			let addr = delta + i + 1 in
+			let label = label addr in
+			if not (has_label addr) then output_at addr OOLabel;
+			label
+		in
+		let todo() =
+			sexpr "hl_fatal(\"%s\")" (ostr (fun id -> "f" ^ string_of_int id) op)
+		in
+		let rec compare_op op a b d =
+			let phys_compare() =
+				sexpr "if( %s %s %s ) goto %s" (reg a) (s_comp op) (rcast b (rtype a)) (label d)
 			in
-			let rec compare_op op a b d =
-				let phys_compare() =
-					sexpr "if( %s %s %s ) goto %s" (reg a) (s_comp op) (rcast b (rtype a)) (label d)
-				in
-				(*
-					safe_cast is already checked
-					two ways (same type) for eq
-					one way for comparisons
-				*)
-				match rtype a, rtype b with
-				| (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool), (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool) ->
-					phys_compare()
-				| HType, HType ->
-					sexpr "if( hl_same_type(%s,%s) %s 0 ) {} else goto %s" (reg a) (reg b) (s_comp op) (label d)
-				| HNull t, HNull _ ->
-					let field = dyn_value_field t in
-					let pcompare = sprintf "(%s%s %s %s%s)" (reg a) field (s_comp op) (reg b) field in
-					if op = CEq then
-						sexpr "if( %s == %s || (%s && %s && %s) ) goto %s" (reg a) (reg b) (reg a) (reg b) pcompare (label d)
-					else if op = CNeq then
-						sexpr "if( %s != %s && (!%s || !%s || %s) ) goto %s" (reg a) (reg b) (reg a) (reg b) pcompare (label d)
-					else
-						sexpr "if( %s && %s && %s ) goto %s" (reg a) (reg b) pcompare (label d)
-				| HDyn , _ | _, HDyn ->
-					let inv = if op = CGt || op = CGte then "&& i != hl_invalid_comparison " else "" in
-					sexpr "{ int i = hl_dyn_compare((vdynamic*)%s,(vdynamic*)%s); if( i %s 0 %s) goto %s; }" (reg a) (reg b) (s_comp op) inv (label d)
-				| HObj oa, HObj _ ->
-					(try
-						let fid = PMap.find "__compare" oa.pfunctions in
-						if op = CEq then
-							sexpr "if( %s == %s || (%s && %s && %s(%s,%s) == 0) ) goto %s" (reg a) (reg b) (reg a) (reg b) funnames.(fid) (reg a) (reg b) (label d)
-						else if op = CNeq then
-							sexpr "if( %s != %s && (!%s || !%s || %s(%s,%s) != 0) ) goto %s" (reg a) (reg b) (reg a) (reg b) funnames.(fid) (reg a) (reg b) (label d)
-						else
-							sexpr "if( %s && %s && %s(%s,%s) %s 0 ) goto %s" (reg a) (reg b) funnames.(fid) (reg a) (reg b) (s_comp op) (label d)
-					with Not_found ->
-						phys_compare())
-				| HVirtual _, HVirtual _ ->
-					if op = CEq then
-						sexpr "if( %s == %s || (%s && %s && %s->value && %s->value && %s->value == %s->value) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
-					else if op = CNeq then
-						sexpr "if( %s != %s && (!%s || !%s || !%s->value || !%s->value || %s->value != %s->value) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
-					else
-						assert false
-				| HEnum _, HEnum _ | HDynObj, HDynObj ->
-					phys_compare()
-				| HVirtual _, HObj _->
+			(*
+				safe_cast is already checked
+				two ways (same type) for eq
+				one way for comparisons
+			*)
+			match rtype a, rtype b with
+			| (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool), (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool) ->
+				phys_compare()
+			| HType, HType ->
+				sexpr "if( hl_same_type(%s,%s) %s 0 ) {} else goto %s" (reg a) (reg b) (s_comp op) (label d)
+			| HNull t, HNull _ ->
+				let field = dyn_value_field t in
+				let pcompare = sprintf "(%s%s %s %s%s)" (reg a) field (s_comp op) (reg b) field in
+				if op = CEq then
+					sexpr "if( %s == %s || (%s && %s && %s) ) goto %s" (reg a) (reg b) (reg a) (reg b) pcompare (label d)
+				else if op = CNeq then
+					sexpr "if( %s != %s && (!%s || !%s || %s) ) goto %s" (reg a) (reg b) (reg a) (reg b) pcompare (label d)
+				else
+					sexpr "if( %s && %s && %s ) goto %s" (reg a) (reg b) pcompare (label d)
+			| HDyn , _ | _, HDyn ->
+				let inv = if op = CGt || op = CGte then "&& i != hl_invalid_comparison " else "" in
+				sexpr "{ int i = hl_dyn_compare((vdynamic*)%s,(vdynamic*)%s); if( i %s 0 %s) goto %s; }" (reg a) (reg b) (s_comp op) inv (label d)
+			| HObj oa, HObj _ ->
+				(try
+					let fid = PMap.find "__compare" oa.pfunctions in
 					if op = CEq then
-						sexpr "if( %s ? (%s && %s->value == (vdynamic*)%s) : (%s == NULL) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (label d)
+						sexpr "if( %s == %s || (%s && %s && %s(%s,%s) == 0) ) goto %s" (reg a) (reg b) (reg a) (reg b) (funname fid) (reg a) (reg b) (label d)
 					else if op = CNeq then
-						sexpr "if( %s ? (%s == NULL || %s->value != (vdynamic*)%s) : (%s != NULL) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (label d)
+						sexpr "if( %s != %s && (!%s || !%s || %s(%s,%s) != 0) ) goto %s" (reg a) (reg b) (reg a) (reg b) (funname fid) (reg a) (reg b) (label d)
 					else
-						assert false
-				| HObj _, HVirtual _ ->
-					compare_op op b a d
-				| HFun _, HFun _ ->
-					phys_compare()
-				| ta, tb ->
-					failwith ("Don't know how to compare " ^ tstr ta ^ " and " ^ tstr tb ^ " (hlc)")
-			in
-			match op with
-			| OMov (r,v) ->
-				if rtype r <> HVoid then sexpr "%s = %s" (reg r) (rcast v (rtype r))
-			| OInt (r,idx) ->
-				if code.ints.(idx) = 0x80000000l then
-					sexpr "%s = 0x80000000" (reg r)
+						sexpr "if( %s && %s && %s(%s,%s) %s 0 ) goto %s" (reg a) (reg b) (funname fid) (reg a) (reg b) (s_comp op) (label d)
+				with Not_found ->
+					phys_compare())
+			| HVirtual _, HVirtual _ ->
+				if op = CEq then
+					sexpr "if( %s == %s || (%s && %s && %s->value && %s->value && %s->value == %s->value) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
+				else if op = CNeq then
+					sexpr "if( %s != %s && (!%s || !%s || !%s->value || !%s->value || %s->value != %s->value) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
 				else
-					sexpr "%s = %ld" (reg r) code.ints.(idx)
-			| OFloat (r,idx) ->
-				sexpr "%s = %.19g" (reg r) code.floats.(idx)
-			| OBool (r,b) ->
-				sexpr "%s = %s" (reg r) (if b then "true" else "false")
-			| OBytes (r,idx) ->
-				sexpr "%s = bytes$%d" (reg r) idx
-			| OString (r,idx) ->
-				sexpr "%s = string$%d" (reg r) idx
-			| ONull r ->
-				sexpr "%s = NULL" (reg r)
-			| OAdd (r,a,b) ->
-				sexpr "%s = %s + %s" (reg r) (reg a) (reg b)
-			| OSub (r,a,b) ->
-				sexpr "%s = %s - %s" (reg r) (reg a) (reg b)
-			| OMul (r,a,b) ->
-				sexpr "%s = %s * %s" (reg r) (reg a) (reg b)
-			| OSDiv (r,a,b) ->
-				(match rtype r with
-				| HUI8 | HUI16 | HI32 ->
-					sexpr "%s = %s == 0 ? 0 : %s / %s" (reg r) (reg b) (reg a) (reg b)
-				| _ ->
-					sexpr "%s = %s / %s" (reg r) (reg a) (reg b))
-			| OUDiv (r,a,b) ->
-				sexpr "%s = %s == 0 ? 0 : ((unsigned)%s) / ((unsigned)%s)" (reg r) (reg b) (reg a) (reg b)
-			| OSMod (r,a,b) ->
-				(match rtype r with
-				| HUI8 | HUI16 | HI32 ->
-					sexpr "%s = %s == 0 ? 0 : %s %% %s" (reg r) (reg b) (reg a) (reg b)
-				| HF32 ->
-					sexpr "%s = fmodf(%s,%s)" (reg r) (reg a) (reg b)
-				| HF64 ->
-					sexpr "%s = fmod(%s,%s)" (reg r) (reg a) (reg b)
-				| _ ->
-					assert false)
-			| OUMod (r,a,b) ->
-				sexpr "%s = %s == 0 ? 0 : ((unsigned)%s) %% ((unsigned)%s)" (reg r) (reg b) (reg a) (reg b)
-			| OShl (r,a,b) ->
-				sexpr "%s = %s << %s" (reg r) (reg a) (reg b)
-			| OSShr (r,a,b) ->
-				sexpr "%s = %s >> %s" (reg r) (reg a) (reg b)
-			| OUShr (r,a,b) ->
-				sexpr "%s = ((unsigned)%s) >> %s" (reg r) (reg a) (reg b)
-			| OAnd (r,a,b) ->
-				sexpr "%s = %s & %s" (reg r) (reg a) (reg b)
-			| OOr (r,a,b) ->
-				sexpr "%s = %s | %s" (reg r) (reg a) (reg b)
-			| OXor (r,a,b) ->
-				sexpr "%s = %s ^ %s" (reg r) (reg a) (reg b)
-			| ONeg (r,v) ->
-				sexpr "%s = -%s" (reg r) (reg v)
-			| ONot (r,v) ->
-				sexpr "%s = !%s" (reg r) (reg v)
-			| OIncr r ->
-				sexpr "++%s" (reg r)
-			| ODecr r ->
-				sexpr "--%s" (reg r)
-			| OCall0 (r,fid) ->
-				ocall r fid []
-			| OCall1 (r,fid,a) ->
-				ocall r fid [a]
-			| OCall2 (r,fid,a,b) ->
-				ocall r fid [a;b]
-			| OCall3 (r,fid,a,b,c) ->
-				ocall r fid [a;b;c]
-			| OCall4 (r,fid,a,b,c,d) ->
-				ocall r fid [a;b;c;d]
-			| OCallN (r,fid,rl) ->
-				ocall r fid rl
-			| OCallMethod (r,fid,pl) ->
-				mcall r fid pl
-			| OCallThis (r,fid,pl) ->
-				mcall r fid (0 :: pl)
-			| OCallClosure (r,cl,pl) ->
-				(match rtype cl with
-				| HDyn ->
-					dyn_call r cl pl
-				| HFun (args,ret) ->
-					let sargs = String.concat "," (List.map2 rcast pl args) in
-					sexpr "%s%s->hasValue ? %s((vdynamic*)%s->value%s) : %s(%s)" (rassign r ret) (reg cl) (rfun cl (HDyn :: args) ret) (reg cl) (if sargs = "" then "" else "," ^ sargs) (rfun cl args ret) sargs
-				| _ ->
-					assert false)
-			| OStaticClosure (r,fid) ->
-				sexpr "%s = &cl$%d" (reg r) fid
-			| OSetMethod (o,f,fid) ->
-				let name, t = resolve_field (match rtype o with HObj o -> o | _ -> assert false) f in
-				sexpr "%s->%s = (%s)&cl$%d" (reg o) (ident name) (ctype t) fid
-			| OInstanceClosure (r,fid,ptr) ->
-				let args, t = tfuns.(fid) in
-				sexpr "%s = hl_alloc_closure_ptr(%s,%s,%s)" (reg r) (type_value (HFun (args,t))) funnames.(fid) (reg ptr)
-			| OVirtualClosure (r,o,m) ->
-				(match rtype o with
-				| HObj p ->
-					let tl,t = tfuns.(p.pvirtuals.(m)) in
-					let s = sprintf "%s->$type->vobj_proto[%d]" (reg o) m in
-					sexpr "%s = hl_alloc_closure_ptr(%s,%s,%s)" (reg r) (type_value (HFun(tl,t))) s (reg o)
-				| _ ->
-					todo())
-			| OGetGlobal (r,g) ->
-				sexpr "%s = (%s)global$%d" (reg r) (ctype (rtype r)) g
-			| OSetGlobal (g,r) ->
-				sexpr "global$%d = (%s)%s" g (ctype code.globals.(g)) (reg r)
-			| ORet r ->
-				if rtype r = HVoid then expr "return" else sexpr "return %s" (rcast r fret)
-			| OJTrue (r,d) | OJNotNull (r,d) ->
-				sexpr "if( %s ) goto %s" (reg r) (label d)
-			| OJFalse (r,d) | OJNull (r,d) ->
-				sexpr "if( !%s ) goto %s" (reg r) (label d)
-			| OJSLt (a,b,d) ->
-				compare_op CLt a b d
-			| OJSGte (a,b,d) ->
-				compare_op CGte a b d
-			| OJSGt (a,b,d) ->
-				compare_op CGt a b d
-			| OJSLte (a,b,d) ->
-				compare_op CLte a b d
-			| OJULt (a,b,d) ->
-				sexpr "if( ((unsigned)%s) < ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
-			| OJUGte (a,b,d) ->
-				sexpr "if( ((unsigned)%s) >= ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
-			| OJEq (a,b,d) ->
-				compare_op CEq a b d
-			| OJNotEq (a,b,d) ->
-				compare_op CNeq a b d
-			| OJAlways d ->
-				sexpr "goto %s" (label d)
-			| OLabel _ ->
-				if not (has_label i) then sline "%s:" (label (-1))
-			| OToDyn (r,v) ->
-				if is_ptr (rtype v) then begin
-					sline "if( %s == NULL ) %s = NULL; else {" (reg v) (reg r);
-					block();
-				end;
-				sexpr "%s = hl_alloc_dynamic(%s)" (reg r) (type_value (rtype v));
-				(match rtype v with
-				| HUI8 | HUI16 | HI32 | HBool ->
-					sexpr "%s->v.i = %s" (reg r) (reg v)
-				| HF32 ->
-					sexpr "%s->v.f = %s" (reg r) (reg v)
-				| HF64 ->
-					sexpr "%s->v.d = %s" (reg r) (reg v)
-				| _ ->
-					sexpr "%s->v.ptr = %s" (reg r) (reg v));
-				if is_ptr (rtype v) then begin
-					unblock();
-					line "}";
-				end;
-			| OToSFloat (r,v) ->
-				sexpr "%s = (%s)%s" (reg r) (ctype (rtype r)) (reg v)
-			| OToUFloat (r,v) ->
-				sexpr "%s = (%s)(unsigned)%s" (reg r) (ctype (rtype r)) (reg v)
-			| OToInt (r,v) ->
-				sexpr "%s = (int)%s" (reg r) (reg v)
-			| ONew r ->
-				(match rtype r with
-				| HObj o -> sexpr "%s = (%s)hl_alloc_obj(%s)" (reg r) (tname o.pname) (tname o.pname ^ "__val")
-				| HDynObj -> sexpr "%s = hl_alloc_dynobj()" (reg r)
-				| HVirtual _ as t -> sexpr "%s = hl_alloc_virtual(%s)" (reg r) (type_value t)
-				| _ -> assert false)
-			| OField (r,obj,fid) ->
-				get_field r obj fid
-			| OSetField (obj,fid,v) ->
-				set_field obj fid v
-			| OGetThis (r,fid) ->
-				get_field r 0 fid
-			| OSetThis (fid,r) ->
-				set_field 0 fid r
-			| OThrow r ->
-				sexpr "hl_throw((vdynamic*)%s)" (reg r)
-			| ORethrow r ->
-				sexpr "hl_rethrow((vdynamic*)%s)" (reg r)
-			| OGetUI8 (r,b,idx) ->
-				sexpr "%s = *(unsigned char*)(%s + %s)" (reg r) (reg b) (reg idx)
-			| OGetUI16 (r,b,idx) ->
-				sexpr "%s = *(unsigned short*)(%s + %s)" (reg r) (reg b) (reg idx)
-			| OGetI32 (r,b,idx) ->
-				sexpr "%s = *(int*)(%s + %s)" (reg r) (reg b) (reg idx)
-			| OGetF32 (r,b,idx) ->
-				sexpr "%s = *(float*)(%s + %s)" (reg r) (reg b) (reg idx)
-			| OGetF64 (r,b,idx) ->
-				sexpr "%s = *(double*)(%s + %s)" (reg r) (reg b) (reg idx)
-			| OGetArray (r, arr, idx) ->
-				sexpr "%s = ((%s*)(%s + 1))[%s]" (reg r) (ctype (rtype r)) (reg arr) (reg idx)
-			| OSetUI8 (b,idx,r) ->
-				sexpr "*(unsigned char*)(%s + %s) = (unsigned char)%s" (reg b) (reg idx) (reg r)
-			| OSetUI16 (b,idx,r) ->
-				sexpr "*(unsigned short*)(%s + %s) = (unsigned short)%s" (reg b) (reg idx) (reg r)
-			| OSetI32 (b,idx,r) ->
-				sexpr "*(int*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
-			| OSetF32 (b,idx,r) ->
-				sexpr "*(float*)(%s + %s) = (float)%s" (reg b) (reg idx) (reg r)
-			| OSetF64 (b,idx,r) ->
-				sexpr "*(double*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
-			| OSetArray (arr,idx,v) ->
-				sexpr "((%s*)(%s + 1))[%s] = %s" (ctype (rtype v)) (reg arr) (reg idx) (reg v)
-			| OSafeCast (r,v) ->
-				let tsrc = rtype v in
-				let t = rtype r in
-				if tsrc = HNull t then
-					sexpr "%s = %s ? %s%s : 0" (reg r) (reg v) (reg v) (dyn_value_field t)
+					assert false
+			| HEnum _, HEnum _ | HDynObj, HDynObj ->
+				phys_compare()
+			| HVirtual _, HObj _->
+				if op = CEq then
+					sexpr "if( %s ? (%s && %s->value == (vdynamic*)%s) : (%s == NULL) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (label d)
+				else if op = CNeq then
+					sexpr "if( %s ? (%s == NULL || %s->value != (vdynamic*)%s) : (%s != NULL) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (label d)
 				else
-					sexpr "%s = (%s)hl_dyn_cast%s(&%s,%s%s)" (reg r) (ctype t) (dyn_prefix t) (reg v) (type_value (rtype v)) (type_value_opt t)
-			| OUnsafeCast (r,v) ->
-				sexpr "%s = (%s)%s" (reg r) (ctype (rtype r)) (reg v)
-			| OArraySize (r,a) ->
-				sexpr "%s = %s->size" (reg r) (reg a)
-			| OType (r,t) ->
-				sexpr "%s = %s" (reg r) (type_value t)
-			| OGetType (r,v) ->
-				sexpr "%s = %s ? ((vdynamic*)%s)->t : &hlt_void" (reg r) (reg v) (reg v)
-			| OGetTID (r,v) ->
-				sexpr "%s = %s->kind" (reg r) (reg v)
-			| ORef (r,v) ->
-				sexpr "%s = &%s" (reg r) (reg v)
-			| OUnref (r,v) ->
-				sexpr "%s = *%s" (reg r) (reg v)
-			| OSetref (r,v) ->
-				sexpr "*%s = %s" (reg r) (reg v)
-			| OToVirtual (r,v) ->
-				sexpr "%s = hl_to_virtual(%s,(vdynamic*)%s)" (reg r) (type_value (rtype r)) (reg v)
-			| ODynGet (r,o,sid) ->
-				let t = rtype r in
-				let h = hash sid in
-				sexpr "%s = (%s)hl_dyn_get%s((vdynamic*)%s,%ld/*%s*/%s)" (reg r) (ctype t) (dyn_prefix t) (reg o) h code.strings.(sid) (type_value_opt t)
-			| ODynSet (o,sid,v) ->
-				let h = hash sid in
-				sexpr "hl_dyn_set%s((vdynamic*)%s,%ld/*%s*/%s,%s)" (dyn_prefix (rtype v)) (reg o) h code.strings.(sid) (type_value_opt (rtype v)) (reg v)
-			| OMakeEnum (r,cid,rl) ->
-				let e, et = (match rtype r with HEnum e -> e, enum_constr_type e cid | _ -> assert false) in
-				let has_ptr = List.exists (fun r -> is_gc_ptr (rtype r)) rl in
-				let need_tmp = List.mem r rl in
-				let tmp = if not need_tmp then reg r else begin
-					sexpr "{ venum *tmp";
-					"tmp"
-				end in
-				sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" tmp (if has_ptr then "" else "_noptr") et;
-				sexpr "%s->index = %d" tmp cid;
-				let _,_,tl = e.efields.(cid) in
-				list_iteri (fun i v ->
-					sexpr "((%s*)%s)->p%d = %s" et tmp i (rcast v tl.(i))
-				) rl;
-				if need_tmp then sexpr "%s = tmp; }" (reg r)
-			| OEnumAlloc (r,cid) ->
-				let et, (_,_,tl) = (match rtype r with HEnum e -> enum_constr_type e cid, e.efields.(cid) | _ -> assert false) in
-				let has_ptr = List.exists is_gc_ptr (Array.to_list tl) in
-				sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" (reg r) (if has_ptr then "" else "_noptr") et;
-				sexpr "memset(%s,0,sizeof(%s))" (reg r) et;
-				if cid <> 0 then sexpr "%s->index = %d" (reg r) cid
-			| OEnumIndex (r,v) ->
-				(match rtype v with
-				| HEnum _ ->
-					sexpr "%s = %s->index" (reg r) (reg v)
-				| HDyn ->
-					sexpr "%s = ((venum*)%s->v.ptr)->index" (reg r) (reg v)
-				| _ ->
-					assert false)
-			| OEnumField (r,e,cid,pid) ->
-				let tname,(_,_,tl) = (match rtype e with HEnum e -> enum_constr_type e cid, e.efields.(cid) | _ -> assert false) in
-				sexpr "%s((%s*)%s)->p%d" (rassign r tl.(pid)) tname (reg e) pid
-			| OSetEnumField (e,pid,r) ->
-				let tname, (_,_,tl) = (match rtype e with HEnum e -> enum_constr_type e 0, e.efields.(0) | _ -> assert false) in
-				sexpr "((%s*)%s)->p%d = (%s)%s" tname (reg e) pid (ctype tl.(pid)) (reg r)
-			| OSwitch (r,idx,eend) ->
-				sline "switch(%s) {" (reg r);
+					assert false
+			| HObj _, HVirtual _ ->
+				compare_op op b a d
+			| HFun _, HFun _ ->
+				phys_compare()
+			| ta, tb ->
+				failwith ("Don't know how to compare " ^ tstr ta ^ " and " ^ tstr tb ^ " (hlc)")
+		in
+		match op with
+		| OMov (r,v) ->
+			if rtype r <> HVoid then sexpr "%s = %s" (reg r) (rcast v (rtype r))
+		| OInt (r,idx) ->
+			if code.ints.(idx) = 0x80000000l then
+				sexpr "%s = 0x80000000" (reg r)
+			else
+				sexpr "%s = %ld" (reg r) code.ints.(idx)
+		| OFloat (r,idx) ->
+			sexpr "%s = %.19g" (reg r) code.floats.(idx)
+		| OBool (r,b) ->
+			sexpr "%s = %s" (reg r) (if b then "true" else "false")
+		| OBytes (r,idx) ->
+			sexpr "%s = bytes$%d" (reg r) idx
+		| OString (r,idx) ->
+			sexpr "%s = (vbyte*)%s" (reg r) (string ctx idx)
+		| ONull r ->
+			sexpr "%s = NULL" (reg r)
+		| OAdd (r,a,b) ->
+			sexpr "%s = %s + %s" (reg r) (reg a) (reg b)
+		| OSub (r,a,b) ->
+			sexpr "%s = %s - %s" (reg r) (reg a) (reg b)
+		| OMul (r,a,b) ->
+			sexpr "%s = %s * %s" (reg r) (reg a) (reg b)
+		| OSDiv (r,a,b) ->
+			(match rtype r with
+			| HUI8 | HUI16 | HI32 ->
+				sexpr "%s = %s == 0 ? 0 : %s / %s" (reg r) (reg b) (reg a) (reg b)
+			| _ ->
+				sexpr "%s = %s / %s" (reg r) (reg a) (reg b))
+		| OUDiv (r,a,b) ->
+			sexpr "%s = %s == 0 ? 0 : ((unsigned)%s) / ((unsigned)%s)" (reg r) (reg b) (reg a) (reg b)
+		| OSMod (r,a,b) ->
+			(match rtype r with
+			| HUI8 | HUI16 | HI32 ->
+				sexpr "%s = %s == 0 ? 0 : %s %% %s" (reg r) (reg b) (reg a) (reg b)
+			| HF32 ->
+				sexpr "%s = fmodf(%s,%s)" (reg r) (reg a) (reg b)
+			| HF64 ->
+				sexpr "%s = fmod(%s,%s)" (reg r) (reg a) (reg b)
+			| _ ->
+				assert false)
+		| OUMod (r,a,b) ->
+			sexpr "%s = %s == 0 ? 0 : ((unsigned)%s) %% ((unsigned)%s)" (reg r) (reg b) (reg a) (reg b)
+		| OShl (r,a,b) ->
+			sexpr "%s = %s << %s" (reg r) (reg a) (reg b)
+		| OSShr (r,a,b) ->
+			sexpr "%s = %s >> %s" (reg r) (reg a) (reg b)
+		| OUShr (r,a,b) ->
+			sexpr "%s = ((unsigned)%s) >> %s" (reg r) (reg a) (reg b)
+		| OAnd (r,a,b) ->
+			sexpr "%s = %s & %s" (reg r) (reg a) (reg b)
+		| OOr (r,a,b) ->
+			sexpr "%s = %s | %s" (reg r) (reg a) (reg b)
+		| OXor (r,a,b) ->
+			sexpr "%s = %s ^ %s" (reg r) (reg a) (reg b)
+		| ONeg (r,v) ->
+			sexpr "%s = -%s" (reg r) (reg v)
+		| ONot (r,v) ->
+			sexpr "%s = !%s" (reg r) (reg v)
+		| OIncr r ->
+			sexpr "++%s" (reg r)
+		| ODecr r ->
+			sexpr "--%s" (reg r)
+		| OCall0 (r,fid) ->
+			ocall r fid []
+		| OCall1 (r,fid,a) ->
+			ocall r fid [a]
+		| OCall2 (r,fid,a,b) ->
+			ocall r fid [a;b]
+		| OCall3 (r,fid,a,b,c) ->
+			ocall r fid [a;b;c]
+		| OCall4 (r,fid,a,b,c,d) ->
+			ocall r fid [a;b;c;d]
+		| OCallN (r,fid,rl) ->
+			ocall r fid rl
+		| OCallMethod (r,fid,pl) ->
+			mcall r fid pl
+		| OCallThis (r,fid,pl) ->
+			mcall r fid (0 :: pl)
+		| OCallClosure (r,cl,pl) ->
+			(match rtype cl with
+			| HDyn ->
+				dyn_call r cl pl
+			| HFun (args,ret) ->
+				let sargs = String.concat "," (List.map2 rcast pl args) in
+				sexpr "%s%s->hasValue ? %s((vdynamic*)%s->value%s) : %s(%s)" (rassign r ret) (reg cl) (rfun cl (HDyn :: args) ret) (reg cl) (if sargs = "" then "" else "," ^ sargs) (rfun cl args ret) sargs
+			| _ ->
+				assert false)
+		| OStaticClosure (r,fid) ->
+			sexpr "%s = &cl$%d" (reg r) fid
+		| OSetMethod (o,f,fid) ->
+			let name, t = resolve_field (match rtype o with HObj o -> o | _ -> assert false) f in
+			sexpr "%s->%s = (%s)&cl$%d" (reg o) (ident name) (ctype t) fid
+		| OInstanceClosure (r,fid,ptr) ->
+			let ft = ctx.ftable.(fid) in
+			sexpr "%s = hl_alloc_closure_ptr(%s,%s,%s)" (reg r) (type_value (HFun (ft.fe_args,ft.fe_ret))) (funname fid) (reg ptr)
+		| OVirtualClosure (r,o,m) ->
+			(match rtype o with
+			| HObj p ->
+				let ft = ctx.ftable.(p.pvirtuals.(m)) in
+				let s = sprintf "%s->$type->vobj_proto[%d]" (reg o) m in
+				sexpr "%s = hl_alloc_closure_ptr(%s,%s,%s)" (reg r) (type_value (HFun(ft.fe_args,ft.fe_ret))) s (reg o)
+			| _ ->
+				todo())
+		| OGetGlobal (r,g) ->
+			sexpr "%s = (%s)global$%d" (reg r) (ctype (rtype r)) g
+		| OSetGlobal (g,r) ->
+			sexpr "global$%d = (%s)%s" g (ctype code.globals.(g)) (reg r)
+		| ORet r ->
+			if rtype r = HVoid then expr "return" else sexpr "return %s" (rcast r fret)
+		| OJTrue (r,d) | OJNotNull (r,d) ->
+			sexpr "if( %s ) goto %s" (reg r) (label d)
+		| OJFalse (r,d) | OJNull (r,d) ->
+			sexpr "if( !%s ) goto %s" (reg r) (label d)
+		| OJSLt (a,b,d) ->
+			compare_op CLt a b d
+		| OJSGte (a,b,d) ->
+			compare_op CGte a b d
+		| OJSGt (a,b,d) ->
+			compare_op CGt a b d
+		| OJSLte (a,b,d) ->
+			compare_op CLte a b d
+		| OJULt (a,b,d) ->
+			sexpr "if( ((unsigned)%s) < ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
+		| OJUGte (a,b,d) ->
+			sexpr "if( ((unsigned)%s) >= ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
+		| OJEq (a,b,d) ->
+			compare_op CEq a b d
+		| OJNotEq (a,b,d) ->
+			compare_op CNeq a b d
+		| OJAlways d ->
+			sexpr "goto %s" (label d)
+		| OLabel _ ->
+			if not (has_label i) then sline "%s:" (label (-1))
+		| OToDyn (r,v) ->
+			if is_ptr (rtype v) then begin
+				sline "if( %s == NULL ) %s = NULL; else {" (reg v) (reg r);
 				block();
-				output_at2 (i + 1) [OODefault;OOIncreaseIndent];
-				Array.iteri (fun k delta -> output_at2 (delta + i + 1) [OODecreaseIndent;OOCase k;OOIncreaseIndent]) idx;
-				let pend = i+1+eend in
-				(* insert at end if we have another switch case here *)
-				let old = output_options.(pend) in
-				output_options.(pend) <- [];
-				output_at2 pend ([OODecreaseIndent;OODecreaseIndent;OOEndBlock] @ List.rev old);
-			| ONullCheck r ->
-				sexpr "if( %s == NULL ) hl_null_access()" (reg r)
-			| OTrap (r,d) ->
-				sexpr "hl_trap(trap$%d,%s,%s)" !trap_depth (reg r) (label d);
-				incr trap_depth
-			| OEndTrap b ->
-				sexpr "hl_endtrap(trap$%d)" (!trap_depth - 1);
-				if b then decr trap_depth;
-			| ONop _ ->
+			end;
+			sexpr "%s = hl_alloc_dynamic(%s)" (reg r) (type_value (rtype v));
+			(match rtype v with
+			| HUI8 | HUI16 | HI32 | HBool ->
+				sexpr "%s->v.i = %s" (reg r) (reg v)
+			| HF32 ->
+				sexpr "%s->v.f = %s" (reg r) (reg v)
+			| HF64 ->
+				sexpr "%s->v.d = %s" (reg r) (reg v)
+			| _ ->
+				sexpr "%s->v.ptr = %s" (reg r) (reg v));
+			if is_ptr (rtype v) then begin
+				unblock();
+				line "}";
+			end;
+		| OToSFloat (r,v) ->
+			sexpr "%s = (%s)%s" (reg r) (ctype (rtype r)) (reg v)
+		| OToUFloat (r,v) ->
+			sexpr "%s = (%s)(unsigned)%s" (reg r) (ctype (rtype r)) (reg v)
+		| OToInt (r,v) ->
+			sexpr "%s = (int)%s" (reg r) (reg v)
+		| ONew r ->
+			(match rtype r with
+			| HObj o -> sexpr "%s = (%s)hl_alloc_obj(%s)" (reg r) (tname o.pname) (tname o.pname ^ "__val")
+			| HDynObj -> sexpr "%s = hl_alloc_dynobj()" (reg r)
+			| HVirtual _ as t -> sexpr "%s = hl_alloc_virtual(%s)" (reg r) (type_value t)
+			| _ -> assert false)
+		| OField (r,obj,fid) ->
+			get_field r obj fid
+		| OSetField (obj,fid,v) ->
+			set_field obj fid v
+		| OGetThis (r,fid) ->
+			get_field r 0 fid
+		| OSetThis (fid,r) ->
+			set_field 0 fid r
+		| OThrow r ->
+			sexpr "hl_throw((vdynamic*)%s)" (reg r)
+		| ORethrow r ->
+			sexpr "hl_rethrow((vdynamic*)%s)" (reg r)
+		| OGetUI8 (r,b,idx) ->
+			sexpr "%s = *(unsigned char*)(%s + %s)" (reg r) (reg b) (reg idx)
+		| OGetUI16 (r,b,idx) ->
+			sexpr "%s = *(unsigned short*)(%s + %s)" (reg r) (reg b) (reg idx)
+		| OGetI32 (r,b,idx) ->
+			sexpr "%s = *(int*)(%s + %s)" (reg r) (reg b) (reg idx)
+		| OGetF32 (r,b,idx) ->
+			sexpr "%s = *(float*)(%s + %s)" (reg r) (reg b) (reg idx)
+		| OGetF64 (r,b,idx) ->
+			sexpr "%s = *(double*)(%s + %s)" (reg r) (reg b) (reg idx)
+		| OGetArray (r, arr, idx) ->
+			sexpr "%s = ((%s*)(%s + 1))[%s]" (reg r) (ctype (rtype r)) (reg arr) (reg idx)
+		| OSetUI8 (b,idx,r) ->
+			sexpr "*(unsigned char*)(%s + %s) = (unsigned char)%s" (reg b) (reg idx) (reg r)
+		| OSetUI16 (b,idx,r) ->
+			sexpr "*(unsigned short*)(%s + %s) = (unsigned short)%s" (reg b) (reg idx) (reg r)
+		| OSetI32 (b,idx,r) ->
+			sexpr "*(int*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
+		| OSetF32 (b,idx,r) ->
+			sexpr "*(float*)(%s + %s) = (float)%s" (reg b) (reg idx) (reg r)
+		| OSetF64 (b,idx,r) ->
+			sexpr "*(double*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
+		| OSetArray (arr,idx,v) ->
+			sexpr "((%s*)(%s + 1))[%s] = %s" (ctype (rtype v)) (reg arr) (reg idx) (reg v)
+		| OSafeCast (r,v) ->
+			let tsrc = rtype v in
+			let t = rtype r in
+			if tsrc = HNull t then
+				sexpr "%s = %s ? %s%s : 0" (reg r) (reg v) (reg v) (dyn_value_field t)
+			else
+				sexpr "%s = (%s)hl_dyn_cast%s(&%s,%s%s)" (reg r) (ctype t) (dyn_prefix t) (reg v) (type_value (rtype v)) (type_value_opt t)
+		| OUnsafeCast (r,v) ->
+			sexpr "%s = (%s)%s" (reg r) (ctype (rtype r)) (reg v)
+		| OArraySize (r,a) ->
+			sexpr "%s = %s->size" (reg r) (reg a)
+		| OType (r,t) ->
+			sexpr "%s = %s" (reg r) (type_value t)
+		| OGetType (r,v) ->
+			sexpr "%s = %s ? ((vdynamic*)%s)->t : &hlt_void" (reg r) (reg v) (reg v)
+		| OGetTID (r,v) ->
+			sexpr "%s = %s->kind" (reg r) (reg v)
+		| ORef (r,v) ->
+			sexpr "%s = &%s" (reg r) (reg v)
+		| OUnref (r,v) ->
+			sexpr "%s = *%s" (reg r) (reg v)
+		| OSetref (r,v) ->
+			sexpr "*%s = %s" (reg r) (reg v)
+		| OToVirtual (r,v) ->
+			sexpr "%s = hl_to_virtual(%s,(vdynamic*)%s)" (reg r) (type_value (rtype r)) (reg v)
+		| ODynGet (r,o,sid) ->
+			let t = rtype r in
+			let h = hash ctx sid in
+			sexpr "%s = (%s)hl_dyn_get%s((vdynamic*)%s,%ld/*%s*/%s)" (reg r) (ctype t) (dyn_prefix t) (reg o) h code.strings.(sid) (type_value_opt t)
+		| ODynSet (o,sid,v) ->
+			let h = hash ctx sid in
+			sexpr "hl_dyn_set%s((vdynamic*)%s,%ld/*%s*/%s,%s)" (dyn_prefix (rtype v)) (reg o) h code.strings.(sid) (type_value_opt (rtype v)) (reg v)
+		| OMakeEnum (r,cid,rl) ->
+			let e, et = (match rtype r with HEnum e -> e, enum_constr_type ctx e cid | _ -> assert false) in
+			let has_ptr = List.exists (fun r -> is_gc_ptr (rtype r)) rl in
+			let need_tmp = List.mem r rl in
+			let tmp = if not need_tmp then reg r else begin
+				sexpr "{ venum *tmp";
+				"tmp"
+			end in
+			sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" tmp (if has_ptr then "" else "_noptr") et;
+			sexpr "%s->index = %d" tmp cid;
+			let _,_,tl = e.efields.(cid) in
+			list_iteri (fun i v ->
+				sexpr "((%s*)%s)->p%d = %s" et tmp i (rcast v tl.(i))
+			) rl;
+			if need_tmp then sexpr "%s = tmp; }" (reg r)
+		| OEnumAlloc (r,cid) ->
+			let et, (_,_,tl) = (match rtype r with HEnum e -> enum_constr_type ctx e cid, e.efields.(cid) | _ -> assert false) in
+			let has_ptr = List.exists is_gc_ptr (Array.to_list tl) in
+			sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" (reg r) (if has_ptr then "" else "_noptr") et;
+			sexpr "memset(%s,0,sizeof(%s))" (reg r) et;
+			if cid <> 0 then sexpr "%s->index = %d" (reg r) cid
+		| OEnumIndex (r,v) ->
+			(match rtype v with
+			| HEnum _ ->
+				sexpr "%s = %s->index" (reg r) (reg v)
+			| HDyn ->
+				sexpr "%s = ((venum*)%s->v.ptr)->index" (reg r) (reg v)
+			| _ ->
+				assert false)
+		| OEnumField (r,e,cid,pid) ->
+			let tname,(_,_,tl) = (match rtype e with HEnum e -> enum_constr_type ctx e cid, e.efields.(cid) | _ -> assert false) in
+			sexpr "%s((%s*)%s)->p%d" (rassign r tl.(pid)) tname (reg e) pid
+		| OSetEnumField (e,pid,r) ->
+			let tname, (_,_,tl) = (match rtype e with HEnum e -> enum_constr_type ctx e 0, e.efields.(0) | _ -> assert false) in
+			sexpr "((%s*)%s)->p%d = (%s)%s" tname (reg e) pid (ctype tl.(pid)) (reg r)
+		| OSwitch (r,idx,eend) ->
+			sline "switch(%s) {" (reg r);
+			block();
+			output_at2 (i + 1) [OODefault;OOIncreaseIndent];
+			Array.iteri (fun k delta -> output_at2 (delta + i + 1) [OODecreaseIndent;OOCase k;OOIncreaseIndent]) idx;
+			let pend = i+1+eend in
+			(* insert at end if we have another switch case here *)
+			let old = output_options.(pend) in
+			output_options.(pend) <- [];
+			output_at2 pend ([OODecreaseIndent;OODecreaseIndent;OOEndBlock] @ List.rev old);
+		| ONullCheck r ->
+			sexpr "if( %s == NULL ) hl_null_access()" (reg r)
+		| OTrap (r,d) ->
+			sexpr "hl_trap(trap$%d,%s,%s)" !trap_depth (reg r) (label d);
+			incr trap_depth
+		| OEndTrap b ->
+			sexpr "hl_endtrap(trap$%d)" (!trap_depth - 1);
+			if b then decr trap_depth;
+		| ONop _ ->
+			()
+	) f.code;
+	flush_options (Array.length f.code);
+	unblock();
+	line "}";
+	line ""
+
+let write_c version file (code:code) =
+
+	let all_types, htypes = gather_types code in
+
+	let ctx = {
+		version = version;
+		out = Buffer.create 1024;
+		tabs = "";
+		hlcode = code;
+		hash_cache = Hashtbl.create 0;
+		dir = (match Filename.dirname file with "" -> "." | dir -> String.concat "/" (ExtString.String.nsplit dir "\\"));
+		curfile = "";
+		cfiles = [];
+		ftable = Array.init (Array.length code.functions + Array.length code.natives) (fun _ -> { fe_args = []; fe_ret = HVoid; fe_name = ""; fe_decl = None; });
+		htypes = htypes;
+	} in
+
+	let line = line ctx and expr = expr ctx in
+	let sline fmt = Printf.ksprintf line fmt and sexpr fmt = Printf.ksprintf expr fmt in
+
+	open_file ctx "hl/code.h";
+	line "#ifndef HL_CODE_H";
+	line "#define HL_CODE_H";
+	line "";
+	line "#define HLC_BOOT";
+	line "#include <hlc.h>";
+	line "#include \"typedefs.h\"";
+	line "#include \"types.h\"";
+	line "#include \"functions.h\"";
+	line "#include \"globals.h\"";
+	line "#include \"natives.h\"";
+	line "";
+	line "#endif";
+
+	let used_closures = Hashtbl.create 0 in
+	let bytes_strings = Hashtbl.create 0 in
+	Array.iter (fun f ->
+		Array.iteri (fun i op ->
+			match op with
+			| OStaticClosure (_,fid) | OSetMethod (_,_,fid) ->
+				Hashtbl.replace used_closures fid ()
+			| OBytes (_,sid) ->
+				Hashtbl.replace bytes_strings sid ()
+			| _ ->
 				()
-		) f.code;
-		flush_options (Array.length f.code);
-		unblock();
-		line "}";
-		line "";
+		) f.code
 	) code.functions;
 
+	open_file ctx "hl/typedefs.h";
+	line "// Types definitions";
+	Array.iter (fun t ->
+		match t with
+		| HObj o ->
+			let name = tname o.pname in
+			expr ("typedef struct _" ^ name ^ " *" ^ name);
+		| HAbstract (name,_) ->
+			expr ("typedef struct _" ^ name ^ " "  ^ name);
+		| _ ->
+			()
+	) all_types;
+
 	line "";
-	line "// Entry point";
-	line "void hl_entry_point() {";
-	block();
-	sexpr "static void *functions_ptrs[] = {%s}" (String.concat "," (Array.to_list funnames));
-	let rec loop i =
-		if i = Array.length code.functions + Array.length code.natives then [] else
-		let args, t = tfuns.(i) in
-		(type_value (HFun (args,t))) :: loop (i + 1)
-	in
-	sexpr "static hl_type *functions_types[] = {%s}" (String.concat "," (loop 0));
-	expr "hl_module_context ctx";
-	expr "hl_alloc_init(&ctx.alloc)";
-	expr "ctx.functions_ptrs = functions_ptrs";
-	expr "ctx.functions_types = functions_types";
-	Hashtbl.iter (fun i _ -> sexpr "hl_hash(string$%d)" i) hash_cache;
+	line "// Types implementation";
+
+	Array.iter (fun t ->
+		match t with
+		| HObj o ->
+			let name = tname o.pname in
+			line ("struct _" ^ name ^ " {");
+			block ctx;
+			let rec loop o =
+				(match o.psuper with
+				| None -> expr ("hl_type *$type");
+				| Some c -> loop c);
+				Array.iteri (fun i (n,_,t) ->
+					let rec abs_index p v =
+						match p with
+						| None -> v
+						| Some o -> abs_index o.psuper (Array.length o.pfields + v)
+					in
+					expr (var_type (if n = "" then unamed_field (abs_index o.psuper i) else n) t)
+				) o.pfields;
+			in
+			loop o;
+			unblock ctx;
+			expr "}";
+		| HEnum e ->
+			Array.iteri (fun i (_,_,pl) ->
+				if Array.length pl <> 0 then begin
+					line ("typedef struct {");
+					block ctx;
+					expr "int index";
+					Array.iteri (fun i t ->
+						expr (var_type ("p" ^ string_of_int i) t)
+					) pl;
+					unblock ctx;
+					sexpr "} %s" (enum_constr_type ctx e i);
+				end;
+			) e.efields
+		| _ ->
+			()
+	) all_types;
+
+	open_file ctx "hl/types.h";
+	line "// Types values declaration";
+	Array.iteri (fun i t ->
+		sexpr "extern hl_type type$%d" i;
+		match t with
+		| HObj o ->
+			sline "#define %s__val &type$%d" (tname o.pname) i
+		| _ ->
+			()
+	) all_types;
+	line "";
+	sexpr "void hl_init_types( hl_module_context *ctx )";
+
+	open_file ctx "hl/natives.h";
+	line "// Natives functions";
+	Array.iter (fun (lib,name,t,idx) ->
+		match t with
+		| HFun (args,t) ->
+			let fname =
+				let lib = code.strings.(lib) in
+				let lib = if lib = "std" then "hl" else lib in
+				lib ^ "_" ^ code.strings.(name)
+			in
+			sexpr "HL_API %s %s(%s)" (ctype t) fname (String.concat "," (List.map ctype args));
+			let ft = ctx.ftable.(idx) in
+			ft.fe_name <- fname;
+			ft.fe_args <- args;
+			ft.fe_ret <- t;
+		| _ ->
+			assert false
+	) code.natives;
+
+	open_file ctx "hl/functions.h";
+	line "// Functions declaration";
+	Array.iter (fun f ->
+		match f.ftype with
+		| HFun (args,t) ->
+			let fname = String.concat "_" (ExtString.String.nsplit (fundecl_name f) ".") in
+			sexpr "%s %s(%s)" (ctype t) fname (String.concat "," (List.map ctype args));
+			let ft = ctx.ftable.(f.findex) in
+			ft.fe_name <- fname;
+			ft.fe_args <- args;
+			ft.fe_ret <- t;
+			ft.fe_decl <- Some f;
+		| _ ->
+			assert false
+	) code.functions;
+	line "";
+	sexpr "extern void *hl_functions_ptrs[]";
+	sexpr "extern hl_type *hl_functions_types[]";
+
+
+	open_file ctx "hl/globals.h";
+	line "// Globals";
+	Array.iteri (fun i t ->
+		let name = "global$" ^ string_of_int i in
+		sexpr "extern %s" (var_type name t)
+	) code.globals;
+
+	Array.iteri (fun i str ->
+		if Hashtbl.mem bytes_strings i then
+			sexpr "extern vbyte bytes$%d[]" i
+		else if String.length str >= string_data_limit then
+			sexpr "vbyte string$%d[]" i
+	) code.strings;
+
+	Hashtbl.iter (fun fid _ -> sexpr "extern vclosure cl$%d" fid) used_closures;
+	line "";
+	sexpr "void hl_init_roots()";
+
+	open_file ctx "hl/globals.c";
+	line "#include <hl/code.h>";
+	line "// Globals";
+	Array.iteri (fun i t ->
+		let name = "global$" ^ string_of_int i in
+		sexpr "%s = 0" (var_type name t)
+	) code.globals;
+	line "";
+	line "void hl_init_roots() {";
+	block ctx;
+	Array.iteri (fun i t ->
+		if is_ptr t then sexpr "hl_add_root((void**)&global$%d)" i;
+	) code.globals;
+	unblock ctx;
+	line "}";
+
+	Array.iteri (fun i str ->
+		let rec loop s i =
+			if i = String.length s then [] else
+			let c = String.get s i in
+			string_of_int (int_of_char c) :: loop s (i+1)
+		in
+		if Hashtbl.mem bytes_strings i then
+			sexpr "vbyte bytes$%d[] = {%s}" i (String.concat "," (loop str 0))
+		else if String.length str >= string_data_limit then
+			let s = utf8_to_utf16 str in
+			sline "// %s" (String.escaped str);
+			sexpr "vbyte string$%d[] = {%s}" i (String.concat "," (loop s 0))
+	) code.strings;
+
+	Hashtbl.iter (fun fid _ ->
+		let ft = ctx.ftable.(fid) in
+		sexpr "vclosure cl$%d = { %s, %s, 0 }" fid (type_value ctx (HFun (ft.fe_args,ft.fe_ret))) ft.fe_name
+	) used_closures;
+
+	open_file ctx "hl/types.c";
+	line "#include <hl/code.h>";
+	line "// Types values";
+	Array.iteri (fun i t ->
+		sexpr "hl_type type$%d = { %s } /* %s */" i (type_id t) (tstr t);
+	) all_types;
+
+	line "";
+	line "// Types values data";
 	Array.iteri (fun i t ->
+		let field_value (_,name_id,t) =
+			sprintf "{(const uchar*)%s, %s, %ld}" (string ctx name_id) (type_value ctx t) (hash ctx name_id)
+		in
 		match t with
 		| HObj o ->
-			sexpr "obj$%d.m = &ctx" i;
+			let proto_value p =
+				sprintf "{(const uchar*)%s, %d, %d, %ld}" (string ctx p.fid) p.fmethod (match p.fvirtual with None -> -1 | Some i -> i) (hash ctx p.fid)
+			in
+			let fields =
+				if Array.length o.pfields = 0 then "NULL" else
+				let name = sprintf "fields$%d" i in
+				sexpr "static hl_obj_field %s[] = {%s}" name (String.concat "," (List.map field_value (Array.to_list o.pfields)));
+				name
+			in
+			let proto =
+				if Array.length o.pproto = 0 then "NULL" else
+				let name = sprintf "proto$%d" i in
+				sexpr "static hl_obj_proto %s[] = {%s}" name (String.concat "," (List.map proto_value (Array.to_list o.pproto)));
+				name
+			in
+			let ofields = [
+				string_of_int (Array.length o.pfields);
+				string_of_int (Array.length o.pproto);
+				sprintf "(const uchar*)%s" (string ctx o.pid);
+				(match o.psuper with None -> "NULL" | Some c -> sprintf "%s__val" (tname c.pname));
+				fields;
+				proto
+			] in
+			sexpr "static hl_type_obj obj$%d = {%s}" i (String.concat "," ofields);
+		| HEnum e ->
+			let constr_name = sprintf "econstructs$%d" i in
+			let constr_value cid (name,nid,tl) =
+				let tval = if Array.length tl = 0 then "NULL" else
+					let name = sprintf "econstruct$%d_%d" i cid in
+					sexpr "static hl_type *%s[] = {%s}" name (String.concat "," (List.map (type_value ctx) (Array.to_list tl)));
+					name
+				in
+				let size = if Array.length tl = 0 then "0" else sprintf "sizeof(%s)" (enum_constr_type ctx e cid) in
+				let offsets = if Array.length tl = 0 then "NULL" else
+					let name = sprintf "eoffsets$%d_%d" i cid in
+					sexpr "static int %s[] = {%s}" name (String.concat "," (List.map (fun _ -> "0") (Array.to_list tl)));
+					name
+				in
+				let has_ptr = List.exists is_gc_ptr (Array.to_list tl) in
+				sprintf "{(const uchar*)%s, %d, %s, %s, %s, %s}" (string ctx nid) (Array.length tl) tval size (if has_ptr then "true" else "false") offsets
+			in
+			sexpr "static hl_enum_construct %s[] = {%s}" constr_name (String.concat "," (Array.to_list (Array.mapi constr_value e.efields)));
+			let efields = [
+				if e.eid = 0 then "NULL" else sprintf "(const uchar*)%s" (string ctx e.eid);
+				string_of_int (Array.length e.efields);
+				constr_name
+			] in
+			sexpr "static hl_type_enum enum$%d = {%s}" i (String.concat "," efields);
+		| HVirtual v ->
+			let fields_name =
+				if Array.length v.vfields = 0 then "NULL" else
+				let name = sprintf "vfields$%d" i in
+				sexpr "static hl_obj_field %s[] = {%s}" name (String.concat "," (List.map field_value (Array.to_list v.vfields)));
+				name
+			in
+			let vfields = [
+				fields_name;
+				string_of_int (Array.length v.vfields)
+			] in
+			sexpr "static hl_type_virtual virt$%d = {%s}" i (String.concat "," vfields);
+		| HFun (args,t) ->
+			let aname = if args = [] then "NULL" else
+				let name = sprintf "fargs$%d" i in
+				sexpr "static hl_type *%s[] = {%s}" name (String.concat "," (List.map (type_value ctx) args));
+				name
+			in
+			sexpr "static hl_type_fun tfun$%d = {%s,%s,%d}" i aname (type_value ctx t) (List.length args)
+		| _ ->
+			()
+	) all_types;
+
+	line "";
+	line "void hl_init_types( hl_module_context *ctx ) {";
+	block ctx;
+	Array.iteri (fun i t ->
+		match t with
+		| HObj o ->
+			sexpr "obj$%d.m = ctx" i;
 			(match o.pclassglobal with None -> () | Some g -> sexpr "obj$%d.global_value = (void**)&global$%d" i g);
 			sexpr "type$%d.obj = &obj$%d" i i
 		| HNull t | HRef t ->
-			sexpr "type$%d.tparam = %s" i (type_value t)
+			sexpr "type$%d.tparam = %s" i (type_value ctx t)
 		| HEnum e ->
 			sexpr "type$%d.tenum = &enum$%d" i i;
 			(match e.eglobal with None -> () | Some g -> sexpr "enum$%d.global_value = (void**)&global$%d" i g);
 			Array.iteri (fun cid (_,_,tl) ->
 				if Array.length tl > 0 then begin
 					line "{";
-					block();
-					sexpr "%s *_e = NULL" (enum_constr_type e cid);
+					block ctx;
+					sexpr "%s *_e = NULL" (enum_constr_type ctx e cid);
 					Array.iteri (fun pid _ -> sexpr "eoffsets$%d_%d[%d] = (int)(int_val)&_e->p%d" i cid pid pid) tl;
-					unblock();
+					unblock ctx;
 					line "}";
 				end
 			) e.efields
 		| HVirtual _ ->
 			sexpr "type$%d.virt = &virt$%d" i i;
-			sexpr "hl_init_virtual(&type$%d,&ctx)" i;
+			sexpr "hl_init_virtual(&type$%d,ctx)" i;
 		| HFun _ ->
 			sexpr "type$%d.fun = &tfun$%d" i i
 		| _ ->
 			()
 	) all_types;
-	Array.iteri (fun i t ->
-		if is_ptr t then sexpr "hl_add_root((void**)&global$%d)" i;
-	) code.globals;
-	sexpr "%s()" funnames.(code.entrypoint);
-	unblock();
+	unblock ctx;
+	line "}";
+
+	open_file ctx "hl/reflect.c";
+	line "#include <hl/code.h>";
+	line "// Reflection helpers";
+	generate_reflection ctx;
+
+	let gen_functions = Hashtbl.create 0 in
+	Array.iter (fun t ->
+		match t with
+		| HObj o ->
+			let file = ref false in
+			Array.iter (fun p ->
+				match ctx.ftable.(p.fmethod).fe_decl with
+				| None -> ()
+				| Some f ->
+					if not !file then begin
+						file := true;
+						let path = ExtString.String.nsplit o.pname "." in
+						let path = List.map (fun n -> if String.length n > 128 then Digest.to_hex (Digest.string n) else n) path in
+						let path = (match path with [name] -> ["_std";name] | _ -> path) in
+						open_file ctx (String.concat "/" path ^ ".c");
+						line "#include <hl/code.h>";
+						line "";
+					end;
+					Hashtbl.replace gen_functions f.findex ();
+					generate_function ctx f
+			) o.pproto;
+		| _ -> ()
+	) all_types;
+
+	open_file ctx "hl/functions.c";
+	line "#include <hl/code.h>";
+	line "";
+	sexpr "void *hl_functions_ptrs[] = {%s}" (String.concat "," (List.map (fun f -> f.fe_name) (Array.to_list ctx.ftable)));
+	let rec loop i =
+		if i = Array.length ctx.ftable then [] else
+		let ft = ctx.ftable.(i) in
+		(type_value ctx (HFun (ft.fe_args,ft.fe_ret))) :: loop (i + 1)
+	in
+	sexpr "hl_type *hl_functions_types[] = {%s}" (String.concat "," (loop 0));
+	line "";
+	Array.iter (fun f ->
+		if not (Hashtbl.mem gen_functions f.findex) then generate_function ctx f;
+	) code.functions;
+
+	open_file ctx "hl/hashes.c";
+	line "#include <hl/code.h>";
+	line "";
+	line "void hl_init_hashes() {";
+	block ctx;
+	Hashtbl.iter (fun i _ -> sexpr "hl_hash((vbyte*)%s)" (string ctx i)) ctx.hash_cache;
+	unblock ctx;
+	line "}";
+
+	open_file ctx (Filename.basename file);
+	line "#include <hl/code.h>";
+	line "#include <hlc_main.c>";
+	line "";
+	line "#ifdef INCLUDE_ALL";
+	List.iter (sline "#  include <%s>") ctx.cfiles;
+	line "#endif";
+	line "";
+	expr "void hl_init_hashes()";
+	line "";
+	line "// Entry point";
+	line "void hl_entry_point() {";
+	block ctx;
+	expr "hl_module_context ctx";
+	expr "hl_alloc_init(&ctx.alloc)";
+	expr "ctx.functions_ptrs = hl_functions_ptrs";
+	expr "ctx.functions_types = hl_functions_types";
+	expr "hl_init_types(&ctx)";
+	expr "hl_init_hashes()";
+	expr "hl_init_roots()";
+	sexpr "%s()" ctx.ftable.(code.entrypoint).fe_name;
+	unblock ctx;
 	line "}";
 	line "";
-	List.iter (fun f -> f()) !end_ch
+
+	open_file ctx "Makefile";
+
+	line ("FILES = " ^ (String.concat " " (ctx.cfiles)));
+
+	close_file ctx
+