Browse Source

clean up user variable fusion handling (closes #5298)

- by default in debug mode we keep them, in release mode we remove them
- can be inverted using -D analyzer-user-var-fusion or -D analyzer-no-user-var-fusion
- as usual can be controlled at type/field level using `@:analyzer(no_user_var_fusion)` and `@:analyzer(user_var_fusion)`
Simon Krajewski 9 years ago
parent
commit
f41a3aef65

+ 6 - 1
src/optimization/analyzerConfig.ml

@@ -36,6 +36,7 @@ type t = {
 	purity_inference : bool;
 	debug_kind : debug_kind;
 	detail_times : bool;
+	user_var_fusion : bool;
 }
 
 let flag_const_propagation = "const_propagation"
@@ -47,11 +48,12 @@ let flag_purity_inference = "purity_inference"
 let flag_ignore = "ignore"
 let flag_dot_debug = "dot_debug"
 let flag_full_debug = "full_debug"
+let flag_user_var_fusion = "user_var_fusion"
 
 let all_flags =
 	List.fold_left (fun acc flag ->
 		flag :: ("no_" ^ flag) :: acc
-	) [] [flag_const_propagation;flag_copy_propagation;flag_code_motion;flag_local_dce;flag_fusion;flag_purity_inference;flag_ignore;flag_dot_debug]
+	) [] [flag_const_propagation;flag_copy_propagation;flag_code_motion;flag_local_dce;flag_fusion;flag_purity_inference;flag_ignore;flag_dot_debug;flag_user_var_fusion]
 
 let has_analyzer_option meta s =
 	try
@@ -88,6 +90,7 @@ let get_base_config com =
 		purity_inference = not (Common.raw_defined com "analyzer-no-purity-inference");
 		debug_kind = DebugNone;
 		detail_times = Common.raw_defined com "analyzer-times";
+		user_var_fusion = Common.raw_defined com "analyzer-user-var-fusion" || (not com.debug && not (Common.raw_defined com "analyzer-no-user-var-fusion"));
 	}
 
 let update_config_from_meta com config meta =
@@ -108,6 +111,8 @@ let update_config_from_meta com config meta =
 				| EConst (Ident s) when s = flag_purity_inference -> { config with purity_inference = true}
 				| EConst (Ident s) when s = flag_dot_debug -> {config with debug_kind = DebugDot}
 				| EConst (Ident s) when s = flag_full_debug -> {config with debug_kind = DebugFull}
+				| EConst (Ident s) when s = flag_user_var_fusion -> {config with user_var_fusion = true}
+				| EConst (Ident s) when s = "no_" ^ flag_user_var_fusion -> {config with user_var_fusion = false}
 				| _ ->
 					let s = Ast.s_expr e in
 					com.warning (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e);

+ 4 - 2
src/optimization/analyzerTexpr.ml

@@ -307,6 +307,8 @@ end
 
 module Fusion = struct
 
+	open AnalyzerConfig
+
 	type interference_kind =
 		| IKVarMod of tvar list
 		| IKSideEffect
@@ -348,7 +350,7 @@ module Fusion = struct
 		let rec block_element acc el = match el with
 			| {eexpr = TBinop((OpAssign | OpAssignOp _),_,_) | TUnop((Increment | Decrement),_,_)} as e1 :: el ->
 				block_element (e1 :: acc) el
-			| {eexpr = TLocal _} as e1 :: el when not config.AnalyzerConfig.local_dce ->
+			| {eexpr = TLocal _} as e1 :: el when not config.local_dce ->
 				block_element (e1 :: acc) el
 			(* no-side-effect *)
 			| {eexpr = TEnumParameter _ | TFunction _ | TConst _ | TTypeExpr _ | TLocal _} :: el ->
@@ -405,7 +407,7 @@ module Fusion = struct
 			let b = get_num_uses v <= 1 &&
 			        get_num_writes v = 0 &&
 			        can_be_used_as_value com e &&
-			        (Meta.has Meta.CompilerGenerated v.v_meta || config.AnalyzerConfig.optimize && config.AnalyzerConfig.fusion && type_change_ok com v.v_type e.etype && v.v_extra = None)
+			        (not (Meta.has Meta.UserVariable v.v_meta) || config.optimize && config.fusion && config.user_var_fusion && type_change_ok com v.v_type e.etype && v.v_extra = None)
 			in
 			(* let st = s_type (print_context()) in *)
 			(* if e.epos.pfile = "src/Main.hx" then print_endline (Printf.sprintf "%s: %i %i %b %s %s (%b %b %b %b %b) -> %b" v.v_name (get_num_uses v) (get_num_writes v) (can_be_used_as_value com e) (st v.v_type) (st e.etype) (Meta.has Meta.CompilerGenerated v.v_meta) config.Config.optimize config.Config.fusion (type_change_ok com v.v_type e.etype) (v.v_extra = None) b); *)

+ 1 - 0
src/syntax/ast.ml

@@ -187,6 +187,7 @@ module Meta = struct
 		| Unsafe
 		| Usage
 		| Used
+		| UserVariable
 		| Value
 		| Void
 		| Last

+ 1 - 0
src/typing/common.ml

@@ -588,6 +588,7 @@ module MetaInfo = struct
 		| Unsafe -> ":unsafe",("Declares a class, or a method with the C#'s 'unsafe' flag",[Platform Cs; UsedOnEither [TClass;TClassField]])
 		| Usage -> ":usage",("?",[])
 		| Used -> ":used",("Internally used by DCE to mark a class or field as used",[Internal])
+		| UserVariable -> ":userVariable",("Internally used to mark variables that come from user code",[Internal])
 		| Value -> ":value",("Used to store default values for fields and function arguments",[UsedOn TClassField])
 		| Void -> ":void",("Use Cpp native 'void' return type",[Platform Cpp])
 		| Last -> assert false

+ 2 - 1
src/typing/typer.ml

@@ -2883,7 +2883,8 @@ and type_vars ctx vl p =
 					Some e
 			) in
 			if v.[0] = '$' && ctx.com.display = DMNone then error "Variables names starting with a dollar are not allowed" p;
-			let v,e = add_local ctx v t pv, e in
+			let v = add_local ctx v t pv in
+			v.v_meta <- (Meta.UserVariable,[],pv) :: v.v_meta;
 			if ctx.in_display && Display.is_display_position pv then
 				Display.display_variable ctx.com.display v pv;
 			v,e

+ 2 - 1
tests/unit/compile-each.hxml

@@ -6,4 +6,5 @@
 -resource res2.bin@re/s?!%[]))("'1.bin
 -dce full
 -D analyzer
--D analyzer-code-motion
+-D analyzer-code-motion
+-D analyzer-user-var-fusion