Browse Source

try at optimizing hl.opt time by doing a cache (no perf gain)

Nicolas Cannasse 6 years ago
parent
commit
2126d53ae1
2 changed files with 286 additions and 190 deletions
  1. 3 3
      src/generators/genhl.ml
  2. 283 187
      src/generators/hlopt.ml

+ 3 - 3
src/generators/genhl.ml

@@ -3231,7 +3231,7 @@ and make_fun ?gen_content ctx name fidx f cthis cparent =
 		op ctx (ORet r)
 	end;
 	let fargs = (match tthis with None -> [] | Some t -> [t]) @ (match rcapt with None -> [] | Some r -> [rtype ctx r]) @ args in
-	let f = {
+	let hlf = {
 		fpath = name;
 		findex = fidx;
 		ftype = HFun (fargs, tret);
@@ -3244,11 +3244,11 @@ and make_fun ?gen_content ctx name fidx f cthis cparent =
 	Hashtbl.add ctx.defined_funs fidx ();
 	let f = if ctx.optimize then begin
 		let t = Timer.timer ["generate";"hl";"opt"] in
-		let f = Hlopt.optimize ctx.dump_out (DynArray.get ctx.cstrings.arr) f in
+		let f = Hlopt.optimize ctx.dump_out (DynArray.get ctx.cstrings.arr) hlf f in
 		t();
 		f
 	end else
-		f
+		hlf
 	in
 	DynArray.add ctx.cfunctions f;
 	capt

+ 283 - 187
src/generators/hlopt.ml

@@ -492,14 +492,224 @@ let code_graph (f:fundecl) =
 	in
 	blocks_pos, make_block 0
 
-let optimize dump get_str (f:fundecl) =
+type rctx = {
+	r_root : block;
+	r_used_regs : int;
+	r_nop_count : int;
+	r_blocks_pos : (int, block) Hashtbl.t;
+	r_reg_moved : (int, (int * int)) Hashtbl.t;
+	r_live_bits : int array;
+	r_reg_map : int array;
+}
+
+let remap_fun ctx f dump get_str =
+	let aget = Array.unsafe_get in
+	let aset = Array.unsafe_set in
+	let op index = aget f.code index in
 	let nregs = Array.length f.regs in
+	let reg_remap = ctx.r_used_regs <> nregs in
+	let assigns = ref f.assigns in
 	let old_code = match dump with None -> f.code | Some _ -> Array.copy f.code in
+	let write str = match dump with None -> () | Some ch -> IO.nwrite ch (Bytes.unsafe_of_string (str ^ "\n")) in
+	let nargs = (match f.ftype with HFun (args,_) -> List.length args | _ -> assert false) in
+
+	let live_bits = ctx.r_live_bits in
+	let reg_map = ctx.r_reg_map in
+
+	let bit_regs = 30 in
+	let stride = (nregs + bit_regs - 1) / bit_regs in
+	let is_live r i =
+		let offset = r / bit_regs in
+		let mask = 1 lsl (r - offset * bit_regs) in
+		Array.unsafe_get live_bits (i * stride + offset) land mask <> 0
+	in
+
+	(* remap assigns *)
+	if ctx.r_nop_count > 0 then begin
+		let rec resolve_block p =
+			try Hashtbl.find ctx.r_blocks_pos p with Not_found -> resolve_block (p - 1)
+		in
+
+		let new_assigns = List.fold_left (fun acc (i,p) ->
+			let gmap = Hashtbl.create 0 in
+			(*
+				For a given assign at position p, that's been optimized out,
+				let's try to find where the last assign that maps to the same value
+				is, and remap the variable name to it
+			*)
+			let rec loop p =
+				if p < 0 || (match op p with ONop _ -> false | _ -> true) then [(i,p)] else
+				let reg, last_w = try Hashtbl.find ctx.r_reg_moved p with Not_found -> (-1,-1) in
+				if reg < 0 then [] (* ? *) else
+				if reg < nargs then [(i,-reg-1)] else
+				let b = resolve_block p in
+				if last_w >= b.bstart && last_w < b.bend && last_w < p then loop last_w else
+				let wp = try PMap.find reg b.bwrite with Not_found -> -1 in
+				let rec gather b =
+					if Hashtbl.mem gmap b.bstart then [] else begin
+						Hashtbl.add gmap b.bstart ();
+						(* lookup in all parent blocks, recursively, to fetch all last writes *)
+						List.fold_left (fun acc bp ->
+							if bp.bstart > b.bstart then acc else
+							try
+								let wp = PMap.find reg bp.bwrite in
+								if wp > p then assert false;
+								loop wp @ acc
+							with Not_found ->
+								gather bp @ acc
+						) [] b.bprev;
+					end
+				in
+				if wp < 0 then
+					gather b
+				else if wp < p then
+					loop wp
+				else
+					(* lookup in writes between p-1 and block bstart *)
+					let rec find_w p =
+						if p < b.bstart then
+							gather b
+						else
+							let found = ref false in
+							opcode_fx (fun r read -> if r = reg && not read then found := true) (aget old_code p);
+							if !found then loop p else find_w (p - 1)
+					in
+					find_w (p - 1)
+			in
+			loop p @ acc
+		) [] (Array.to_list !assigns) in
+		let new_assigns = List.sort (fun (_,p1) (_,p2) -> p1 - p2) (List.rev new_assigns) in
+		assigns := Array.of_list new_assigns;
+	end;
+
+	(* done *)
+	if dump <> None then begin
+		let old_assigns = Hashtbl.create 0 in
+		let new_assigns = Hashtbl.create 0 in
+		Array.iter (fun (var,pos) -> if pos >= 0 then Hashtbl.replace old_assigns pos var) f.assigns;
+		Array.iter (fun (var,pos) ->
+			if pos >= 0 then begin
+				let f = try Hashtbl.find new_assigns pos with Not_found -> let v = ref [] in Hashtbl.add new_assigns pos v; v in
+				f := var :: !f;
+			end
+		) !assigns;
+		let rec loop i block =
+			if i = Array.length f.code then () else
+			let block = try
+				let b = Hashtbl.find ctx.r_blocks_pos i in
+				write (Printf.sprintf "\t----- [%s] (%X)"
+					(String.concat "," (List.map (fun b -> Printf.sprintf "%X" b.bstart) b.bnext))
+					b.bend
+				);
+				let need = String.concat "," (List.map string_of_int (ISet.elements b.bneed)) in
+				let wr = String.concat " " (List.rev (PMap.foldi (fun r p acc -> Printf.sprintf "%d@%X" r p :: acc) b.bwrite [])) in
+				write ("\t" ^ (if b.bloop then "LOOP " else "") ^ "NEED=" ^ need ^ "\tWRITE=" ^ wr);
+				b
+			with Not_found ->
+				block
+			in
+			let old = aget old_code i in
+			let op = op i in
+			let rec live_loop r l =
+				if r = nregs then List.rev l else
+				live_loop (r + 1) (if is_live r i then r :: l else l)
+			in
+			let live = "LIVE=" ^ String.concat "," (List.map string_of_int (live_loop 0 [])) in
+			let var_set = (try let v = Hashtbl.find old_assigns i in "set " ^ get_str v with Not_found -> "") in
+			let nvar_set = (try let v = Hashtbl.find new_assigns i in "set " ^ String.concat "," (List.map get_str !v) with Not_found -> "") in
+			write (Printf.sprintf "\t@%-3X %-20s %-20s %-20s %-20s %s" i (ostr string_of_int old) (if opcode_eq old op then "" else ostr string_of_int op) var_set nvar_set live);
+			loop (i + 1) block
+		in
+		write (Printf.sprintf "%s@%d" (fundecl_name f) f.findex);
+		let rec loop_arg = function
+			| [] -> []
+			| (_,p) :: _ when p >= 0 -> []
+			| (str,p) :: l -> (get_str str ^ ":" ^ string_of_int p) :: loop_arg l
+		in
+		write (Printf.sprintf "ARGS = %s\n" (String.concat ", " (loop_arg (Array.to_list f.assigns))));
+		if reg_remap then begin
+			for i=0 to nregs-1 do
+				write (Printf.sprintf "\tr%-2d %-10s%s" i (tstr f.regs.(i)) (if ctx.r_reg_map.(i) < 0 then " unused" else if ctx.r_reg_map.(i) = i then "" else Printf.sprintf " r%-2d" ctx.r_reg_map.(i)))
+			done;
+		end;
+		loop 0 ctx.r_root;
+		write "";
+		write "";
+		(match dump with None -> () | Some ch -> IO.flush ch);
+	end;
+
+	let code = ref f.code in
+	let regs = ref f.regs in
+	let debug = ref f.debug in
+
+	if ctx.r_nop_count > 0 || reg_remap then begin
+		let new_pos = Array.make (Array.length f.code) 0 in
+		let jumps = ref [] in
+		let out_pos = ref 0 in
+		let out_code = Array.make (Array.length f.code - ctx.r_nop_count) (ONop "") in
+		let new_debug = Array.make (Array.length f.code - ctx.r_nop_count) (0,0) in
+		Array.iteri (fun i op ->
+			Array.unsafe_set new_pos i !out_pos;
+			match op with
+			| ONop _ -> ()
+			| _ ->
+				(match op with
+				| OJTrue _ | OJFalse _ | OJNull _ | OJNotNull _  | OJSLt _ | OJSGte _ | OJSGt _ | OJSLte _ | OJNotLt _ | OJNotGte _ | OJULt _ | OJUGte _ | OJEq _ | OJNotEq _ | OJAlways _ | OSwitch _  | OTrap _ ->
+					jumps := i :: !jumps
+				| _ -> ());
+				let op = if reg_remap then opcode_map (fun r -> aget reg_map r) (fun r -> aget reg_map r) op else op in
+				aset out_code (!out_pos) op;
+				aset new_debug (!out_pos) (aget f.debug i);
+				incr out_pos
+		) f.code;
+		List.iter (fun j ->
+			let pos d =
+				aget new_pos (j + 1 + d) - aget new_pos (j + 1)
+			in
+			let p = new_pos.(j) in
+			aset out_code p (match aget out_code p with
+			| OJTrue (r,d) -> OJTrue (r,pos d)
+			| OJFalse (r,d) -> OJFalse (r,pos d)
+			| OJNull (r,d) -> OJNull (r, pos d)
+			| OJNotNull (r,d) -> OJNotNull (r, pos d)
+			| OJSLt (a,b,d) -> OJSLt (a,b,pos d)
+			| OJSGte (a,b,d) -> OJSGte (a,b,pos d)
+			| OJSLte (a,b,d) -> OJSLte (a,b,pos d)
+			| OJSGt (a,b,d) -> OJSGt (a,b,pos d)
+			| OJULt (a,b,d) -> OJULt (a,b,pos d)
+			| OJUGte (a,b,d) -> OJUGte (a,b,pos d)
+			| OJNotLt (a,b,d) -> OJNotLt (a,b,pos d)
+			| OJNotGte (a,b,d) -> OJNotGte (a,b,pos d)
+			| OJEq (a,b,d) -> OJEq (a,b,pos d)
+			| OJNotEq (a,b,d) -> OJNotEq (a,b,pos d)
+			| OJAlways d -> OJAlways (pos d)
+			| OSwitch (r,cases,send) -> OSwitch (r, Array.map pos cases, pos send)
+			| OTrap (r,d) -> OTrap (r,pos d)
+			| _ -> assert false)
+		) !jumps;
+
+		let assigns = !assigns in
+		Array.iteri (fun idx (i,p) -> if p >= 0 then aset assigns idx (i, aget new_pos p)) assigns;
+
+		code := out_code;
+		debug := new_debug;
+		if reg_remap then begin
+			let new_regs = Array.make ctx.r_used_regs HVoid in
+			for i=0 to nregs-1 do
+				let p = aget reg_map i in
+				if p >= 0 then aset new_regs p  (aget f.regs i)
+			done;
+			regs := new_regs;
+		end;
+	end;
+	{ f with code = !code; regs = !regs; debug = !debug; assigns = !assigns }
+
+let _optimize (f:fundecl) =
+	let nregs = Array.length f.regs in
 	let op index = f.code.(index) in
 	let set_op index op = f.code.(index) <- op in
 	let nop_count = ref 0 in
 	let set_nop index r = f.code.(index) <- (ONop r); incr nop_count in
-	let write str = match dump with None -> () | Some ch -> IO.nwrite ch (Bytes.unsafe_of_string (str ^ "\n")) in
 
 	let blocks_pos, root = code_graph f in
 
@@ -539,7 +749,7 @@ let optimize dump get_str (f:fundecl) =
 		r.ralias <- r;
 		r
 	) in
-
+(*
 	let print_state i s =
 		let state_str s =
 			if s.ralias == s && s.rbind == [] then "" else
@@ -547,8 +757,7 @@ let optimize dump get_str (f:fundecl) =
 		in
 		write (Printf.sprintf "@%X %s" i (String.concat " " (Array.to_list (Array.map state_str s))))
 	in
-
-	let dstate = false in
+*)
 
 	let rec propagate b =
 		let state = if b.bloop then
@@ -614,7 +823,7 @@ let optimize dump get_str (f:fundecl) =
 			in
 			if i > b.bend then () else
 			let op = op i in
-			if dstate then print_state i state;
+			(* print_state i state; (* debug *) *)
 			(match op with
 			| OIncr r | ODecr r | ORef (_,r) -> unalias state.(r)
 			| OCallClosure (_,r,_) when f.regs.(r) = HDyn && (match f.regs.(state.(r).ralias.rindex) with HFun (_,rt) -> not (is_dynamic rt) | HDyn -> false | _ -> true) -> unalias state.(r) (* Issue3218.hx *)
@@ -766,187 +975,74 @@ let optimize dump get_str (f:fundecl) =
 		end else
 			reg_map.(i) <- -1;
 	done;
-	let reg_remap = !used_regs <> nregs in
-	let assigns = ref f.assigns in
-
-	(* remap assigns *)
-	if !nop_count > 0 then begin
-		let rec resolve_block p =
-			try Hashtbl.find blocks_pos p with Not_found -> resolve_block (p - 1)
-		in
-
-		let new_assigns = List.fold_left (fun acc (i,p) ->
-			let gmap = Hashtbl.create 0 in
-			(*
-				For a given assign at position p, that's been optimized out,
-				let's try to find where the last assign that maps to the same value
-				is, and remap the variable name to it
-			*)
-			let rec loop p =
-				if p < 0 || (match f.code.(p) with ONop _ -> false | _ -> true) then [(i,p)] else
-				let reg, last_w = try Hashtbl.find reg_moved p with Not_found -> (-1,-1) in
-				if reg < 0 then [] (* ? *) else
-				if reg < nargs then [(i,-reg-1)] else
-				let b = resolve_block p in
-				if last_w >= b.bstart && last_w < b.bend && last_w < p then loop last_w else
-				let wp = try PMap.find reg b.bwrite with Not_found -> -1 in
-				let rec gather b =
-					if Hashtbl.mem gmap b.bstart then [] else begin
-						Hashtbl.add gmap b.bstart ();
-						(* lookup in all parent blocks, recursively, to fetch all last writes *)
-						List.fold_left (fun acc bp ->
-							if bp.bstart > b.bstart then acc else
-							try
-								let wp = PMap.find reg bp.bwrite in
-								if wp > p then assert false;
-								loop wp @ acc
-							with Not_found ->
-								gather bp @ acc
-						) [] b.bprev;
-					end
-				in
-				if wp < 0 then
-					gather b
-				else if wp < p then
-					loop wp
-				else
-					(* lookup in writes between p-1 and block bstart *)
-					let rec find_w p =
-						if p < b.bstart then
-							gather b
-						else
-							let found = ref false in
-							opcode_fx (fun r read -> if r = reg && not read then found := true) old_code.(p);
-							if !found then loop p else find_w (p - 1)
-					in
-					find_w (p - 1)
-			in
-			loop p @ acc
-		) [] (Array.to_list !assigns) in
-		let new_assigns = List.sort (fun (_,p1) (_,p2) -> p1 - p2) (List.rev new_assigns) in
-		assigns := Array.of_list new_assigns;
-	end;
+	{
+		r_root = root;
+		r_blocks_pos = blocks_pos;
+		r_nop_count = !nop_count;
+		r_used_regs = !used_regs;
+		r_live_bits = live_bits;
+		r_reg_map = reg_map;
+		r_reg_moved = reg_moved;
+	}
 
-	(* done *)
-	if dump <> None then begin
-		let old_assigns = Hashtbl.create 0 in
-		let new_assigns = Hashtbl.create 0 in
-		Array.iter (fun (var,pos) -> if pos >= 0 then Hashtbl.replace old_assigns pos var) f.assigns;
-		Array.iter (fun (var,pos) ->
-			if pos >= 0 then begin
-				let f = try Hashtbl.find new_assigns pos with Not_found -> let v = ref [] in Hashtbl.add new_assigns pos v; v in
-				f := var :: !f;
-			end
-		) !assigns;
-		let rec loop i block =
-			if i = Array.length f.code then () else
-			let block = try
-				let b = Hashtbl.find blocks_pos i in
-				write (Printf.sprintf "\t----- [%s] (%X)"
-					(String.concat "," (List.map (fun b -> Printf.sprintf "%X" b.bstart) b.bnext))
-					b.bend
-				);
-				let need = String.concat "," (List.map string_of_int (ISet.elements b.bneed)) in
-				let wr = String.concat " " (List.rev (PMap.foldi (fun r p acc -> Printf.sprintf "%d@%X" r p :: acc) b.bwrite [])) in
-				write ("\t" ^ (if b.bloop then "LOOP " else "") ^ "NEED=" ^ need ^ "\tWRITE=" ^ wr);
-				b
-			with Not_found ->
-				block
-			in
-			let old = old_code.(i) in
-			let op = op i in
-			let rec live_loop r l =
-				if r = nregs then List.rev l else
-				live_loop (r + 1) (if is_live r i then r :: l else l)
-			in
-			let live = "LIVE=" ^ String.concat "," (List.map string_of_int (live_loop 0 [])) in
-			let var_set = (try let v = Hashtbl.find old_assigns i in "set " ^ get_str v with Not_found -> "") in
-			let nvar_set = (try let v = Hashtbl.find new_assigns i in "set " ^ String.concat "," (List.map get_str !v) with Not_found -> "") in
-			write (Printf.sprintf "\t@%-3X %-20s %-20s %-20s %-20s %s" i (ostr string_of_int old) (if opcode_eq old op then "" else ostr string_of_int op) var_set nvar_set live);
-			loop (i + 1) block
-		in
-		write (Printf.sprintf "%s@%d" (fundecl_name f) f.findex);
-		let rec loop_arg = function
-			| [] -> []
-			| (_,p) :: _ when p >= 0 -> []
-			| (str,p) :: l -> (get_str str ^ ":" ^ string_of_int p) :: loop_arg l
-		in
-		write (Printf.sprintf "ARGS = %s\n" (String.concat ", " (loop_arg (Array.to_list f.assigns))));
-		if reg_remap then begin
-			for i=0 to nregs-1 do
-				write (Printf.sprintf "\tr%-2d %-10s%s" i (tstr f.regs.(i)) (if reg_map.(i) < 0 then " unused" else if reg_map.(i) = i then "" else Printf.sprintf " r%-2d" reg_map.(i)))
-			done;
-		end;
-		loop 0 root;
-		write "";
-		write "";
-		(match dump with None -> () | Some ch -> IO.flush ch);
-	end;
-
-	(* remap *)
-
-	let code = ref f.code in
-	let regs = ref f.regs in
-	let debug = ref f.debug in
-
-	if !nop_count > 0 || reg_remap then begin
-		let new_pos = Array.make (Array.length f.code) 0 in
-		let jumps = ref [] in
-		let out_pos = ref 0 in
-		let out_code = Array.make (Array.length f.code - !nop_count) (ONop "") in
-		let new_debug = Array.make (Array.length f.code - !nop_count) (0,0) in
-		Array.iteri (fun i op ->
-			Array.unsafe_set new_pos i !out_pos;
-			match op with
-			| ONop _ -> ()
-			| _ ->
-				(match op with
-				| OJTrue _ | OJFalse _ | OJNull _ | OJNotNull _  | OJSLt _ | OJSGte _ | OJSGt _ | OJSLte _ | OJNotLt _ | OJNotGte _ | OJULt _ | OJUGte _ | OJEq _ | OJNotEq _ | OJAlways _ | OSwitch _  | OTrap _ ->
-					jumps := i :: !jumps
-				| _ -> ());
-				let op = if reg_remap then opcode_map (fun r -> reg_map.(r)) (fun r -> reg_map.(r)) op else op in
-				out_code.(!out_pos) <- op;
-				new_debug.(!out_pos) <- f.debug.(i);
-				incr out_pos
-		) f.code;
-		List.iter (fun j ->
-			let pos d =
-				new_pos.(j + 1 + d) - new_pos.(j + 1)
-			in
-			let p = new_pos.(j) in
-			out_code.(p) <- (match out_code.(p) with
-			| OJTrue (r,d) -> OJTrue (r,pos d)
-			| OJFalse (r,d) -> OJFalse (r,pos d)
-			| OJNull (r,d) -> OJNull (r, pos d)
-			| OJNotNull (r,d) -> OJNotNull (r, pos d)
-			| OJSLt (a,b,d) -> OJSLt (a,b,pos d)
-			| OJSGte (a,b,d) -> OJSGte (a,b,pos d)
-			| OJSLte (a,b,d) -> OJSLte (a,b,pos d)
-			| OJSGt (a,b,d) -> OJSGt (a,b,pos d)
-			| OJULt (a,b,d) -> OJULt (a,b,pos d)
-			| OJUGte (a,b,d) -> OJUGte (a,b,pos d)
-			| OJNotLt (a,b,d) -> OJNotLt (a,b,pos d)
-			| OJNotGte (a,b,d) -> OJNotGte (a,b,pos d)
-			| OJEq (a,b,d) -> OJEq (a,b,pos d)
-			| OJNotEq (a,b,d) -> OJNotEq (a,b,pos d)
-			| OJAlways d -> OJAlways (pos d)
-			| OSwitch (r,cases,send) -> OSwitch (r, Array.map pos cases, pos send)
-			| OTrap (r,d) -> OTrap (r,pos d)
-			| _ -> assert false)
-		) !jumps;
-
-		Array.iteri (fun idx (i,p) -> if p >= 0 then (!assigns).(idx) <- (i, new_pos.(p))) !assigns;
+type cache_elt = {
+	c_hfx : Type.tfunc;
+	c_code : opcode array;
+	c_rctx : rctx;
+	mutable c_last_used : int;
+}
 
-		code := out_code;
-		debug := new_debug;
-		if reg_remap then begin
-			let new_regs = Array.make !used_regs HVoid in
-			for i=0 to nregs-1 do
-				let p = reg_map.(i) in
-				if p >= 0 then new_regs.(p) <- f.regs.(i)
-			done;
-			regs := new_regs;
-		end;
-	end;
+let opt_cache = Hashtbl.create 0
+let used_mark = ref 0
 
-	{ f with code = !code; regs = !regs; debug = !debug; assigns = !assigns }
+let optimize dump get_str (f:fundecl) hxf =
+	try
+		let c = Hashtbl.find opt_cache f.fpath in
+		if c.c_hfx != hxf then raise Not_found;
+		c.c_last_used <- !used_mark;
+		if Array.length f.code <> Array.length c.c_code then assert false;
+		let f = { f with code = Array.mapi (fun i op ->
+			match op, Array.unsafe_get f.code i with
+			(* only regs : ok ! *)
+			| OInt (r,_), OInt (_,idx) -> OInt (r,idx)
+			| OFloat (r,_), OFloat (_,idx) -> OFloat (r,idx)
+			| OBytes (r,_), OBytes (_,idx) -> OBytes (r,idx)
+			| OString (r,_), OString (_,idx) -> OString (r,idx)
+			| OCall0 (r,_), OCall0 (_,idx) -> OCall0 (r,idx)
+			| OCall1 (r,_,a), OCall1 (_,idx,_) -> OCall1 (r,idx,a)
+			| OCall2 (r,_,a,b), OCall2 (_,idx,_,_) -> OCall2 (r,idx,a,b)
+			| OCall3 (r,_,a,b,c), OCall3 (_,idx,_,_,_) -> OCall3 (r,idx,a,b,c)
+			| OCall4 (r,_,a,b,c,d), OCall4 (_,idx,_,_,_,_) -> OCall4 (r,idx,a,b,c,d)
+			| OCallN (r,_,pl), OCallN (_,idx,_) -> OCallN (r,idx,pl)
+			| OStaticClosure (r,_), OStaticClosure (_,idx) -> OStaticClosure (r,idx)
+			| OInstanceClosure (r,_,v), OInstanceClosure (_,idx,_) -> OInstanceClosure (r,idx,v)
+			| OGetGlobal (r,_), OGetGlobal (_,g) -> OGetGlobal (r,g)
+			| OSetGlobal (_,v), OSetGlobal (g,_) -> OSetGlobal (g,v)
+			| ODynGet (r,o,_), ODynGet (_,_,idx) -> ODynGet (r,o,idx)
+			| ODynSet (o,_,v), ODynSet (_,idx,_) -> ODynSet (o,idx,v)
+			| OType (r,_), OType (_,t) -> OType (r,t)
+			| (ONop _ | OMov _ | OBool _ | ONull _
+			  | OAdd _ | OSub _ | OMul _ | OSDiv _ | OUDiv _ | OSMod _ | OUMod _ | OShl _ | OSShr _ | OUShr _ | OAnd _ | OOr _ | OXor _
+			  | ONeg _ | ONot _ | OIncr _ | ODecr _ | OCallMethod _ | OCallThis _ | OCallClosure _ | OVirtualClosure _
+			  | OField _ | OSetField _ | OGetThis _ | OSetThis _
+			  | OJTrue _ | OJFalse _ | OJNull _ | OJNotNull _ | OJSLt _ | OJSGte _ | OJSGt _ | OJSLte _ | OJULt _ | OJUGte _ | OJNotLt _ | OJNotGte _ | OJEq _ | OJNotEq _ | OJAlways _
+			  | OToDyn _ | OToSFloat _ | OToUFloat _ | OToInt _ | OSafeCast _ | OUnsafeCast _ | OToVirtual _ | OLabel _ | ORet _ | OThrow _ | ORethrow _ | OSwitch _ | ONullCheck _
+			  | OTrap _ | OEndTrap _ | OGetUI8 _ | OGetUI16 _ | OGetMem _ | OGetArray _ | OSetUI8 _ | OSetUI16 _ | OSetMem _ | OSetArray _ | ONew _ | OArraySize _ | OGetType _
+			  | OGetTID _ | ORef _ | OUnref _ | OSetref _ | OMakeEnum _ | OEnumAlloc _ | OEnumIndex _ | OEnumField _ | OSetEnumField _ | OAssert _ | ORefData _ | ORefOffset _)
+			  , _ -> op
+			| a, b ->
+				prerr_endline (ostr get_str a);
+				prerr_endline (ostr get_str b);
+				assert false
+		) c.c_code } in
+		remap_fun c.c_rctx f dump get_str
+	with Not_found ->
+		let rctx = _optimize f in
+		let fopt = remap_fun rctx f dump get_str in
+		if f.fpath <> ("","") then Hashtbl.replace opt_cache f.fpath {
+			c_hfx = hxf;
+			c_code = f.code;
+			c_rctx = rctx;
+			c_last_used = !used_mark;
+		};
+		fopt