Title: [279850] trunk/Source/_javascript_Core
Revision
279850
Author
yijia_hu...@apple.com
Date
2021-07-12 13:51:11 -0700 (Mon, 12 Jul 2021)

Log Message

Add SMNEGL, UMNEGL, UMADDL, and UMSUBL for ARM64 and select this instruction in Air
https://bugs.webkit.org/show_bug.cgi?id=227857

Reviewed by Robin Morisset.

The previous patches have already added MNEG, MADD, MSUB, SMADDL, and SMSUBL.
This patch completes the corresponding signed or unsigned variants (SMNEGL,
UMNEGL, UMADDL, and UMSUBL) of them. In addition, this patch refactors
the implementation and the associative test cases of MADD, MSUB, and MNEG
to be more readable and maintainable w.r.t their variants.

------------------------------
### SMNEGL/UMNEGL Xd Wn Wm ###
------------------------------
Signed/Unsigned Multiply-Negate Long multiplies two 32-bit register values,
negates the product, and writes the result to the 64-bit destination register.
The equivalent patterns are

d = -(SExt32(n) * SExt32(m)) and d = -(ZExt32(n) * ZExt32(m)) respectively.

Given B3 IR:
Int @0 = S/ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = S/ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = Mul(@0, @1)
Int @3 = Neg(@2)
Void@4 = Return(@3, Terminal)

// Old optimized AIR
Move32      %x0, %x0,      @0
Move32      %x1, %x1,      @1
MultiplyNeg %x0, %x1, %x0, @3
Ret         %x0,           @4

// New optimized AIR
MultiplyNegSign/ZeroExtend  %x0, %x1, %x0, @3
Ret                         %x0,           @4

--------------------------
### UMADDL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Add Long multiplies two 32-bit register values, adds a 64-bit
register value, and writes the result to the 64-bit destination register. The
equivalent patterns are

d = ZExt32(n) * ZExt32(m) + a or d = a + ZExt32(n) * ZExt32(m)

Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Add(@3, @2)
Void@5 = Return(@4, Terminal)

// Old optimized AIR
Move32        %x0, %x0,           @1
Move32        %x1, %x1,           @2
MultiplyAdd   %x0, %x1, %x2, %x0, @4
Ret64         %x0,                @5

// New optimized AIR
MultiplyAddZeroExtend %x0, %x1, %x2, %x0, @8
Ret                   %x0,                @9

--------------------------
### UMSUBL Xd Wn Wm Xa ###
--------------------------
Unsigned Multiply-Subtract Long multiplies two 32-bit register values, subtracts
the product from a 64-bit register value, and writes the result to the 64-bit
destination register. The equivalent patterns are

d = a - ZExt32(n) * ZExt32(m)

Given B3 IR:
Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
Int @2 = ArgumentReg(%x2)
Int @3 = Mul(@0, @1)
Int @4 = Sub(@2, @3)
Void@5 = Return(@4, Terminal)

// Old optimized AIR
Move32        %x0, %x0,           @1
Move32        %x1, %x1,           @2
MultiplySub   %x0, %x1, %x2, %x0, @4
Ret64         %x0,                @5

// New optimized AIR
MultiplySubZeroExtend %x0, %x1, %x2, %x0, @8
Ret                   %x0,                @9

* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::multiplyNeg32):
(JSC::MacroAssemblerARM64::multiplyAddZeroExtend32):
(JSC::MacroAssemblerARM64::multiplySubZeroExtend32):
(JSC::MacroAssemblerARM64::multiplyNeg64):
(JSC::MacroAssemblerARM64::multiplyNegSignExtend32):
(JSC::MacroAssemblerARM64::multiplyNegZeroExtend32):
* assembler/testmasm.cpp:
(JSC::testMultiplyAddSignExtend32):
(JSC::testMultiplyAddZeroExtend32):
(JSC::testMultiplySubSignExtend32):
(JSC::testMultiplySubZeroExtend32):
(JSC::testMultiplyNegSignExtend32):
(JSC::testMultiplyNegZeroExtend32):
(JSC::testMultiplyAddSignExtend32Left): Deleted.
(JSC::testMultiplyAddSignExtend32Right): Deleted.
* b3/B3LowerToAir.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testMulAddArgsLeft):
(testMulAddArgsRight):
(testMulAddSignExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsLeft):
(testMulAddZeroExtend32ArgsRight):
(testMulSubArgsLeft):
(testMulSubArgsRight):
(testMulSubArgsRight32):
(testMulSubSignExtend32):
(testMulSubZeroExtend32):
(testMulNegArgArg):
(testMulNegArgs):
(testMulNegArgs32):
(testMulNegSignExtend32):
(testMulNegZeroExtend32):
(testMulSubSignExtend32Args): Deleted.
* b3/testb3_3.cpp:
(addArgTests):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (279849 => 279850)


--- trunk/Source/_javascript_Core/ChangeLog	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-07-12 20:51:11 UTC (rev 279850)
@@ -1,3 +1,135 @@
+2021-07-12  Yijia Huang  <yijia_hu...@apple.com>
+
+        Add SMNEGL, UMNEGL, UMADDL, and UMSUBL for ARM64 and select this instruction in Air
+        https://bugs.webkit.org/show_bug.cgi?id=227857
+
+        Reviewed by Robin Morisset.
+
+        The previous patches have already added MNEG, MADD, MSUB, SMADDL, and SMSUBL. 
+        This patch completes the corresponding signed or unsigned variants (SMNEGL, 
+        UMNEGL, UMADDL, and UMSUBL) of them. In addition, this patch refactors 
+        the implementation and the associative test cases of MADD, MSUB, and MNEG 
+        to be more readable and maintainable w.r.t their variants.
+
+        ------------------------------
+        ### SMNEGL/UMNEGL Xd Wn Wm ###
+        ------------------------------
+        Signed/Unsigned Multiply-Negate Long multiplies two 32-bit register values, 
+        negates the product, and writes the result to the 64-bit destination register. 
+        The equivalent patterns are 
+
+        d = -(SExt32(n) * SExt32(m)) and d = -(ZExt32(n) * ZExt32(m)) respectively.
+
+        Given B3 IR:
+        Int @0 = S/ZExt32(Trunc(ArgumentReg(%x0)))
+        Int @1 = S/ZExt32(Trunc(ArgumentReg(%x1)))
+        Int @2 = Mul(@0, @1)
+        Int @3 = Neg(@2)
+        Void@4 = Return(@3, Terminal)
+
+        // Old optimized AIR
+        Move32      %x0, %x0,      @0
+        Move32      %x1, %x1,      @1
+        MultiplyNeg %x0, %x1, %x0, @3
+        Ret         %x0,           @4
+
+        // New optimized AIR
+        MultiplyNegSign/ZeroExtend  %x0, %x1, %x0, @3
+        Ret                         %x0,           @4
+
+        --------------------------
+        ### UMADDL Xd Wn Wm Xa ###
+        --------------------------
+        Unsigned Multiply-Add Long multiplies two 32-bit register values, adds a 64-bit 
+        register value, and writes the result to the 64-bit destination register. The 
+        equivalent patterns are 
+
+        d = ZExt32(n) * ZExt32(m) + a or d = a + ZExt32(n) * ZExt32(m)
+
+        Given B3 IR:
+        Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
+        Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
+        Int @2 = ArgumentReg(%x2)
+        Int @3 = Mul(@0, @1)
+        Int @4 = Add(@3, @2)
+        Void@5 = Return(@4, Terminal)
+
+        // Old optimized AIR
+        Move32        %x0, %x0,           @1
+        Move32        %x1, %x1,           @2
+        MultiplyAdd   %x0, %x1, %x2, %x0, @4
+        Ret64         %x0,                @5
+
+        // New optimized AIR
+        MultiplyAddZeroExtend %x0, %x1, %x2, %x0, @8
+        Ret                   %x0,                @9
+
+        --------------------------
+        ### UMSUBL Xd Wn Wm Xa ###
+        --------------------------
+        Unsigned Multiply-Subtract Long multiplies two 32-bit register values, subtracts 
+        the product from a 64-bit register value, and writes the result to the 64-bit 
+        destination register. The equivalent patterns are 
+
+        d = a - ZExt32(n) * ZExt32(m)
+
+        Given B3 IR:
+        Int @0 = ZExt32(Trunc(ArgumentReg(%x0)))
+        Int @1 = ZExt32(Trunc(ArgumentReg(%x1)))
+        Int @2 = ArgumentReg(%x2)
+        Int @3 = Mul(@0, @1)
+        Int @4 = Sub(@2, @3)
+        Void@5 = Return(@4, Terminal)
+
+        // Old optimized AIR
+        Move32        %x0, %x0,           @1
+        Move32        %x1, %x1,           @2
+        MultiplySub   %x0, %x1, %x2, %x0, @4
+        Ret64         %x0,                @5
+
+        // New optimized AIR
+        MultiplySubZeroExtend %x0, %x1, %x2, %x0, @8
+        Ret                   %x0,                @9
+
+        * assembler/MacroAssemblerARM64.h:
+        (JSC::MacroAssemblerARM64::multiplyNeg32):
+        (JSC::MacroAssemblerARM64::multiplyAddZeroExtend32):
+        (JSC::MacroAssemblerARM64::multiplySubZeroExtend32):
+        (JSC::MacroAssemblerARM64::multiplyNeg64):
+        (JSC::MacroAssemblerARM64::multiplyNegSignExtend32):
+        (JSC::MacroAssemblerARM64::multiplyNegZeroExtend32):
+        * assembler/testmasm.cpp:
+        (JSC::testMultiplyAddSignExtend32):
+        (JSC::testMultiplyAddZeroExtend32):
+        (JSC::testMultiplySubSignExtend32):
+        (JSC::testMultiplySubZeroExtend32):
+        (JSC::testMultiplyNegSignExtend32):
+        (JSC::testMultiplyNegZeroExtend32):
+        (JSC::testMultiplyAddSignExtend32Left): Deleted.
+        (JSC::testMultiplyAddSignExtend32Right): Deleted.
+        * b3/B3LowerToAir.cpp:
+        * b3/air/AirOpcode.opcodes:
+        * b3/testb3.h:
+        * b3/testb3_2.cpp:
+        (testMulAddArgsLeft):
+        (testMulAddArgsRight):
+        (testMulAddSignExtend32ArgsLeft):
+        (testMulAddZeroExtend32ArgsLeft):
+        (testMulAddZeroExtend32ArgsRight):
+        (testMulSubArgsLeft):
+        (testMulSubArgsRight):
+        (testMulSubArgsRight32):
+        (testMulSubSignExtend32):
+        (testMulSubZeroExtend32):
+        (testMulNegArgArg):
+        (testMulNegArgs):
+        (testMulNegArgs32):
+        (testMulNegSignExtend32):
+        (testMulNegZeroExtend32):
+        (testMulSubSignExtend32Args): Deleted.
+        * b3/testb3_3.cpp:
+        (addArgTests):
+
 2021-07-12  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] JITCage already includes guard pages

Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h (279849 => 279850)


--- trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h	2021-07-12 20:51:11 UTC (rev 279850)
@@ -770,7 +770,7 @@
 
     void multiplyNeg32(RegisterID mulLeft, RegisterID mulRight, RegisterID dest)
     {
-        m_assembler.msub<32>(dest, mulLeft, mulRight, ARM64Registers::zr);
+        m_assembler.mneg<32>(dest, mulLeft, mulRight);
     }
 
     void multiplyAdd64(RegisterID mulLeft, RegisterID mulRight, RegisterID summand, RegisterID dest)
@@ -783,6 +783,11 @@
         m_assembler.smaddl(dest, mulLeft, mulRight, summand);
     }
 
+    void multiplyAddZeroExtend32(RegisterID mulLeft, RegisterID mulRight, RegisterID summand, RegisterID dest)
+    {
+        m_assembler.umaddl(dest, mulLeft, mulRight, summand);
+    }
+
     void multiplySub64(RegisterID mulLeft, RegisterID mulRight, RegisterID minuend, RegisterID dest)
     {
         m_assembler.msub<64>(dest, mulLeft, mulRight, minuend);
@@ -793,11 +798,26 @@
         m_assembler.smsubl(dest, mulLeft, mulRight, minuend);
     }
 
+    void multiplySubZeroExtend32(RegisterID mulLeft, RegisterID mulRight, RegisterID minuend, RegisterID dest)
+    {
+        m_assembler.umsubl(dest, mulLeft, mulRight, minuend);
+    }
+
     void multiplyNeg64(RegisterID mulLeft, RegisterID mulRight, RegisterID dest)
     {
-        m_assembler.msub<64>(dest, mulLeft, mulRight, ARM64Registers::zr);
+        m_assembler.mneg<64>(dest, mulLeft, mulRight);
     }
 
+    void multiplyNegSignExtend32(RegisterID mulLeft, RegisterID mulRight, RegisterID dest)
+    {
+        m_assembler.smnegl(dest, mulLeft, mulRight);
+    }
+
+    void multiplyNegZeroExtend32(RegisterID mulLeft, RegisterID mulRight, RegisterID dest)
+    {
+        m_assembler.umnegl(dest, mulLeft, mulRight);
+    }
+
     void multiplySignExtend32(RegisterID left, RegisterID right, RegisterID dest)
     {
         m_assembler.smull(dest, left, right);

Modified: trunk/Source/_javascript_Core/assembler/testmasm.cpp (279849 => 279850)


--- trunk/Source/_javascript_Core/assembler/testmasm.cpp	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/assembler/testmasm.cpp	2021-07-12 20:51:11 UTC (rev 279850)
@@ -921,7 +921,7 @@
     }
 }
 
-void testMultiplyAddSignExtend32Left()
+void testMultiplyAddSignExtend32()
 {
     // d = SExt32(n) * SExt32(m) + a
     auto add = compile([=] (CCallHelpers& jit) {
@@ -944,15 +944,15 @@
     }
 }
 
-void testMultiplyAddSignExtend32Right()
+void testMultiplyAddZeroExtend32()
 {
-    // d = a + SExt32(n) * SExt32(m)
+    // d = ZExt32(n) * ZExt32(m) + a
     auto add = compile([=] (CCallHelpers& jit) {
         emitFunctionPrologue(jit);
 
-        jit.multiplyAddSignExtend32(GPRInfo::argumentGPR1, 
+        jit.multiplyAddZeroExtend32(GPRInfo::argumentGPR0, 
+            GPRInfo::argumentGPR1, 
             GPRInfo::argumentGPR2, 
-            GPRInfo::argumentGPR0, 
             GPRInfo::returnValueGPR);
 
         emitFunctionEpilogue(jit);
@@ -959,10 +959,13 @@
         jit.ret();
     });
 
-    for (auto a : int64Operands()) {
-        for (auto n : int32Operands()) {
-            for (auto m : int32Operands())
-                CHECK_EQ(invoke<int64_t>(add, a, n, m), a + static_cast<int64_t>(n) * static_cast<int64_t>(m));
+    for (auto n : int32Operands()) {
+        for (auto m : int32Operands()) {
+            for (auto a : int64Operands()) {
+                uint32_t un = n;
+                uint32_t um = m;
+                CHECK_EQ(invoke<int64_t>(add, n, m, a), static_cast<int64_t>(un) * static_cast<int64_t>(um) + a);
+            }
         }
     }
 }
@@ -1091,7 +1094,7 @@
 
 void testMultiplySubSignExtend32()
 {
-    // d = a - SExt32(n) *  SExt32(m)
+    // d = a - SExt32(n) * SExt32(m)
     auto sub = compile([=] (CCallHelpers& jit) {
         emitFunctionPrologue(jit);
 
@@ -1112,6 +1115,71 @@
     }
 }
 
+void testMultiplySubZeroExtend32()
+{
+    // d = a - (ZExt32(n) * ZExt32(m))
+    auto sub = compile([=] (CCallHelpers& jit) {
+        emitFunctionPrologue(jit);
+
+        jit.multiplySubZeroExtend32(GPRInfo::argumentGPR1, 
+            GPRInfo::argumentGPR2,
+            GPRInfo::argumentGPR0, 
+            GPRInfo::returnValueGPR);
+
+        emitFunctionEpilogue(jit);
+        jit.ret();
+    });
+
+    for (auto a : int64Operands()) {
+        for (auto n : int32Operands()) {
+            for (auto m : int32Operands()) {
+                uint32_t un = n;
+                uint32_t um = m;
+                CHECK_EQ(invoke<int64_t>(sub, a, n, m), a - static_cast<int64_t>(un) * static_cast<int64_t>(um));
+            }
+        }
+    }
+}
+
+void testMultiplyNegSignExtend32()
+{
+    // d = - (SExt32(n) * SExt32(m))
+    auto neg = compile([=] (CCallHelpers& jit) {
+        emitFunctionPrologue(jit);
+
+        jit.multiplyNegSignExtend32(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+        emitFunctionEpilogue(jit);
+        jit.ret();
+    });
+
+    for (auto n : int32Operands()) {
+        for (auto m : int32Operands())
+            CHECK_EQ(invoke<int64_t>(neg, n, m), -(static_cast<int64_t>(n) * static_cast<int64_t>(m)));
+    }
+}
+
+void testMultiplyNegZeroExtend32()
+{
+    // d = - ZExt32(n) * ZExt32(m)
+    auto neg = compile([=] (CCallHelpers& jit) {
+        emitFunctionPrologue(jit);
+
+        jit.multiplyNegZeroExtend32(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+        emitFunctionEpilogue(jit);
+        jit.ret();
+    });
+
+    for (auto n : int32Operands()) {
+        for (auto m : int32Operands()) {
+            uint32_t un = n;
+            uint32_t um = m;
+            CHECK_EQ(invoke<uint64_t>(neg, n, m), -(static_cast<uint64_t>(un) * static_cast<uint64_t>(um)));
+        }
+    }
+}
+
 void testExtractUnsignedBitfield32()
 {
     uint32_t src = ""
@@ -4164,9 +4232,12 @@
     RUN(testSub64Imm64());
     RUN(testSub64ArgImm64());
 
-    RUN(testMultiplyAddSignExtend32Left());
-    RUN(testMultiplyAddSignExtend32Right());
+    RUN(testMultiplyAddSignExtend32());
+    RUN(testMultiplyAddZeroExtend32());
     RUN(testMultiplySubSignExtend32());
+    RUN(testMultiplySubZeroExtend32());
+    RUN(testMultiplyNegSignExtend32());
+    RUN(testMultiplyNegZeroExtend32());
 
     RUN(testExtractUnsignedBitfield32());
     RUN(testExtractUnsignedBitfield64());

Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (279849 => 279850)


--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2021-07-12 20:51:11 UTC (rev 279850)
@@ -2485,6 +2485,15 @@
     void lower()
     {
         using namespace Air;
+
+        auto isMergeableB3Opcode = [&] (Value* v, B3::Opcode b3Opcode) -> bool { 
+            if (v->opcode() != b3Opcode || !canBeInternal(v))
+                return false;
+            if (m_locked.contains(v->child(0)))
+                return false;
+            return true;
+        };
+
         switch (m_value->opcode()) {
         case B3::Nop: {
             // Yes, we will totally see Nop's because some phases will replaceWithNop() instead of
@@ -2567,72 +2576,71 @@
         case Add: {
             if (tryAppendLea())
                 return;
-
             Value* left = m_value->child(0);
             Value* right = m_value->child(1);
 
-            ASSERT(isValidForm(MultiplyAdd64, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp) 
-                == isValidForm(MultiplyAddSignExtend32, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp));
-            Air::Opcode multiplyAddOpcode = tryOpcodeForType(MultiplyAdd32, MultiplyAdd64, m_value->type());
-            if (isValidForm(multiplyAddOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
-                if (!imm(right) || m_valueToTmp[right]) {
-                    auto tryMultiply = [&] (Value* v) -> bool { 
-                        if (v->opcode() != Mul || !canBeInternal(v))
-                            return false;
-                        if (m_locked.contains(v->child(0)) || m_locked.contains(v->child(1)))
-                            return false;
-                        return true;
-                    };
+            auto tryMultiplyAdd = [&] () -> bool {
+                if (imm(right) && !m_valueToTmp[right])
+                    return false;
 
-                    auto trySExt32 = [&] (Value* v) -> bool { 
-                        return v->opcode() == SExt32 && canBeInternal(v);
+                // MADD: d = n * m + a
+                auto tryAppendMultiplyAdd = [&] (Value* left, Value* right) -> bool {
+                    if (left->opcode() != Mul || !canBeInternal(left) || m_locked.contains(right))
+                        return false;
+                    Value* multiplyLeft = left->child(0);
+                    Value* multiplyRight = left->child(1);
+                    Air::Opcode airOpcode = tryOpcodeForType(MultiplyAdd32, MultiplyAdd64, m_value->type());
+                    auto tryNewAirOpcode = [&] () -> Air::Opcode {
+                        if (airOpcode != MultiplyAdd64)
+                            return Air::Oops;
+                        // SMADDL: d = SExt32(n) * SExt32(m) + a
+                        if (isMergeableB3Opcode(multiplyLeft, SExt32) && isMergeableB3Opcode(multiplyRight, SExt32)) 
+                            return MultiplyAddSignExtend32;
+                        // UMADDL: d = ZExt32(n) * ZExt32(m) + a
+                        if (isMergeableB3Opcode(multiplyLeft, ZExt32) && isMergeableB3Opcode(multiplyRight, ZExt32)) 
+                            return MultiplyAddZeroExtend32;
+                        return Air::Oops;
                     };
 
-                    // MADD: d = n * m + a
-                    auto tryAppendMultiplyAdd = [&] (Value* left, Value* right) -> bool {
-                        if (!tryMultiply(left))
-                            return false;
-                        Value* multiplyLeft = left->child(0);
-                        Value* multiplyRight = left->child(1);
-
-                        // SMADDL: d = SExt32(n) * SExt32(m) + a
-                        if (multiplyAddOpcode == MultiplyAdd64 && trySExt32(multiplyLeft) && trySExt32(multiplyRight)) {
-                            append(MultiplyAddSignExtend32, 
-                                tmp(multiplyLeft->child(0)), 
-                                tmp(multiplyRight->child(0)), 
-                                tmp(right), 
-                                tmp(m_value));
-                            commitInternal(multiplyLeft);
-                            commitInternal(multiplyRight);
-                            commitInternal(left);
-                            return true;
-                        }
-
-                        append(multiplyAddOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(right), tmp(m_value));
+                    Air::Opcode newAirOpcode = tryNewAirOpcode();
+                    if (isValidForm(newAirOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
+                        append(newAirOpcode, tmp(multiplyLeft->child(0)), tmp(multiplyRight->child(0)), tmp(right), tmp(m_value));
+                        commitInternal(multiplyLeft);
+                        commitInternal(multiplyRight);
                         commitInternal(left);
                         return true;
-                    };
+                    }
 
-                    if (tryAppendMultiplyAdd(left, right) || tryAppendMultiplyAdd(right, left))
-                        return;
-                }
-            }
+                    if (!isValidForm(airOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp))
+                        return false;
+                    if (m_locked.contains(multiplyLeft) || m_locked.contains(multiplyRight))
+                        return false;
+                    append(airOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(right), tmp(m_value));
+                    commitInternal(left);
+                    return true;
+                };
 
-            // add-with-shift Pattern: n + (m ShiftType amount)
-            auto tryOpcode = [&] (JSC::B3::Opcode opcode) -> Air::Opcode {
-                switch (opcode) {
-                case Shl:
-                    return tryOpcodeForType(AddLeftShift32, AddLeftShift64, m_value->type());
-                case SShr:
-                    return tryOpcodeForType(AddRightShift32, AddRightShift64, m_value->type());
-                case ZShr:
-                    return tryOpcodeForType(AddUnsignedRightShift32, AddUnsignedRightShift64, m_value->type());
-                default:
-                    return Air::Oops;
-                }
+                return tryAppendMultiplyAdd(left, right) || tryAppendMultiplyAdd(right, left);
             };
 
-            auto tryAppendAddAndShift = [&] (Value* left, Value* right) -> bool {
+            if (tryMultiplyAdd())
+                return;
+
+            // add-with-shift Pattern: n + (m ShiftType amount)
+            auto tryAppendAddWithShift = [&] (Value* left, Value* right) -> bool {
+                auto tryOpcode = [&] (B3::Opcode opcode) -> Air::Opcode {
+                    switch (opcode) {
+                    case Shl:
+                        return tryOpcodeForType(AddLeftShift32, AddLeftShift64, m_value->type());
+                    case SShr:
+                        return tryOpcodeForType(AddRightShift32, AddRightShift64, m_value->type());
+                    case ZShr:
+                        return tryOpcodeForType(AddUnsignedRightShift32, AddUnsignedRightShift64, m_value->type());
+                    default:
+                        return Air::Oops;
+                    }
+                };
+
                 Air::Opcode opcode = tryOpcode(right->opcode());
                 if (!isValidForm(opcode, Arg::Tmp, Arg::Tmp, Arg::Imm, Arg::Tmp)) 
                     return false;
@@ -2649,7 +2657,7 @@
                 return true;
             };
 
-            if (tryAppendAddAndShift(left, right) || tryAppendAddAndShift(right, left))
+            if (tryAppendAddWithShift(left, right) || tryAppendAddWithShift(right, left))
                 return;
 
             appendBinOp<Add32, Add64, AddDouble, AddFloat, Commutative>(left, right);
@@ -2660,72 +2668,64 @@
             Value* left = m_value->child(0);
             Value* right = m_value->child(1);
 
-            Air::Opcode multiplySubOpcode = tryOpcodeForType(MultiplySub32, MultiplySub64, m_value->type());
-            if (multiplySubOpcode != Air::Oops
-                && isValidForm(multiplySubOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
-                auto tryMultiply = [&] (Value* v) -> bool {
-                    if (v->opcode() != Mul || !canBeInternal(v))
-                        return false;
-                    if (m_locked.contains(v->child(0)) || m_locked.contains(v->child(1)))
-                        return false;
-                    return true;
-                };
+            auto tryAppendMultiplySub = [&] () -> bool {
+                if (imm(right) && !m_valueToTmp[right])
+                    return false;
 
                 // MSUB: d = a - n * m
-                if ((!imm(right) || m_valueToTmp[right]) && tryMultiply(right)) {
-                    Value* multiplyLeft = right->child(0);
-                    Value* multiplyRight = right->child(1);
-
+                if (m_locked.contains(left) || right->opcode() != Mul || !canBeInternal(right))
+                    return false;
+                Value* multiplyLeft = right->child(0);
+                Value* multiplyRight = right->child(1);
+                Air::Opcode airOpcode = tryOpcodeForType(MultiplySub32, MultiplySub64, m_value->type());
+                auto tryNewAirOpcode = [&] () -> Air::Opcode {
+                    if (airOpcode != MultiplySub64)
+                        return Air::Oops;
                     // SMSUBL: d = a - SExt32(n) * SExt32(m)
-                    if (multiplySubOpcode == MultiplySub64
-                        && isValidForm(MultiplySubSignExtend32, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
-                        auto trySExt32 = [&] (Value* v) {
-                            if (v->opcode() != SExt32 || !canBeInternal(v))
-                                return false;
-                            if (m_locked.contains(v->child(0)))
-                                return false;
-                            return true;
-                        };
+                    if (isMergeableB3Opcode(multiplyLeft, SExt32) && isMergeableB3Opcode(multiplyRight, SExt32)) 
+                        return MultiplySubSignExtend32;
+                    // UMSUBL: d = a - ZExt32(n) * ZExt32(m)
+                    if (isMergeableB3Opcode(multiplyLeft, ZExt32) && isMergeableB3Opcode(multiplyRight, ZExt32)) 
+                        return MultiplySubZeroExtend32;
+                    return Air::Oops;
+                };
 
-                        auto tryAppendMultiplySubSignExtend32 = [&] () -> bool {
-                            if (!trySExt32(multiplyLeft) || !trySExt32(multiplyRight))
-                                return false;
-                            append(MultiplySubSignExtend32, 
-                                tmp(multiplyLeft->child(0)), 
-                                tmp(multiplyRight->child(0)), 
-                                tmp(left), 
-                                tmp(m_value));
-                            commitInternal(right);
-                            commitInternal(multiplyLeft);
-                            commitInternal(multiplyRight);
-                            return true;
-                        };
-
-                        if (tryAppendMultiplySubSignExtend32())
-                            return;
-                    }
-
-                    append(multiplySubOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(left), tmp(m_value));
+                Air::Opcode newAirOpcode = tryNewAirOpcode();
+                if (isValidForm(newAirOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
+                    append(newAirOpcode, tmp(multiplyLeft->child(0)), tmp(multiplyRight->child(0)), tmp(left), tmp(m_value));
+                    commitInternal(multiplyLeft);
+                    commitInternal(multiplyRight);
                     commitInternal(right);
-                    return;
+                    return true;
                 }
-            }
 
-            // sub-with-shift Pattern: n - (m ShiftType amount)
-            auto tryOpcode = [&] (JSC::B3::Opcode opcode) -> Air::Opcode {
-                switch (opcode) {
-                case Shl:
-                    return tryOpcodeForType(SubLeftShift32, SubLeftShift64, m_value->type());
-                case SShr:
-                    return tryOpcodeForType(SubRightShift32, SubRightShift64, m_value->type());
-                case ZShr:
-                    return tryOpcodeForType(SubUnsignedRightShift32, SubUnsignedRightShift64, m_value->type());
-                default:
-                    return Air::Oops;
-                }
+                if (!isValidForm(airOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp))
+                    return false;
+                if (m_locked.contains(multiplyLeft) || m_locked.contains(multiplyRight))
+                    return false;
+                append(airOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(left), tmp(m_value));
+                commitInternal(right);
+                return true;
             };
 
+            if (tryAppendMultiplySub())
+                return;
+
+            // sub-with-shift Pattern: n - (m ShiftType amount)
             auto tryAppendSubAndShift = [&] () -> bool {
+                auto tryOpcode = [&] (B3::Opcode opcode) -> Air::Opcode {
+                    switch (opcode) {
+                    case Shl:
+                        return tryOpcodeForType(SubLeftShift32, SubLeftShift64, m_value->type());
+                    case SShr:
+                        return tryOpcodeForType(SubRightShift32, SubRightShift64, m_value->type());
+                    case ZShr:
+                        return tryOpcodeForType(SubUnsignedRightShift32, SubUnsignedRightShift64, m_value->type());
+                    default:
+                        return Air::Oops;
+                    }
+                };
+
                 Air::Opcode opcode = tryOpcode(right->opcode());
                 if (!isValidForm(opcode, Arg::Tmp, Arg::Tmp, Arg::Imm, Arg::Tmp)) 
                     return false;
@@ -2750,21 +2750,46 @@
         }
 
         case Neg: {
-            Air::Opcode multiplyNegOpcode = tryOpcodeForType(MultiplyNeg32, MultiplyNeg64, m_value->type());
-            if (multiplyNegOpcode != Air::Oops
-                && isValidForm(multiplyNegOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp)
-                && m_value->child(0)->opcode() == Mul
-                && canBeInternal(m_value->child(0))) {
-                Value* multiplyOperation = m_value->child(0);
-                Value* multiplyLeft = multiplyOperation->child(0);
-                Value* multiplyRight = multiplyOperation->child(1);
-                if (!m_locked.contains(multiplyLeft) && !m_locked.contains(multiplyRight)) {
-                    append(multiplyNegOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(m_value));
-                    commitInternal(multiplyOperation);
-                    return;
+            auto tryAppendMultiplyNeg = [&] () -> bool {
+                // MNEG : d = -(n * m)
+                if (m_value->child(0)->opcode() != Mul || !canBeInternal(m_value->child(0)))
+                    return false;
+                Value* multiplyLeft = m_value->child(0)->child(0);
+                Value* multiplyRight = m_value->child(0)->child(1);
+                Air::Opcode airOpcode = tryOpcodeForType(MultiplyNeg32, MultiplyNeg64, m_value->type());
+                auto tryNewAirOpcode = [&] () -> Air::Opcode {
+                    if (airOpcode != MultiplyNeg64)
+                        return Air::Oops;
+                    // SMNEGL: d = -(SExt32(n) * SExt32(m))
+                    if (isMergeableB3Opcode(multiplyLeft, SExt32) && isMergeableB3Opcode(multiplyRight, SExt32)) 
+                        return MultiplyNegSignExtend32;
+                    // UMNEGL: d = -(ZExt32(n) * ZExt32(m))
+                    if (isMergeableB3Opcode(multiplyLeft, ZExt32) && isMergeableB3Opcode(multiplyRight, ZExt32)) 
+                        return MultiplyNegZeroExtend32;
+                    return Air::Oops;
+                };
+
+                Air::Opcode newAirOpcode = tryNewAirOpcode();
+                if (isValidForm(newAirOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
+                    append(newAirOpcode, tmp(multiplyLeft->child(0)), tmp(multiplyRight->child(0)), tmp(m_value));
+                    commitInternal(multiplyLeft);
+                    commitInternal(multiplyRight);
+                    commitInternal(m_value->child(0));
+                    return true;
                 }
-            }
 
+                if (!isValidForm(airOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp))
+                    return false;
+                if (m_locked.contains(multiplyLeft) || m_locked.contains(multiplyRight))
+                    return false;
+                append(airOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(m_value));
+                commitInternal(m_value->child(0));
+                return true;
+            };
+
+            if (tryAppendMultiplyNeg())
+                return;
+
             appendUnOp<Neg32, Neg64, NegateDouble, NegateFloat>(m_value->child(0));
             return;
         }

Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (279849 => 279850)


--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2021-07-12 20:51:11 UTC (rev 279850)
@@ -254,6 +254,9 @@
 arm64: MultiplyAddSignExtend32 U:G:32, U:G:32, U:G:64, D:G:64
     Tmp, Tmp, Tmp, Tmp
 
+arm64: MultiplyAddZeroExtend32 U:G:32, U:G:32, U:G:64, D:G:64
+    Tmp, Tmp, Tmp, Tmp
+
 arm64: MultiplySub32 U:G:32, U:G:32, U:G:32, ZD:G:32
     Tmp, Tmp, Tmp, Tmp
 
@@ -263,6 +266,9 @@
 arm64: MultiplySubSignExtend32 U:G:32, U:G:32, U:G:64, D:G:64
     Tmp, Tmp, Tmp, Tmp
 
+arm64: MultiplySubZeroExtend32 U:G:32, U:G:32, U:G:64, D:G:64
+    Tmp, Tmp, Tmp, Tmp
+
 arm64: MultiplyNeg32 U:G:32, U:G:32, ZD:G:32
     Tmp, Tmp, Tmp
 
@@ -269,9 +275,15 @@
 arm64: MultiplyNeg64 U:G:64, U:G:64, ZD:G:64
     Tmp, Tmp, Tmp
 
-arm64: MultiplySignExtend32 U:G:32, U:G:32, ZD:G:64
+arm64: MultiplyNegSignExtend32 U:G:32, U:G:32, D:G:64
     Tmp, Tmp, Tmp
 
+arm64: MultiplyNegZeroExtend32 U:G:32, U:G:32, D:G:64
+    Tmp, Tmp, Tmp
+
+arm64: MultiplySignExtend32 U:G:32, U:G:32, D:G:64
+    Tmp, Tmp, Tmp
+
 arm64: Div32 U:G:32, U:G:32, ZD:G:32
     Tmp, Tmp, Tmp
 

Modified: trunk/Source/_javascript_Core/b3/testb3.h (279849 => 279850)


--- trunk/Source/_javascript_Core/b3/testb3.h	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/b3/testb3.h	2021-07-12 20:51:11 UTC (rev 279850)
@@ -913,13 +913,18 @@
 void testMulAddArgsRight32();
 void testMulAddSignExtend32ArgsLeft();
 void testMulAddSignExtend32ArgsRight();
+void testMulAddZeroExtend32ArgsLeft();
+void testMulAddZeroExtend32ArgsRight();
 void testMulSubArgsLeft();
 void testMulSubArgsRight();
 void testMulSubArgsLeft32();
 void testMulSubArgsRight32();
-void testMulSubSignExtend32Args();
+void testMulSubSignExtend32();
+void testMulSubZeroExtend32();
 void testMulNegArgs();
 void testMulNegArgs32();
+void testMulNegSignExtend32();
+void testMulNegZeroExtend32();
 void testMulArgDouble(double);
 void testMulArgsDouble(double, double);
 void testCallSimpleDouble(double, double);

Modified: trunk/Source/_javascript_Core/b3/testb3_2.cpp (279849 => 279850)


--- trunk/Source/_javascript_Core/b3/testb3_2.cpp	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/b3/testb3_2.cpp	2021-07-12 20:51:11 UTC (rev 279850)
@@ -874,19 +874,6 @@
     CHECK(compileAndRun<int>(proc, a, b) == a * (-b));
 }
 
-void testMulNegArgArg(int a, int b)
-{
-    Procedure proc;
-    BasicBlock* root = proc.addBlock();
-    Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
-    Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
-    Value* negA = root->appendNew<Value>(proc, Neg, Origin(), argA);
-    Value* result = root->appendNew<Value>(proc, Mul, Origin(), negA, argB);
-    root->appendNew<Value>(proc, Return, Origin(), result);
-
-    CHECK(compileAndRun<int>(proc, a, b) == (-a) * b);
-}
-
 void testMulArgImm(int64_t a, int64_t b)
 {
     Procedure proc;
@@ -1014,7 +1001,7 @@
     for (auto a : testValues) {
         for (auto b : testValues) {
             for (auto c : testValues)
-                CHECK(invoke<int64_t>(*code, a.value, b.value, c.value) == a.value * b.value + c.value);
+                CHECK_EQ(invoke<int64_t>(*code, a.value, b.value, c.value), a.value * b.value + c.value);
         }
     }
 }
@@ -1039,7 +1026,7 @@
     for (auto a : testValues) {
         for (auto b : testValues) {
             for (auto c : testValues)
-                CHECK(invoke<int64_t>(*code, a.value, b.value, c.value) == a.value + b.value * c.value);
+                CHECK_EQ(invoke<int64_t>(*code, a.value, b.value, c.value), a.value + b.value * c.value);
         }
     }
 }
@@ -1046,7 +1033,7 @@
 
 void testMulAddSignExtend32ArgsLeft()
 {
-    // d = SExt32(n) *  SExt32(m) + a
+    // d = SExt32(n) * SExt32(m) + a
     Procedure proc;
     BasicBlock* root = proc.addBlock();
 
@@ -1118,6 +1105,84 @@
     }
 }
 
+void testMulAddZeroExtend32ArgsLeft()
+{
+    // d = ZExt32(n) * ZExt32(m) + a
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* nValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(), 
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
+    Value* mValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(), 
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
+    Value* aValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
+
+    Value* mulValue = root->appendNew<Value>(proc, Mul, Origin(), nValue, mValue);
+    Value* addValue = root->appendNew<Value>(proc, Add, Origin(), mulValue, aValue);
+    root->appendNewControlValue(proc, Return, Origin(), addValue);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "umaddl");
+
+    for (auto n : int32Operands()) {
+        for (auto m : int32Operands()) {
+            for (auto a : int64Operands()) {
+                uint32_t un = n.value;
+                uint32_t um = m.value;
+                int64_t lhs = invoke<int64_t>(*code, un, um, a.value);
+                int64_t rhs = static_cast<int64_t>(un) * static_cast<int64_t>(um) + a.value;
+                CHECK(lhs == rhs);
+            }
+        }
+    }
+}
+
+void testMulAddZeroExtend32ArgsRight()
+{
+    // d = a + ZExt32(n) * ZExt32(m)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* aValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* nValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(), 
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
+    Value* mValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(), 
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
+
+    Value* mulValue = root->appendNew<Value>(proc, Mul, Origin(), nValue, mValue);
+    Value* addValue = root->appendNew<Value>(proc, Add, Origin(), aValue, mulValue);
+    root->appendNewControlValue(proc, Return, Origin(), addValue);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "umaddl");
+
+    for (auto a : int64Operands()) {
+        for (auto n : int32Operands()) {
+            for (auto m : int32Operands()) {
+                uint32_t un = n.value;
+                uint32_t um = m.value;
+                int64_t lhs = invoke<int64_t>(*code, a.value, un, um);
+                int64_t rhs = a.value + static_cast<int64_t>(un) * static_cast<int64_t>(um);
+                CHECK(lhs == rhs);
+            }
+        }
+    }
+}
+
 void testMulAddArgsLeft32()
 {
     Procedure proc;
@@ -1194,7 +1259,7 @@
     for (auto a : testValues) {
         for (auto b : testValues) {
             for (auto c : testValues)
-                CHECK(invoke<int64_t>(*code, a.value, b.value, c.value) == a.value * b.value - c.value);
+                CHECK_EQ(invoke<int64_t>(*code, a.value, b.value, c.value), a.value * b.value - c.value);
         }
     }
 }
@@ -1219,7 +1284,7 @@
     for (auto a : testValues) {
         for (auto b : testValues) {
             for (auto c : testValues)
-                CHECK(invoke<int64_t>(*code, a.value, b.value, c.value) == a.value - b.value * c.value);
+                CHECK_EQ(invoke<int64_t>(*code, a.value, b.value, c.value), a.value - b.value * c.value);
         }
     }
 }
@@ -1275,12 +1340,12 @@
     for (auto a : testValues) {
         for (auto b : testValues) {
             for (auto c : testValues)
-                CHECK(invoke<int32_t>(*code, a.value, b.value, c.value) == a.value - b.value * c.value);
+                CHECK_EQ(invoke<int32_t>(*code, a.value, b.value, c.value), a.value - b.value * c.value);
         }
     }
 }
 
-void testMulSubSignExtend32Args()
+void testMulSubSignExtend32()
 {
     // d = a - SExt32(n) * SExt32(m)
     Procedure proc;
@@ -1317,6 +1382,62 @@
     }
 }
 
+void testMulSubZeroExtend32()
+{
+    // d = a - ZExt32(n) * ZExt32(m)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* aValue = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* nValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
+    Value* mValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
+
+    Value* mulValue = root->appendNew<Value>(proc, Mul, Origin(), nValue, mValue);
+    Value* subValue = root->appendNew<Value>(proc, Sub, Origin(), aValue, mulValue);
+    root->appendNewControlValue(proc, Return, Origin(), subValue);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "umsubl");
+
+    for (auto a : int64Operands()) {
+        for (auto n : int32Operands()) {
+            for (auto m : int32Operands()) {
+                uint32_t un = n.value;
+                uint32_t um = m.value;
+                int64_t lhs = invoke<int64_t>(*code, a.value, un, um);
+                int64_t rhs = a.value - static_cast<int64_t>(un) * static_cast<int64_t>(um);
+                CHECK(lhs == rhs);
+            }
+        }
+    }
+}
+
+void testMulNegArgArg(int32_t a, int32_t b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+    Value* negA = root->appendNew<Value>(proc, Neg, Origin(), argA);
+    Value* result = root->appendNew<Value>(proc, Mul, Origin(), negA, argB);
+    root->appendNew<Value>(proc, Return, Origin(), result);
+
+    auto code = compileProc(proc);
+    if (isARM64() && JSC::Options::defaultB3OptLevel() > 1)
+        checkUsesInstruction(*code, "mneg");
+    CHECK_EQ(invoke<int32_t>(*code, a, b), (-a) * b);
+}
+
 void testMulNegArgs()
 {
     Procedure proc;
@@ -1330,11 +1451,13 @@
     root->appendNewControlValue(proc, Return, Origin(), added);
 
     auto code = compileProc(proc);
+    if (isARM64() && JSC::Options::defaultB3OptLevel() > 1)
+        checkUsesInstruction(*code, "mneg");
 
     auto testValues = int64Operands();
     for (auto a : testValues) {
         for (auto b : testValues) {
-            CHECK(invoke<int64_t>(*code, a.value, b.value) == -(a.value * b.value));
+            CHECK_EQ(invoke<int64_t>(*code, a.value, b.value), -(a.value * b.value));
         }
     }
 }
@@ -1354,15 +1477,86 @@
     root->appendNewControlValue(proc, Return, Origin(), added);
 
     auto code = compileProc(proc);
+    if (isARM64() && JSC::Options::defaultB3OptLevel() > 1)
+        checkUsesInstruction(*code, "mneg");
 
     auto testValues = int32Operands();
     for (auto a : testValues) {
-        for (auto b : testValues) {
-            CHECK(invoke<int32_t>(*code, a.value, b.value) == -(a.value * b.value));
+        for (auto b : testValues)
+            CHECK_EQ(invoke<int32_t>(*code, a.value, b.value), -(a.value * b.value));
+    }
+}
+
+void testMulNegSignExtend32()
+{
+    // d = - (SExt32(n) * SExt32(m))
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* nValue = root->appendNew<Value>(
+        proc, SExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
+    Value* mValue = root->appendNew<Value>(
+        proc, SExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
+
+    Value* mulValue = root->appendNew<Value>(proc, Mul, Origin(), nValue, mValue);
+    Value* negValue = root->appendNew<Value>(proc, Neg, Origin(), mulValue);
+    root->appendNewControlValue(proc, Return, Origin(), negValue);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "smnegl");
+
+    for (auto n : int32Operands()) {
+        for (auto m : int32Operands()) {
+            int64_t lhs = invoke<int64_t>(*code, n.value, m.value);
+            int64_t rhs = -(static_cast<int64_t>(n.value) * static_cast<int64_t>(m.value));
+            CHECK(lhs == rhs);
         }
     }
 }
 
+void testMulNegZeroExtend32()
+{
+    // d = - (ZExt32(n) * ZExt32(m))
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* nValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
+    Value* mValue = root->appendNew<Value>(
+        proc, ZExt32, Origin(),
+        root->appendNew<Value>(
+            proc, Trunc, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
+
+    Value* mulValue = root->appendNew<Value>(proc, Mul, Origin(), nValue, mValue);
+    Value* negValue = root->appendNew<Value>(proc, Neg, Origin(), mulValue);
+    root->appendNewControlValue(proc, Return, Origin(), negValue);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "umnegl");
+
+    for (auto n : int32Operands()) {
+        for (auto m : int32Operands()) {
+            uint32_t un = n.value;
+            uint32_t um = m.value;
+            int64_t lhs = invoke<int64_t>(*code, un, um);
+            int64_t rhs = -(static_cast<int64_t>(un) * static_cast<int64_t>(um));
+            CHECK(lhs == rhs);
+        }
+    }
+}
+
 void testMulArgDouble(double a)
 {
     Procedure proc;

Modified: trunk/Source/_javascript_Core/b3/testb3_3.cpp (279849 => 279850)


--- trunk/Source/_javascript_Core/b3/testb3_3.cpp	2021-07-12 20:46:41 UTC (rev 279849)
+++ trunk/Source/_javascript_Core/b3/testb3_3.cpp	2021-07-12 20:51:11 UTC (rev 279850)
@@ -3370,13 +3370,18 @@
     RUN(testMulAddArgsRight32());
     RUN(testMulAddSignExtend32ArgsLeft());
     RUN(testMulAddSignExtend32ArgsRight());
+    RUN(testMulAddZeroExtend32ArgsLeft());
+    RUN(testMulAddZeroExtend32ArgsRight());
     RUN(testMulSubArgsLeft());
     RUN(testMulSubArgsRight());
     RUN(testMulSubArgsLeft32());
     RUN(testMulSubArgsRight32());
-    RUN(testMulSubSignExtend32Args());
+    RUN(testMulSubSignExtend32());
+    RUN(testMulSubZeroExtend32());
     RUN(testMulNegArgs());
     RUN(testMulNegArgs32());
+    RUN(testMulNegSignExtend32());
+    RUN(testMulNegZeroExtend32());
     
     RUN_BINARY(testMulArgNegArg, int64Operands(), int64Operands())
     RUN_BINARY(testMulNegArgArg, int64Operands(), int64Operands())
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to