Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve inline constructor handling of 'if', 'try' and 'while' expressions. #11356

Merged
merged 1 commit into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
73 changes: 50 additions & 23 deletions src/optimization/inlineConstructors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)->
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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) ->
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -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) ->
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions tests/optimization/src/TestInlineConstructors.hx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {};
Expand Down Expand Up @@ -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;
}
}