Skip to content

Commit

Permalink
DCE cleanup 2024 (HaxeFoundation#11485)
Browse files Browse the repository at this point in the history
* remove m_if_feature, make DCE check the meta directly instead

also copy class @:ifFeature to field @:ifFeature

* add CfUsed, CfMaybeUsed, CUsed

Can't get rid of Meta.Used entirely yet because enums and abstracts don't have flags

* don't play silly meta merge games in the typeloader

instead play them in DCE because otherwise we won't deal with __init__ correctly

* update haxelib

it won't help
  • Loading branch information
Simn authored and 0b1kn00b committed Jan 25, 2024
1 parent 9ca3156 commit 153caa4
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/codegen/genxml.ml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ let rec follow_param t =
t

let gen_meta meta =
let meta = List.filter (fun (m,_,_) -> match m with Meta.Used | Meta.RealPath | Meta.Pure | Meta.HxbId -> false | _ -> true) meta in
let meta = List.filter (fun (m,_,_) -> match m with Meta.Used | Meta.RealPath | Meta.Pure -> false | _ -> true) meta in
match meta with
| [] -> []
| _ ->
Expand Down
100 changes: 36 additions & 64 deletions src/optimization/dce.ml
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ let mk_keep_meta pos =
*)
let rec keep_field dce cf c kind =
let is_static = kind = CfrStatic in
Meta.has_one_of keep_metas cf.cf_meta
|| has_class_field_flag cf CfUsed
Meta.has_one_of (Meta.Used :: keep_metas) cf.cf_meta
|| cf.cf_name = "__init__"
|| has_class_field_flag cf CfExtern
|| (not is_static && overrides_extern_field cf c)
Expand Down Expand Up @@ -154,7 +153,7 @@ let rec check_feature dce s =
List.iter (fun cfr ->
let (c, cf) = resolve_class_field_ref dce.com cfr in
mark_field dce c cf cfr.cfr_kind
) !l;
) l;
Hashtbl.remove dce.features s;
with Not_found ->
()
Expand All @@ -167,8 +166,8 @@ and check_and_add_feature dce s =
(* mark a field as kept *)
and mark_field dce c cf kind =
let add c' cf =
if not (has_class_field_flag cf CfUsed) then begin
add_class_field_flag cf CfUsed;
if not (Meta.has Meta.Used cf.cf_meta) then begin
cf.cf_meta <- (mk_used_meta cf.cf_pos) :: cf.cf_meta;
dce.added_fields <- (c',cf,kind) :: dce.added_fields;
dce.marked_fields <- cf :: dce.marked_fields;
check_feature dce (Printf.sprintf "%s.%s" (s_type_path c.cl_path) cf.cf_name);
Expand All @@ -186,11 +185,6 @@ and mark_field dce c cf kind =
| None -> ()
in
loop c
| CfrInit ->
begin match c.cl_init with
| Some cf -> add c cf
| None -> ()
end
| CfrStatic | CfrMember ->
let stat = kind = CfrStatic in
if not (PMap.mem cf.cf_name (if stat then c.cl_statics else c.cl_fields)) then begin
Expand All @@ -208,20 +202,20 @@ let rec update_marked_class_fields dce c =
let pop = push_class dce c in
(* mark all :?used fields as surely :used now *)
List.iter (fun cf ->
if has_class_field_flag cf CfMaybeUsed then mark_field dce c cf CfrStatic
if Meta.has Meta.MaybeUsed cf.cf_meta then mark_field dce c cf CfrStatic
) c.cl_ordered_statics;
List.iter (fun cf ->
if has_class_field_flag cf CfMaybeUsed then mark_field dce c cf CfrMember
if Meta.has Meta.MaybeUsed cf.cf_meta then mark_field dce c cf CfrMember
) c.cl_ordered_fields;
(* we always have to keep super classes and implemented interfaces *)
(match TClass.get_cl_init c with None -> () | Some init -> dce.follow_expr dce init);
(match c.cl_init with None -> () | Some init -> dce.follow_expr dce init);
List.iter (fun (c,_) -> mark_class dce c) c.cl_implements;
(match c.cl_super with None -> () | Some (csup,pl) -> mark_class dce csup);
pop()

(* mark a class as kept. If the class has fields marked as @:?keep, make sure to keep them *)
and mark_class dce c = if not (has_class_flag c CUsed) then begin
add_class_flag c CUsed;
and mark_class dce c = if not (Meta.has Meta.Used c.cl_meta) then begin
c.cl_meta <- (mk_used_meta c.cl_pos) :: c.cl_meta;
check_feature dce (Printf.sprintf "%s.*" (s_type_path c.cl_path));
update_marked_class_fields dce c;
end
Expand All @@ -244,8 +238,8 @@ and mark_t dce p t =
dce.t_stack <- t :: dce.t_stack;
begin match follow t with
| TInst({cl_kind = KTypeParameter ttp} as c,pl) ->
if not (has_class_flag c CUsed) then begin
add_class_flag c CUsed;
if not (Meta.has Meta.Used c.cl_meta) then begin
c.cl_meta <- (mk_used_meta c.cl_pos) :: c.cl_meta;
List.iter (mark_t dce p) (get_constraints ttp);
end;
List.iter (mark_t dce p) pl
Expand Down Expand Up @@ -294,10 +288,10 @@ let mark_dependent_fields dce csup n kind =
let cf = PMap.find n (if stat then c.cl_statics else c.cl_fields) in
(* if it's clear that the class is kept, the field has to be kept as well. This is also true for
extern interfaces because we cannot remove fields from them *)
if has_class_flag c CUsed || ((has_class_flag csup CInterface) && (has_class_flag csup CExtern)) then mark_field dce c cf kind
if Meta.has Meta.Used c.cl_meta || ((has_class_flag csup CInterface) && (has_class_flag csup CExtern)) then mark_field dce c cf kind
(* otherwise it might be kept if the class is kept later, so mark it as :?used *)
else if not (has_class_field_flag cf CfMaybeUsed) then begin
add_class_field_flag cf CfMaybeUsed;
else if not (Meta.has Meta.MaybeUsed cf.cf_meta) then begin
cf.cf_meta <- (Meta.MaybeUsed,[],cf.cf_pos) :: cf.cf_meta;
dce.marked_maybe_fields <- cf :: dce.marked_maybe_fields;
end
with Not_found ->
Expand Down Expand Up @@ -691,7 +685,7 @@ and expr dce e =
let fix_accessors com =
List.iter (fun mt -> match mt with
(* filter empty abstract implementation classes (issue #1885). *)
| TClassDecl({cl_kind = KAbstractImpl _} as c) when c.cl_ordered_statics = [] && c.cl_ordered_fields = [] && not (has_class_flag c CUsed) ->
| TClassDecl({cl_kind = KAbstractImpl _} as c) when c.cl_ordered_statics = [] && c.cl_ordered_fields = [] && not (Meta.has Meta.Used c.cl_meta) ->
add_class_flag c CExtern;
| TClassDecl({cl_kind = KAbstractImpl a} as c) when a.a_enum ->
let is_runtime_field cf =
Expand Down Expand Up @@ -722,44 +716,16 @@ let fix_accessors com =
| _ -> ()
) com.types

let extract_if_feature meta =
let rec loop = function
| [] ->
[]
| (Meta.IfFeature,el,_) :: _ ->
List.map (fun (e,p) -> match e with
| EConst (String(s,_)) -> s
| _ -> Error.raise_typing_error "String expected" p
) el
| _ :: l ->
loop l
in
loop meta

let collect_entry_points dce com =
let delayed = ref [] in
let check_feature cf_ref meta =
List.iter (fun s ->
try
let l = Hashtbl.find dce.features s in
l := cf_ref :: !l
with Not_found ->
Hashtbl.add dce.features s (ref [cf_ref])
) meta;
in
List.iter (fun t ->
let mt = t_infos t in
mt.mt_meta <- Meta.remove Meta.Used mt.mt_meta;
match t with
| TClassDecl c ->
remove_class_flag c CUsed;
let cl_if_feature = extract_if_feature c.cl_meta in
let keep_class = keep_whole_class dce c && (not (has_class_flag c CExtern) || (has_class_flag c CInterface)) in
let is_struct = dce.com.platform = Hl && Meta.has Meta.Struct c.cl_meta in
let loop kind cf =
let cf_ref = mk_class_field_ref c cf kind com.is_macro_context in
let cf_if_feature = extract_if_feature cf.cf_meta in
check_feature cf_ref (cl_if_feature @ cf_if_feature);
(* Have to delay mark_field so that we see all @:ifFeature *)
if keep_class || is_struct || keep_field dce cf c kind then delayed := (fun () -> mark_field dce c cf kind) :: !delayed
if keep_class || is_struct || keep_field dce cf c kind then mark_field dce c cf kind
in
List.iter (loop CfrStatic) c.cl_ordered_statics;
List.iter (loop CfrMember) c.cl_ordered_fields;
Expand All @@ -768,22 +734,21 @@ let collect_entry_points dce com =
| None -> ()
end;
begin match c.cl_init with
| Some cf when keep_class || Meta.has Meta.KeepInit c.cl_meta ->
loop CfrInit cf
| Some e when keep_class || Meta.has Meta.KeepInit c.cl_meta ->
(* create a fake field to deal with our internal logic (issue #3286) *)
let cf = mk_field "__init__" e.etype e.epos null_pos in
cf.cf_expr <- Some e;
loop CfrStatic cf
| _ ->
()
end;
| TEnumDecl en when keep_whole_enum dce en ->
en.e_meta <- Meta.remove Meta.Used en.e_meta;
delayed := (fun () ->
let pop = push_class dce {null_class with cl_module = en.e_module} in
mark_enum dce en;
pop()
) :: !delayed;
let pop = push_class dce {null_class with cl_module = en.e_module} in
mark_enum dce en;
pop()
| _ ->
()
) com.types;
List.iter (fun f -> f()) !delayed;
if dce.debug then begin
List.iter (fun (c,cf,_) -> match cf.cf_expr with
| None -> ()
Expand Down Expand Up @@ -875,8 +840,8 @@ let sweep dce com =
let inef cf = is_physical_field cf in
let has_non_extern_fields = List.exists inef c.cl_ordered_fields || List.exists inef c.cl_ordered_statics in
(* we keep a class if it was used or has a used field *)
if has_class_flag c CUsed || has_non_extern_fields then loop (mt :: acc) l else begin
(match TClass.get_cl_init c with
if Meta.has Meta.Used c.cl_meta || has_non_extern_fields then loop (mt :: acc) l else begin
(match c.cl_init with
| Some f when Meta.has Meta.KeepInit c.cl_meta ->
(* it means that we only need the __init__ block *)
add_class_flag c CExtern;
Expand Down Expand Up @@ -914,6 +879,12 @@ let run com main mode =
features = Hashtbl.create 0;
curclass = null_class;
} in
List.iter (fun m ->
List.iter (fun (s,v) ->
if Hashtbl.mem dce.features s then Hashtbl.replace dce.features s (v :: Hashtbl.find dce.features s)
else Hashtbl.add dce.features s [v]
) m.m_extra.m_if_feature;
) com.modules;

(* first step: get all entry points, which is the main method and all class methods which are marked with @:keep *)
collect_entry_points dce com;
Expand Down Expand Up @@ -957,4 +928,5 @@ let run com main mode =
) com.types;

(* cleanup added fields metadata - compatibility with compilation server *)
List.iter (fun cf -> remove_class_field_flag cf CfUsed) dce.marked_fields
List.iter (fun cf -> cf.cf_meta <- Meta.remove Meta.Used cf.cf_meta) dce.marked_fields;
List.iter (fun cf -> cf.cf_meta <- Meta.remove Meta.MaybeUsed cf.cf_meta) dce.marked_maybe_fields

0 comments on commit 153caa4

Please sign in to comment.