2
0
Эх сурвалжийг харах

Reduce shared data in analyzer (#12082)

* remove shared lattice

* lose analyzer data dependency on com
Simon Krajewski 5 сар өмнө
parent
commit
c3e42c7e0d

+ 6 - 3
src/codegen/dump.ml

@@ -2,6 +2,9 @@ open Globals
 open Common
 open Type
 
+let dump_path defines =
+	Define.defined_value_safe ~default:"dump" defines Define.DumpPath
+
 (*
 	Make a dump of the full typed AST of all types
 *)
@@ -13,7 +16,7 @@ let create_dumpfile acc l =
 		close_out ch)
 
 let create_dumpfile_from_path com path =
-	let buf,close = create_dumpfile [] ((dump_path com) :: (platform_name_macro com) :: fst path @ [snd path]) in
+	let buf,close = create_dumpfile [] ((dump_path com.defines) :: (platform_name_macro com) :: fst path @ [snd path]) in
 	buf,close
 
 let dump_types com pretty =
@@ -176,7 +179,7 @@ let dump_dependencies ?(target_override=None) com =
 		| None -> platform_name_macro com
 		| Some s -> s
 	in
-	let dump_dependencies_path = [dump_path com;target_name;"dependencies"] in
+	let dump_dependencies_path = [dump_path com.defines;target_name;"dependencies"] in
 	let buf,close = create_dumpfile [] dump_dependencies_path in
 	let print fmt = Printf.kprintf (fun s -> Buffer.add_string buf s) fmt in
 	let dep = Hashtbl.create 0 in
@@ -199,7 +202,7 @@ let dump_dependencies ?(target_override=None) com =
 		) m.m_extra.m_deps;
 	) com.Common.modules;
 	close();
-	let dump_dependants_path = [dump_path com;target_name;"dependants"] in
+	let dump_dependants_path = [dump_path com.defines;target_name;"dependants"] in
 	let buf,close = create_dumpfile [] dump_dependants_path in
 	let print fmt = Printf.kprintf (fun s -> Buffer.add_string buf s) fmt in
 	Hashtbl.iter (fun n ml ->

+ 0 - 3
src/context/common.ml

@@ -1117,9 +1117,6 @@ let display_error_ext com err =
 let display_error com ?(depth = 0) msg p =
 	display_error_ext com (Error.make_error ~depth (Custom msg) p)
 
-let dump_path com =
-	Define.defined_value_safe ~default:"dump" com.defines Define.DumpPath
-
 let adapt_defines_to_macro_context defines =
 	let to_remove = "java" :: List.map Globals.platform_name Globals.platforms in
 	let to_remove = List.fold_left (fun acc d -> Define.get_define_key d :: acc) to_remove [Define.NoTraces] in

+ 92 - 66
src/optimization/analyzer.ml

@@ -20,7 +20,6 @@
 open StringHelper
 open Ast
 open Type
-open Common
 open AnalyzerTexpr
 open AnalyzerTypes
 open OptimizerTexpr
@@ -185,17 +184,29 @@ end
 
 module type DataFlowApi = sig
 	type t
+
+	type opt_ctx
+
 	val to_string : t -> string
 	val flag : BasicBlock.cfg_edge_Flag
-	val transfer : analyzer_context -> BasicBlock.t -> texpr -> t (* The transfer function *)
-	val equals : t -> t -> bool                                   (* The equality function *)
-	val bottom : t                                                (* The bottom element of the lattice *)
-	val top : t                                                   (* The top element of the lattice *)
-	val get_cell : int -> t                                       (* Lattice cell getter *)
-	val set_cell : int -> t -> unit                               (* Lattice cell setter *)
-	val init : analyzer_context -> unit                           (* The initialization function which is called at the start *)
-	val commit : analyzer_context -> unit                         (* The commit function which is called at the end *)
-	val conditional : bool                                        (* Whether or not conditional branches are checked *)
+	(* The transfer function *)
+	val transfer : analyzer_context -> opt_ctx -> BasicBlock.t -> texpr -> t
+	(* The equality function *)
+	val equals : t -> t -> bool
+	(* The bottom element of the lattice *)
+	val bottom : t
+	(* The top element of the lattice *)
+	val top : t
+	(* Lattice cell getter *)
+	val get_cell : opt_ctx -> int -> t
+	(* Lattice cell setter *)
+	val set_cell : opt_ctx -> int -> t -> unit
+	(* The initialization function which is called at the start *)
+	val init : analyzer_context -> opt_ctx
+	(* The commit function which is called at the end *)
+	val commit : analyzer_context -> opt_ctx -> unit
+	(* Whether or not conditional branches are checked *)
+	val conditional : bool
 end
 
 (*
@@ -220,8 +231,8 @@ module DataFlow (M : DataFlowApi) = struct
 	let get_ssa_edges_from g v =
 		(get_var_info g v).vi_ssa_edges
 
-	let run ctx =
-		let g = ctx.graph in
+	let run actx ctx =
+		let g = actx.graph in
 		let ssa_work_list = ref [] in
 		let cfg_work_list = ref g.g_root.bb_outgoing in
 		let add_ssa_edge edge =
@@ -234,7 +245,7 @@ module DataFlow (M : DataFlowApi) = struct
 			let el = List.fold_left2 (fun acc e edge ->
 				if has_flag edge M.flag then e :: acc else acc
 			) [] el bb.bb_incoming in
-			let el = List.map (fun e -> M.transfer ctx bb e) el in
+			let el = List.map (fun e -> M.transfer actx ctx bb e) el in
 			match el with
 				| e1 :: el when List.for_all (M.equals e1) el ->
 					e1;
@@ -242,8 +253,8 @@ module DataFlow (M : DataFlowApi) = struct
 					M.bottom;
 		in
 		let set_lattice_cell v e =
-			let e' = M.get_cell v.v_id in
-			M.set_cell v.v_id e;
+			let e' = M.get_cell ctx v.v_id in
+			M.set_cell ctx v.v_id e;
 			if not (M.equals e e') then
 				List.iter (fun edge -> add_ssa_edge edge) (get_ssa_edges_from g v);
 		in
@@ -253,7 +264,7 @@ module DataFlow (M : DataFlowApi) = struct
 				set_lattice_cell v (visit_phi bb v el)
 			| _ ->
 				if List.exists (fun edge -> has_flag edge M.flag) bb.bb_incoming then
-					set_lattice_cell v (M.transfer ctx bb e)
+					set_lattice_cell v (M.transfer actx ctx bb e)
 		in
 		let visit_expression bb cond_branch e =
 			match e.eexpr with
@@ -261,7 +272,7 @@ module DataFlow (M : DataFlowApi) = struct
 				visit_assignment bb v e2;
 				false
 			| _ when M.conditional && cond_branch ->
-				let e1 = M.transfer ctx bb e in
+				let e1 = M.transfer actx ctx bb e in
 				let edges = if e1 == M.bottom || e1 == M.top then
 					bb.bb_outgoing
 				else begin
@@ -269,7 +280,7 @@ module DataFlow (M : DataFlowApi) = struct
 						| edge :: edges ->
 							begin match edge.cfg_kind with
 							| CFGCondBranch e ->
-								let e = M.transfer ctx bb e in
+								let e = M.transfer actx ctx bb e in
 								if M.equals e e1 then
 									loop (edge :: yes) maybe also edges
 								else
@@ -341,10 +352,10 @@ module DataFlow (M : DataFlowApi) = struct
 		in
 		loop ()
 
-	let apply ctx =
-		M.init ctx;
-		run ctx;
-		M.commit ctx
+	let apply actx =
+		let ctx = M.init actx in
+		run actx ctx;
+		M.commit actx ctx
 end
 
 (*
@@ -354,7 +365,7 @@ end
 
 	This module also deals with binop/unop optimization and standard API inlining.
 *)
-module ConstPropagation = DataFlow(struct
+module ConstPropagationImpl = struct
 	open BasicBlock
 
 	type t =
@@ -365,6 +376,8 @@ module ConstPropagation = DataFlow(struct
 		| EnumValue of int * t list
 		| ModuleType of module_type * Type.t
 
+	type opt_ctx = (int,t) Hashtbl.t
+
 	let rec to_string =
 		let st = s_type (print_context()) in
 		function
@@ -378,10 +391,8 @@ module ConstPropagation = DataFlow(struct
 	let conditional = true
 	let flag = FlagExecutable
 
-	let lattice = Hashtbl.create 0
-
-	let get_cell i = try Hashtbl.find lattice i with Not_found -> Top
-	let set_cell i ct = Hashtbl.replace lattice i ct
+	let get_cell ctx i = try Hashtbl.find ctx i with Not_found -> Top
+	let set_cell ctx i ct = Hashtbl.replace ctx i ct
 
 	let top = Top
 	let bottom = Bottom
@@ -394,7 +405,7 @@ module ConstPropagation = DataFlow(struct
 		| ModuleType(mt1,_),ModuleType (mt2,_) -> mt1 == mt2
 		| _ -> false
 
-	let transfer ctx bb e =
+	let transfer actx ctx bb e =
 		let rec eval bb e =
 			let wrap = function
 				| Const(ct,t) -> mk (TConst ct) t null_pos
@@ -418,7 +429,7 @@ module ConstPropagation = DataFlow(struct
 				if (follow v.v_type) == t_dynamic || has_var_flag v VCaptured then
 					Bottom
 				else
-					get_cell v.v_id
+					get_cell ctx v.v_id
 			| TBinop(OpAssign,_,e2) ->
 				eval bb e2
 			| TBinop(op,e1,e2) ->
@@ -453,14 +464,14 @@ module ConstPropagation = DataFlow(struct
 				end;
 			| TEnumIndex e1 ->
 				begin match eval bb e1 with
-					| EnumValue(i,_) -> Const (TInt (Int32.of_int i),ctx.com.basic.tint)
+					| EnumValue(i,_) -> Const (TInt (Int32.of_int i),actx.com.basic.tint)
 					| _ -> raise Exit
 				end;
-			| TCall ({ eexpr = TField (_,FStatic({cl_path=[],"Type"} as c,({cf_name="enumIndex"} as cf)))},[e1]) when ctx.com.platform = Eval ->
+			| TCall ({ eexpr = TField (_,FStatic({cl_path=[],"Type"} as c,({cf_name="enumIndex"} as cf)))},[e1]) when actx.com.platform = Eval ->
 				begin match follow e1.etype,eval bb e1 with
-					| TEnum _,EnumValue(i,_) -> Const (TInt (Int32.of_int i),ctx.com.basic.tint)
+					| TEnum _,EnumValue(i,_) -> Const (TInt (Int32.of_int i),actx.com.basic.tint)
 					| _,e1 ->
-						begin match Inline.api_inline2 ctx.com c cf.cf_name [wrap e1] e.epos with
+						begin match Inline.api_inline2 actx.com.basic actx.com.platform c cf.cf_name [wrap e1] e.epos with
 							| None -> raise Exit
 							| Some e -> eval bb e
 						end
@@ -468,22 +479,22 @@ module ConstPropagation = DataFlow(struct
 			| TCall ({ eexpr = TField (_,FStatic(c,cf))},el) ->
 				let el = List.map (eval bb) el in
 				let el = List.map wrap el in
-				begin match Inline.api_inline2 ctx.com c cf.cf_name el e.epos with
+				begin match Inline.api_inline2 actx.com.basic actx.com.platform c cf.cf_name el e.epos with
 					| None -> raise Exit
 					| Some e -> eval bb e
 				end
 			| TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) ->
 				eval bb e1
 			| _ ->
-				let e1 = match ctx.com.platform,e.eexpr with
-					| Js,TArray(e1,{eexpr = TConst(TInt i)}) when Int32.to_int i = 1 && Define.defined ctx.com.defines Define.JsEnumsAsArrays -> e1
-					| Js,TField(e1,FDynamic "_hx_index") when not (Define.defined ctx.com.defines Define.JsEnumsAsArrays) -> e1
+				let e1 = match actx.com.platform,e.eexpr with
+					| Js,TArray(e1,{eexpr = TConst(TInt i)}) when Int32.to_int i = 1 && Define.defined actx.com.defines Define.JsEnumsAsArrays -> e1
+					| Js,TField(e1,FDynamic "_hx_index") when not (Define.defined actx.com.defines Define.JsEnumsAsArrays) -> e1
 					| Cpp,TCall({eexpr = TField(e1,FDynamic "__Index")},[]) -> e1
 					| Neko,TField(e1,FDynamic "index") -> e1
 					| _ -> raise Exit
 				in
 				begin match follow e1.etype,eval bb e1 with
-					| TEnum _,EnumValue(i,_) -> Const (TInt (Int32.of_int i),ctx.com.basic.tint)
+					| TEnum _,EnumValue(i,_) -> Const (TInt (Int32.of_int i),actx.com.basic.tint)
 					| _ -> raise Exit
 				end
 		in
@@ -493,19 +504,19 @@ module ConstPropagation = DataFlow(struct
 			Bottom
 
 	let init ctx =
-		Hashtbl.clear lattice
+		Hashtbl.create 0
 
-	let commit ctx =
-		let inline e i = match get_cell i with
+	let commit actx ctx =
+		let inline e i = match get_cell ctx i with
 			| Top | Bottom | EnumValue _ | Null _ ->
 				raise Not_found
 			| Const(ct,t) ->
-				let e' = Texpr.type_constant ctx.com.basic (tconst_to_const ct) e.epos in
+				let e' = Texpr.type_constant actx.com.basic (tconst_to_const ct) e.epos in
 				let e' = {e' with etype = t} in
-				if not (type_change_ok ctx.com e'.etype e.etype) then raise Not_found;
+				if not (type_change_ok actx.com e'.etype e.etype) then raise Not_found;
 				e'
 			| ModuleType(mt,t) ->
-				if not (type_change_ok ctx.com t e.etype) then raise Not_found;
+				if not (type_change_ok actx.com t e.etype) then raise Not_found;
 				mk (TTypeExpr mt) t e.epos
 		in
 		let is_special_var v = has_var_flag v VCaptured || ExtType.has_variable_semantics v.v_type in
@@ -530,19 +541,21 @@ module ConstPropagation = DataFlow(struct
 			| _ ->
 				Type.map_expr commit e
 		in
-		Graph.iter_dom_tree ctx.graph (fun bb ->
-			if not (List.exists (fun edge -> has_flag edge FlagExecutable) bb.bb_incoming) then bb.bb_dominator <- ctx.graph.Graph.g_unreachable;
+		Graph.iter_dom_tree actx.graph (fun bb ->
+			if not (List.exists (fun edge -> has_flag edge FlagExecutable) bb.bb_incoming) then bb.bb_dominator <- actx.graph.Graph.g_unreachable;
 			dynarray_map commit bb.bb_el;
 			bb.bb_terminator <- terminator_map commit bb.bb_terminator;
 		);
-end)
+end
+
+module ConstPropagation = DataFlow(ConstPropagationImpl)
 
 (*
 	Propagates local variables to other local variables.
 
 	Respects scopes on targets where it matters (all except JS).
 *)
-module CopyPropagation = DataFlow(struct
+module CopyPropagationImpl = struct
 	open BasicBlock
 	open Graph
 
@@ -552,6 +565,8 @@ module CopyPropagation = DataFlow(struct
 		| Local of tvar
 		| This of Type.t
 
+	type opt_ctx = (int,t) Hashtbl.t
+
 	let to_string = function
 		| Top -> "Top"
 		| Bottom -> "Bottom"
@@ -560,10 +575,9 @@ module CopyPropagation = DataFlow(struct
 
 	let conditional = false
 	let flag = FlagCopyPropagation
-	let lattice = Hashtbl.create 0
 
-	let get_cell i = try Hashtbl.find lattice i with Not_found -> Top
-	let set_cell i ct = Hashtbl.replace lattice i ct
+	let get_cell ctx i = try Hashtbl.find ctx i with Not_found -> Top
+	let set_cell ctx i ct = Hashtbl.replace ctx i ct
 
 	let top = Top
 	let bottom = Bottom
@@ -575,7 +589,7 @@ module CopyPropagation = DataFlow(struct
 		| This t1,This t2 -> t1 == t2
 		| _ -> false
 
-	let transfer ctx bb e =
+	let transfer actx ctx bb e =
 		let rec loop e = match e.eexpr with
 			| TLocal v when not (has_var_flag v VCaptured) ->
 				Local v
@@ -588,33 +602,33 @@ module CopyPropagation = DataFlow(struct
 		in
 		loop e
 
-	let init ctx =
-		Hashtbl.clear lattice
+	let init actx =
+		Hashtbl.create 0
 
-	let commit ctx =
+	let commit actx ctx =
 		let rec commit bb e = match e.eexpr with
 			| TLocal v when not (has_var_flag v VCaptured) ->
 				begin try
-					let lat = get_cell v.v_id in
+					let lat = get_cell ctx v.v_id in
 					let leave () =
-						Hashtbl.remove lattice v.v_id;
+						Hashtbl.remove ctx v.v_id;
 						raise Not_found
 					in
 					begin match lat with
 					| Local v' ->
-						if not (type_change_ok ctx.com v'.v_type v.v_type) then leave();
-						let v'' = get_var_origin ctx.graph v' in
+						if not (type_change_ok actx.com v'.v_type v.v_type) then leave();
+						let v'' = get_var_origin actx.graph v' in
 						(* This restriction is in place due to how we currently reconstruct the AST. Multiple SSA-vars may be turned back to
 						the same origin var, which creates interference that is not tracked in the analysis. We address this by only
 						considering variables whose origin-variables are assigned to at most once. *)
-						let writes = (get_var_info ctx.graph v'').vi_writes in
+						let writes = (get_var_info actx.graph v'').vi_writes in
 						begin match writes with
 							| [bb'] when in_scope bb bb' -> ()
 							| _ -> leave()
 						end;
 						commit bb {e with eexpr = TLocal v'}
 					| This t ->
-						if not (type_change_ok ctx.com t v.v_type) then leave();
+						if not (type_change_ok actx.com t v.v_type) then leave();
 						mk (TConst TThis) t e.epos
 					| Top | Bottom ->
 						leave()
@@ -628,11 +642,13 @@ module CopyPropagation = DataFlow(struct
 			| _ ->
 				Type.map_expr (commit bb) e
 		in
-		Graph.iter_dom_tree ctx.graph (fun bb ->
+		Graph.iter_dom_tree actx.graph (fun bb ->
 			dynarray_map (commit bb) bb.bb_el;
 			bb.bb_terminator <- terminator_map (commit bb) bb.bb_terminator;
 		);
-end)
+end
+
+module CopyPropagation = DataFlow(CopyPropagationImpl)
 
 (*
 	LocalDce implements a mark & sweep dead code elimination. The mark phase follows the CFG edges of the graphs to find
@@ -853,8 +869,12 @@ module Debug = struct
 			end
 		) g.g_var_infos
 
+	let platform_name_macro com =
+		if Define.defined com.defines Define.Macro then "macro"
+		else platform_name com.platform
+
 	let get_dump_path ctx c cf =
-		(dump_path ctx.com) :: [platform_name_macro ctx.com] @ (fst c.cl_path) @ [Printf.sprintf "%s.%s" (snd c.cl_path) cf.cf_name]
+		(Dump.dump_path ctx.com.defines) :: [platform_name_macro ctx.com] @ (fst c.cl_path) @ [Printf.sprintf "%s.%s" (snd c.cl_path) cf.cf_name]
 
 	let dot_debug ctx c cf =
 		let g = ctx.graph in
@@ -961,10 +981,16 @@ module Run = struct
 		let timer = Timer.timer name in
 		Std.finally timer f ()
 
-	let create_analyzer_context com config identifier e =
+	let create_analyzer_context (com : Common.context) config identifier e =
 		let g = Graph.create e.etype e.epos in
 		let ctx = {
-			com = com;
+			com = {
+				basic = com.basic;
+				platform = com.platform;
+				platform_config = com.config;
+				defines = com.defines;
+				debug = com.debug;
+			};
 			config = config;
 			graph = g;
 			(* For CPP we want to use variable names which are "probably" not used by users in order to

+ 5 - 4
src/optimization/analyzerTexpr.ml

@@ -20,6 +20,7 @@
 open Ast
 open Type
 open Common
+open AnalyzerTypes
 open OptimizerTexpr
 open Globals
 
@@ -108,11 +109,11 @@ let target_handles_unops com = match com.platform with
 let target_handles_assign_ops com e2 = match com.platform with
 	| Php -> not (has_side_effect e2)
 	| Lua -> false
-	| Cpp when not (Common.defined com Define.Cppia) -> false
+	| Cpp when not (Define.defined com.defines Define.Cppia) -> false
 	| _ -> true
 
 let target_handles_side_effect_order com = match com.platform with
-	| Cpp -> Common.defined com Define.Cppia
+	| Cpp -> Define.defined com.defines Define.Cppia
 	| Php -> false
 	| _ -> true
 
@@ -183,7 +184,7 @@ let type_change_ok com t1 t2 =
 		t1 == t2 || match follow t1,follow t2 with
 			| TDynamic _,_ | _,TDynamic _ -> false
 			| _ ->
-				if com.config.pf_static && is_nullable_or_whatever t1 <> is_nullable_or_whatever t2 then false
+				if com.platform_config.pf_static && is_nullable_or_whatever t1 <> is_nullable_or_whatever t2 then false
 				else type_iseq t1 t2
 	end
 
@@ -827,7 +828,7 @@ module Fusion = struct
 				can_be_used_as_value com e1 &&
 				not (ExtType.is_void e1.etype) &&
 				(match com.platform with
-					| Cpp when not (Common.defined com Define.Cppia) -> false
+					| Cpp when not (Define.defined com.defines Define.Cppia) -> false
 					| _ -> true)
 				->
 				begin try

+ 9 - 1
src/optimization/analyzerTypes.ml

@@ -597,8 +597,16 @@ module Graph = struct
 		Hashtbl.iter (fun _ (bb,_,_,_) -> loop [0] bb) g.g_functions
 end
 
+type light_com = {
+	basic : basic_types;
+	platform : platform;
+	defines : Define.define;
+	platform_config : platform_config;
+	debug : bool;
+}
+
 type analyzer_context = {
-	com : Common.context;
+	com : light_com;
 	config : AnalyzerConfig.t;
 	graph : Graph.t;
 	temp_var_name : string;

+ 15 - 15
src/optimization/inline.ml

@@ -15,36 +15,36 @@ let mk_untyped_call name p params =
 		epos = p;
 	}
 
-let api_inline2 com c field params p =
+let api_inline2 basic platform c field params p =
 	match c.cl_path, field, params with
 	| ([],"Type"),"enumIndex",[{ eexpr = TField (_,FEnum (en,f)) }] ->
-		Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p)
+		Some (mk (TConst (TInt (Int32.of_int f.ef_index))) basic.tint p)
 	| ([],"Type"),"enumIndex",[{ eexpr = TCall({ eexpr = TField (_,FEnum (en,f)) },pl) }] when List.for_all (fun e -> not (has_side_effect e)) pl ->
-		Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p)
+		Some (mk (TConst (TInt (Int32.of_int f.ef_index))) basic.tint p)
 	| ([],"Std"),"int",[{ eexpr = TConst (TInt _) } as e] ->
 		Some { e with epos = p }
 	| ([],"String"),"fromCharCode",[{ eexpr = TConst (TInt i) }] when i > 0l && i < 128l ->
-		Some (mk (TConst (TString (String.make 1 (char_of_int (Int32.to_int i))))) com.basic.tstring p)
+		Some (mk (TConst (TString (String.make 1 (char_of_int (Int32.to_int i))))) basic.tstring p)
 	| ([],"Std"),"string",[{ eexpr = TCast ({ eexpr = TConst c } as e, None)}]
 	| ([],"Std"),"string",[{ eexpr = TConst c } as e] ->
 		(match c with
 		| TString s ->
 			Some { e with epos = p }
 		| TInt i ->
-			Some { eexpr = TConst (TString (Int32.to_string i)); epos = p; etype = com.basic.tstring }
+			Some { eexpr = TConst (TString (Int32.to_string i)); epos = p; etype = basic.tstring }
 		| TBool b ->
-			Some { eexpr = TConst (TString (if b then "true" else "false")); epos = p; etype = com.basic.tstring }
+			Some { eexpr = TConst (TString (if b then "true" else "false")); epos = p; etype = basic.tstring }
 		| _ ->
 			None)
 	| ([],"Std"),"string",[{ eexpr = TIf (_,{ eexpr = TConst (TString _)},Some { eexpr = TConst (TString _) }) } as e] ->
 		Some e
-	| ([],"Std"),"string",[{ eexpr = TLocal v | TField({ eexpr = TLocal v },_) } as ev] when (com.platform = Js || com.platform = Flash) && (match v.v_kind with VUser _ -> true | _ -> false) ->
+	| ([],"Std"),"string",[{ eexpr = TLocal v | TField({ eexpr = TLocal v },_) } as ev] when (platform = Js || platform = Flash) && (match v.v_kind with VUser _ -> true | _ -> false) ->
 		let pos = ev.epos in
 		let stringv() =
-			let to_str = mk (TBinop (Ast.OpAdd, mk (TConst (TString "")) com.basic.tstring pos, ev)) com.basic.tstring pos in
-			if com.platform = Js || is_nullable ev.etype then
-				let chk_null = mk (TBinop (Ast.OpEq, ev, mk (TConst TNull) t_dynamic pos)) com.basic.tbool pos in
-				mk (TIf (chk_null, mk (TConst (TString "null")) com.basic.tstring pos, Some to_str)) com.basic.tstring pos
+			let to_str = mk (TBinop (Ast.OpAdd, mk (TConst (TString "")) basic.tstring pos, ev)) basic.tstring pos in
+			if platform = Js || is_nullable ev.etype then
+				let chk_null = mk (TBinop (Ast.OpEq, ev, mk (TConst TNull) t_dynamic pos)) basic.tbool pos in
+				mk (TIf (chk_null, mk (TConst (TString "null")) basic.tstring pos, Some to_str)) basic.tstring pos
 			else
 				to_str
 		in
@@ -69,7 +69,7 @@ let api_inline2 com c field params p =
 		| _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int +. 1. ->
 			None (* out range, keep platform-specific behavior *)
 		| _ ->
-			Some { eexpr = TConst (TInt (Int32.of_float f)); etype = com.basic.tint; epos = p })
+			Some { eexpr = TConst (TInt (Int32.of_float f)); etype = basic.tint; epos = p })
 	| ([],"Math"),"ceil",[{ eexpr = TConst (TFloat f) }] ->
 		let f = float_of_string f in
 		(match classify_float f with
@@ -78,7 +78,7 @@ let api_inline2 com c field params p =
 		| _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int ->
 			None (* out range, keep platform-specific behavior *)
 		| _ ->
-			Some { eexpr = TConst (TInt (Int32.of_float (ceil f))); etype = com.basic.tint; epos = p })
+			Some { eexpr = TConst (TInt (Int32.of_float (ceil f))); etype = basic.tint; epos = p })
 	| ([],"Math"),"floor",[{ eexpr = TConst (TFloat f) }] ->
 		let f = float_of_string f in
 		(match classify_float f with
@@ -87,7 +87,7 @@ let api_inline2 com c field params p =
 		| _ when f <= Int32.to_float Int32.min_int || f >= Int32.to_float Int32.max_int +. 1. ->
 			None (* out range, keep platform-specific behavior *)
 		| _ ->
-			Some { eexpr = TConst (TInt (Int32.of_float (floor f))); etype = com.basic.tint; epos = p })
+			Some { eexpr = TConst (TInt (Int32.of_float (floor f))); etype = basic.tint; epos = p })
 	| (["java"],"Lib"),("lock"),[obj;block] ->
 			Some (mk_untyped_call ("__lock__") p [obj;mk_block block])
 	| _ ->
@@ -185,7 +185,7 @@ let api_inline ctx c field params p =
 		with | Exit ->
 			None)
 	| _ ->
-		api_inline2 ctx.com c field params p
+		api_inline2 ctx.com.basic ctx.com.platform c field params p
 
 type in_local = {
 	i_var : tvar;

+ 1 - 1
src/typing/macroContext.ml

@@ -41,7 +41,7 @@ let macro_interp_cache = ref None
 
 let safe_decode com v expected t p f =
 	let raise_decode_error s =
-		let path = [dump_path com;"decoding_error"] in
+		let path = [Dump.dump_path com.defines;"decoding_error"] in
 		let ch = Path.create_file false ".txt" [] path  in
 		let errors = Interp.handle_decoding_error (output_string ch) v t in
 		List.iter (fun (s,i) -> Printf.fprintf ch "\nline %i: %s" i s) (List.rev errors);