Procházet zdrojové kódy

Inline ctors improved handling of ignored exprs. (#11356)

This commit changes the captured argument of analyze_aliases from a
boolean into a three value enum.
What used to be captured = false is now IEHNotHandled.
What used to be captured = true is now split between IEHCaptured and
IEHIgnored.
Knowing that the value will be ignored allows if and try expressions to
not cancel the inlining of their bodies.
Mario Carbajal před 1 rokem
rodič
revize
01dde74b8a

+ 50 - 23
src/optimization/inlineConstructors.ml

@@ -107,6 +107,15 @@ and inline_object_field =
 	| IOFInlineVar of inline_var
 	| IOFNone
 
+(*
+	inline_expression_handled
+	Defines what will happen to the expression being analized by analyze_aliases
+*)
+and inline_expression_handled = 
+	| IEHCaptured (* The expression will be assigned to a variable *)
+	| IEHIgnored (* The result of the expression will not be used *)
+	| IEHNotHandled (* Cases that are not handled (usually leads to cancelling inlining *)
+
 let inline_constructors ctx original_e =
 	let inline_objs = ref IntMap.empty in
 	let vars = ref IntMap.empty in
@@ -259,7 +268,7 @@ let inline_constructors ctx original_e =
 
 		e: The expression to analyze
 	*)
-	let rec analyze_aliases (seen_ctors:tclass_field list) (captured:bool) (is_lvalue:bool) (e:texpr) : inline_var option =
+	let rec analyze_aliases (seen_ctors:tclass_field list) (captured:inline_expression_handled) (is_lvalue:bool) (e:texpr) : inline_var option =
 		let mk_io ?(has_untyped=false) (iok : inline_object_kind) (id:int) (expr:texpr) : inline_object =
 			let io = {
 				io_kind = iok;
@@ -297,8 +306,8 @@ let inline_constructors ctx original_e =
 			| _ -> None
 			end
 		in
-		let handle_field_case ?(captured=false) ?(is_lvalue=false) efield ethis fname validate_io : inline_object_field =
-			begin match analyze_aliases true ethis with
+		let handle_field_case ?(captured=IEHNotHandled) ?(is_lvalue=false) efield ethis fname validate_io : inline_object_field =
+			begin match analyze_aliases IEHCaptured ethis with
 			| Some({iv_state = IVSAliasing io} as iv) when validate_io io ->
 				begin match get_io_inline_method io fname with
 				| Some(c, tl, cf, tf)->
@@ -321,7 +330,7 @@ let inline_constructors ctx original_e =
 							warning ctx WConstructorInliningCancelled ("Constructor inlining cancelled because of use of uninitialized member field " ^ fname) ethis.epos;
 							raise Not_found
 						);
-						if not captured then cancel_iv fiv efield.epos;
+						if captured == IEHNotHandled then cancel_iv fiv efield.epos;
 						IOFInlineVar(fiv)
 					with Not_found ->
 						cancel_iv iv efield.epos;
@@ -343,7 +352,7 @@ let inline_constructors ctx original_e =
 		let handle_default_case e =
 			let old = !scoped_ivs in
 			scoped_ivs := [];
-			let f e = ignore(analyze_aliases false e) in
+			let f e = ignore(analyze_aliases IEHNotHandled e) in
 			Type.iter f e;
 			List.iter (fun iv -> iv.iv_closed <- true) !scoped_ivs;
 			scoped_ivs := old;
@@ -357,7 +366,7 @@ let inline_constructors ctx original_e =
 					| _ ->
 						let v = alloc_var VGenerated "arg" e.etype e.epos in
 						let decle = mk (TVar(v, Some e)) ctx.t.tvoid e.epos in
-						ignore(analyze_aliases true decle);
+						ignore(analyze_aliases IEHIgnored decle);
 						let mde = (Meta.InlineConstructorArgument (v.v_id, 0)), [], e.epos in
 						let e = mk (TMeta(mde, e)) e.etype e.epos in
 						loop (v::vs, e::es) el
@@ -369,7 +378,7 @@ let inline_constructors ctx original_e =
 		let handle_inline_object_case (io_id:int) (force_inline:bool) (e:texpr) =
 			match e.eexpr, e.etype with
 			| TNew({ cl_constructor = Some ({cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,tl,pl),_
-				when captured && not (List.memq cf seen_ctors) ->
+				when captured!=IEHNotHandled && not (List.memq cf seen_ctors) ->
 				begin
 					let argvs, pl = analyze_call_args pl in
 					let _, cname = c.cl_path in
@@ -395,12 +404,12 @@ let inline_constructors ctx original_e =
 					in loop c tl;
 					let iv = add v IVKLocal in
 					set_iv_alias iv io;
-					ignore(analyze_aliases_in_ctor cf true io.io_expr);
+					ignore(analyze_aliases_in_ctor cf IEHIgnored io.io_expr);
 					Some iv
 				end
 			| TNew({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some _} as cf)} as c,_,pl),_ when is_extern_ctor c cf ->
 				raise_typing_error "Extern constructor could not be inlined" e.epos;
-			| TObjectDecl fl, _ when captured && fl <> [] && List.for_all (fun((s,_,_),_) -> Lexer.is_valid_identifier s) fl ->
+			| TObjectDecl fl, _ when captured!=IEHNotHandled && fl <> [] && List.for_all (fun((s,_,_),_) -> Lexer.is_valid_identifier s) fl ->
 				let v = alloc_var VGenerated "inlobj" e.etype e.epos in
 				let ev = mk (TLocal v) v.v_type e.epos in
 				let el = List.map (fun ((s,_,_),e) ->
@@ -413,9 +422,9 @@ let inline_constructors ctx original_e =
 				List.iter (fun ((s,_,_),e) -> ignore(alloc_io_field io s e.etype v.v_pos)) fl;
 				let iv = add v IVKLocal in
 				set_iv_alias iv io;
-				List.iter (fun e -> ignore(analyze_aliases true e)) el;
+				List.iter (fun e -> ignore(analyze_aliases IEHIgnored e)) el;
 				Some iv
-			| TArrayDecl el, TInst(_, [elemtype]) when captured ->
+			| TArrayDecl el, TInst(_, [elemtype]) when captured!=IEHNotHandled ->
 				let len = List.length el in
 				let v = alloc_var VGenerated "inlarr" e.etype e.epos in
 				let ev = mk (TLocal v) v.v_type e.epos in
@@ -429,7 +438,7 @@ let inline_constructors ctx original_e =
 				for i = 0 to len-1 do ignore(alloc_io_field io (int_field_name i) elemtype v.v_pos) done;
 				let iv = add v IVKLocal in
 				set_iv_alias iv io;
-				List.iter (fun e -> ignore(analyze_aliases true e)) el;
+				List.iter (fun e -> ignore(analyze_aliases IEHIgnored e)) el;
 				Some iv
 			| _ ->
 				handle_default_case e
@@ -443,7 +452,7 @@ let inline_constructors ctx original_e =
 			handle_inline_object_case io_id false e
 		| TVar(v,None) -> ignore(add v IVKLocal); None
 		| TVar(v,Some rve) ->
-			begin match analyze_aliases true rve with
+			begin match analyze_aliases IEHCaptured rve with
 			| Some({iv_state = IVSAliasing(io)}) ->
 				let iv = add v IVKLocal in
 				set_iv_alias iv io;
@@ -453,15 +462,15 @@ let inline_constructors ctx original_e =
 		| TBinop(OpAssign, lve, rve) ->
 			begin match analyze_aliases_in_lvalue lve with
 			| Some({iv_state = IVSUnassigned} as iv) ->
-				begin match analyze_aliases true rve with
+				begin match analyze_aliases IEHCaptured rve with
 				| Some({iv_state = IVSAliasing(io)}) ->
 					scoped_ivs := iv :: !scoped_ivs;
 					set_iv_alias iv io
 				| _ -> cancel_iv iv lve.epos
 				end;
 				Some iv
-			| Some(iv) -> cancel_iv iv e.epos; ignore(analyze_aliases false rve); None
-			| _ -> ignore(analyze_aliases false rve); None
+			| Some(iv) -> cancel_iv iv e.epos; ignore(analyze_aliases IEHNotHandled rve); None
+			| _ -> ignore(analyze_aliases IEHNotHandled rve); None
 			end
 		| TField(ethis, fa) ->
 			handle_field_case_no_methods e ethis (field_name fa) (fun _ -> true)
@@ -471,19 +480,19 @@ let inline_constructors ctx original_e =
 			handle_field_case_no_methods e ethis (int_field_name i) validate_io
 		| TLocal(v) when v.v_id < 0 ->
 			let iv = get_iv v.v_id in
-			if iv.iv_closed || not captured then cancel_iv iv e.epos;
+			if iv.iv_closed || captured==IEHNotHandled then cancel_iv iv e.epos;
 			Some iv
 		| TBlock(el) ->
 			let rec loop = function
 				| [e] -> analyze_aliases captured e
-				| e::el -> ignore(analyze_aliases true e); loop (el)
+				| e::el -> ignore(analyze_aliases IEHIgnored e); loop (el)
 				| [] -> None
 			in loop el
 		| TMeta((Meta.InlineConstructorArgument (vid,_),_,_),_) ->
 			(* The contents have already been analyzed, so we must skip the wrapped expression *)
 			(try
 				let iv = get_iv vid in
-				if iv.iv_closed || not captured then cancel_iv iv e.epos;
+				if iv.iv_closed || captured==IEHNotHandled then cancel_iv iv e.epos;
 				Some(get_iv vid)
 			with Not_found -> None)
 		| TParenthesis e | TMeta(_,e) | TCast(e,None) ->
@@ -517,14 +526,32 @@ let inline_constructors ctx original_e =
 				end
 			| IOFInlineVar(iv) ->
 				cancel_iv iv e.epos;
-				List.iter (fun ca -> ignore(analyze_aliases false ca)) call_args;
+				List.iter (fun ca -> ignore(analyze_aliases IEHNotHandled ca)) call_args;
 				None
 			| IOFNone ->
-				List.iter (fun ca -> ignore(analyze_aliases false ca)) call_args;
+				List.iter (fun ca -> ignore(analyze_aliases IEHNotHandled ca)) call_args;
 				None
 			end
 		| TFunction tf ->
-			analyze_aliases true tf.tf_expr
+			let old = !scoped_ivs in
+			scoped_ivs := [];
+			ignore(analyze_aliases IEHIgnored tf.tf_expr);
+			List.iter (fun iv -> iv.iv_closed <- true) !scoped_ivs;
+			scoped_ivs := old;
+			None
+		| TWhile(condition, body, _)  ->
+			ignore(analyze_aliases IEHNotHandled condition);
+			ignore(analyze_aliases IEHIgnored body);
+			None
+		| TIf (e,e1,e2) when captured=IEHIgnored ->
+			ignore(analyze_aliases IEHNotHandled e);
+			ignore(analyze_aliases IEHIgnored e1);
+			(match e2 with None -> () | Some e -> ignore(analyze_aliases IEHIgnored e));
+			None
+		| TTry (e,catches) when captured==IEHIgnored ->
+			ignore(analyze_aliases IEHIgnored e);
+			List.iter (fun (_,e) -> ignore(analyze_aliases IEHIgnored e)) catches;
+			None
 		| _ ->
 			handle_default_case e
 	in
@@ -690,7 +717,7 @@ let inline_constructors ctx original_e =
 	in
 	if not (check_for_ctors original_e) then original_e else
 	let e = mark_ctors original_e in
-	ignore(analyze_aliases [] false false e);
+	ignore(analyze_aliases [] IEHNotHandled false e);
 	if IntMap.for_all (fun _ io -> io.io_cancelled) !inline_objs then begin
 		IntMap.iter (fun _ iv -> let v = iv.iv_var in if v.v_id < 0 then v.v_id <- -v.v_id ) !vars;
 		original_e

+ 15 - 0
tests/optimization/src/TestInlineConstructors.hx

@@ -19,6 +19,12 @@ class InlineClass {
 	}
 }
 
+class ExternInlineClass {
+	public var a = 1;
+	public extern inline function new() {
+	}
+}
+
 class InlineIterator {
 	public var i = 0;
 	public inline function new() {};
@@ -130,4 +136,13 @@ class TestInlineConstructors extends TestBase {
 		}
 		return acc;
 	}
+
+	static var condition = false;
+	static function testIgnoredValuesNotCancelling() {
+		var a = new ExternInlineClass();
+		if ( condition ) a else a;
+		while ( condition ) a;
+		try { a; } catch(_) { a; };
+		return a.a;
+	}
 }