Skip to content

Commit dff6374

Browse files
committed
Upgrade exponentiation to unified operator
Also make its output to use ES7 exponentiation (`**`) operator. `**` is more concise, faster than `Math.pow()`, works well with all numeric types include bigint. We were already using it for bigint, now for int and float too.
1 parent 7a58f3b commit dff6374

26 files changed

+103
-27
lines changed

compiler/core/js_exp_make.ml

+7
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,7 @@ let unchecked_int32_minus ?comment e1 e2 : J.expression =
15331533
float_minus ?comment e1 e2
15341534

15351535
let float_div ?comment e1 e2 = bin ?comment Div e1 e2
1536+
let float_pow ?comment e1 e2 = bin ?comment Pow e1 e2
15361537
let float_notequal ?comment e1 e2 = bin ?comment NotEqEq e1 e2
15371538

15381539
let int32_asr ?comment e1 e2 : J.expression =
@@ -1604,6 +1605,12 @@ let int32_mul ?comment (e1 : J.expression) (e2 : J.expression) : J.expression =
16041605
let unchecked_int32_mul ?comment e1 e2 : J.expression =
16051606
{comment; expression_desc = Bin (Mul, e1, e2)}
16061607

1608+
let int32_pow ?comment (e1 : t) (e2 : t) : J.expression =
1609+
match (e1.expression_desc, e2.expression_desc) with
1610+
| Number (Int {i = i1}), Number (Int {i = i2}) ->
1611+
int ?comment (Ext_int.int32_pow i1 i2)
1612+
| _ -> {comment; expression_desc = Bin (Pow, e1, e2)}
1613+
16071614
let rec int32_bxor ?comment (e1 : t) (e2 : t) : J.expression =
16081615
match (e1.expression_desc, e2.expression_desc) with
16091616
| Number (Int {i = i1}), Number (Int {i = i2}) ->

compiler/core/js_exp_make.mli

+4
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ val int32_div : checked:bool -> ?comment:string -> t -> t -> t
251251

252252
val int32_mod : checked:bool -> ?comment:string -> t -> t -> t
253253

254+
val int32_pow : ?comment:string -> t -> t -> t
255+
254256
val int32_lsl : ?comment:string -> t -> t -> t
255257

256258
val int32_lsr : ?comment:string -> t -> t -> t
@@ -275,6 +277,8 @@ val float_notequal : ?comment:string -> t -> t -> t
275277

276278
val float_mod : ?comment:string -> t -> t -> t
277279

280+
val float_pow : ?comment:string -> t -> t -> t
281+
278282
val int_comp : Lam_compat.comparison -> ?comment:string -> t -> t -> t
279283

280284
val bool_comp : Lam_compat.comparison -> ?comment:string -> t -> t -> t

compiler/core/js_op.ml

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ type int_op =
105105
| Div
106106
(* x / y | 0 *)
107107
| Mod
108-
(* x % y *)
108+
(* x % y *)
109+
| Pow
109110

110111
(* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise_operators
111112
{[

compiler/core/js_op_util.ml

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ let op_prec (op : Js_op.binop) =
4040
| Band -> (7, 7, 7)
4141
| Lsl | Lsr | Asr -> (10, 10, 11)
4242
| Plus | Minus -> (11, 11, 12)
43-
| Mul | Div | Mod | Pow -> (12, 12, 13)
43+
| Mul | Div | Mod -> (12, 12, 13)
44+
| Pow -> (12, 14, 12)
4445

4546
let op_int_prec (op : Js_op.int_op) =
4647
match op with
@@ -50,6 +51,7 @@ let op_int_prec (op : Js_op.int_op) =
5051
| Lsl | Lsr | Asr -> (10, 10, 11)
5152
| Plus | Minus -> (11, 11, 12)
5253
| Mul | Div | Mod -> (12, 12, 13)
54+
| Pow -> (12, 14, 12)
5355

5456
let op_str (op : Js_op.binop) =
5557
match op with
@@ -89,6 +91,7 @@ let op_int_str (op : Js_op.int_op) =
8991
| Mul -> "*"
9092
| Div -> "/"
9193
| Mod -> "%"
94+
| Pow -> "**"
9295

9396
let str_of_used_stats x =
9497
match (x : Js_op.used_stats) with

compiler/core/lam_analysis.ml

+5-4
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ let rec no_side_effects (lam : Lam.t) : bool =
5959
(* bool primitives *)
6060
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
6161
(* int primitives *)
62-
| Pnegint | Paddint | Psubint | Pmulint | Pandint | Porint | Pxorint
63-
| Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder | Pintmin | Pintmax
62+
| Pnegint | Paddint | Psubint | Pmulint | Ppowint | Pandint | Porint
63+
| Pxorint | Plslint | Plsrint | Pasrint | Pintcomp _ | Pintorder | Pintmin
64+
| Pintmax
6465
(* float primitives *)
6566
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
66-
| Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder | Pfloatmin
67-
| Pfloatmax
67+
| Ppowfloat | Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder
68+
| Pfloatmin | Pfloatmax
6869
(* bigint primitives *)
6970
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Ppowbigint
7071
| Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint

compiler/core/lam_compile_primitive.ml

+8
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
250250
match args with
251251
| [e1; e2] -> E.bigint_mod ~checked:!Js_config.check_div_by_zero e1 e2
252252
| _ -> assert false)
253+
| Ppowint -> (
254+
match args with
255+
| [e1; e2] -> E.int32_pow e1 e2
256+
| _ -> assert false)
257+
| Ppowfloat -> (
258+
match args with
259+
| [e1; e2] -> E.float_pow e1 e2
260+
| _ -> assert false)
253261
| Ppowbigint -> (
254262
match args with
255263
| [e1; e2] -> E.bigint_op Pow e1 e2

compiler/core/lam_convert.ml

+2
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
251251
| Pmulint -> prim ~primitive:Pmulint ~args loc
252252
| Pdivint _is_safe (*FIXME*) -> prim ~primitive:Pdivint ~args loc
253253
| Pmodint _is_safe (*FIXME*) -> prim ~primitive:Pmodint ~args loc
254+
| Ppowint -> prim ~primitive:Ppowint ~args loc
254255
| Pandint -> prim ~primitive:Pandint ~args loc
255256
| Porint -> prim ~primitive:Porint ~args loc
256257
| Pxorint -> prim ~primitive:Pxorint ~args loc
@@ -283,6 +284,7 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
283284
| Pmulfloat -> prim ~primitive:Pmulfloat ~args loc
284285
| Pdivfloat -> prim ~primitive:Pdivfloat ~args loc
285286
| Pmodfloat -> prim ~primitive:Pmodfloat ~args loc
287+
| Ppowfloat -> prim ~primitive:Ppowfloat ~args loc
286288
| Pfloatorder -> prim ~primitive:Pfloatorder ~args loc
287289
| Pfloatmin -> prim ~primitive:Pfloatmin ~args loc
288290
| Pfloatmax -> prim ~primitive:Pfloatmax ~args loc

compiler/core/lam_primitive.ml

+6-4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type t =
7575
| Pmulint
7676
| Pdivint
7777
| Pmodint
78+
| Ppowint
7879
| Pandint
7980
| Porint
8081
| Pxorint
@@ -96,6 +97,7 @@ type t =
9697
| Pmulfloat
9798
| Pdivfloat
9899
| Pmodfloat
100+
| Ppowfloat
99101
| Pfloatcomp of Lam_compat.comparison
100102
| Pfloatorder
101103
| Pfloatmin
@@ -199,12 +201,12 @@ let eq_primitive_approx (lhs : t) (rhs : t) =
199201
(* bool primitives *)
200202
| Psequand | Psequor | Pnot | Pboolcomp _ | Pboolorder | Pboolmin | Pboolmax
201203
(* int primitives *)
202-
| Pisint | Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint | Pandint
203-
| Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintorder | Pintmin
204-
| Pintmax
204+
| Pisint | Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint | Ppowint
205+
| Pandint | Porint | Pxorint | Plslint | Plsrint | Pasrint | Pintorder
206+
| Pintmin | Pintmax
205207
(* float primitives *)
206208
| Pintoffloat | Pfloatofint | Pnegfloat | Paddfloat | Psubfloat | Pmulfloat
207-
| Pdivfloat | Pmodfloat | Pfloatorder | Pfloatmin | Pfloatmax
209+
| Pdivfloat | Pmodfloat | Ppowfloat | Pfloatorder | Pfloatmin | Pfloatmax
208210
(* bigint primitives *)
209211
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Pdivbigint | Pmodbigint
210212
| Ppowbigint | Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint

compiler/core/lam_primitive.mli

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ type t =
6969
| Pmulint
7070
| Pdivint
7171
| Pmodint
72+
| Ppowint
7273
| Pandint
7374
| Porint
7475
| Pxorint
@@ -90,6 +91,7 @@ type t =
9091
| Pmulfloat
9192
| Pdivfloat
9293
| Pmodfloat
94+
| Ppowfloat
9395
| Pfloatcomp of Lam_compat.comparison
9496
| Pfloatorder
9597
| Pfloatmin

compiler/core/lam_print.ml

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ let primitive ppf (prim : Lam_primitive.t) =
116116
| Pmulint -> fprintf ppf "*"
117117
| Pdivint -> fprintf ppf "/"
118118
| Pmodint -> fprintf ppf "mod"
119+
| Ppowint -> fprintf ppf "**"
119120
| Pandint -> fprintf ppf "and"
120121
| Porint -> fprintf ppf "or"
121122
| Pxorint -> fprintf ppf "xor"
@@ -141,6 +142,7 @@ let primitive ppf (prim : Lam_primitive.t) =
141142
| Pmulfloat -> fprintf ppf "*."
142143
| Pdivfloat -> fprintf ppf "/."
143144
| Pmodfloat -> fprintf ppf "mod"
145+
| Ppowfloat -> fprintf ppf "**"
144146
| Pfloatcomp Ceq -> fprintf ppf "==."
145147
| Pfloatcomp Cneq -> fprintf ppf "!=."
146148
| Pfloatcomp Clt -> fprintf ppf "<."

compiler/ext/ext_int.ml

+8
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,11 @@ let move = 0x1_0000_0000
3434
let int32_unsigned_to_int (n : int32) : int =
3535
let i = Int32.to_int n in
3636
if i < 0 then i + move else i
37+
38+
let rec int32_pow (a : int32) = function
39+
| 0l -> 1l
40+
| 1l -> a
41+
| n ->
42+
let b = int32_pow a (Int32.div n 2l) in
43+
let b = Int32.mul b b in
44+
Int32.mul b (if Int32.rem n 2l = 0l then 1l else a)

compiler/ext/ext_int.mli

+2
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ val int32_unsigned_to_int : int32 -> int
3333
works on 64 bit platform only
3434
given input as an uint32 and convert it io int64
3535
*)
36+
37+
val int32_pow : int32 -> int32 -> int32

compiler/ml/lambda.ml

+2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ type primitive =
220220
| Pmulint
221221
| Pdivint of is_safe
222222
| Pmodint of is_safe
223+
| Ppowint
223224
| Pandint
224225
| Porint
225226
| Pxorint
@@ -242,6 +243,7 @@ type primitive =
242243
| Psubfloat
243244
| Pmulfloat
244245
| Pdivfloat
246+
| Ppowfloat
245247
| Pfloatcomp of comparison
246248
| Pfloatorder
247249
| Pfloatmin

compiler/ml/lambda.mli

+2
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ type primitive =
183183
| Pmulint
184184
| Pdivint of is_safe
185185
| Pmodint of is_safe
186+
| Ppowint
186187
| Pandint
187188
| Porint
188189
| Pxorint
@@ -205,6 +206,7 @@ type primitive =
205206
| Psubfloat
206207
| Pmulfloat
207208
| Pdivfloat
209+
| Ppowfloat
208210
| Pfloatcomp of comparison
209211
| Pfloatorder
210212
| Pfloatmin

compiler/ml/printlambda.ml

+2
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ let primitive ppf = function
156156
| Pdivint Unsafe -> fprintf ppf "/u"
157157
| Pmodint Safe -> fprintf ppf "mod"
158158
| Pmodint Unsafe -> fprintf ppf "mod_unsafe"
159+
| Ppowint -> fprintf ppf "**"
159160
| Pandint -> fprintf ppf "and"
160161
| Porint -> fprintf ppf "or"
161162
| Pxorint -> fprintf ppf "xor"
@@ -182,6 +183,7 @@ let primitive ppf = function
182183
| Pmulfloat -> fprintf ppf "*."
183184
| Pdivfloat -> fprintf ppf "/."
184185
| Pmodfloat -> fprintf ppf "mod"
186+
| Ppowfloat -> fprintf ppf "**"
185187
| Pfloatcomp Ceq -> fprintf ppf "==."
186188
| Pfloatcomp Cneq -> fprintf ppf "!=."
187189
| Pfloatcomp Clt -> fprintf ppf "<."

compiler/ml/unified_ops.ml

+13
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,19 @@ let entries =
148148
string = None;
149149
};
150150
};
151+
{
152+
path = builtin "**";
153+
name = "%pow";
154+
form = Binary;
155+
specialization =
156+
{
157+
int = Ppowint;
158+
bool = None;
159+
float = Some Ppowfloat;
160+
bigint = Some Ppowbigint;
161+
string = None;
162+
};
163+
};
151164
|]
152165

153166
let index_by_path =

compiler/syntax/src/res_parens.ml

+7-5
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,14 @@ let binary_expr_operand ~is_lhs expr =
148148
else Nothing)
149149

150150
let sub_binary_expr_operand parent_operator child_operator =
151-
let prec_parent = ParsetreeViewer.operator_precedence parent_operator in
152-
let prec_child = ParsetreeViewer.operator_precedence child_operator in
151+
let open ParsetreeViewer in
152+
let prec_parent = operator_precedence parent_operator in
153+
let prec_child = operator_precedence child_operator in
154+
Printf.eprintf "parent %s, %d; child %s, %d" parent_operator prec_parent
155+
child_operator prec_child;
153156
prec_parent > prec_child
154-
|| prec_parent == prec_child
155-
&& not
156-
(ParsetreeViewer.flattenable_operators parent_operator child_operator)
157+
|| is_equality_operator parent_operator
158+
&& is_equality_operator child_operator
157159
|| (* a && b || c, add parens to (a && b) for readability, who knows the difference by heart… *)
158160
(parent_operator = "||" && child_operator = "&&")
159161

compiler/syntax/src/res_parsetree_viewer.mli

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ val is_unary_expression : Parsetree.expression -> bool
8181
val is_binary_operator : string -> bool
8282
val is_binary_expression : Parsetree.expression -> bool
8383
val is_rhs_binary_operator : string -> bool
84+
val is_equality_operator : string -> bool
8485

8586
val flattenable_operators : string -> string -> bool
8687

runtime/Pervasives.res

+1-3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ external \"-": ('a, 'a) => 'a = "%sub"
5050
external \"*": ('a, 'a) => 'a = "%mul"
5151
external \"/": ('a, 'a) => 'a = "%div"
5252
external mod: ('a, 'a) => 'a = "%mod"
53+
external \"**": ('a, 'a) => 'a = "%pow"
5354

5455
/* Comparisons */
5556
/* Note: Later comparisons will be converted to unified operations too */
@@ -113,9 +114,6 @@ external \"-.": (float, float) => float = "%subfloat"
113114
external \"*.": (float, float) => float = "%mulfloat"
114115
external \"/.": (float, float) => float = "%divfloat"
115116

116-
@deprecated("Use Core instead. This will be removed in v13") @val @scope("Math")
117-
external \"**": (float, float) => float = "pow"
118-
119117
@deprecated("Use Core instead. This will be removed in v13") @val @scope("Math")
120118
external exp: float => float = "exp"
121119

runtime/Pervasives_mini.res

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ external \"-": (int, int) => int = "%subint"
2929
external \"*": (int, int) => int = "%mulint"
3030
external \"/": (int, int) => int = "%divint"
3131
external mod: (int, int) => int = "%modint"
32+
external \"**": (int, int) => int = "%powint"
3233

3334
/* Comparisons */
3435
/* Note: Later comparisons will be converted to unified operations too */

tests/tests/src/b.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
function f(point) {
55
let y = point.y;
66
let x = point.x;
7-
return Math.pow(x * x + y * y, 2);
7+
return (x * x + y * y) ** 2;
88
}
99

1010
export {
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
// Generated by ReScript, PLEASE EDIT WITH CARE
22

33

4-
let a = Math.pow(2, Math.pow(3, 2));
4+
let a = 2 ** 3 ** 2;
55

6-
let b = Math.pow(2, Math.pow(-3, 2));
6+
let b = 2 ** (-3) ** 2;
77

8-
let c = Math.pow(Math.pow(2, 3), 2);
8+
let c = (2 ** 3) ** 2;
99

10-
let d = Math.pow(-2, 2);
10+
let d = (-2) ** 2;
1111

1212
export {
1313
a,
1414
b,
1515
c,
1616
d,
1717
}
18-
/* a Not a pure module */
18+
/* No side effect */

tests/tests/src/test_pervasive.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function a17(prim) {
187187
}
188188

189189
function a18(prim0, prim1) {
190-
return Math.pow(prim0, prim1);
190+
return prim0 ** prim1;
191191
}
192192

193193
let f = Pervasives.$at;

tests/tests/src/unified_ops_test.mjs

+9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ function case2(a, b) {
5151
return a + "test" + b;
5252
}
5353

54+
let pow1 = 4;
55+
56+
let pow2 = 2 ** 2;
57+
58+
let pow3 = 2n ** 2n;
59+
5460
let int = 3;
5561

5662
export {
@@ -69,5 +75,8 @@ export {
6975
rhsstring,
7076
case1,
7177
case2,
78+
pow1,
79+
pow2,
80+
pow3,
7281
}
7382
/* No side effect */

0 commit comments

Comments
 (0)