From 153caa46c93a3ba67a92f5da3546ed3b9ebbe16a Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Fri, 12 Jan 2024 08:26:33 +0100 Subject: [PATCH] DCE cleanup 2024 (#11485) * 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 --- src/codegen/genxml.ml | 2 +- src/optimization/dce.ml | 100 +++++++++++++++------------------------- 2 files changed, 37 insertions(+), 65 deletions(-) diff --git a/src/codegen/genxml.ml b/src/codegen/genxml.ml index 70ebc26efae..6b737f484f3 100644 --- a/src/codegen/genxml.ml +++ b/src/codegen/genxml.ml @@ -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 | [] -> [] | _ -> diff --git a/src/optimization/dce.ml b/src/optimization/dce.ml index 6d122f3fc82..70189e74be4 100644 --- a/src/optimization/dce.ml +++ b/src/optimization/dce.ml @@ -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) @@ -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 -> () @@ -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); @@ -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 @@ -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 @@ -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 @@ -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 -> @@ -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 = @@ -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; @@ -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 -> () @@ -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; @@ -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; @@ -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