Title: [280493] trunk/Source/_javascript_Core
Revision
280493
Author
yijia_hu...@apple.com
Date
2021-07-30 13:44:47 -0700 (Fri, 30 Jul 2021)

Log Message

Add Pre/Post-Indexed Address Mode to Air for ARM64
https://bugs.webkit.org/show_bug.cgi?id=228047

Reviewed by Phil Pizlo.

Pre-indexed addressing means that the address is the sum of the value in the 64-bit base register
and an offset, and the address is then written back to the base register. And post-indexed
addressing means that the address is the value in the 64-bit base register, and the sum of the
address and the offset is then written back to the base register. They are relatively common for
loops to iterate over an array by increasing/decreasing a pointer into the array at each iteration.
With such an addressing mode, the instruction selector can merge the increment and access the array.

#####################################
## Pre-Index Address Mode For Load ##
#####################################

LDR Wt, [Xn, #imm]!

In B3 Reduction Strength, since we have this reduction rule:
    Turn this: Load(Add(address, offset1), offset = offset2)
    Into this: Load(address, offset = offset1 + offset2)

Then, the equivalent pattern is:
    address = Add(base, offset)
    ...
    memory = Load(base, offset)

First, we convert it to the canonical form:
    address = Add(base, offset)
    newMemory = Load(base, offset) // move the memory to just after the address
    ...
    memory = Identity(newMemory)

Next, lower to Air:
    Move %base, %address
    Move (%address, prefix(offset)), %newMemory

######################################
## Post-Index Address Mode For Load ##
######################################

LDR Wt, [Xn], #imm

Then, the equivalent pattern is:
    memory = Load(base, 0)
    ...
    address = Add(base, offset)

First, we convert it to the canonical form:
    newOffset = Constant
    newAddress = Add(base, offset)
    memory = Load(base, 0) // move the offset and address to just before the memory
    ...
    offset = Identity(newOffset)
    address = Identity(newAddress)

Next, lower to Air:
    Move %base, %newAddress
    Move (%newAddress, postfix(offset)), %memory

#############################
## Pattern Match Algorithm ##
#############################

To detect the pattern for prefix/postfix increment address is tricky due to the structure in B3 IR. The
algorithm used in this patch is to collect the first valid values (add/load), then search for any
paired value (load/add) to match all of them. In worst case, the runtime complexity is O(n^2)
when n is the number of all values.

After collecting two sets of candidates, we match the prefix incremental address first since it seems
more beneficial to the compiler (shown in the next section). And then, go for the postfix one.

##############################################
## Test for Pre/Post-Increment Address Mode ##
##############################################

Given Loop with Pre-Increment:
int64_t ldr_pre(int64_t *p) {
    int64_t res = 0;
    while (res < 10)
        res += *++p;
    return res;
}

B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
    Int64 b@0 = Const64(0)
    Int64 b@2 = ArgumentReg(%x0)
    Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
    Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
    Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
    Int64 b@18 = Phi(ReadsLocalState)
    Int64 b@19 = Phi(ReadsLocalState)
    Int64 b@7 = Const64(10)
    Int32 b@8 = AboveEqual(b@18, $10(b@7))
    Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
    Int64 b@10 = Const64(8)
    Int64 b@11 = Add(b@19, $8(b@10))
    Int64 b@13 = Load(b@11, ControlDependent|Reads:Top)
    Int64 b@14 = Add(b@18, b@13)
    Void b@22 = Upsilon(b@14, ^18, WritesLocalState)
    Void b@23 = Upsilon(b@11, ^19, WritesLocalState)
    Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
    Void b@17 = Return(b@18, Terminal)
Variables:
    Int64 var0
    Int64 var1
------------------------------------------------------

W/O Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
    Move $8, %x3, $8(b@12)
    Add64 $8, %x0, %x1, b@11
    Move (%x0,%x3), %x0, b@13
    Add64 %x0, %x2, %x2, b@14
    Move %x1, %x0, b@23
    Jump b@16
Successors: #1
...
------------------------------------------------------

W/ Pre-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
    MoveWithIncrement64 (%x0,Pre($8)), %x2, b@13
    Add64 %x2, %x1, %x1, b@14
    Jump b@16
Successors: #1
...
------------------------------------------------------

Given Loop with Post-Increment:
int64_t ldr_pre(int64_t *p) {
    int64_t res = 0;
    while (res < 10)
        res += *p++;
    return res;
}

B3 IR:
------------------------------------------------------
BB#0: ; frequency = 1.000000
    Int64 b@0 = Const64(0)
    Int64 b@2 = ArgumentReg(%x0)
    Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
    Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
    Void b@4 = Jump(Terminal)
Successors: #1
BB#1: ; frequency = 1.000000
Predecessors: #0, #2
    Int64 b@18 = Phi(ReadsLocalState)
    Int64 b@19 = Phi(ReadsLocalState)
    Int64 b@7 = Const64(10)
    Int32 b@8 = AboveEqual(b@18, $10(b@7))
    Void b@9 = Branch(b@8, Terminal)
Successors: Then:#3, Else:#2
BB#2: ; frequency = 1.000000
Predecessors: #1
    Int64 b@10 = Load(b@19, ControlDependent|Reads:Top)
    Int64 b@11 = Add(b@18, b@10)
    Int64 b@12 = Const64(8)
    Int64 b@13 = Add(b@19, $8(b@12))
    Void b@22 = Upsilon(b@11, ^18, WritesLocalState)
    Void b@23 = Upsilon(b@13, ^19, WritesLocalState)
    Void b@16 = Jump(Terminal)
Successors: #1
BB#3: ; frequency = 1.000000
Predecessors: #1
    Void b@17 = Return(b@18, Terminal)
Variables:
    Int64 var0
    Int64 var1
------------------------------------------------------

W/O Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
    Move (%x0), %x2, b@10
    Add64 %x2, %x1, %x1, b@11
    Add64 $8, %x0, %x0, b@13
    Jump b@16
Successors: #1
...
------------------------------------------------------

W/ Post-Increment Address Mode:
------------------------------------------------------
...
BB#2: ; frequency = 1.000000
Predecessors: #1
    MoveWithIncrement64 (%x0,Post($8)), %x2, b@10
    Add64 %x2, %x1, %x1, b@11
    Jump b@16
Successors: #1
...
------------------------------------------------------

* Sources.txt:
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::PreIndexAddress::PreIndexAddress):
(JSC::AbstractMacroAssembler::PostIndexAddress::PostIndexAddress):
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::load64):
(JSC::MacroAssemblerARM64::load32):
(JSC::MacroAssemblerARM64::store64):
(JSC::MacroAssemblerARM64::store32):
* assembler/testmasm.cpp:
(JSC::testStorePrePostIndex32):
(JSC::testStorePrePostIndex64):
(JSC::testLoadPrePostIndex32):
(JSC::testLoadPrePostIndex64):
* b3/B3CanonicalizePrePostIncrements.cpp: Added.
(JSC::B3::canonicalizePrePostIncrements):
* b3/B3CanonicalizePrePostIncrements.h: Copied from Source/_javascript_Core/b3/B3ValueKeyInlines.h.
* b3/B3Generate.cpp:
(JSC::B3::generateToAir):
* b3/B3LowerToAir.cpp:
* b3/B3ValueKey.h:
* b3/B3ValueKeyInlines.h:
(JSC::B3::ValueKey::ValueKey):
* b3/air/AirArg.cpp:
(JSC::B3::Air::Arg::jsHash const):
(JSC::B3::Air::Arg::dump const):
(WTF::printInternal):
* b3/air/AirArg.h:
(JSC::B3::Air::Arg::preIndex):
(JSC::B3::Air::Arg::postIndex):
(JSC::B3::Air::Arg::isPreIndex const):
(JSC::B3::Air::Arg::isPostIndex const):
(JSC::B3::Air::Arg::isMemory const):
(JSC::B3::Air::Arg::base const):
(JSC::B3::Air::Arg::offset const):
(JSC::B3::Air::Arg::isGP const):
(JSC::B3::Air::Arg::isFP const):
(JSC::B3::Air::Arg::isValidPreIndexForm):
(JSC::B3::Air::Arg::isValidPostIndexForm):
(JSC::B3::Air::Arg::isValidForm const):
(JSC::B3::Air::Arg::forEachTmpFast):
(JSC::B3::Air::Arg::forEachTmp):
(JSC::B3::Air::Arg::asPreIndexAddress const):
(JSC::B3::Air::Arg::asPostIndexAddress const):
* b3/air/AirOpcode.opcodes:
* b3/air/opcode_generator.rb:
* b3/testb3.h:
* b3/testb3_3.cpp:
(testLoadPreIndex32):
(testLoadPreIndex64):
(testLoadPostIndex32):
(testLoadPostIndex64):
(addShrTests):
* jit/ExecutableAllocator.cpp:
(JSC::jitWriteThunkGenerator):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (280492 => 280493)


--- trunk/Source/_javascript_Core/ChangeLog	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-07-30 20:44:47 UTC (rev 280493)
@@ -1,3 +1,275 @@
+2021-07-30  Yijia Huang  <yijia_hu...@apple.com>
+
+        Add Pre/Post-Indexed Address Mode to Air for ARM64
+        https://bugs.webkit.org/show_bug.cgi?id=228047
+
+        Reviewed by Phil Pizlo.
+
+        Pre-indexed addressing means that the address is the sum of the value in the 64-bit base register 
+        and an offset, and the address is then written back to the base register. And post-indexed 
+        addressing means that the address is the value in the 64-bit base register, and the sum of the 
+        address and the offset is then written back to the base register. They are relatively common for 
+        loops to iterate over an array by increasing/decreasing a pointer into the array at each iteration. 
+        With such an addressing mode, the instruction selector can merge the increment and access the array.
+
+        #####################################
+        ## Pre-Index Address Mode For Load ##
+        #####################################
+
+        LDR Wt, [Xn, #imm]!
+
+        In B3 Reduction Strength, since we have this reduction rule:
+            Turn this: Load(Add(address, offset1), offset = offset2)
+            Into this: Load(address, offset = offset1 + offset2)
+
+        Then, the equivalent pattern is:
+            address = Add(base, offset)
+            ...
+            memory = Load(base, offset)
+
+        First, we convert it to the canonical form:
+            address = Add(base, offset)
+            newMemory = Load(base, offset) // move the memory to just after the address
+            ...
+            memory = Identity(newMemory)
+
+        Next, lower to Air:
+            Move %base, %address
+            Move (%address, prefix(offset)), %newMemory
+
+        ######################################
+        ## Post-Index Address Mode For Load ##
+        ######################################
+
+        LDR Wt, [Xn], #imm
+
+        Then, the equivalent pattern is:
+            memory = Load(base, 0)
+            ...
+            address = Add(base, offset)
+
+        First, we convert it to the canonical form:
+            newOffset = Constant
+            newAddress = Add(base, offset)
+            memory = Load(base, 0) // move the offset and address to just before the memory
+            ...
+            offset = Identity(newOffset)
+            address = Identity(newAddress)
+
+        Next, lower to Air:
+            Move %base, %newAddress
+            Move (%newAddress, postfix(offset)), %memory
+
+        #############################
+        ## Pattern Match Algorithm ##
+        #############################
+
+        To detect the pattern for prefix/postfix increment address is tricky due to the structure in B3 IR. The 
+        algorithm used in this patch is to collect the first valid values (add/load), then search for any 
+        paired value (load/add) to match all of them. In worst case, the runtime complexity is O(n^2) 
+        when n is the number of all values.
+
+        After collecting two sets of candidates, we match the prefix incremental address first since it seems 
+        more beneficial to the compiler (shown in the next section). And then, go for the postfix one.
+
+        ##############################################
+        ## Test for Pre/Post-Increment Address Mode ##
+        ##############################################
+
+        Given Loop with Pre-Increment:
+        int64_t ldr_pre(int64_t *p) {
+            int64_t res = 0;
+            while (res < 10)
+                res += *++p;
+            return res;
+        }
+
+        B3 IR:
+        ------------------------------------------------------
+        BB#0: ; frequency = 1.000000
+            Int64 b@0 = Const64(0)
+            Int64 b@2 = ArgumentReg(%x0)
+            Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
+            Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
+            Void b@4 = Jump(Terminal)
+        Successors: #1
+        BB#1: ; frequency = 1.000000
+        Predecessors: #0, #2
+            Int64 b@18 = Phi(ReadsLocalState)
+            Int64 b@19 = Phi(ReadsLocalState)
+            Int64 b@7 = Const64(10)
+            Int32 b@8 = AboveEqual(b@18, $10(b@7))
+            Void b@9 = Branch(b@8, Terminal)
+        Successors: Then:#3, Else:#2
+        BB#2: ; frequency = 1.000000
+        Predecessors: #1
+            Int64 b@10 = Const64(8)
+            Int64 b@11 = Add(b@19, $8(b@10))
+            Int64 b@13 = Load(b@11, ControlDependent|Reads:Top)
+            Int64 b@14 = Add(b@18, b@13)
+            Void b@22 = Upsilon(b@14, ^18, WritesLocalState)
+            Void b@23 = Upsilon(b@11, ^19, WritesLocalState)
+            Void b@16 = Jump(Terminal)
+        Successors: #1
+        BB#3: ; frequency = 1.000000
+        Predecessors: #1
+            Void b@17 = Return(b@18, Terminal)
+        Variables:
+            Int64 var0
+            Int64 var1
+        ------------------------------------------------------
+
+        W/O Pre-Increment Address Mode:
+        ------------------------------------------------------
+        ...
+        BB#2: ; frequency = 1.000000
+        Predecessors: #1
+            Move $8, %x3, $8(b@12)
+            Add64 $8, %x0, %x1, b@11
+            Move (%x0,%x3), %x0, b@13
+            Add64 %x0, %x2, %x2, b@14
+            Move %x1, %x0, b@23
+            Jump b@16
+        Successors: #1
+        ...
+        ------------------------------------------------------
+
+        W/ Pre-Increment Address Mode:
+        ------------------------------------------------------
+        ...
+        BB#2: ; frequency = 1.000000
+        Predecessors: #1
+            MoveWithIncrement64 (%x0,Pre($8)), %x2, b@13
+            Add64 %x2, %x1, %x1, b@14
+            Jump b@16
+        Successors: #1
+        ...
+        ------------------------------------------------------
+
+        Given Loop with Post-Increment:
+        int64_t ldr_pre(int64_t *p) {
+            int64_t res = 0;
+            while (res < 10)
+                res += *p++;
+            return res;
+        }
+
+        B3 IR:
+        ------------------------------------------------------
+        BB#0: ; frequency = 1.000000
+            Int64 b@0 = Const64(0)
+            Int64 b@2 = ArgumentReg(%x0)
+            Void b@20 = Upsilon($0(b@0), ^18, WritesLocalState)
+            Void b@21 = Upsilon(b@2, ^19, WritesLocalState)
+            Void b@4 = Jump(Terminal)
+        Successors: #1
+        BB#1: ; frequency = 1.000000
+        Predecessors: #0, #2
+            Int64 b@18 = Phi(ReadsLocalState)
+            Int64 b@19 = Phi(ReadsLocalState)
+            Int64 b@7 = Const64(10)
+            Int32 b@8 = AboveEqual(b@18, $10(b@7))
+            Void b@9 = Branch(b@8, Terminal)
+        Successors: Then:#3, Else:#2
+        BB#2: ; frequency = 1.000000
+        Predecessors: #1
+            Int64 b@10 = Load(b@19, ControlDependent|Reads:Top)
+            Int64 b@11 = Add(b@18, b@10)
+            Int64 b@12 = Const64(8)
+            Int64 b@13 = Add(b@19, $8(b@12))
+            Void b@22 = Upsilon(b@11, ^18, WritesLocalState)
+            Void b@23 = Upsilon(b@13, ^19, WritesLocalState)
+            Void b@16 = Jump(Terminal)
+        Successors: #1
+        BB#3: ; frequency = 1.000000
+        Predecessors: #1
+            Void b@17 = Return(b@18, Terminal)
+        Variables:
+            Int64 var0
+            Int64 var1
+        ------------------------------------------------------
+
+        W/O Post-Increment Address Mode:
+        ------------------------------------------------------
+        ...
+        BB#2: ; frequency = 1.000000
+        Predecessors: #1
+            Move (%x0), %x2, b@10
+            Add64 %x2, %x1, %x1, b@11
+            Add64 $8, %x0, %x0, b@13
+            Jump b@16
+        Successors: #1
+        ...
+        ------------------------------------------------------
+
+        W/ Post-Increment Address Mode:
+        ------------------------------------------------------
+        ...
+        BB#2: ; frequency = 1.000000
+        Predecessors: #1
+            MoveWithIncrement64 (%x0,Post($8)), %x2, b@10
+            Add64 %x2, %x1, %x1, b@11
+            Jump b@16
+        Successors: #1
+        ...
+        ------------------------------------------------------
+
+        * Sources.txt:
+        * assembler/AbstractMacroAssembler.h:
+        (JSC::AbstractMacroAssembler::PreIndexAddress::PreIndexAddress):
+        (JSC::AbstractMacroAssembler::PostIndexAddress::PostIndexAddress):
+        * assembler/MacroAssemblerARM64.h:
+        (JSC::MacroAssemblerARM64::load64):
+        (JSC::MacroAssemblerARM64::load32):
+        (JSC::MacroAssemblerARM64::store64):
+        (JSC::MacroAssemblerARM64::store32):
+        * assembler/testmasm.cpp:
+        (JSC::testStorePrePostIndex32):
+        (JSC::testStorePrePostIndex64):
+        (JSC::testLoadPrePostIndex32):
+        (JSC::testLoadPrePostIndex64):
+        * b3/B3CanonicalizePrePostIncrements.cpp: Added.
+        (JSC::B3::canonicalizePrePostIncrements):
+        * b3/B3CanonicalizePrePostIncrements.h: Copied from Source/_javascript_Core/b3/B3ValueKeyInlines.h.
+        * b3/B3Generate.cpp:
+        (JSC::B3::generateToAir):
+        * b3/B3LowerToAir.cpp:
+        * b3/B3ValueKey.h:
+        * b3/B3ValueKeyInlines.h:
+        (JSC::B3::ValueKey::ValueKey):
+        * b3/air/AirArg.cpp:
+        (JSC::B3::Air::Arg::jsHash const):
+        (JSC::B3::Air::Arg::dump const):
+        (WTF::printInternal):
+        * b3/air/AirArg.h:
+        (JSC::B3::Air::Arg::preIndex):
+        (JSC::B3::Air::Arg::postIndex):
+        (JSC::B3::Air::Arg::isPreIndex const):
+        (JSC::B3::Air::Arg::isPostIndex const):
+        (JSC::B3::Air::Arg::isMemory const):
+        (JSC::B3::Air::Arg::base const):
+        (JSC::B3::Air::Arg::offset const):
+        (JSC::B3::Air::Arg::isGP const):
+        (JSC::B3::Air::Arg::isFP const):
+        (JSC::B3::Air::Arg::isValidPreIndexForm):
+        (JSC::B3::Air::Arg::isValidPostIndexForm):
+        (JSC::B3::Air::Arg::isValidForm const):
+        (JSC::B3::Air::Arg::forEachTmpFast):
+        (JSC::B3::Air::Arg::forEachTmp):
+        (JSC::B3::Air::Arg::asPreIndexAddress const):
+        (JSC::B3::Air::Arg::asPostIndexAddress const):
+        * b3/air/AirOpcode.opcodes:
+        * b3/air/opcode_generator.rb:
+        * b3/testb3.h:
+        * b3/testb3_3.cpp:
+        (testLoadPreIndex32):
+        (testLoadPreIndex64):
+        (testLoadPostIndex32):
+        (testLoadPostIndex64):
+        (addShrTests):
+        * jit/ExecutableAllocator.cpp:
+        (JSC::jitWriteThunkGenerator):
+
 2021-07-30  Alexey Shvayka  <shvaikal...@gmail.com>
 
         REGRESSION (r280460): 42 JSC test failures on Debug arm64 with ASSERTION FAILED: !m_needExceptionCheck

Modified: trunk/Source/_javascript_Core/Sources.txt (280492 => 280493)


--- trunk/Source/_javascript_Core/Sources.txt	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/Sources.txt	2021-07-30 20:44:47 UTC (rev 280493)
@@ -113,6 +113,7 @@
 b3/B3BlockInsertionSet.cpp
 b3/B3BottomTupleValue.cpp
 b3/B3BreakCriticalEdges.cpp
+b3/B3CanonicalizePrePostIncrements.cpp
 b3/B3CCallValue.cpp
 b3/B3CaseCollection.cpp
 b3/B3CheckSpecial.cpp

Modified: trunk/Source/_javascript_Core/assembler/AbstractMacroAssembler.h (280492 => 280493)


--- trunk/Source/_javascript_Core/assembler/AbstractMacroAssembler.h	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/assembler/AbstractMacroAssembler.h	2021-07-30 20:44:47 UTC (rev 280493)
@@ -245,6 +245,34 @@
         }
     };
 
+    // PreIndexAddress:
+    //
+    // Describes an address with base address and pre-increment/decrement index.
+    struct PreIndexAddress {
+        PreIndexAddress(RegisterID base, int index)
+            : base(base)
+            , index(index)
+        {
+        }
+
+        RegisterID base;
+        int index;
+    };
+
+    // PostIndexAddress:
+    //
+    // Describes an address with base address and post-increment/decrement index.
+    struct PostIndexAddress {
+        PostIndexAddress(RegisterID base, int index)
+            : base(base)
+            , index(index)
+        {
+        }
+
+        RegisterID base;
+        int index;
+    };
+
     // AbsoluteAddress:
     //
     // Describes an memory operand given by a pointer.  For regular load & store

Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h (280492 => 280493)


--- trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h	2021-07-30 20:44:47 UTC (rev 280493)
@@ -1447,11 +1447,16 @@
         load<64>(address, dest);
     }
 
-    void load64(RegisterID src, PostIndex simm, RegisterID dest)
+    void load64(PreIndexAddress src, RegisterID dest)
     {
-        m_assembler.ldr<64>(dest, src, simm);
+        m_assembler.ldr<64>(dest, src.base, PreIndex(src.index));
     }
 
+    void load64(PostIndexAddress src, RegisterID dest)
+    {
+        m_assembler.ldr<64>(dest, src.base, PostIndex(src.index));
+    }
+
     DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID dest)
     {
         DataLabel32 label(this);
@@ -1548,6 +1553,16 @@
         load<32>(address, dest);
     }
 
+    void load32(PreIndexAddress src, RegisterID dest)
+    {
+        m_assembler.ldr<32>(dest, src.base, PreIndex(src.index));
+    }
+
+    void load32(PostIndexAddress src, RegisterID dest)
+    {
+        m_assembler.ldr<32>(dest, src.base, PostIndex(src.index));
+    }
+
     DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest)
     {
         DataLabel32 label(this);
@@ -1747,7 +1762,17 @@
         m_assembler.add<64>(memoryTempRegister, memoryTempRegister, address.index, indexExtendType(address), address.scale);
         m_assembler.str<64>(src, address.base, memoryTempRegister);
     }
-    
+
+    void store64(RegisterID src, PreIndexAddress dest)
+    {
+        m_assembler.str<64>(src, dest.base, PreIndex(dest.index));
+    }
+
+    void store64(RegisterID src, PostIndexAddress dest)
+    {
+        m_assembler.str<64>(src, dest.base, PostIndex(dest.index));
+    }
+
     void store64(RegisterID src, const void* address)
     {
         store<64>(src, address);
@@ -1791,11 +1816,6 @@
         store64(dataTempRegister, address);
     }
 
-    void store64(RegisterID src, RegisterID dest, PostIndex simm)
-    {
-        m_assembler.str<64>(src, dest, simm);
-    }
-    
     DataLabel32 store64WithAddressOffsetPatch(RegisterID src, Address address)
     {
         DataLabel32 label(this);
@@ -1895,6 +1915,16 @@
         store32(dataTempRegister, address);
     }
 
+    void store32(RegisterID src, PreIndexAddress dest)
+    {
+        m_assembler.str<32>(src, dest.base, PreIndex(dest.index));
+    }
+
+    void store32(RegisterID src, PostIndexAddress dest)
+    {
+        m_assembler.str<32>(src, dest.base, PostIndex(dest.index));
+    }
+
     DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address)
     {
         DataLabel32 label(this);

Modified: trunk/Source/_javascript_Core/assembler/testmasm.cpp (280492 => 280493)


--- trunk/Source/_javascript_Core/assembler/testmasm.cpp	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/assembler/testmasm.cpp	2021-07-30 20:44:47 UTC (rev 280493)
@@ -2212,6 +2212,176 @@
         }
     }
 }
+
+void testStorePrePostIndex32()
+{
+    int32_t nums[] = { 1, 2, 3 };
+    intptr_t addr = bitwise_cast<intptr_t>(&nums[1]);
+    int32_t index = sizeof(int32_t);
+
+    auto test1 = [&] (int32_t src) {
+        auto store = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // *++p1 = 4; return p1;
+            jit.store32(GPRInfo::argumentGPR0, CCallHelpers::PreIndexAddress(GPRInfo::argumentGPR1, index));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<intptr_t>(store, src, addr);
+    };
+
+    int32_t* p1 = bitwise_cast<int32_t*>(test1(4));
+    CHECK_EQ(*p1, 4);
+    CHECK_EQ(*--p1, nums[1]);
+
+    auto test2 = [&] (int32_t src) {
+        auto store = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // *p2++ = 5; return p2;
+            jit.store32(GPRInfo::argumentGPR0, CCallHelpers::PostIndexAddress(GPRInfo::argumentGPR1, index));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<intptr_t>(store, src, addr);
+    };
+
+    int32_t* p2 = bitwise_cast<int32_t*>(test2(5));
+    CHECK_EQ(*p2, 4);
+    CHECK_EQ(*--p2, 5);
+}
+
+void testStorePrePostIndex64()
+{
+    int64_t nums[] = { 1, 2, 3 };
+    intptr_t addr = bitwise_cast<intptr_t>(&nums[1]);
+    int32_t index = sizeof(int64_t);
+
+    auto test1 = [&] (int64_t src) {
+        auto store = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // *++p1 = 4; return p1;
+            jit.store64(GPRInfo::argumentGPR0, CCallHelpers::PreIndexAddress(GPRInfo::argumentGPR1, index));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<intptr_t>(store, src, addr);
+    };
+
+    int64_t* p1 = bitwise_cast<int64_t*>(test1(4));
+    CHECK_EQ(*p1, 4);
+    CHECK_EQ(*--p1, nums[1]);
+
+    auto test2 = [&] (int64_t src) {
+        auto store = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // *p2++ = 5; return p2;
+            jit.store64(GPRInfo::argumentGPR0, CCallHelpers::PostIndexAddress(GPRInfo::argumentGPR1, index));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<intptr_t>(store, src, addr);
+    };
+
+    int64_t* p2 = bitwise_cast<int64_t*>(test2(5));
+    CHECK_EQ(*p2, 4);
+    CHECK_EQ(*--p2, 5);
+}
+
+void testLoadPrePostIndex32()
+{
+    int32_t nums[] = { 1, 2, 3 };
+    int32_t index = sizeof(int32_t);
+
+    auto test1 = [&] (int32_t replace) {
+        auto load = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // res = *++p1; *p1 = 4; return res;
+            jit.load32(CCallHelpers::PreIndexAddress(GPRInfo::argumentGPR0, index), GPRInfo::argumentGPR1);
+            jit.store32(CCallHelpers::TrustedImm32(replace), CCallHelpers::ImplicitAddress(GPRInfo::argumentGPR0));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<int32_t>(load, &nums[1]);
+    };
+
+    CHECK_EQ(test1(4), 3);
+    CHECK_EQ(nums[2], 4);
+
+    auto test2 = [&] (int32_t replace) {
+        auto load = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // res = *p2++; *p2 = 5; return res;
+            jit.load32(CCallHelpers::PostIndexAddress(GPRInfo::argumentGPR0, index), GPRInfo::argumentGPR1);
+            jit.store32(CCallHelpers::TrustedImm32(replace), CCallHelpers::ImplicitAddress(GPRInfo::argumentGPR0));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<int32_t>(load, &nums[1]);
+    };
+
+    CHECK_EQ(test2(5), 2);
+    CHECK_EQ(nums[2], 5);
+}
+
+void testLoadPrePostIndex64()
+{
+    int64_t nums[] = { 1, 2, 3 };
+    int32_t index = sizeof(int64_t);
+
+    auto test1 = [&] (int64_t replace) {
+        auto load = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // res = *++p1; *p1 = 4; return res;
+            jit.load64(CCallHelpers::PreIndexAddress(GPRInfo::argumentGPR0, index), GPRInfo::argumentGPR1);
+            jit.store64(CCallHelpers::TrustedImm64(replace), CCallHelpers::ImplicitAddress(GPRInfo::argumentGPR0));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<int64_t>(load, &nums[1]);
+    };
+
+    CHECK_EQ(test1(4), 3);
+    CHECK_EQ(nums[2], 4);
+
+    auto test2 = [&] (int64_t replace) {
+        auto load = compile([=] (CCallHelpers& jit) {
+            emitFunctionPrologue(jit);
+
+            // res = *p2++; *p2 = 5; return res;
+            jit.load64(CCallHelpers::PostIndexAddress(GPRInfo::argumentGPR0, index), GPRInfo::argumentGPR1);
+            jit.store64(CCallHelpers::TrustedImm64(replace), CCallHelpers::ImplicitAddress(GPRInfo::argumentGPR0));
+            jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
+
+            emitFunctionEpilogue(jit);
+            jit.ret();
+        });
+        return invoke<int64_t>(load, &nums[1]);
+    };
+
+    CHECK_EQ(test2(5), 2);
+    CHECK_EQ(nums[2], 5);
+}
 #endif
 
 #if CPU(X86) || CPU(X86_64) || CPU(ARM64)
@@ -4805,6 +4975,11 @@
     RUN(testXorNotWithLeftShift64());
     RUN(testXorNotWithRightShift64());
     RUN(testXorNotWithUnsignedRightShift64());
+
+    RUN(testStorePrePostIndex32());
+    RUN(testStorePrePostIndex64());
+    RUN(testLoadPrePostIndex32());
+    RUN(testLoadPrePostIndex64());
 #endif
 
 #if CPU(ARM64E)

Added: trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.cpp (0 => 280493)


--- trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.cpp	2021-07-30 20:44:47 UTC (rev 280493)
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "B3CanonicalizePrePostIncrements.h"
+
+#include "B3BackwardsDominators.h"
+#include "B3BasicBlockInlines.h"
+#include "B3BlockInsertionSet.h"
+#include "B3Dominators.h"
+#include "B3InsertionSetInlines.h"
+#include "B3PhaseScope.h"
+#include "B3ProcedureInlines.h"
+#include "B3ValueInlines.h"
+#include "B3ValueKeyInlines.h"
+#include <wtf/HashMap.h>
+#include <wtf/IndexSet.h>
+#include <wtf/StdLibExtras.h>
+
+#if ENABLE(B3_JIT)
+
+namespace JSC { namespace B3 {
+
+bool canonicalizePrePostIncrements(Procedure& proc)
+{
+    if (!isARM64())
+        return false;
+    PhaseScope phaseScope(proc, "canonicalizePrePostIncrements");
+    using Arg = Air::Arg;
+
+    InsertionSet insertionSet { proc };
+    BlockInsertionSet blockInsertionSet { proc };
+    BasicBlock* block { nullptr };
+    unsigned index { 0 };
+
+    Dominators& dominators = proc.dominators();
+    BackwardsDominators& backwardsDominators = proc.backwardsDominators();
+
+    HashMap<ValueKey, Vector<Value*>> baseOffsetToAddresses;
+    HashMap<MemoryValue*, Value*> preIndexCandidates;
+    HashMap<Value*, Vector<MemoryValue*>> baseToMemories;
+    HashMap<MemoryValue*, Value*> postIndexCandidates;
+    HashMap<Value*, unsigned> valueToIndex;
+    IndexSet<Value*> ignoredValues;
+
+    // Pre-Index Pattern:
+    //     address = Add(base, offset)
+    //     ...
+    //     memory = Load(base, offset)
+    // Post-Index Pattern:
+    //     memory = Load(base, 0)
+    //     ...
+    //     address = Add(base, offset)
+    auto tryPrePostIndex = [&] (Value* value) {
+        switch (value->opcode()) {
+        case Load: {
+            MemoryValue* memory = value->as<MemoryValue>();
+
+            auto tryPrePostIndexMemory = [&] () {
+                if (memory->type() != Int32 && memory->type() != Int64)
+                    return;
+                if (memory->offset()) {
+                    if (!Arg::isValidPreIndexForm(memory->offset()))
+                        return;
+                    ValueKey baseOffsetkey = ValueKey(memory->child(0), static_cast<int64_t>(memory->offset()));
+                    if (!baseOffsetToAddresses.contains(baseOffsetkey))
+                        return;
+                    for (Value* address : baseOffsetToAddresses.get(baseOffsetkey))
+                        preIndexCandidates.add(memory, address);
+                } else
+                    baseToMemories.add(memory->child(0), Vector<MemoryValue*>()).iterator->value.append(memory);
+                valueToIndex.add(value, index);
+            };
+
+            tryPrePostIndexMemory();
+            break;
+        }
+
+        case Add: {
+            Value* left = value->child(0);
+            Value* right = value->child(1);
+
+            auto tryPotentialPostIndexAddress = [&] () {
+                if (!right->hasIntPtr() || value->type() != Int64)
+                    return;
+                intptr_t offset = right->asIntPtr();
+                Value::OffsetType smallOffset = static_cast<Value::OffsetType>(offset);
+                if (smallOffset != offset || !Arg::isValidPreIndexForm(smallOffset))
+                    return;
+                // so far this Add value is a valid address candidate for both prefix and postfix pattern
+                ValueKey baseOffsetkey = ValueKey(left, static_cast<int64_t>(smallOffset));
+                baseOffsetToAddresses.add(baseOffsetkey, Vector<Value*>()).iterator->value.append(value);
+                valueToIndex.add(value, index);
+                if (!baseToMemories.contains(left))
+                    return;
+                for (MemoryValue* memory : baseToMemories.get(left))
+                    postIndexCandidates.add(memory, value);
+            };
+
+            tryPotentialPostIndexAddress();
+            break;
+        }
+
+        default:
+            break;
+        }
+    };
+
+    for (BasicBlock* basicBlock : proc.blocksInPreOrder()) {
+        block = basicBlock;
+        for (index = 0; index < basicBlock->size(); ++index) {
+            Value* value = basicBlock->at(index);
+            tryPrePostIndex(value);
+        }
+    }
+
+    auto controlEquivalent = [&] (Value* v1, Value* v2) -> bool {
+        return (dominators.dominates(v1->owner, v2->owner) && backwardsDominators.dominates(v2->owner, v1->owner))
+            || (dominators.dominates(v2->owner, v1->owner) && backwardsDominators.dominates(v1->owner, v2->owner));
+    };
+
+    auto detect = [&] (HashMap<MemoryValue*, Value*> candidates) {
+        for (auto itr = candidates.begin(); itr != candidates.end(); ++itr) {
+            MemoryValue* memory = itr->key;
+            Value* address = itr->value;
+            if (ignoredValues.contains(memory) || ignoredValues.contains(address) || !controlEquivalent(memory, address))
+                continue;
+
+            if (candidates == preIndexCandidates) {
+                // address = Add(base, offset)       address = Add(base, offset)
+                //                              -->  newMemory = Load(base, offset)
+                // ...                               ...
+                // memory = Load(base, offset)       memory = Identity(newMemory)
+                MemoryValue* newMemory = insertionSet.insert<MemoryValue>(valueToIndex.get(address) + 1, Load, memory->type(), address->origin(), memory->lastChild());
+                newMemory->setOffset(memory->offset());
+                memory->replaceWithIdentity(newMemory);
+                insertionSet.execute(address->owner);
+            } else {
+                //                                   newOffset = Constant
+                //                                   newAddress = Add(base, newOffset)
+                // memory = Load(base, 0)            memory = Load(base, 0)
+                // ...                          -->  ...
+                // offset = Constant                 offset = Identity(newOffset)
+                // address = Add(base, offset)       address = Identity(newAddress)
+                unsigned memoryIndex = valueToIndex.get(memory);
+                Value* newOffset = insertionSet.insert<Const64Value>(memoryIndex, memory->origin(), address->child(1)->asInt());
+                Value* newAddress = insertionSet.insert<Value>(memoryIndex, Add, memory->origin(), address->child(0), newOffset);
+                address->child(1)->replaceWithIdentity(newOffset);
+                address->replaceWithIdentity(newAddress);
+                insertionSet.execute(memory->owner);
+            }
+
+            ignoredValues.add(address);
+            ignoredValues.add(memory);
+        }
+    };
+
+    detect(preIndexCandidates);
+    detect(postIndexCandidates);
+    return true;
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)

Copied: trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.h (from rev 280492, trunk/Source/_javascript_Core/b3/B3ValueKeyInlines.h) (0 => 280493)


--- trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.h	2021-07-30 20:44:47 UTC (rev 280493)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+namespace JSC { namespace B3 {
+
+class Procedure;
+
+JS_EXPORT_PRIVATE bool canonicalizePrePostIncrements(Procedure&);
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+

Modified: trunk/Source/_javascript_Core/b3/B3Generate.cpp (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/B3Generate.cpp	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/B3Generate.cpp	2021-07-30 20:44:47 UTC (rev 280493)
@@ -29,6 +29,7 @@
 #if ENABLE(B3_JIT)
 
 #include "AirGenerate.h"
+#include "B3CanonicalizePrePostIncrements.h"
 #include "B3Common.h"
 #include "B3DuplicateTails.h"
 #include "B3EliminateCommonSubexpressions.h"
@@ -119,6 +120,9 @@
     moveConstants(procedure);
     eliminateDeadCode(procedure);
 
+    if (procedure.optLevel() >= 2)
+        canonicalizePrePostIncrements(procedure);
+
     // FIXME: We should run pureCSE here to clean up some platform specific changes from the previous phases.
     // https://bugs.webkit.org/show_bug.cgi?id=164873
 

Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2021-07-30 20:44:47 UTC (rev 280493)
@@ -207,7 +207,7 @@
 
             finishAppendingInstructions(m_blockToBlock[block]);
         }
-        
+
         m_blockInsertionSet.execute();
 
         Air::InsertionSet insertionSet(m_code);
@@ -546,7 +546,7 @@
             return std::nullopt;
         return scale;
     }
-    
+
     // This turns the given operand into an address.
     template<typename Int, typename = Value::IsLegalOffset<Int>>
     Arg effectiveAddr(Value* address, Int offset, Width width)
@@ -2559,6 +2559,46 @@
                     }
                 }
             }
+
+            // Pre-Index Canonical Form:
+            //     address = add(base, offset)
+            //     memory = load(base, offset)
+            // Post-Index Canonical Form:
+            //     memory = load(base, 0)
+            //     address = add(base, offset)
+            auto tryAppendIncrementAddress = [&] () -> bool {
+                Air::Opcode opcode = tryOpcodeForType(MoveWithIncrement32, MoveWithIncrement64, memory->type());
+                if (!isValidForm(opcode, Arg::PreIndex, Arg::Tmp) || !m_index)
+                    return false;
+                Value* address = m_block->at(m_index - 1);
+                if (address->opcode() != Add || address->type() != Int64)
+                    return false;
+                if (address->child(0) != memory->lastChild() || !address->child(1)->hasIntPtr())
+                    return false;
+                intptr_t offset = address->child(1)->asIntPtr();
+                Value::OffsetType smallOffset = static_cast<Value::OffsetType>(offset);
+                if (smallOffset != offset || !Arg::isValidPostIndexForm(smallOffset))
+                    return false;
+
+                Arg incrementArg = Arg();
+                if (memory->offset()) {
+                    if (smallOffset == memory->offset())
+                        incrementArg = Arg::preIndex(tmp(address), smallOffset);
+                } else
+                    incrementArg = Arg::postIndex(tmp(address), smallOffset);
+
+                if (incrementArg) {
+                    append(relaxedMoveForType(address->type()), tmp(address->child(0)), tmp(address));
+                    append(opcode, incrementArg, tmp(memory));
+                    m_locked.add(address);
+                    return true;
+                }
+                return false;
+            };
+
+            if (tryAppendIncrementAddress())
+                return;
+
             append(trappingInst(m_value, kind, m_value, addr(m_value), tmp(m_value)));
             return;
         }

Modified: trunk/Source/_javascript_Core/b3/B3ValueKey.h (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/B3ValueKey.h	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/B3ValueKey.h	2021-07-30 20:44:47 UTC (rev 280493)
@@ -56,6 +56,8 @@
     {
     }
 
+    ValueKey(Value* child, int64_t value);
+
     ValueKey(Kind, Type, Value* child);
 
     ValueKey(Kind, Type, Value* left, Value* right);

Modified: trunk/Source/_javascript_Core/b3/B3ValueKeyInlines.h (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/B3ValueKeyInlines.h	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/B3ValueKeyInlines.h	2021-07-30 20:44:47 UTC (rev 280493)
@@ -33,6 +33,14 @@
 
 namespace JSC { namespace B3 {
 
+inline ValueKey::ValueKey(Value* child, int64_t value)
+{
+    m_kind = Oops;
+    m_type = Void;
+    u.indices[0] = child->index();
+    u.value = value;
+}
+
 inline ValueKey::ValueKey(Kind kind, Type type, Value* child)
     : m_kind(kind)
     , m_type(type)

Modified: trunk/Source/_javascript_Core/b3/air/AirArg.cpp (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/air/AirArg.cpp	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.cpp	2021-07-30 20:44:47 UTC (rev 280493)
@@ -130,6 +130,11 @@
         result += m_base.internalValue();
         result += m_index.internalValue();
         break;
+    case PreIndex:
+    case PostIndex:
+        result += m_offset;
+        result += m_base.internalValue();
+        break;
     case Stack:
         result += static_cast<unsigned>(m_scale);
         result += stackSlot()->index();
@@ -161,7 +166,7 @@
         out.printf("$0x%llx", static_cast<long long unsigned>(m_offset));
         return;
     case ZeroReg:
-        out.print("%%xzr");
+        out.print("%xzr");
         return;
     case SimpleAddr:
         out.print("(", base(), ")");
@@ -180,6 +185,12 @@
             out.print(",", scale());
         out.print(")");
         return;
+    case PreIndex:
+        out.print("(", base(), ",Pre($", offset(), "))");
+        return;
+    case PostIndex:
+        out.print("(", base(), ",Post($", offset(), "))");
+        return;
     case Stack:
         if (offset())
             out.print(offset());
@@ -261,6 +272,12 @@
     case Arg::Index:
         out.print("Index");
         return;
+    case Arg::PreIndex:
+        out.print("PreIndex");
+        return;
+    case Arg::PostIndex:
+        out.print("PostIndex");
+        return;
     case Arg::RelCond:
         out.print("RelCond");
         return;

Modified: trunk/Source/_javascript_Core/b3/air/AirArg.h (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/air/AirArg.h	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.h	2021-07-30 20:44:47 UTC (rev 280493)
@@ -77,6 +77,8 @@
         Stack,
         CallArg,
         Index,
+        PreIndex,
+        PostIndex,
 
         // Immediate operands that customize the behavior of an operation. You can think of them as
         // secondary opcodes. They are always "Use"'d.
@@ -649,6 +651,30 @@
         return Arg::index(base, index, scale, 0);
     }
 
+    template<typename Int, typename = Value::IsLegalOffset<Int>>
+    static Arg preIndex(Air::Tmp base, Int index)
+    {
+        ASSERT(base.isGP());
+        ASSERT(isInt9(index));
+        Arg result;
+        result.m_kind = PreIndex;
+        result.m_base = base;
+        result.m_offset = index;
+        return result;
+    }
+
+    template<typename Int, typename = Value::IsLegalOffset<Int>>
+    static Arg postIndex(Air::Tmp base, Int index)
+    {
+        ASSERT(base.isGP());
+        ASSERT(isInt9(index));
+        Arg result;
+        result.m_kind = PostIndex;
+        result.m_base = base;
+        result.m_offset = index;
+        return result;
+    }
+
     static Arg relCond(MacroAssembler::RelationalCondition condition)
     {
         Arg result;
@@ -799,6 +825,16 @@
         return kind() == Index;
     }
 
+    bool isPreIndex() const
+    {
+        return kind() == PreIndex;
+    }
+
+    bool isPostIndex() const
+    {
+        return kind() == PostIndex;
+    }
+
     bool isMemory() const
     {
         switch (kind()) {
@@ -808,6 +844,8 @@
         case Stack:
         case CallArg:
         case Index:
+        case PreIndex:
+        case PostIndex:
             return true;
         default:
             return false;
@@ -972,7 +1010,7 @@
 
     Air::Tmp base() const
     {
-        ASSERT(kind() == SimpleAddr || kind() == Addr || kind() == ExtendedOffsetAddr || kind() == Index);
+        ASSERT(kind() == SimpleAddr || kind() == Addr || kind() == ExtendedOffsetAddr || kind() == Index || kind() == PreIndex || kind() == PostIndex);
         return m_base;
     }
 
@@ -982,7 +1020,7 @@
     {
         if (kind() == Stack)
             return static_cast<Value::OffsetType>(m_scale);
-        ASSERT(kind() == Addr || kind() == ExtendedOffsetAddr || kind() == CallArg || kind() == Index);
+        ASSERT(kind() == Addr || kind() == ExtendedOffsetAddr || kind() == CallArg || kind() == Index || kind() == PreIndex || kind() == PostIndex);
         return static_cast<Value::OffsetType>(m_offset);
     }
 
@@ -1044,6 +1082,8 @@
         case Addr:
         case ExtendedOffsetAddr:
         case Index:
+        case PreIndex:
+        case PostIndex:
         case Stack:
         case CallArg:
         case RelCond:
@@ -1081,6 +1121,8 @@
         case Addr:
         case ExtendedOffsetAddr:
         case Index:
+        case PreIndex:
+        case PostIndex:
         case Stack:
         case CallArg:
         case BigImm: // Yes, we allow BigImm as a double immediate. We use this for implementing stackmaps.
@@ -1237,6 +1279,22 @@
         return false;
     }
 
+    template<typename Int, typename = Value::IsLegalOffset<Int>>
+    static bool isValidPreIndexForm(Int offset)
+    {
+        if (isARM64())
+            return isValidSignedImm9(offset);
+        return false;
+    }
+
+    template<typename Int, typename = Value::IsLegalOffset<Int>>
+    static bool isValidPostIndexForm(Int offset)
+    {
+        if (isARM64())
+            return isValidSignedImm9(offset);
+        return false;
+    }
+
     // If you don't pass a width then this optimistically assumes that you're using the right width. But
     // the width is relevant to validity, so passing a null width is only useful for assertions. Don't
     // pass null widths when cascading through Args in the instruction selector!
@@ -1265,6 +1323,10 @@
             return isValidAddrForm(offset(), width);
         case Index:
             return isValidIndexForm(scale(), offset(), width);
+        case PreIndex:
+            return isValidPreIndexForm(offset());
+        case PostIndex:
+            return isValidPostIndexForm(offset());
         case RelCond:
         case ResCond:
         case DoubleCond:
@@ -1284,6 +1346,8 @@
         case SimpleAddr:
         case Addr:
         case ExtendedOffsetAddr:
+        case PreIndex:
+        case PostIndex:
             functor(m_base);
             break;
         case Index:
@@ -1328,6 +1392,10 @@
         case ExtendedOffsetAddr:
             functor(m_base, Use, GP, argRole == UseAddr ? argWidth : pointerWidth());
             break;
+        case PreIndex:
+        case PostIndex:
+            functor(m_base, UseDef, GP, argRole == UseAddr ? argWidth : pointerWidth());
+            break;
         case Index:
             functor(m_base, Use, GP, argRole == UseAddr ? argWidth : pointerWidth());
             functor(m_index, Use, GP, argRole == UseAddr ? argWidth : pointerWidth());
@@ -1383,6 +1451,18 @@
             static_cast<Value::OffsetType>(m_offset), m_extend);
     }
 
+    MacroAssembler::PreIndexAddress asPreIndexAddress() const
+    {
+        ASSERT(isPreIndex());
+        return MacroAssembler::PreIndexAddress(m_base.gpr(), offset());
+    }
+
+    MacroAssembler::PostIndexAddress asPostIndexAddress() const
+    {
+        ASSERT(isPostIndex());
+        return MacroAssembler::PostIndexAddress(m_base.gpr(), offset());
+    }
+
     MacroAssembler::RelationalCondition asRelationalCondition() const
     {
         ASSERT(isRelCond());

Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2021-07-30 20:44:47 UTC (rev 280493)
@@ -661,6 +661,12 @@
     Tmp, Tmp
     Tmp, Addr
 
+arm64: MoveWithIncrement64 UD:G:64, D:G:64
+    PreIndex, Tmp as load64
+    PostIndex, Tmp as load64
+    Tmp, PreIndex as store64
+    Tmp, PostIndex as store64
+
 Move32 U:G:32, ZD:G:32
     Tmp, Tmp as zeroExtend32ToWord
     Addr, Tmp as load32
@@ -671,6 +677,12 @@
     x86: Imm, Addr as store32
     x86: Imm, Index as store32
 
+arm64: MoveWithIncrement32 UD:G:32, ZD:G:32
+    PreIndex, Tmp as load32
+    PostIndex, Tmp as load32
+    Tmp, PreIndex as store32
+    Tmp, PostIndex as store32
+
 # This is for moving between spill slots.
 Move32 U:G:32, ZD:G:32, S:G:32
     Addr, Addr, Tmp

Modified: trunk/Source/_javascript_Core/b3/air/opcode_generator.rb (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/air/opcode_generator.rb	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/air/opcode_generator.rb	2021-07-30 20:44:47 UTC (rev 280493)
@@ -229,7 +229,7 @@
 end
 
 def isKind(token)
-    token =~ /\A((Tmp)|(Imm)|(BigImm)|(BitImm)|(BitImm64)|(ZeroReg)|(SimpleAddr)|(Addr)|(ExtendedOffsetAddr)|(Index)|(RelCond)|(ResCond)|(DoubleCond)|(StatusCond))\Z/
+    token =~ /\A((Tmp)|(Imm)|(BigImm)|(BitImm)|(BitImm64)|(ZeroReg)|(SimpleAddr)|(Addr)|(ExtendedOffsetAddr)|(Index)|(PreIndex)|(PostIndex)|(RelCond)|(ResCond)|(DoubleCond)|(StatusCond))\Z/
 end
 
 def isArch(token)
@@ -303,7 +303,7 @@
 
     def consumeKind
         result = token.string
-        parseError("Expected kind (Imm, BigImm, BitImm, BitImm64, ZeroReg, Tmp, SimpleAddr, Addr, ExtendedOffsetAddr, Index, RelCond, ResCond, DoubleCond, or StatusCond)") unless isKind(result)
+        parseError("Expected kind (Imm, BigImm, BitImm, BitImm64, ZeroReg, Tmp, SimpleAddr, Addr, ExtendedOffsetAddr, Index, PreIndex, PostIndex, RelCond, ResCond, DoubleCond, or StatusCond)") unless isKind(result)
         advance
         result
     end
@@ -929,6 +929,12 @@
                 when "Index"
                     outp.puts "if (!Arg::isValidIndexForm(args[#{index}].scale(), args[#{index}].offset(), #{arg.widthCode}))"
                     outp.puts "OPGEN_RETURN(false);"
+                when "PreIndex"
+                    outp.puts "if (!Arg::isValidPreIndexForm(args[#{index}].offset()))"
+                    outp.puts "OPGEN_RETURN(false);"
+                when "PostIndex"
+                    outp.puts "if (!Arg::isValidPostIndexForm(args[#{index}].offset()))"
+                    outp.puts "OPGEN_RETURN(false);"
                 when "BigImm"
                 when "RelCond"
                 when "ResCond"
@@ -1235,6 +1241,10 @@
                     outp.print "args[#{index}].asAddress()"
                 when "Index"
                     outp.print "args[#{index}].asBaseIndex()"
+                when "PreIndex"
+                    outp.print "args[#{index}].asPreIndexAddress()"
+                when "PostIndex"
+                    outp.print "args[#{index}].asPostIndexAddress()"
                 when "RelCond"
                     outp.print "args[#{index}].asRelationalCondition()"
                 when "ResCond"

Modified: trunk/Source/_javascript_Core/b3/testb3.h (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/testb3.h	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/testb3.h	2021-07-30 20:44:47 UTC (rev 280493)
@@ -1140,4 +1140,9 @@
 
 bool shouldRun(const char* filter, const char* testName);
 
+void testLoadPreIndex32();
+void testLoadPreIndex64();
+void testLoadPostIndex32();
+void testLoadPostIndex64();
+
 #endif // ENABLE(B3_JIT)

Modified: trunk/Source/_javascript_Core/b3/testb3_3.cpp (280492 => 280493)


--- trunk/Source/_javascript_Core/b3/testb3_3.cpp	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/b3/testb3_3.cpp	2021-07-30 20:44:47 UTC (rev 280493)
@@ -28,6 +28,300 @@
 
 #if ENABLE(B3_JIT)
 
+
+void testLoadPreIndex32()
+{
+    if (Options::defaultB3OptLevel() < 2)
+        return;
+
+    int32_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    int32_t* ptr = nums;
+
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    BasicBlock* loopTest = proc.addBlock();
+    BasicBlock* loopBody = proc.addBlock();
+    BasicBlock* done = proc.addBlock();
+
+    Variable* r = proc.addVariable(Int32);
+    Variable* p = proc.addVariable(Int64);
+
+    // ---------------------- Root_Block
+    // r1 = 0
+    // Upsilon(r1, ^r2)
+    // p1 = addr
+    // Upsilon(p1, ^p2)
+    Value* r1 = root->appendIntConstant(proc, Origin(), Int32, 0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
+    Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
+    root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Loop_Test_Block
+    // loop:
+    // p2 = Phi()
+    // r2 = Phi()
+    // if r2 >= 10 goto done
+    Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
+    Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
+    Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const32Value>(proc, Origin(), 10));
+    loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
+
+    // ---------------------- Loop_Body_Block
+    // p3 = p2 + 1
+    // Upsilon(p3, ^p2)
+    // r3 = r2 + load(p3)
+    // Upsilon(r3, ^r2)
+    // goto loop
+    Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 4));
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
+    Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int32, Origin(), p3));
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
+    loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Done_Block
+    // done:
+    // return r2
+    done->appendNewControlValue(proc, Return, Origin(), r2);
+
+    proc.resetReachability();
+    validate(proc);
+    fixSSA(proc);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "#4]!");
+
+    auto test = [&] () -> int32_t {
+        int32_t r = 0;
+        while (r < 10)
+            r += *++ptr;
+        return r;
+    };
+
+    CHECK_EQ(invoke<int32_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
+}
+
+void testLoadPreIndex64()
+{
+    if (Options::defaultB3OptLevel() < 2)
+        return;
+
+    int64_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    int64_t* ptr = nums;
+
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    BasicBlock* loopTest = proc.addBlock();
+    BasicBlock* loopBody = proc.addBlock();
+    BasicBlock* done = proc.addBlock();
+
+    Variable* r = proc.addVariable(Int64);
+    Variable* p = proc.addVariable(Int64);
+
+    // ---------------------- Root_Block
+    // r1 = 0
+    // Upsilon(r1, ^r2)
+    // p1 = addr
+    // Upsilon(p1, ^p2)
+    Value* r1 = root->appendIntConstant(proc, Origin(), Int64, 0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
+    Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
+    root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Loop_Test_Block
+    // loop:
+    // p2 = Phi()
+    // r2 = Phi()
+    // if r2 >= 10 goto done
+    Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
+    Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
+    Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const64Value>(proc, Origin(), 10));
+    loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
+
+    // ---------------------- Loop_Body_Block
+    // p3 = p2 + 1
+    // Upsilon(p3, ^p2)
+    // r3 = r2 + load(p3)
+    // Upsilon(r3, ^r2)
+    // goto loop
+    Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 8));
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
+    Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int64, Origin(), p3));
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
+    loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Done_Block
+    // done:
+    // return r2
+    done->appendNewControlValue(proc, Return, Origin(), r2);
+
+    proc.resetReachability();
+    validate(proc);
+    fixSSA(proc);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "#8]!");
+
+    auto test = [&] () -> int64_t {
+        int64_t r = 0;
+        while (r < 10)
+            r += *++ptr;
+        return r;
+    };
+
+    CHECK_EQ(invoke<int64_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
+}
+
+void testLoadPostIndex32()
+{
+    if (Options::defaultB3OptLevel() < 2)
+        return;
+
+    int32_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    int32_t* ptr = nums;
+
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    BasicBlock* loopTest = proc.addBlock();
+    BasicBlock* loopBody = proc.addBlock();
+    BasicBlock* done = proc.addBlock();
+
+    Variable* r = proc.addVariable(Int32);
+    Variable* p = proc.addVariable(Int64);
+
+    // ---------------------- Root_Block
+    // r1 = 0
+    // Upsilon(r1, ^r2)
+    // p1 = addr
+    // Upsilon(p1, ^p2)
+    Value* r1 = root->appendIntConstant(proc, Origin(), Int32, 0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
+    Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
+    root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Loop_Test_Block
+    // loop:
+    // p2 = Phi()
+    // r2 = Phi()
+    // if r2 >= 10 goto done
+    Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
+    Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
+    Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const32Value>(proc, Origin(), 10));
+    loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
+
+    // ---------------------- Loop_Body_Block
+    // r3 = r2 + load(p2)
+    // p3 = p2 + 1
+    // Upsilon(r3, ^r2)
+    // Upsilon(p3, ^p2)
+    // goto loop
+    Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int32, Origin(), p2));
+    Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 4));
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
+    loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Done_Block
+    // done:
+    // return r2
+    done->appendNewControlValue(proc, Return, Origin(), r2);
+
+    proc.resetReachability();
+    validate(proc);
+    fixSSA(proc);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "], #4");
+
+    auto test = [&] () -> int32_t {
+        int32_t r = 0;
+        while (r < 10)
+            r += *ptr++;
+        return r;
+    };
+
+    CHECK_EQ(invoke<int32_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
+}
+
+void testLoadPostIndex64()
+{
+    if (Options::defaultB3OptLevel() < 2)
+        return;
+
+    int64_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    int64_t* ptr = nums;
+
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    BasicBlock* loopTest = proc.addBlock();
+    BasicBlock* loopBody = proc.addBlock();
+    BasicBlock* done = proc.addBlock();
+
+    Variable* r = proc.addVariable(Int64);
+    Variable* p = proc.addVariable(Int64);
+
+    // ---------------------- Root_Block
+    // r1 = 0
+    // Upsilon(r1, ^r2)
+    // p1 = addr
+    // Upsilon(p1, ^p2)
+    Value* r1 = root->appendIntConstant(proc, Origin(), Int64, 0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
+    Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
+    root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Loop_Test_Block
+    // loop:
+    // p2 = Phi()
+    // r2 = Phi()
+    // if r2 >= 10 goto done
+    Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
+    Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
+    Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const64Value>(proc, Origin(), 10));
+    loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
+
+    // ---------------------- Loop_Body_Block
+    // r3 = r2 + load(p2)
+    // p3 = p2 + 1
+    // Upsilon(r3, ^r2)
+    // Upsilon(p3, ^p2)
+    // goto loop
+    Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int64, Origin(), p2));
+    Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 8));
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
+    loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
+    loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
+
+    // ---------------------- Done_Block
+    // done:
+    // return r2
+    done->appendNewControlValue(proc, Return, Origin(), r2);
+
+    proc.resetReachability();
+    validate(proc);
+    fixSSA(proc);
+
+    auto code = compileProc(proc);
+    if (isARM64())
+        checkUsesInstruction(*code, "], #8");
+
+    auto test = [&] () -> int64_t {
+        int64_t r = 0;
+        while (r < 10)
+            r += *ptr++;
+        return r;
+    };
+
+    CHECK_EQ(invoke<int64_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
+}
+
+
 void testInsertSignedBitfieldInZero32()
 {
     if (JSC::Options::defaultB3OptLevel() < 2)
@@ -3697,6 +3991,11 @@
     RUN(testZShrArgImm32(0xffffffff, 0));
     RUN(testZShrArgImm32(0xffffffff, 1));
     RUN(testZShrArgImm32(0xffffffff, 63));
+
+    RUN(testLoadPreIndex32());
+    RUN(testLoadPreIndex64());
+    RUN(testLoadPostIndex32());
+    RUN(testLoadPostIndex64());
 }
 
 #endif // ENABLE(B3_JIT)

Modified: trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp (280492 => 280493)


--- trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp	2021-07-30 20:26:51 UTC (rev 280492)
+++ trunk/Source/_javascript_Core/jit/ExecutableAllocator.cpp	2021-07-30 20:44:47 UTC (rev 280493)
@@ -233,8 +233,8 @@
     jit.ret();
 
     MacroAssembler::Label local0 = jit.label();
-    jit.load64(x1, PostIndex(8), x6);
-    jit.store64(x6, x3, PostIndex(8));
+    jit.load64(MacroAssembler::PostIndexAddress(x1, 8), x6);
+    jit.store64(x6, MacroAssembler::PostIndexAddress(x3, 8));
     smallCopy.link(&jit);
     jit.branchSub64(MacroAssembler::AboveOrEqual, TrustedImm32(8), x2).linkTo(local0, &jit);
     MacroAssembler::Jump local2 = jit.branchAdd64(MacroAssembler::Equal, TrustedImm32(8), x2);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to