浏览代码

add code motion

Simon Krajewski 9 年之前
父节点
当前提交
a9de6f215c
共有 2 个文件被更改,包括 232 次插入1 次删除
  1. 149 1
      analyzer.ml
  2. 83 0
      tests/optimization/src/TestJs.hx

+ 149 - 1
analyzer.ml

@@ -358,6 +358,8 @@ module TexprFilter = struct
 						found := true;
 						changed := true;
 						e1
+					| TWhile _ ->
+						e
 					| _ ->
 						Type.map_expr replace e
 				in
@@ -485,6 +487,7 @@ module BasicBlock = struct
 	type cfg_edge_Flag =
 		| FlagExecutable     (* Used by constant propagation to handle live edges *)
 		| FlagDceDone    (* Used by DCE to keep track of handled edges *)
+		| FlagCodeMotionDone (* Used by code motion to track handled edges *)
 
 	type cfg_edge_kind =
 		| CFGGoto                (* An unconditional branch *)
@@ -847,8 +850,9 @@ module TexprTransformer = struct
 				assert false
 		and ordered_value_list bb el =
 			let might_be_affected,collect_modified_locals = Optimizer.create_affection_checker() in
-			let can_be_optimized e = match e.eexpr with
+			let rec can_be_optimized e = match e.eexpr with
 				| TBinop _ | TArray _ | TCall _ -> true
+				| TParenthesis e1 -> can_be_optimized e1
 				| _ -> false
 			in
 			let _,el = List.fold_left (fun (had_side_effect,acc) e ->
@@ -1732,6 +1736,148 @@ module ConstPropagation = DataFlow(struct
 		);
 end)
 
+
+module CodeMotion = DataFlow(struct
+	open Graph
+	open BasicBlock
+
+	let conditional = false
+	let flag = FlagCodeMotionDone
+
+	type t_def =
+		| Top
+		| Bottom
+		| Const of tconstant
+		| Local of tvar * BasicBlock.t
+		| Binop of binop * t * t
+
+	and t = (t_def * Type.t * pos)
+
+	let top = (Top,t_dynamic,null_pos)
+	let bottom = (Bottom,t_dynamic,null_pos)
+
+	let rec equals (lat1,_,_) (lat2,_,_) = match lat1,lat2 with
+		| Top,Top
+		| Bottom,Bottom ->
+			true
+		| Const ct1,Const ct2 ->
+			ct1 = ct2
+		| Local(v1,_),Local(v2,_) ->
+			v1 == v2
+		| Binop(op1,lat11,lat12),Binop(op2,lat21,lat22) ->
+			op1 = op2 && equals lat11 lat21 && equals lat12 lat22
+		| _ ->
+			false
+
+	let lattice = ref IntMap.empty
+
+	let get_cell i = try IntMap.find i !lattice with Not_found -> top
+	let set_cell i ct = lattice := IntMap.add i ct !lattice
+
+	let rec transfer ctx bb e =
+		let rec eval e = match e.eexpr with
+			| TConst ct ->
+				Const ct
+			| TLocal v ->
+				let bb_def = match IntMap.find v.v_id ctx.graph.g_var_writes with _,[bb] -> bb | _ -> raise Exit in
+				Local(v,bb_def)
+			| TBinop(op,e1,e2) ->
+				let lat1 = transfer ctx bb e1 in
+				let lat2 = transfer ctx bb e2 in
+				Binop(op,lat1,lat2)
+			| _ ->
+				raise Exit
+		in
+		try
+			(eval e,e.etype,e.epos)
+		with Exit | Not_found ->
+			bottom
+
+	let init ctx =
+		lattice := IntMap.empty
+
+	let commit ctx =
+		let rec s_lat (lat,_,_) = match lat with
+			| Top -> "Top"
+			| Bottom -> "Bottom"
+			| Const ct -> s_const ct
+			| Local(v,bb) -> Printf.sprintf "(%i) %s<%i>" bb.bb_id v.v_name v.v_id
+			| Binop(op,lat1,lat2) -> Printf.sprintf "%s %s %s" (s_lat lat1) (s_binop op) (s_lat lat2)
+		in
+		let rec filter_loops lat loops = match lat with
+			| Local(v,bb),_,_ ->
+				let loops2 = List.filter (fun i -> not (List.mem i bb.bb_loop_groups)) loops in
+				if loops2 = [] then filter_loops (get_cell v.v_id) loops else lat,loops2
+			| Const _,_,_ ->
+				lat,loops
+			| Binop(op,lat1,lat2),t,p ->
+				let lat1,loops = filter_loops lat1 loops in
+				let lat2,loops = filter_loops lat2 loops in
+				(Binop(op,lat1,lat2),t,p),loops
+			| _ ->
+				raise Exit
+		in
+		let rec to_texpr (lat,t,p) =
+			let def = match lat with
+				| Local(v,_) -> TLocal v
+				| Const ct -> TConst ct
+				| Binop(op,lat1,lat2) -> TBinop(op,to_texpr lat1,to_texpr lat2)
+				| _ -> raise Exit
+			in
+			{ eexpr = def; etype = t; epos = p }
+		in
+		let replace decl bb v =
+			let lat,t,p = get_cell v.v_id in
+			match lat with
+			| Binop(op,lat1,lat2) ->
+				let lat1,loops = filter_loops lat1 bb.bb_loop_groups in
+				let lat2,loops = filter_loops lat2 loops in
+				if loops = [] then raise Exit;
+				let lat = ((Binop(op,lat1,lat2)),t,p) in
+				let bb_loop_pre = IntMap.find (List.hd loops) ctx.graph.g_loops in
+				let v' = if decl then begin
+					set_cell v.v_id (Local(v,bb_loop_pre),t,p);
+					v
+				end else begin
+					let v' = alloc_var "tmp" v.v_type in
+					v'.v_meta <- [Meta.CompilerGenerated,[],p];
+					v'
+				end in
+				let e = to_texpr lat in
+				let ev' = mk (TLocal v') v'.v_type p in
+				let e = mk (TVar(v',Some e)) ctx.com.basic.tvoid p in
+				add_texpr ctx.graph bb_loop_pre e;
+				let ev = mk (TLocal v) v.v_type p in
+				if decl then begin
+					set_var_value ctx.graph v bb_loop_pre false (DynArray.length bb_loop_pre.bb_el - 1);
+					mk (TConst TNull) t p
+				end else
+					mk (TBinop(OpAssign,ev,ev')) t p
+			| _ ->
+				raise Exit
+		in
+		let rec commit bb e = match e.eexpr with
+			| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
+				begin try
+					replace false bb v
+				with Exit ->
+					{e with eexpr = TBinop(OpAssign,e1,commit bb e2)}
+				end
+			| TVar(v,Some e1) when Meta.has Meta.CompilerGenerated v.v_meta ->
+				begin try
+					replace true bb v
+				with Exit ->
+					{e with eexpr = TVar(v,Some (commit bb e1))}
+				end
+			| _ ->
+				Type.map_expr (commit bb) e
+		in
+		Graph.iter_dom_tree ctx.graph (fun bb ->
+			if bb.bb_loop_groups <> [] then dynarray_map (commit bb) bb.bb_el
+		);
+end)
+
+
 (*
 	LocalDce implements a mark & sweep dead code elimination. The mark phase follows the CFG edges of the graphs to find
 	variable usages and marks variables accordingly. If ConstPropagation was run before, only CFG edges which are
@@ -1861,6 +2007,7 @@ module Debug = struct
 		let s_edge_flag = function
 			| FlagExecutable -> "executable"
 			| FlagDceDone -> "dce-done"
+			| FlagCodeMotionDone -> "code-motion-done"
 		in
 		let label = label ^ match edge.cfg_flags with
 			| [] -> ""
@@ -2018,6 +2165,7 @@ module Run = struct
 			if not ctx.has_unbound then begin
 				with_timer "analyzer-ssa-apply" (fun () -> Ssa.apply ctx);
 				if config.const_propagation then with_timer "analyzer-const-propagation" (fun () -> ConstPropagation.apply ctx);
+				CodeMotion.apply ctx;
 				with_timer "analyzer-local-dce" (fun () -> LocalDce.apply ctx);
 			end;
 			if config.dot_debug then Debug.dot_debug ctx c cf;

+ 83 - 0
tests/optimization/src/TestJs.hx

@@ -307,6 +307,89 @@ class TestJs {
 		use(b);
 	}
 
+	@:js('
+		var a = TestJs.getInt();
+		var b = TestJs.getInt();
+		var x;
+		var tmp = a + b;
+		while(a != b) {
+			x = tmp;
+			TestJs["use"](x);
+		}
+	')
+	static function testCodeMotion1() {
+		var a = getInt();
+		var b = getInt();
+		var x;
+		while (a != b) {
+			x = a + b;
+			use(x);
+		}
+	}
+
+	@:js('
+		var a = TestJs.getInt();
+		var b = TestJs.getInt();
+		var x = 0;
+		while(a != b) {
+			x = a + x;
+			TestJs["use"](x);
+		}
+	')
+	static function testCodeMotion2() {
+		var a = getInt();
+		var b = getInt();
+		var x = 0;
+		while (a != b) {
+			x = a + x;
+			use(x);
+		}
+	}
+
+	@:js('
+		var a = TestJs.getInt();
+		var b = TestJs.getInt();
+		var x;
+		while(a != b) {
+			var tmp = a + b;
+			while(a != b) {
+				x = tmp;
+				TestJs["use"](x);
+			}
+		}
+	')
+	static function testCodeMotion3() {
+		var a = getInt();
+		var b = getInt();
+		var x;
+		while (a != b) {
+			while (a != b) {
+				x = a + b;
+				use(x);
+			}
+		}
+	}
+
+	@:js('
+		var a = TestJs.getInt();
+		var b = TestJs.getInt();
+		var x;
+		var tmp = a + b + b;
+		while(a != b) {
+			x = tmp;
+			TestJs["use"](x);
+		}
+	')
+	static function testCodeMotion4() {
+		var a = getInt();
+		var b = getInt();
+		var x;
+		while (a != b) {
+			x = a + b + b;
+			use(x);
+		}
+	}
+
 	static function getInt(?d:Dynamic) { return 1; }
 	static function call(d1:Dynamic, d2:Dynamic) { return d1; }
 	static function use<T>(t:T) { }