From 1aaad4a32e6fcf5df01d9f8a5447afd0e29ea8dd Mon Sep 17 00:00:00 2001
From: Frank Hunleth <fhunleth@troodon-software.com>
Date: Fri, 25 Aug 2023 15:18:39 -0400
Subject: [PATCH] erlang: add aarch64 div/rem JIT fix

---
 ...-Fix-incorrect-fusion-of-div-and-rem.patch | 293 ++++++++++++++++++
 1 file changed, 293 insertions(+)
 create mode 100644 package/erlang/26.0.2/0004-Fix-incorrect-fusion-of-div-and-rem.patch

diff --git a/package/erlang/26.0.2/0004-Fix-incorrect-fusion-of-div-and-rem.patch b/package/erlang/26.0.2/0004-Fix-incorrect-fusion-of-div-and-rem.patch
new file mode 100644
index 0000000000..6f2f5957a3
--- /dev/null
+++ b/package/erlang/26.0.2/0004-Fix-incorrect-fusion-of-div-and-rem.patch
@@ -0,0 +1,293 @@
+From 9f46c61840c699228694716edbd31ee002e733ba Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
+Date: Wed, 16 Aug 2023 18:17:31 +0200
+Subject: [PATCH] Fix incorrect fusion of `div` and `rem`
+
+The JIT would generate code that calculated the remainder incorrectly
+for the following example:
+
+    bug(Bin) ->
+        N = byte_size(Bin),
+        {N rem 128, N div 128}.
+
+Essentially, the JIT would rewrite the code like this:
+
+    bug(Bin) ->
+        N = byte_size(Bin),
+        Q = N bsr 7,
+        {Q band 16#7f, Q}.
+
+That is, the remainder would be calculated as `(N div 128) rem 128`.
+
+Fixes #7566
+---
+ erts/emulator/beam/jit/arm/instr_arith.cpp |   7 +-
+ erts/emulator/test/small_SUITE.erl         | 198 +++++++++++++++++++--
+ 2 files changed, 188 insertions(+), 17 deletions(-)
+
+diff --git a/erts/emulator/beam/jit/arm/instr_arith.cpp b/erts/emulator/beam/jit/arm/instr_arith.cpp
+index 1fbec6dbb2..485f93956d 100644
+--- a/erts/emulator/beam/jit/arm/instr_arith.cpp
++++ b/erts/emulator/beam/jit/arm/instr_arith.cpp
+@@ -696,9 +696,14 @@ void BeamModuleAssembler::emit_div_rem(const ArgLabel &Fail,
+         if (Support::isPowerOf2(divisor) &&
+             std::get<0>(getClampedRange(LHS)) >= 0) {
+             int trailing_bits = Support::ctz<Eterm>(divisor);
++            arm::Gp LHS_reg = lhs.reg;
+             if (need_div) {
+                 comment("optimized div by replacing with right shift");
+                 ERTS_CT_ASSERT(_TAG_IMMED1_SMALL == _TAG_IMMED1_MASK);
++                if (need_rem && quotient.reg == lhs.reg) {
++                    LHS_reg = TMP1;
++                    a.mov(LHS_reg, lhs.reg);
++                }
+                 a.lsr(quotient.reg, lhs.reg, imm(trailing_bits));
+                 a.orr(quotient.reg, quotient.reg, imm(_TAG_IMMED1_SMALL));
+             }
+@@ -706,7 +711,7 @@ void BeamModuleAssembler::emit_div_rem(const ArgLabel &Fail,
+                 comment("optimized rem by replacing with masking");
+                 auto mask = Support::lsbMask<Uint>(trailing_bits +
+                                                    _TAG_IMMED1_SIZE);
+-                a.and_(remainder.reg, lhs.reg, imm(mask));
++                a.and_(remainder.reg, LHS_reg, imm(mask));
+             }
+         } else {
+             a.asr(TMP1, lhs.reg, imm(_TAG_IMMED1_SIZE));
+diff --git a/erts/emulator/test/small_SUITE.erl b/erts/emulator/test/small_SUITE.erl
+index c3ca540cd5..7d49522f05 100644
+--- a/erts/emulator/test/small_SUITE.erl
++++ b/erts/emulator/test/small_SUITE.erl
+@@ -521,17 +521,21 @@ div_gen_pairs() ->
+     {_, MaxSmall} = determine_small_limits(0),
+     NumBitsMaxSmall = num_bits(MaxSmall),
+ 
++    Divisors = [-8,-2,-1,1,2,3,4,5,8,16,17,64,22222333] ++
++        [1 bsl P || P <- lists:seq(8, 12) ++ lists:seq(26, 36)],
++
+     %% Generate random pairs of smalls.
+-    Pairs0 = [{rand:uniform(MaxSmall) * rand_sign(),
++    Pairs0 = [{rand:uniform(MaxSmall),
+                rand:uniform(MaxSmall) * rand_sign()} ||
+-                 _ <- lists:seq(1, 75)],
+-
+-    Pairs1 = [{rand:uniform(MaxSmall), N} ||
+-                 N <- [-4,-3,-2,-1,1,2,3,5,17,63,64,1111,22222]] ++ Pairs0,
++                 _ <- lists:seq(1, 50)],
++    Pairs1 = [{rand:uniform(MaxSmall), N} || N <- Divisors] ++ Pairs0,
++    Pairs2 = [{N, M} || N <- lists:seq(0, 7), M <- [-2,-1,1,2,3,4]] ++ Pairs1,
++    Pairs3 = [{abs(M) * (rand:uniform(10)+1) + rand:uniform(1000), M} ||
++                 M <- Divisors] ++ Pairs2,
+ 
+     %% Generate pairs of numbers whose product are bignums.
+     [{rand:uniform(MaxSmall),1 bsl Pow} ||
+-        Pow <- lists:seq(NumBitsMaxSmall - 4, NumBitsMaxSmall - 1)] ++ Pairs1.
++        Pow <- lists:seq(NumBitsMaxSmall - 4, NumBitsMaxSmall - 1)] ++ Pairs3.
+ 
+ rand_sign() ->
+     case rand:uniform() < 0.2 of
+@@ -572,6 +576,22 @@ gen_div_function({Name,{A,B}}) ->
+            put(prevent_div_rem_fusion, Q),
+            R = X rem Y,
+            {Q, R};
++        '@Name@'(pos_integer1, X, fixed) when is_integer(X), 0 =< X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(pos_integer2, X, fixed) when is_integer(X), 0 =< X, X < _@APlusOne@ ->
++           Y = _@B@,
++           R = X rem Y,
++           Q = X div Y,
++           {Q, R};
++        '@Name@'(pos_integer3, X, fixed) when is_integer(X), 0 =< X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           put(prevent_div_rem_fusion, Q),
++           R = X rem Y,
++           {Q, R};
+         '@Name@'(number0, X, Y) when -_@APlusOne@ < X, X < _@APlusOne@,
+                                     -_@BPlusOne@ < Y, Y < _@BPlusOne@ ->
+            Q = X div Y,
+@@ -593,6 +613,108 @@ gen_div_function({Name,{A,B}}) ->
+            Q = X div Y,
+            {Q, R};
+         '@Name@'(number4, X, fixed) when -_@APlusOne@ < X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           put(prevent_div_rem_fusion, Q),
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(any0, X, fixed) ->
++           Y = _@B@,
++           Q = X div Y,
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(any1, X, fixed) ->
++           Y = _@B@,
++           R = X rem Y,
++           Q = X div Y,
++           {Q, R};
++        '@Name@'(any2, X, fixed) ->
++           Y = _@B@,
++           Q = X div Y,
++           put(prevent_div_rem_fusion, Q),
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X0, Y0, integer0) ->
++           Q = X0 div Y0,
++           R = X0 rem Y0,
++           if X0 > 0, Y0 > 0 ->
++             <<X:_@NumBitsA@>> = <<X0:_@NumBitsA@>>,
++             <<Y:_@NumBitsB@>> = <<Y0:_@NumBitsB@>>,
++             Q = X div Y,
++             R = X rem Y,
++             {Q, R};
++           true ->
++             {Q, R}
++           end;
++        '@Name@'(X, fixed, integer1) when is_integer(X), -_@APlusOne@ < X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, fixed, integer2) when is_integer(X), -_@APlusOne@ < X, X < _@APlusOne@ ->
++           Y = _@B@,
++           R = X rem Y,
++           Q = X div Y,
++           {Q, R};
++        '@Name@'(X, fixed, integer3) when is_integer(X), -_@APlusOne@ < X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           put(prevent_div_rem_fusion, Q),
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, fixed, pos_integer1) when is_integer(X), 0 =< X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, fixed, pos_integer2) when is_integer(X), 0 =< X, X < _@APlusOne@ ->
++           Y = _@B@,
++           R = X rem Y,
++           Q = X div Y,
++           {Q, R};
++        '@Name@'(X, fixed, pos_integer3) when is_integer(X), 0 =< X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           put(prevent_div_rem_fusion, Q),
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, Y, number0) when -_@APlusOne@ < X, X < _@APlusOne@,
++                                    -_@BPlusOne@ < Y, Y < _@BPlusOne@ ->
++           Q = X div Y,
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, Y, number1) when -_@APlusOne@ < X, X < _@APlusOne@,
++                                    -_@BPlusOne@ < Y, Y < _@BPlusOne@ ->
++           R = X rem Y,
++           Q = X div Y,
++           {Q, R};
++        '@Name@'(X, fixed, number2) when -_@APlusOne@ < X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, fixed, number3) when -_@APlusOne@ < X, X < _@APlusOne@ ->
++           Y = _@B@,
++           R = X rem Y,
++           Q = X div Y,
++           {Q, R};
++        '@Name@'(X, fixed, number4) when -_@APlusOne@ < X, X < _@APlusOne@ ->
++           Y = _@B@,
++           Q = X div Y,
++           put(prevent_div_rem_fusion, Q),
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, fixed, any0) ->
++           Y = _@B@,
++           Q = X div Y,
++           R = X rem Y,
++           {Q, R};
++        '@Name@'(X, fixed, any1) ->
++           Y = _@B@,
++           R = X rem Y,
++           Q = X div Y,
++           {Q, R};
++        '@Name@'(X, fixed, any2) ->
+            Y = _@B@,
+            Q = X div Y,
+            put(prevent_div_rem_fusion, Q),
+@@ -602,16 +724,60 @@ gen_div_function({Name,{A,B}}) ->
+ test_division([{Name,{A,B}}|T], Mod) ->
+     F = fun Mod:Name/3,
+     try
+-        Res0 = {A div B, A rem B},
+-        Res0 = F(integer0, A, B),
+-        Res0 = F(integer1, A, fixed),
+-        Res0 = F(integer2, A, fixed),
+-        Res0 = F(integer3, A, fixed),
+-        Res0 = F(number0, A, B),
+-        Res0 = F(number1, A, B),
+-        Res0 = F(number2, A, fixed),
+-        Res0 = F(number3, A, fixed),
+-        Res0 = F(number4, A, fixed)
++        PosRes = {A div B, A rem B},
++        NegRes = {-A div B, -A rem B},
++
++        PosRes = F(integer0, A, B),
++        PosRes = F(integer1, A, fixed),
++        PosRes = F(integer2, A, fixed),
++        PosRes = F(integer3, A, fixed),
++        PosRes = F(pos_integer1, A, fixed),
++        PosRes = F(pos_integer2, A, fixed),
++        PosRes = F(pos_integer3, A, fixed),
++        PosRes = F(number0, A, B),
++        PosRes = F(number1, A, B),
++        PosRes = F(number2, A, fixed),
++        PosRes = F(number3, A, fixed),
++        PosRes = F(number4, A, fixed),
++        PosRes = F(any0, A, fixed),
++        PosRes = F(any1, A, fixed),
++        PosRes = F(any2, A, fixed),
++
++        PosRes = F(A, B, integer0),
++        PosRes = F(A, fixed, integer1),
++        PosRes = F(A, fixed, integer2),
++        PosRes = F(A, fixed, integer3),
++        PosRes = F(A, fixed, pos_integer1),
++        PosRes = F(A, fixed, pos_integer2),
++        PosRes = F(A, fixed, pos_integer3),
++        PosRes = F(A, B, number0),
++        PosRes = F(A, B, number1),
++        PosRes = F(A, fixed, number2),
++        PosRes = F(A, fixed, number3),
++        PosRes = F(A, fixed, number4),
++        PosRes = F(A, fixed, any0),
++        PosRes = F(A, fixed, any1),
++        PosRes = F(A, fixed, any2),
++
++        NegRes = F(integer0, -A, B),
++        NegRes = F(integer1, -A, fixed),
++        NegRes = F(integer2, -A, fixed),
++        NegRes = F(integer3, -A, fixed),
++        NegRes = F(number0, -A, B),
++        NegRes = F(number1, -A, B),
++        NegRes = F(number2, -A, fixed),
++        NegRes = F(number3, -A, fixed),
++        NegRes = F(number4, -A, fixed),
++
++        NegRes = F(-A, B, integer0),
++        NegRes = F(-A, fixed, integer1),
++        NegRes = F(-A, fixed, integer2),
++        NegRes = F(-A, fixed, integer3),
++        NegRes = F(-A, B, number0),
++        NegRes = F(-A, B, number1),
++        NegRes = F(-A, fixed, number2),
++        NegRes = F(-A, fixed, number3),
++        NegRes = F(-A, fixed, number4)
+     catch
+         C:R:Stk ->
+             io:format("~p failed. numbers: ~p ~p\n", [Name,A,B]),
+-- 
+2.34.1
+
-- 
2.34.1

