Skip to content

Commit ab9ec3a

Browse files
authored
core: optimize binary operations codegen (#1526)
* core: optimize binary operations codegen * changelog entry
1 parent 7895c4c commit ab9ec3a

6 files changed

Lines changed: 80 additions & 110 deletions

File tree

Changes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Unreleased
2424
[#1459](https://github.com/melange-re/melange/pull/1459))
2525
- add `-cmi-file` for flag compatibility with upstream OCaml
2626
([#1488]((https://github.com/melange-re/melange/pull/1488)))
27+
- code generation: inline more binary operations
28+
([#1526](https://github.com/melange-re/melange/pull/1526))
2729

2830
5.1.0-53 2025-03-23
2931
---------------

jscomp/core/js_exp_make.ml

Lines changed: 68 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ let no_side_effect = Js_analyzer.no_side_effect_expression
4242
type t = J.expression
4343

4444
let make_expression ?loc ?comment desc =
45-
J.{ expression_desc = desc; comment; loc }
45+
{ J.expression_desc = desc; comment; loc }
4646

4747
(*
4848
[remove_pure_sub_exp x]
@@ -85,8 +85,7 @@ let call ?loc ?comment ~info e0 args : t =
8585
make_expression ?loc ?comment (Call { expr = e0; args; info })
8686

8787
(* TODO: optimization when es is known at compile time
88-
to be an array
89-
*)
88+
to be an array *)
9089
let flat_call ?loc ?comment e0 es : t =
9190
make_expression ?loc ?comment (FlatCall { expr = e0; args = es })
9291

@@ -172,8 +171,7 @@ let optional_not_nest_block e : J.expression =
172171
make_expression (Optional_block (e, true))
173172

174173
(* used in normal property
175-
like [e.length], no dependency introduced
176-
*)
174+
like [e.length], no dependency introduced *)
177175
let dot ?loc ?comment (e0 : t) (e1 : string) : t =
178176
make_expression ?loc ?comment
179177
(Static_index { expr = e0; field = e1; pos = None })
@@ -437,8 +435,50 @@ let string_index ?loc ?comment (e0 : t) (e1 : t) : t =
437435
make_expression ?loc ?comment (String_index { expr = e0; index = e1 })
438436
| _ -> make_expression ?loc ?comment (String_index { expr = e0; index = e1 })
439437

440-
let assign ?loc ?comment e0 e1 : t =
441-
make_expression ?loc ?comment (Bin { op = Eq; expr1 = e0; expr2 = e1 })
438+
let rec triple_equal ?loc ?comment (e0 : t) (e1 : t) : t =
439+
match (e0.expression_desc, e1.expression_desc) with
440+
| ( (Null | Undefined),
441+
( Char_of_int _ | Char_to_int _ | Bool _ | Number _ | Typeof _ | Fun _
442+
| Array _ | Caml_block _ ) )
443+
when no_side_effect e1 ->
444+
false_ (* TODO: rename it as [caml_false] *)
445+
| ( ( Char_of_int _ | Char_to_int _ | Bool _ | Number _ | Typeof _ | Fun _
446+
| Array _ | Caml_block _ ),
447+
(Null | Undefined) )
448+
when no_side_effect e0 ->
449+
false_
450+
| Char_to_int a, Char_to_int b -> triple_equal ?comment a b
451+
| Char_to_int a, Number (Int { i = _; c = Some v })
452+
| Number (Int { i = _; c = Some v }), Char_to_int a ->
453+
triple_equal ?comment a (str (String.make 1 v))
454+
| Unicode x, Unicode y -> bool (String.equal x y)
455+
| Number (Int { i = i0; _ }), Number (Int { i = i1; _ }) -> bool (i0 = i1)
456+
| Char_of_int a, Char_of_int b | Optional_block (a, _), Optional_block (b, _)
457+
->
458+
triple_equal ?comment a b
459+
| Undefined, Optional_block _
460+
| Optional_block _, Undefined
461+
| Null, Undefined
462+
| Undefined, Null ->
463+
false_
464+
| Null, Null | Undefined, Undefined -> true_
465+
| _ ->
466+
make_expression ?loc ?comment
467+
(Bin { op = EqEqEq; expr1 = e0; expr2 = e1 })
468+
469+
let bin ?loc ?comment (op : J.binop) (e0 : t) (e1 : t) : t =
470+
match (op, e0.expression_desc, e1.expression_desc) with
471+
| EqEqEq, _, _ -> triple_equal ?comment e0 e1
472+
| Ge, Length { expr = e; _ }, Number (Int { i = 0l; _ }) when no_side_effect e
473+
->
474+
true_ (* x.length >=0 | [x] is pure -> true*)
475+
| Gt, Length _, Number (Int { i = 0l; _ }) ->
476+
(* [e] is kept so no side effect check needed *)
477+
make_expression ?loc ?comment
478+
(Bin { op = NotEqEq; expr1 = e0; expr2 = e1 })
479+
| _ -> make_expression ?loc ?comment (Bin { op; expr1 = e0; expr2 = e1 })
480+
481+
let assign ?loc ?comment e0 e1 : t = bin ?loc ?comment Eq e0 e1
442482

443483
let assign_by_exp (e : t) index value : t =
444484
match e.expression_desc with
@@ -593,9 +633,7 @@ let float ?loc ?comment f : t =
593633
make_expression ?loc ?comment (Number (Float { f }))
594634

595635
let zero_float_lit : t = make_expression (Number (Float { f = "0." }))
596-
597-
let float_mod ?loc ?comment e1 e2 : J.expression =
598-
make_expression ?loc ?comment (Bin { op = Mod; expr1 = e1; expr2 = e2 })
636+
let float_mod ?loc ?comment e1 e2 : J.expression = bin ?loc ?comment Mod e1 e2
599637

600638
let str_equal (str0 : J.expression_desc) (str1 : J.expression_desc) =
601639
match (str0, str1) with
@@ -609,49 +647,6 @@ let str_equal (str0 : J.expression_desc) (str1 : J.expression_desc) =
609647
| Str _, Unicode _ | Unicode _, Str _ -> None
610648
| _ -> None
611649

612-
let rec triple_equal ?loc ?comment (e0 : t) (e1 : t) : t =
613-
match (e0.expression_desc, e1.expression_desc) with
614-
| ( (Null | Undefined),
615-
( Char_of_int _ | Char_to_int _ | Bool _ | Number _ | Typeof _ | Fun _
616-
| Array _ | Caml_block _ ) )
617-
when no_side_effect e1 ->
618-
false_ (* TODO: rename it as [caml_false] *)
619-
| ( ( Char_of_int _ | Char_to_int _ | Bool _ | Number _ | Typeof _ | Fun _
620-
| Array _ | Caml_block _ ),
621-
(Null | Undefined) )
622-
when no_side_effect e0 ->
623-
false_
624-
| Char_to_int a, Char_to_int b -> triple_equal ?comment a b
625-
| Char_to_int a, Number (Int { i = _; c = Some v })
626-
| Number (Int { i = _; c = Some v }), Char_to_int a ->
627-
triple_equal ?comment a (str (String.make 1 v))
628-
| Unicode x, Unicode y -> bool (String.equal x y)
629-
| Number (Int { i = i0; _ }), Number (Int { i = i1; _ }) -> bool (i0 = i1)
630-
| Char_of_int a, Char_of_int b | Optional_block (a, _), Optional_block (b, _)
631-
->
632-
triple_equal ?comment a b
633-
| Undefined, Optional_block _
634-
| Optional_block _, Undefined
635-
| Null, Undefined
636-
| Undefined, Null ->
637-
false_
638-
| Null, Null | Undefined, Undefined -> true_
639-
| _ ->
640-
make_expression ?loc ?comment
641-
(Bin { op = EqEqEq; expr1 = e0; expr2 = e1 })
642-
643-
let bin ?loc ?comment (op : J.binop) (e0 : t) (e1 : t) : t =
644-
match (op, e0.expression_desc, e1.expression_desc) with
645-
| EqEqEq, _, _ -> triple_equal ?comment e0 e1
646-
| Ge, Length { expr = e; _ }, Number (Int { i = 0l; _ }) when no_side_effect e
647-
->
648-
true_ (* x.length >=0 | [x] is pure -> true*)
649-
| Gt, Length _, Number (Int { i = 0l; _ }) ->
650-
(* [e] is kept so no side effect check needed *)
651-
make_expression ?loc ?comment
652-
(Bin { op = NotEqEq; expr1 = e0; expr2 = e1 })
653-
| _ -> make_expression ?loc ?comment (Bin { op; expr1 = e0; expr2 = e1 })
654-
655650
(* TODO: Constant folding, Google Closure will do that?,
656651
Even if Google Clsoure can do that, we will see how it interact with other
657652
optimizations
@@ -699,8 +694,7 @@ let and_ ?loc ?comment (e1 : t) (e2 : t) : t =
699694
} )
700695
when Js_analyzer.same_vident i j ->
701696
e2
702-
| _, _ ->
703-
make_expression ?loc ?comment (Bin { op = And; expr1 = e1; expr2 = e2 })
697+
| _, _ -> bin ?loc ?comment And e1 e2
704698

705699
let or_ ?loc ?comment (e1 : t) (e2 : t) =
706700
match (e1.expression_desc, e2.expression_desc) with
@@ -712,8 +706,7 @@ let or_ ?loc ?comment (e1 : t) (e2 : t) =
712706
Bin { op = Or; expr1 = l; expr2 = { expression_desc = Var j; _ } as r } )
713707
when Js_analyzer.same_vident i j ->
714708
{ e2 with expression_desc = Bin { op = Or; expr1 = r; expr2 = l } }
715-
| _, _ ->
716-
make_expression ?loc ?comment (Bin { op = Or; expr1 = e1; expr2 = e2 })
709+
| _, _ -> bin ?loc ?comment Or e1 e2
717710

718711
(* return a value of type boolean *)
719712
(* TODO:
@@ -829,18 +822,14 @@ let rec float_equal ?loc ?comment (e0 : t) (e1 : t) : t =
829822
| Number (Int { i = _; c = Some v }), Char_to_int a ->
830823
float_equal ?comment a (str (String.make 1 v))
831824
| Char_of_int a, Char_of_int b -> float_equal ?comment a b
832-
| _ ->
833-
make_expression ?loc ?comment
834-
(Bin { op = EqEqEq; expr1 = e0; expr2 = e1 })
825+
| _ -> bin ?loc ?comment EqEqEq e0 e1
835826

836827
let int_equal = float_equal
837828

838829
let string_equal ?loc ?comment (e0 : t) (e1 : t) : t =
839830
match str_equal e0.expression_desc e1.expression_desc with
840831
| Some b -> bool b
841-
| None ->
842-
make_expression ?loc ?comment
843-
(Bin { op = EqEqEq; expr1 = e0; expr2 = e1 })
832+
| None -> bin ?loc ?comment EqEqEq e0 e1
844833

845834
let is_type_number ?loc ?comment (e : t) : t =
846835
string_equal ?loc ?comment (typeof e) (str "number")
@@ -926,10 +915,9 @@ let rec int32_bor ?loc ?comment (e1 : J.expression) (e2 : J.expression) :
926915
},
927916
Number (Int { i = 0l; _ } | Uint 0l) ) ->
928917
int32_bor e1 e2
929-
| _ ->
930-
make_expression ?loc ?comment (Bin { op = Bor; expr1 = e1; expr2 = e2 })
918+
| _ -> bin ?loc ?comment Bor e1 e2
931919

932-
(* Arithmatic operations
920+
(* Arithmetic operations
933921
TODO: distinguish between int and float
934922
TODO: Note that we have to use Int64 to avoid integer overflow, this is fine
935923
since Js only have .
@@ -1099,9 +1087,7 @@ let rec int32_lsr ?loc ?comment (e1 : J.expression) (e2 : J.expression) :
10991087
},
11001088
Number (Int { i = 0l; _ } | Uint 0l) ) ->
11011089
int32_lsr ?comment e1 e2
1102-
| _, _ ->
1103-
make_expression ?loc ?comment
1104-
(Bin { op = Lsr; expr1 = e1; expr2 = e2 } (* uint32 *))
1090+
| _, _ -> bin ?loc ?comment Lsr e1 e2 (* uint32 *)
11051091

11061092
let to_uint32 ?loc ?comment (e : J.expression) : J.expression =
11071093
int32_lsr ?loc ?comment e zero_int_literal
@@ -1195,8 +1181,7 @@ let rec float_add ?loc ?comment (e1 : t) (e2 : t) =
11951181
expr2 = { expression_desc = Number (Int { i = k; _ }); _ };
11961182
},
11971183
Number (Int { i = j; _ }) ) ->
1198-
make_expression ?loc ?comment
1199-
(Bin { op = Plus; expr1 = a1; expr2 = int (Int32.add k j) })
1184+
bin ?loc ?comment Plus a1 (int (Int32.add k j))
12001185
(* bin ?loc ?comment Plus a1 (int (k + j)) *)
12011186
(* TODO remove commented code ?? *)
12021187
(* | Bin(Plus, a0 , ({expression_desc = Number (Int a1)} )), *)
@@ -1214,17 +1199,15 @@ let rec float_add ?loc ?comment (e1 : t) (e2 : t) =
12141199
(* | Number _, _ *)
12151200
(* -> *)
12161201
(* bin ?loc ?comment Plus e2 e1 *)
1217-
| _ ->
1218-
make_expression ?loc ?comment (Bin { op = Plus; expr1 = e1; expr2 = e2 })
1202+
| _ -> bin ?loc ?comment Plus e1 e2
12191203

12201204
(* bin ?loc ?comment Plus e1 e2 *)
12211205
(* associative is error prone due to overflow *)
12221206
and float_minus ?loc ?comment (e1 : t) (e2 : t) : t =
12231207
match (e1.expression_desc, e2.expression_desc) with
12241208
| Number (Int { i; _ }), Number (Int { i = j; _ }) ->
12251209
int ?comment (Int32.sub i j)
1226-
| _ ->
1227-
make_expression ?loc ?comment (Bin { op = Minus; expr1 = e1; expr2 = e2 })
1210+
| _ -> bin ?loc ?comment Minus e1 e2
12281211
(* bin ?loc ?comment Minus e1 e2 *)
12291212

12301213
let unchecked_int32_add ?loc ?comment e1 e2 = float_add ?loc ?comment e1 e2
@@ -1241,9 +1224,7 @@ let unchecked_int32_minus ?loc ?comment e1 e2 : J.expression =
12411224

12421225
let float_div ?loc ?comment e1 e2 = bin ?loc ?comment Div e1 e2
12431226
let float_notequal ?loc ?comment e1 e2 = bin ?loc ?comment NotEqEq e1 e2
1244-
1245-
let int32_asr ?loc ?comment e1 e2 : J.expression =
1246-
make_expression ?loc ?comment (Bin { op = Asr; expr1 = e1; expr2 = e2 })
1227+
let int32_asr ?loc ?comment e1 e2 : J.expression = bin ?loc ?comment Asr e1 e2
12471228

12481229
(** Division by zero is undefined behavior*)
12491230
let int32_div ~checked ?loc ?comment (e1 : t) (e2 : t) : t =
@@ -1262,14 +1243,12 @@ let int32_div ~checked ?loc ?comment (e1 : t) (e2 : t) : t =
12621243

12631244
let int32_mod ~checked ?loc ?comment e1 (e2 : t) : J.expression =
12641245
match e2.expression_desc with
1265-
| Number (Int { i; _ }) when i <> 0l ->
1266-
make_expression ?loc ?comment (Bin { op = Mod; expr1 = e1; expr2 = e2 })
1246+
| Number (Int { i; _ }) when i <> 0l -> bin ?loc ?comment Mod e1 e2
12671247
| _ ->
12681248
if checked then
12691249
runtime_call ~module_name:Js_runtime_modules.int32 ~fn_name:"mod_"
12701250
[ e1; e2 ]
1271-
else
1272-
make_expression ?loc ?comment (Bin { op = Mod; expr1 = e1; expr2 = e2 })
1251+
else bin ?loc ?comment Mod e1 e2
12731252

12741253
let float_mul ?loc ?comment e1 e2 = bin ?loc ?comment Mul e1 e2
12751254

@@ -1279,8 +1258,7 @@ let int32_lsl ?loc ?comment (e1 : J.expression) (e2 : J.expression) :
12791258
| ( { expression_desc = Number (Int { i = i0; _ } | Uint i0); _ },
12801259
{ expression_desc = Number (Int { i = i1; _ } | Uint i1); _ } ) ->
12811260
int ?comment (Int32.shift_left i0 (Int32.to_int i1))
1282-
| _ ->
1283-
make_expression ?loc ?comment (Bin { op = Lsl; expr1 = e1; expr2 = e2 })
1261+
| _ -> bin ?loc ?comment Lsl e1 e2
12841262

12851263
let is_pos_pow n =
12861264
let module M = struct
@@ -1320,7 +1298,7 @@ let int32_mul ?loc ?comment (e1 : J.expression) (e2 : J.expression) :
13201298
[ e1; e2 ]
13211299

13221300
let unchecked_int32_mul ?loc ?comment e1 e2 : J.expression =
1323-
make_expression ?loc ?comment (Bin { op = Mul; expr1 = e1; expr2 = e2 })
1301+
bin ?loc ?comment Mul e1 e2
13241302

13251303
let rec int32_bxor ?loc ?comment (e1 : t) (e2 : t) : J.expression =
13261304
match (e1.expression_desc, e2.expression_desc) with
@@ -1342,8 +1320,7 @@ let rec int32_bxor ?loc ?comment (e1 : t) (e2 : t) : J.expression =
13421320
},
13431321
_ ) ->
13441322
int32_bxor e1 e2
1345-
| _ ->
1346-
make_expression ?loc ?comment (Bin { op = Bxor; expr1 = e1; expr2 = e2 })
1323+
| _ -> bin ?loc ?comment Bxor e1 e2
13471324

13481325
let rec int32_band ?loc ?comment (e1 : J.expression) (e2 : J.expression) :
13491326
J.expression =
@@ -1359,8 +1336,7 @@ let rec int32_band ?loc ?comment (e1 : J.expression) (e2 : J.expression) :
13591336
{[ (-1 >>> 0 | 0 ) & 0xffffff ]}
13601337
*)
13611338
int32_band a e2
1362-
| _ ->
1363-
make_expression ?loc ?comment (Bin { op = Band; expr1 = e1; expr2 = e2 })
1339+
| _ -> bin ?loc ?comment Band e1 e2
13641340

13651341
(* let int32_bin ?loc ?comment op e1 e2 : J.expression = *)
13661342
(* make_expression ?loc ?comment (Int32_bin(op,e1, e2)) *)
@@ -1410,8 +1386,7 @@ let eq_null_undefined_boolean ?loc ?comment (a : t) (b : t) =
14101386
false_
14111387
| Null, Undefined | Undefined, Null -> false_
14121388
| Null, Null | Undefined, Undefined -> true_
1413-
| _ ->
1414-
make_expression ?loc ?comment (Bin { op = EqEqEq; expr1 = a; expr2 = b })
1389+
| _ -> bin ?loc ?comment EqEqEq a b
14151390

14161391
let neq_null_undefined_boolean ?loc ?comment (a : t) (b : t) =
14171392
match (a.expression_desc, b.expression_desc) with
@@ -1423,10 +1398,9 @@ let neq_null_undefined_boolean ?loc ?comment (a : t) (b : t) =
14231398
| Array _ | Caml_block _ ),
14241399
(Null | Undefined) ) ->
14251400
true_
1426-
| Null, Null | Undefined, Undefined -> false_
14271401
| Null, Undefined | Undefined, Null -> true_
1428-
| _ ->
1429-
make_expression ?loc ?comment (Bin { op = NotEqEq; expr1 = a; expr2 = b })
1402+
| Null, Null | Undefined, Undefined -> false_
1403+
| _ -> bin ?loc ?comment NotEqEq a b
14301404

14311405
(* TODO: in the future add a flag
14321406
to set globalThis

jscomp/test/dist/jscomp/test/bs_min_max_test.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/dist/jscomp/test/caml_compare_test.js

Lines changed: 2 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)