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

Improved global deadcode elimination #1681

Merged
merged 13 commits into from
Sep 25, 2024
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
requested at compile time (--enable with-js-error) or at startup (OCAMLRUNPARAM=b=1)
* Runtime: allow dynlink of precompiled js with separate compilation (#1676)
* Lib: Modify Typed_array API for compatibility with WebAssembly
* Compiler: improved global dead code elimination (#2206)


## Bug fixes
Expand Down
53 changes: 7 additions & 46 deletions compiler/lib/code.ml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ module Var : sig

module Map : Map.S with type key = t

module Hashtbl : Hashtbl.S with type key = t

module Tbl : sig
type key = t

Expand All @@ -108,14 +110,6 @@ module Var : sig
val fold : ('a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
end

module DataMap : sig
type ('a, 'b) t

val iter : ('a -> 'b -> unit) -> ('a, 'b) t -> unit

val fold : ('a -> 'b -> 'acc -> 'acc) -> ('a, 'b) t -> 'acc -> 'acc
end

type size = unit

val get : 'a t -> key -> 'a
Expand All @@ -124,12 +118,8 @@ module Var : sig

val make : size -> 'a -> 'a t

val make_map : size -> ('a, 'b) DataMap.t t

val make_set : size -> 'a DataSet.t t

val add_map : ('a, 'b) DataMap.t t -> key -> 'a -> 'b -> unit

val add_set : 'a DataSet.t t -> key -> 'a -> unit

val iter : (key -> 'a -> unit) -> 'a t -> unit
Expand Down Expand Up @@ -159,6 +149,8 @@ end = struct
let compare : t -> t -> int = compare

let equal (a : t) (b : t) = a = b

let hash x = x
end

include T
Expand Down Expand Up @@ -246,24 +238,6 @@ end = struct
| Many t -> Hashtbl.fold (fun k () acc -> f k acc) t acc
end

module DataMap = struct
type ('a, 'b) t =
| Empty
| One of 'a * 'b
| Many of ('a, 'b) Hashtbl.t

let iter f = function
| Empty -> ()
| One (a, b) -> f a b
| Many t -> Hashtbl.iter f t

let fold f t acc =
match t with
| Empty -> acc
| One (a, b) -> f a b acc
| Many t -> Hashtbl.fold f t acc
end

type key = T.t

type size = unit
Expand All @@ -276,18 +250,6 @@ end = struct

let make_set () = Array.make (count ()) DataSet.Empty

let make_map () = Array.make (count ()) DataMap.Empty

let add_map t x k v =
match t.(x) with
| DataMap.Empty -> t.(x) <- One (k, v)
| One (k', v') ->
let tbl = Hashtbl.create 0 in
Hashtbl.replace tbl k' v';
Hashtbl.replace tbl k v;
t.(x) <- Many tbl
| Many tbl -> Hashtbl.replace tbl k v

let add_set t x k =
match t.(x) with
| DataSet.Empty -> t.(x) <- One k
Expand All @@ -304,6 +266,8 @@ end = struct
done
end

module Hashtbl = Hashtbl.Make (T)

module ISet = struct
type t = BitSet.t

Expand Down Expand Up @@ -452,9 +416,7 @@ type prim_arg =
| Pv of Var.t
| Pc of constant

type special =
| Undefined
| Alias_prim of string
type special = Alias_prim of string

type mutability =
| Immutable
Expand Down Expand Up @@ -603,7 +565,6 @@ module Print = struct

let special f s =
match s with
| Undefined -> Format.fprintf f "undefined"
| Alias_prim s -> Format.fprintf f "alias %s" s

let expr f e =
Expand Down
18 changes: 3 additions & 15 deletions compiler/lib/code.mli
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ module Var : sig

module Map : Map.S with type key = t

module Hashtbl : Hashtbl.S with type key = t

module Tbl : sig
type key = t

Expand All @@ -99,14 +101,6 @@ module Var : sig
val fold : ('a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
end

module DataMap : sig
type ('a, 'b) t

val iter : ('a -> 'b -> unit) -> ('a, 'b) t -> unit

val fold : ('a -> 'b -> 'acc -> 'acc) -> ('a, 'b) t -> 'acc -> 'acc
end

type 'a t

type size = unit
Expand All @@ -117,12 +111,8 @@ module Var : sig

val make : size -> 'a -> 'a t

val make_map : size -> ('a, 'b) DataMap.t t

val make_set : size -> 'a DataSet.t t

val add_map : ('a, 'b) DataMap.t t -> key -> 'a -> 'b -> unit

val add_set : 'a DataSet.t t -> key -> 'a -> unit

val iter : (key -> 'a -> unit) -> 'a t -> unit
Expand Down Expand Up @@ -210,9 +200,7 @@ type prim_arg =
| Pv of Var.t
| Pc of constant

type special =
| Undefined
| Alias_prim of string
type special = Alias_prim of string

type mutability =
| Immutable
Expand Down
87 changes: 87 additions & 0 deletions compiler/lib/dgraph.ml
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,90 @@ let t3 = Timer.get t3 in
let f size g f = f' size g (fun ~update:_ v x -> f v x)
end
end

module type ACTION = sig
type t
end

module type DOMAIN = sig
type t

val equal : t -> t -> bool

val bot : t

val top : t

val join : t -> t -> t
end

module Solver
(N : sig
type t
end)
(NSet : ISet with type elt = N.t)
(NTbl : Tbl with type key = N.t)
(A : ACTION)
(D : DOMAIN) =
struct
type t =
{ domain : NSet.t
; iter_children : (N.t -> A.t -> unit) -> N.t -> unit
}

type queue =
{ queue : N.t Queue.t
; set : NSet.t
}

let is_empty st = Queue.is_empty st.queue

let pop st =
let x = Queue.pop st.queue in
NSet.add st.set x;
x

let push x st =
if NSet.mem st.set x
then (
Queue.push x st.queue;
NSet.remove st.set x)

let rec iterate g f ~state w =
if not (is_empty w)
then (
let dep = pop w in
if not (D.equal (NTbl.get state dep) D.bot)
then
g.iter_children
(fun target action ->
let a = NTbl.get state target in
if not (D.equal a D.top)
then
let b = D.join a (f ~state ~dep ~target ~action) in
if not (D.equal a b)
then (
NTbl.set state target b;
push target w))
dep;
iterate g f ~state w)

let rec traverse g to_visit lst x =
if NSet.mem to_visit x
then (
NSet.remove to_visit x;
g.iter_children (fun y _ -> traverse g to_visit lst y) x;
lst := x :: !lst)

let traverse_all g =
let lst = ref [] in
let to_visit = NSet.copy g.domain in
NSet.iter (fun x -> traverse g to_visit lst x) g.domain;
let queue = Queue.create () in
List.iter ~f:(fun x -> Queue.push x queue) !lst;
{ queue; set = to_visit }

let f ~state g f =
let w = traverse_all g in
iterate g f ~state w
end
36 changes: 36 additions & 0 deletions compiler/lib/dgraph.mli
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,39 @@ module Make_Imperative
-> D.t NTbl.t
end
end

module type ACTION = sig
type t
end

module type DOMAIN = sig
type t

val equal : t -> t -> bool

val bot : t

val top : t

val join : t -> t -> t
end

module Solver
(N : sig
type t
end)
(NSet : ISet with type elt = N.t)
(NTbl : Tbl with type key = N.t)
(A : ACTION)
(D : DOMAIN) : sig
type t =
{ domain : NSet.t
; iter_children : (N.t -> A.t -> unit) -> N.t -> unit
}

val f :
state:D.t NTbl.t
-> t
-> (state:D.t NTbl.t -> dep:N.t -> target:N.t -> action:A.t -> D.t)
-> unit
end
2 changes: 1 addition & 1 deletion compiler/lib/driver.ml
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ let full ~standalone ~wrap_with_fun ~profile ~link ~source_map formatter d p =
in
let deadcode_sentinal =
(* If deadcode is disabled, this field is just fresh variable *)
Code.Var.fresh_n "undef"
Code.Var.fresh_n "dummy"
in
let opt =
specialize_js_once
Expand Down
30 changes: 27 additions & 3 deletions compiler/lib/generate.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1031,10 +1031,32 @@ let throw_statement ctx cx k loc =
, loc )
]

let remove_unused_tail_args ctx exact trampolined args =
if exact && not trampolined
then
let has_unused_tail_args =
List.fold_left
~f:(fun _ x -> Var.equal x ctx.Ctx.deadcode_sentinal)
~init:false
args
in
if has_unused_tail_args
then
List.fold_right
~f:(fun x args ->
match args with
| [] when Var.equal x ctx.Ctx.deadcode_sentinal -> []
| _ -> x :: args)
~init:[]
args
else args
else args

let rec translate_expr ctx queue loc x e level : _ * J.statement_list =
match e with
| Apply { f; args; exact } ->
let trampolined = Var.Set.mem x ctx.Ctx.trampolined_calls in
let args = remove_unused_tail_args ctx exact trampolined args in
let args, prop, queue =
List.fold_right
~f:(fun x (args, prop, queue) ->
Expand Down Expand Up @@ -1090,16 +1112,18 @@ let rec translate_expr ctx queue loc x e level : _ * J.statement_list =
(st, loc) :: rem
| _ -> clo
in
let clo = J.EFun (None, J.fun_ (List.map args ~f:(fun v -> J.V v)) clo loc) in
let clo =
J.EFun
( None
, J.fun_ (List.map args ~f:(fun v -> J.V v)) (Js_simpl.function_body clo) loc )
in
(clo, (fst const_p, fv), queue), []
| Constant c ->
let js, instrs = constant ~ctx c level in
(js, const_p, queue), instrs
| Special (Alias_prim name) ->
let prim = Share.get_prim (runtime_fun ctx) name ctx.Ctx.share in
(prim, const_p, queue), []
| Special Undefined ->
(J.(EVar (ident (Utf8_string.of_string_exn "undefined"))), const_p, queue), []
| Prim (Extern "debugger", _) ->
let ins =
if Config.Flag.debugger () then J.Debugger_statement else J.Empty_statement
Expand Down
Loading