ソースを参照

support `inline for` (#12432)

Simon Krajewski 6 日 前
コミット
dc26554bed

+ 26 - 24
src/typing/forLoop.ml

@@ -57,12 +57,13 @@ let optimize_for_loop_iterator ctx v e1 e2 p =
 
 type unroll_parameters = {
 	expression_weight : int;
+	force_unroll : bool;
 }
 
 module IterationKind = struct
 	type t_kind =
 		| IteratorIntConst of texpr * texpr * bool (* ascending? *)
-		| IteratorIntUnroll of int * int * bool * unroll_parameters
+		| IteratorIntUnroll of int * int * bool
 		| IteratorInt of texpr * texpr
 		| IteratorArrayDecl of texpr list
 		| IteratorArray
@@ -177,6 +178,8 @@ module IterationKind = struct
 	let map_unroll_params ctx unroll_params i = match unroll_params with
 		| None ->
 			None
+		| Some {force_unroll = true} ->
+			unroll_params
 		| Some unroll_params ->
 			let cost = i * unroll_params.expression_weight in
 			let max_cost = try
@@ -208,6 +211,12 @@ module IterationKind = struct
 					| (TMono _ | TDynamic _) -> dynamic_iterator e1;
 					| _ -> (IteratorIterator,e1,pt)
 		in
+		let cannot_force () = match unroll_params with
+			| Some {force_unroll = true} ->
+				display_error ctx.com "Could not force inlining on this loop" p
+			| _ ->
+				()
+		in
 		let try_forward_array_iterator () =
 			match follow e.etype with
 			| TAbstract ({ a_this = (TInst ({ cl_path = [],"Array" }, [_]) as array_type)} as abstr, params) ->
@@ -232,11 +241,12 @@ module IterationKind = struct
 					let diff = Int32.to_int (Int32.sub a b) in
 					begin match map_unroll_params ctx unroll_params (abs diff) with
 					| Some unroll_params ->
-						IteratorIntUnroll(Int32.to_int a,abs(diff),diff <= 0,unroll_params)
+						IteratorIntUnroll(Int32.to_int a,abs(diff),diff <= 0)
 					| None ->
 						IteratorIntConst(efrom,eto,diff <= 0)
 					end
 				| _ ->
+					cannot_force();
 					let eto = match follow eto.etype with
 						| TAbstract ({ a_path = ([],"Int") }, []) -> eto
 						| _ -> { eto with eexpr = TCast(eto, None); etype = ctx.t.tint }
@@ -252,8 +262,10 @@ module IterationKind = struct
 			(it,e,pt)
 		| _,TInst({ cl_path = [],"Array" },[pt])
 		| _,TInst({ cl_path = ["flash"],"Vector" },[pt]) ->
+			cannot_force();
 			IteratorArray,e,pt
 		| _,TAbstract({ a_impl = Some c },_) ->
+			cannot_force();
 			(try
 				let v_tmp = gen_local ctx e.etype e.epos in
 				let e_tmp = make_local v_tmp v_tmp.v_pos in
@@ -272,13 +284,16 @@ module IterationKind = struct
 				with Not_found -> check_iterator ())
 			)
 		| _, TAbstract _ ->
+			cannot_force();
 			(try try_forward_array_iterator ()
 			with Not_found -> check_iterator ())
 		| _,TInst ({ cl_kind = KGenericInstance ({ cl_path = ["haxe";"ds"],"GenericStack" },[pt]) } as c,[]) ->
+			cannot_force();
 			IteratorGenericStack c,e,pt
 		| _,(TMono _ | TDynamic _) ->
 			dynamic_iterator e
 		| _ ->
+			cannot_force();
 			check_iterator ()
 		in
 		{
@@ -326,7 +341,7 @@ module IterationKind = struct
 			mk (TBlock el) t_void p
 		in
 		match iterator.it_kind with
-		| IteratorIntUnroll(offset,length,ascending,unroll_params) ->
+		| IteratorIntUnroll(offset,length,ascending) ->
 			if not ascending then raise_typing_error "Cannot iterate backwards" p;
 			let rec unroll acc i =
 				if i = length then
@@ -439,7 +454,7 @@ module IterationKind = struct
 			mk (TBlock []) t_void p
 end
 
-let get_unroll_params ctx e2 =
+let get_unroll_params ctx e2 force_unroll =
 	let num_expr = ref 0 in
 	let rec loop e = match fst e with
 		| EContinue | EBreak ->
@@ -453,24 +468,7 @@ let get_unroll_params ctx e2 =
 		ignore(loop e2);
 		Some {
 			expression_weight = !num_expr;
-		}
-	with Exit ->
-		None
-
-let get_unroll_params_t ctx e2 =
-	let num_expr = ref 0 in
-	let rec loop e = match e.eexpr with
-		| TContinue | TBreak ->
-			raise Exit
-		| _ ->
-			incr num_expr;
-			Type.map_expr loop e
-	in
-	try
-		if ctx.com.display.dms_kind <> DMNone then raise Exit;
-		ignore(loop e2);
-		Some {
-			expression_weight = !num_expr;
+			force_unroll;
 		}
 	with Exit ->
 		None
@@ -490,9 +488,10 @@ let type_for_loop ctx handle_display ik e1 e2 p =
 		| None -> ()
 		| Some dk -> ignore(handle_display ctx (EConst(Ident i.v_name),i.v_pos) dk MGet (WithType.with_type i.v_type))
 	in
+	let force_unroll = Meta.has Meta.Inline ctx.f.meta in
 	match ik with
 	| IKNormal(i,pi,dko) ->
-		let iterator = IterationKind.of_texpr ctx e1 (get_unroll_params ctx e2) p in
+		let iterator = IterationKind.of_texpr ctx e1 (get_unroll_params ctx e2 force_unroll) p in
 		let i = add_local_with_origin ctx TVOForVariable i iterator.it_type pi in
 		let e2 = type_expr ctx e2 NoValue in
 		check_display (i,pi,dko);
@@ -503,7 +502,10 @@ let type_for_loop ctx handle_display ik e1 e2 p =
 		(match follow e1.etype with
 		| TDynamic _ | TMono _ ->
 			display_error ctx.com "You can't iterate on a Dynamic value, please specify KeyValueIterator or KeyValueIterable" e1.epos;
-		| _ -> ()
+		| _ ->
+			if force_unroll then
+				display_error ctx.com "Cannot force inlining on key => value loops" p;
+			()
 		);
 		let e1,pt = IterationKind.check_iterator ctx "keyValueIterator" e1 e1.epos in
 		let vtmp = gen_local ctx e1.etype e1.epos in

+ 5 - 0
src/typing/typer.ml

@@ -1356,6 +1356,7 @@ and type_array_comprehension ctx e with_type p =
 			| [] -> e,p
 			end
 		| EParenthesis e2 -> (EParenthesis (map_compr e2),p)
+		| EMeta(m,e2) -> (EMeta(m,map_compr e2), p)
 		| EBinop(OpArrow,a,b) ->
 			et := (ENew(make_ptp {tpackage=["haxe";"ds"];tname="Map";tparams=[];tsub=None} null_pos,[]),comprehension_pos);
 			(ECall ((efield (e_ref,"set"),p),[a;b]),p)
@@ -1536,6 +1537,8 @@ and type_meta ?(mode=MGet) ctx m e1 with_type p =
 			| ENew (t,el) ->
 				let e = type_new ctx t el with_type true p in
 				{e with eexpr = TMeta((Meta.Inline,[],null_pos),e)}
+			| EFor (it,e2) ->
+				ForLoop.type_for_loop ctx TyperDisplay.handle_display it e2 p
 			| _ ->
 				display_error ctx.com "Call or function expected after inline keyword" p;
 				e();
@@ -1801,6 +1804,8 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) =
 		type_object_decl ctx fl with_type p
 	| EArrayDecl [(EFor _,_) | (EWhile _,_) as e] ->
 		type_array_comprehension ctx e with_type p
+	| EArrayDecl [EMeta((Meta.Inline,[],_),(EFor _,_)),_ as e] -> (* awkward... *)
+		type_array_comprehension ctx e with_type p
 	| EArrayDecl ((EBinop(OpArrow,_,_),_) as e1 :: el) ->
 		type_map_declaration ctx e1 el with_type p
 	| EArrayDecl el ->

+ 33 - 0
tests/misc/projects/inline-for/Main.hx

@@ -0,0 +1,33 @@
+function main() {
+	var a = 1;
+	inline for (x in 0...a) {}
+	var a = [1, 2];
+	inline for (x in a) {}
+	var a:A = cast [1, 2];
+	inline for (x in a) {}
+	var a:A2 = cast [1, 2];
+	inline for (x in a) {}
+	var a = new haxe.ds.GenericStack();
+	inline for(x in a) {}
+	var a = [1 => 2];
+	inline for(x in a) {}
+	inline for (x => y in a) {}
+
+	// working cases from here
+	inline for (x in 0...3) {
+		trace(x);
+	}
+	inline for (x in [0, 1, 2]) {
+		trace(x);
+	}
+}
+
+abstract A(Array<Int>) {
+	public function hasNext() { return this.iterator().hasNext(); }
+	public function next() { return this.iterator().next(); }
+}
+
+@:forward
+abstract A2(Array<Int>) {}
+
+

+ 3 - 0
tests/misc/projects/inline-for/compile-fail.hxml

@@ -0,0 +1,3 @@
+--main Main
+--interp
+-D loop-unroll-max-cost=0

+ 7 - 0
tests/misc/projects/inline-for/compile-fail.hxml.stderr

@@ -0,0 +1,7 @@
+Main.hx:3: characters 2-28 : Could not force inlining on this loop
+Main.hx:5: characters 2-24 : Could not force inlining on this loop
+Main.hx:7: characters 2-24 : Could not force inlining on this loop
+Main.hx:9: characters 2-24 : Could not force inlining on this loop
+Main.hx:11: characters 2-23 : Could not force inlining on this loop
+Main.hx:13: characters 2-23 : Could not force inlining on this loop
+Main.hx:14: characters 2-29 : Cannot force inlining on key => value loops

+ 1 - 0
tests/optimization/run.hxml

@@ -1,6 +1,7 @@
 -p src
 -D analyzer-optimize
 -D analyzer-user-var-fusion
+-D loop-unroll-max-cost=0
 --each
 
 --main TestAnalyzer

+ 2 - 2
tests/optimization/src/TestLocalDce.hx

@@ -137,7 +137,7 @@ class TestLocalDce {
 	')
 	static function testLoopUnroll() {
 		var s = keep(1);
-		for (i in [0, 3, 4]) {
+		inline for (i in [0, 3, 4]) {
 			s += i * 2;
 		}
 		use(s);
@@ -148,7 +148,7 @@ class TestLocalDce {
 		var s = 0.0;
 		inline function foo(r)
 			return 2.0 + r;
-		for ( r in [0.0,1.0] )
+		inline for ( r in [0.0,1.0] )
 			s+=foo(r);
 		use(s);
 	}

+ 1 - 1
tests/optimization/src/issues/Issue10188.hx

@@ -26,7 +26,7 @@ class Issue10188 {
 		issues_Issue10188.use(_g);
 	')
 	static function testKevin() {
-		use([for(v in [V1]) if(has(v)) v.toString()]);
+		use([inline for(v in [V1]) if(has(v)) v.toString()]);
 	}
 
 

+ 1 - 1
tests/optimization/src/issues/Issue11800.hx

@@ -10,7 +10,7 @@ class Issue11800 {
 	static function test() {
 		static var a = 0;
 
-		for (i in 0...3) {
+		inline for (i in 0...3) {
 			switch i {
 				case n if (n < 2):
 					use(++a);