diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e0eb8c6f1..c9facbc69e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Replace ~date with ~day in Date.make\*. https://github.com/rescript-lang/rescript/pull/7324 - Remove `-bs-jsx-mode`. https://github.com/rescript-lang/rescript/pull/7327 - Drop Node.js version <20 support, as it is reaching End-of-Life. https://github.com/rescript-lang/rescript-compiler/pull/7354 +- Treat `int` multiplication as a normal int32 operation instead of using `Math.imul`. https://github.com/rescript-lang/rescript/pull/7358 #### :house: Internal diff --git a/compiler/core/js_exp_make.ml b/compiler/core/js_exp_make.ml index 1fa740e16c..7ab4cf29b6 100644 --- a/compiler/core/js_exp_make.ml +++ b/compiler/core/js_exp_make.ml @@ -1607,14 +1607,8 @@ let int32_mul ?comment (e1 : J.expression) (e2 : J.expression) : J.expression = | {expression_desc = Number (Int {i = i0}); _}, e -> let i = is_pos_pow i0 in if i >= 0 then int32_lsl e (small_int i) - else - call ?comment ~info:Js_call_info.builtin_runtime_call - (dot (js_global "Math") Literals.imul) - [e1; e2] - | _ -> - call ?comment ~info:Js_call_info.builtin_runtime_call - (dot (js_global "Math") Literals.imul) - [e1; e2] + else to_int32 (float_mul ?comment e1 e2) + | _ -> to_int32 (float_mul ?comment e1 e2) let unchecked_int32_mul ?comment e1 e2 : J.expression = {comment; expression_desc = Bin (Mul, e1, e2)} diff --git a/compiler/ext/literals.ml b/compiler/ext/literals.ml index 34163c0c6d..9c413e7603 100644 --- a/compiler/ext/literals.ml +++ b/compiler/ext/literals.ml @@ -50,8 +50,6 @@ let runtime = "runtime" (* runtime directory *) let stdlib = "stdlib" -let imul = "imul" (* signed int32 mul *) - let setter_suffix = "#=" let setter_suffix_len = String.length setter_suffix diff --git a/lib/es6/Primitive_hash.js b/lib/es6/Primitive_hash.js index 6807c3b44d..5a6688256a 100644 --- a/lib/es6/Primitive_hash.js +++ b/lib/es6/Primitive_hash.js @@ -36,7 +36,7 @@ function unsafe_pop(q) { RE_EXN_ID: "Assert_failure", _1: [ "Primitive_hash.res", - 70, + 73, 12 ], Error: new Error() diff --git a/lib/es6/Stdlib_Int.js b/lib/es6/Stdlib_Int.js index f8a2f4a351..873cbddf0a 100644 --- a/lib/es6/Stdlib_Int.js +++ b/lib/es6/Stdlib_Int.js @@ -46,7 +46,7 @@ function range(start, end, optionsOpt) { let range$2 = options.inclusive === true ? range$1 + 1 | 0 : range$1; length = Math.ceil(range$2 / abs(step)) | 0; } - return Stdlib_Array.fromInitializer(length, i => start + Math.imul(i, step) | 0); + return Stdlib_Array.fromInitializer(length, i => start + (i * step | 0) | 0); } function rangeWithOptions(start, end, options) { diff --git a/lib/js/Primitive_hash.js b/lib/js/Primitive_hash.js index 6ae6d31529..e501682ae1 100644 --- a/lib/js/Primitive_hash.js +++ b/lib/js/Primitive_hash.js @@ -36,7 +36,7 @@ function unsafe_pop(q) { RE_EXN_ID: "Assert_failure", _1: [ "Primitive_hash.res", - 70, + 73, 12 ], Error: new Error() diff --git a/lib/js/Stdlib_Int.js b/lib/js/Stdlib_Int.js index 44c2f7080d..8befa23d40 100644 --- a/lib/js/Stdlib_Int.js +++ b/lib/js/Stdlib_Int.js @@ -46,7 +46,7 @@ function range(start, end, optionsOpt) { let range$2 = options.inclusive === true ? range$1 + 1 | 0 : range$1; length = Math.ceil(range$2 / abs(step)) | 0; } - return Stdlib_Array.fromInitializer(length, i => start + Math.imul(i, step) | 0); + return Stdlib_Array.fromInitializer(length, i => start + (i * step | 0) | 0); } function rangeWithOptions(start, end, options) { diff --git a/runtime/Primitive_hash.res b/runtime/Primitive_hash.res index 0adc4ade6b..8cda31c764 100644 --- a/runtime/Primitive_hash.res +++ b/runtime/Primitive_hash.res @@ -29,6 +29,9 @@ module String = Primitive_string_extern @send external charCodeAt: (string, int) => int = "charCodeAt" +// Multiply int32 with C-style overflow behavior +external imul: (int, int) => int = "Math.imul" + type rec cell<'a> = { content: 'a, mutable next: option>, @@ -85,9 +88,9 @@ let rotl32 = (x: int, n) => lor(lsl(x, n), lsr(x, 32 - n)) let hash_mix_int = (h, d) => { let d = ref(d) - d.contents = d.contents * 0xcc9e2d51 + d.contents = imul(d.contents, 0xcc9e2d51) d.contents = rotl32(d.contents, 15) - d.contents = d.contents * 0x1b873593 + d.contents = imul(d.contents, 0x1b873593) let h = ref(lxor(h, d.contents)) h.contents = rotl32(h.contents, 13) h.contents + lsl(h.contents, 2) + 0xe6546b64 @@ -95,9 +98,9 @@ let hash_mix_int = (h, d) => { let hash_final_mix = h => { let h = ref(lxor(h, lsr(h, 16))) - h.contents = h.contents * 0x85ebca6b + h.contents = imul(h.contents, 0x85ebca6b) h.contents = lxor(h.contents, lsr(h.contents, 13)) - h.contents = h.contents * 0xc2b2ae35 + h.contents = imul(h.contents, 0xc2b2ae35) lxor(h.contents, lsr(h.contents, 16)) } diff --git a/tests/gentype_tests/typescript-react-example/src/Records.res.js b/tests/gentype_tests/typescript-react-example/src/Records.res.js index 66e2b276dc..5671e37021 100644 --- a/tests/gentype_tests/typescript-react-example/src/Records.res.js +++ b/tests/gentype_tests/typescript-react-example/src/Records.res.js @@ -6,7 +6,7 @@ import * as Belt_Option from "rescript/lib/es6/Belt_Option.js"; import * as Primitive_option from "rescript/lib/es6/Primitive_option.js"; function computeArea(param) { - return Math.imul(Math.imul(param.x, param.y), Belt_Option.mapWithDefault(param.z, 1, n => n)); + return (param.x * param.y | 0) * Belt_Option.mapWithDefault(param.z, 1, n => n) | 0; } function coord2d(x, y) { @@ -80,11 +80,11 @@ let someBusiness2 = { }; function computeArea3(o) { - return Math.imul(Math.imul(o.x, o.y), Belt_Option.mapWithDefault(Primitive_option.fromNullable(o.z), 1, n => n)); + return (o.x * o.y | 0) * Belt_Option.mapWithDefault(Primitive_option.fromNullable(o.z), 1, n => n) | 0; } function computeArea4(o) { - return Math.imul(Math.imul(o.x, o.y), Belt_Option.mapWithDefault(o.z, 1, n => n)); + return (o.x * o.y | 0) * Belt_Option.mapWithDefault(o.z, 1, n => n) | 0; } function testMyRec(x) { diff --git a/tests/gentype_tests/typescript-react-example/src/nested/Tuples.res.js b/tests/gentype_tests/typescript-react-example/src/nested/Tuples.res.js index cf774aaa10..df2b77e2f4 100644 --- a/tests/gentype_tests/typescript-react-example/src/nested/Tuples.res.js +++ b/tests/gentype_tests/typescript-react-example/src/nested/Tuples.res.js @@ -7,15 +7,15 @@ function testTuple(param) { } function computeArea(param) { - return Math.imul(Math.imul(param[0], param[1]), Belt_Option.mapWithDefault(param[2], 1, n => n)); + return (param[0] * param[1] | 0) * Belt_Option.mapWithDefault(param[2], 1, n => n) | 0; } function computeAreaWithIdent(param) { - return Math.imul(Math.imul(param[0], param[1]), Belt_Option.mapWithDefault(param[2], 1, n => n)); + return (param[0] * param[1] | 0) * Belt_Option.mapWithDefault(param[2], 1, n => n) | 0; } function computeAreaNoConverters(param) { - return Math.imul(param[0], param[1]); + return param[0] * param[1] | 0; } function coord2d(x, y) { diff --git a/tests/tests/src/a_scope_bug.mjs b/tests/tests/src/a_scope_bug.mjs index 157799023c..aaf3201c52 100644 --- a/tests/tests/src/a_scope_bug.mjs +++ b/tests/tests/src/a_scope_bug.mjs @@ -4,7 +4,7 @@ function odd(_z) { while (true) { let z = _z; - let even = Math.imul(z, z); + let even = z * z | 0; let a = (even + 4 | 0) + even | 0; console.log(a.toString()); _z = 32; diff --git a/tests/tests/src/bdd.mjs b/tests/tests/src/bdd.mjs index 63e0a8bcdb..794ee5b1d2 100644 --- a/tests/tests/src/bdd.mjs +++ b/tests/tests/src/bdd.mjs @@ -348,7 +348,7 @@ let seed = { }; function random() { - seed.contents = Math.imul(seed.contents, 25173) + 17431 | 0; + seed.contents = (seed.contents * 25173 | 0) + 17431 | 0; return (seed.contents & 1) > 0; } diff --git a/tests/tests/src/belt_list_test.mjs b/tests/tests/src/belt_list_test.mjs index d31ec27bb5..a3c5c28f36 100644 --- a/tests/tests/src/belt_list_test.mjs +++ b/tests/tests/src/belt_list_test.mjs @@ -29,8 +29,8 @@ function sum2(xs, ys) { Mocha.describe("Belt_list_test", () => { Mocha.test("makeBy", () => { - let u = Belt_List.makeBy(5, i => Math.imul(i, i)); - let f = i => Test_utils.eq("File \"belt_list_test.res\", line 23, characters 20-27", Belt_List.getExn(u, i), Math.imul(i, i)); + let u = Belt_List.makeBy(5, i => i * i | 0); + let f = i => Test_utils.eq("File \"belt_list_test.res\", line 23, characters 20-27", Belt_List.getExn(u, i), i * i | 0); for (let i = 0; i <= 4; ++i) { f(i); } diff --git a/tests/tests/src/core/Core_ArrayTests.mjs b/tests/tests/src/core/Core_ArrayTests.mjs index 88de742280..08371223f5 100644 --- a/tests/tests/src/core/Core_ArrayTests.mjs +++ b/tests/tests/src/core/Core_ArrayTests.mjs @@ -257,7 +257,7 @@ Test.run([ 6 ], n => { if (n % 2 === 0) { - return Math.imul(n, n); + return n * n | 0; } }), eq, [ @@ -293,7 +293,7 @@ Test.run([ "filterMap - empty" ], Stdlib_Array.filterMap([], n => { if (n % 2 === 0) { - return Math.imul(n, n); + return n * n | 0; } }), eq, []); @@ -406,7 +406,7 @@ Test.run([ "findMap - empty" ], Stdlib_Array.findMap([], n => { if (n % 2 === 0) { - return Math.imul(n, n); + return n * n | 0; } }), eq, undefined); diff --git a/tests/tests/src/core/Core_ResultTests.mjs b/tests/tests/src/core/Core_ResultTests.mjs index 6bb3154f2a..e47f4dcdca 100644 --- a/tests/tests/src/core/Core_ResultTests.mjs +++ b/tests/tests/src/core/Core_ResultTests.mjs @@ -63,7 +63,7 @@ Test.run([ ], Stdlib_Result.mapError({ TAG: "Ok", _0: 5 -}, i => Math.imul(i, 3)), eq, { +}, i => i * 3 | 0), eq, { TAG: "Ok", _0: 5 }); @@ -79,7 +79,7 @@ Test.run([ ], Stdlib_Result.mapError({ TAG: "Error", _0: 5 -}, i => Math.imul(i, 3)), eq, { +}, i => i * 3 | 0), eq, { TAG: "Error", _0: 15 }); diff --git a/tests/tests/src/dollar_escape_test.mjs b/tests/tests/src/dollar_escape_test.mjs index 8257d45abb..98ec15d6e7 100644 --- a/tests/tests/src/dollar_escape_test.mjs +++ b/tests/tests/src/dollar_escape_test.mjs @@ -32,7 +32,7 @@ function $$(x, y) { let v = 3; function $$$plus(x, y) { - return Math.imul(x, y); + return x * y | 0; } let u = 3; diff --git a/tests/tests/src/ext_pervasives_test.mjs b/tests/tests/src/ext_pervasives_test.mjs index f15c564ef2..e1836c3740 100644 --- a/tests/tests/src/ext_pervasives_test.mjs +++ b/tests/tests/src/ext_pervasives_test.mjs @@ -16,7 +16,7 @@ function $$finally(v, action, f) { function hash_variant(s) { let accu = 0; for (let i = 0, i_finish = s.length; i < i_finish; ++i) { - accu = Math.imul(223, accu) + s.codePointAt(i) | 0; + accu = (223 * accu | 0) + s.codePointAt(i) | 0; } accu = accu & 2147483647; if (accu > 1073741823) { diff --git a/tests/tests/src/ffi_arity_test.mjs b/tests/tests/src/ffi_arity_test.mjs index 155e3928f7..85dffafa2e 100644 --- a/tests/tests/src/ffi_arity_test.mjs +++ b/tests/tests/src/ffi_arity_test.mjs @@ -4,7 +4,7 @@ import * as Mt from "./mt.mjs"; function f(v) { if (v % 2 === 0) { - return v => Math.imul(v, v); + return v => v * v | 0; } else { return v => v + v | 0; } diff --git a/tests/tests/src/for_loop_test.mjs b/tests/tests/src/for_loop_test.mjs index 773daf9f45..d5e5c3602f 100644 --- a/tests/tests/src/for_loop_test.mjs +++ b/tests/tests/src/for_loop_test.mjs @@ -40,7 +40,7 @@ function for_5(x, u) { }; let arr = Belt_Array.map(x, param => (() => {})); for (let i = 0, i_finish = x.length; i < i_finish; ++i) { - let k = Math.imul((u << 1), u); + let k = (u << 1) * u | 0; arr[i] = () => { v.contents = v.contents + k | 0; }; @@ -68,7 +68,7 @@ function for_6(x, u) { contents: 0 }; for (let i = 0, i_finish = x.length; i < i_finish; ++i) { - let k = Math.imul((u << 1), u); + let k = (u << 1) * u | 0; let h = (v5.contents << 1); v2.contents = v2.contents + 1 | 0; arr[i] = () => { @@ -93,7 +93,7 @@ function for_7() { let arr = Belt_Array.make(21, () => {}); for (let i = 0; i <= 6; ++i) { for (let j = 0; j <= 2; ++j) { - arr[Math.imul(i, 3) + j | 0] = () => { + arr[(i * 3 | 0) + j | 0] = () => { v.contents = (v.contents + i | 0) + j | 0; }; } @@ -111,7 +111,7 @@ function for_8() { let k = (i << 1); for (let j = 0; j <= 2; ++j) { let h = i + j | 0; - arr[Math.imul(i, 3) + j | 0] = () => { + arr[(i * 3 | 0) + j | 0] = () => { v.contents = (((v.contents + i | 0) + j | 0) + h | 0) + k | 0; }; } diff --git a/tests/tests/src/gpr_1822_test.mjs b/tests/tests/src/gpr_1822_test.mjs index 9706a4d490..053f75f08a 100644 --- a/tests/tests/src/gpr_1822_test.mjs +++ b/tests/tests/src/gpr_1822_test.mjs @@ -32,7 +32,7 @@ let myShape = { let area; -area = myShape.TAG === "Circle" ? 100 * 3.14 : Math.imul(10, myShape._1); +area = myShape.TAG === "Circle" ? 100 * 3.14 : 10 * myShape._1 | 0; eq("File \"gpr_1822_test.res\", line 23, characters 3-10", area, 314); diff --git a/tests/tests/src/gpr_2413_test.mjs b/tests/tests/src/gpr_2413_test.mjs index 1bfc18d21a..42a0ec1a93 100644 --- a/tests/tests/src/gpr_2413_test.mjs +++ b/tests/tests/src/gpr_2413_test.mjs @@ -16,7 +16,7 @@ function f(x) { break; } let a$3 = x._0._0; - return Math.imul(a$3, a$3); + return a$3 * a$3 | 0; } function ff(c) { diff --git a/tests/tests/src/gpr_3536_test.mjs b/tests/tests/src/gpr_3536_test.mjs index 052dc887ae..9aaf6cf88d 100644 --- a/tests/tests/src/gpr_3536_test.mjs +++ b/tests/tests/src/gpr_3536_test.mjs @@ -22,7 +22,7 @@ function xx(obj, a0, a1, a2, a3, a4, a5) { eq("File \"gpr_3536_test.res\", line 18, characters 12-19", 5, 5); -eq("File \"gpr_3536_test.res\", line 20, characters 12-19", xx(3, (prim0, prim1) => prim0 - prim1 | 0, 2, (prim0, prim1) => prim0 + prim1 | 0, 4, (prim0, prim1) => Math.imul(prim0, prim1), 3), 11); +eq("File \"gpr_3536_test.res\", line 20, characters 12-19", xx(3, (prim0, prim1) => prim0 - prim1 | 0, 2, (prim0, prim1) => prim0 + prim1 | 0, 4, (prim0, prim1) => prim0 * prim1 | 0, 3), 11); Mt.from_pair_suites("Gpr_3536_test", suites.contents); diff --git a/tests/tests/src/int_overflow_test.mjs b/tests/tests/src/int_overflow_test.mjs index 192ca3ca25..5ab54d1060 100644 --- a/tests/tests/src/int_overflow_test.mjs +++ b/tests/tests/src/int_overflow_test.mjs @@ -5,7 +5,7 @@ import * as Mt from "./mt.mjs"; function hash_variant(s) { let accu = 0; for (let i = 0, i_finish = s.length; i < i_finish; ++i) { - accu = Math.imul(223, accu) + s.codePointAt(i) & 2147483647; + accu = (223 * accu | 0) + s.codePointAt(i) & 2147483647; } if (accu > 1073741823) { return accu - -2147483648 | 0; @@ -17,7 +17,7 @@ function hash_variant(s) { function hash_variant2(s) { let accu = 0; for (let i = 0, i_finish = s.length; i < i_finish; ++i) { - accu = Math.imul(223, accu) + s.codePointAt(i) | 0; + accu = (223 * accu | 0) + s.codePointAt(i) | 0; } accu = accu & 2147483647; if (accu > 1073741823) { diff --git a/tests/tests/src/mario_game.mjs b/tests/tests/src/mario_game.mjs index 07016a05be..ab8da6cb66 100644 --- a/tests/tests/src/mario_game.mjs +++ b/tests/tests/src/mario_game.mjs @@ -1999,7 +1999,7 @@ function process_collision(dir, c1, c2, state) { evolve_enemy(o1.dir, typ, s2, o2, context) ]; } - let score = Math.imul(100, state.multiplier); + let score = 100 * state.multiplier | 0; update_score(state, score); o2.score = score; state.multiplier = (state.multiplier << 1); diff --git a/tests/tests/src/miss_colon_test.mjs b/tests/tests/src/miss_colon_test.mjs index 1a96a1dd91..33724b26e8 100644 --- a/tests/tests/src/miss_colon_test.mjs +++ b/tests/tests/src/miss_colon_test.mjs @@ -55,7 +55,7 @@ function $star$colon(_f, _g) { if (g.TAG === "Int") { return { TAG: "Int", - _0: Math.imul(n, g._0) + _0: n * g._0 | 0 }; } if (n === 0) { diff --git a/tests/tests/src/recursive_module_test.mjs b/tests/tests/src/recursive_module_test.mjs index 1d2b18e5f6..e713398349 100644 --- a/tests/tests/src/recursive_module_test.mjs +++ b/tests/tests/src/recursive_module_test.mjs @@ -69,7 +69,7 @@ function fact(n) { if (n <= 1) { return 1; } else { - return Math.imul(n, M.fact(n - 1 | 0)); + return n * M.fact(n - 1 | 0) | 0; } } diff --git a/tests/tests/src/recursive_records_test.mjs b/tests/tests/src/recursive_records_test.mjs index 0505ade90e..a223c082c9 100644 --- a/tests/tests/src/recursive_records_test.mjs +++ b/tests/tests/src/recursive_records_test.mjs @@ -25,7 +25,7 @@ rec_cell.next = rec_cell; function f0(x) { let rec_cell = {}; Primitive_object.updateDummy(rec_cell, { - content: Math.imul(x, x) - 6 | 0, + content: (x * x | 0) - 6 | 0, next: rec_cell }); return rec_cell; @@ -49,7 +49,7 @@ function f2(x) { let rec_cell2 = {}; Primitive_object.updateDummy(rec_cell2, { TAG: "Cons", - content: Math.imul(x, x) - 6 | 0, + content: (x * x | 0) - 6 | 0, next: rec_cell2 }); return rec_cell2; @@ -93,7 +93,7 @@ rec_cell3.tl = rec_cell3; function f3(x) { let rec_cell3 = {}; Primitive_object.updateDummy(rec_cell3, { - hd: Math.imul(x, x) - 6 | 0, + hd: (x * x | 0) - 6 | 0, tl: rec_cell3 }); return rec_cell3; diff --git a/tests/tests/src/tagged_template_test.mjs b/tests/tests/src/tagged_template_test.mjs index 2dbaaacc24..f3f3b66558 100644 --- a/tests/tests/src/tagged_template_test.mjs +++ b/tests/tests/src/tagged_template_test.mjs @@ -26,7 +26,7 @@ function foo(strings, values) { let res = ""; let valueCount = values.length; for (let i = 0; i < valueCount; ++i) { - res = res + Primitive_array.get(strings, i) + Math.imul(Primitive_array.get(values, i), 10).toString(); + res = res + Primitive_array.get(strings, i) + (Primitive_array.get(values, i) * 10 | 0).toString(); } return res + Primitive_array.get(strings, valueCount); } diff --git a/tests/tests/src/test_export2.mjs b/tests/tests/src/test_export2.mjs index d3b61d62a5..e8a67959eb 100644 --- a/tests/tests/src/test_export2.mjs +++ b/tests/tests/src/test_export2.mjs @@ -2,7 +2,7 @@ function f(x, y) { - return Math.imul(x, y); + return x * y | 0; } export { diff --git a/tests/tests/src/test_for_loop.mjs b/tests/tests/src/test_for_loop.mjs index c0aa40b8b7..b82b3d3f73 100644 --- a/tests/tests/src/test_for_loop.mjs +++ b/tests/tests/src/test_for_loop.mjs @@ -51,7 +51,7 @@ function for_5(x, u) { }; let arr = Belt_Array.map(x, param => (() => {})); for (let i = 0, i_finish = x.length; i <= i_finish; ++i) { - let k = Math.imul((u << 1), u); + let k = (u << 1) * u | 0; arr[i] = () => { v.contents = v.contents + k | 0; }; @@ -78,7 +78,7 @@ function for_6(x, u) { contents: 0 }; for (let i = 0, i_finish = x.length; i <= i_finish; ++i) { - let k = Math.imul((u << 1), u); + let k = (u << 1) * u | 0; let h = (v5.contents << 1); v2.contents = v2.contents + 1 | 0; arr[i] = () => { diff --git a/tests/tests/src/test_tuple.mjs b/tests/tests/src/test_tuple.mjs index 70c8d929d9..975e54d9e4 100644 --- a/tests/tests/src/test_tuple.mjs +++ b/tests/tests/src/test_tuple.mjs @@ -10,9 +10,9 @@ for (let k = 1; k <= 10; ++k) { (i << 1) ] : [ 2, - Math.imul(i, 3) + i * 3 | 0 ]; - r = Math.imul(r, match[0]) + match[1] | 0; + r = (r * match[0] | 0) + match[1] | 0; } } diff --git a/tests/tests/src/then_mangle_test.mjs b/tests/tests/src/then_mangle_test.mjs index 1552358a36..33a20b3d64 100644 --- a/tests/tests/src/then_mangle_test.mjs +++ b/tests/tests/src/then_mangle_test.mjs @@ -16,7 +16,7 @@ function eq(loc, x, y) { function then(a, b) { console.log("no inline"); - return Math.imul(a, a) + Math.imul(b, b) | 0; + return (a * a | 0) + (b * b | 0) | 0; } eq("File \"then_mangle_test.res\", line 14, characters 3-10", then(1, 2), 5);