mandreel should run just as fast in FTL B3 as FTL LLVM
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jan 2016 20:47:18 +0000 (20:47 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jan 2016 20:47:18 +0000 (20:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=153394

Reviewed by Gavin Barraclough.

This fixes two performance bugs and one disassembler bug.

- B3 now turns Branches into Jumps when they are dominated by a Check on the same
  condition. This is like the opposite of foldPathConstants() was doing.

- Air now supports adding to 8-bit or 16-bit memory locations on x86. B3 now knows how to
  lower Store8(Add(Load8Z(...))) and various other things to these new instructions.

- Disassembler now knows to print out the instruction's width, whenever it has one.
  Previously, we'd print movb, movw, movl, and movq as "mov", which is unhelpful if
  you're storing an immediate, for example.

This adds a bunch of tests for the new instruction forms. This is a big speed-up on
mandreel. It makes us just as fast as LLVM on that benchmark.

* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::add32):
(JSC::MacroAssemblerX86Common::add8):
(JSC::MacroAssemblerX86Common::add16):
(JSC::MacroAssemblerX86Common::add32AndSetFlags):
(JSC::MacroAssemblerX86Common::clz32AfterBsr):
* assembler/X86Assembler.h:
(JSC::X86Assembler::addl_rm):
(JSC::X86Assembler::addb_rm):
(JSC::X86Assembler::addw_rm):
(JSC::X86Assembler::addl_ir):
(JSC::X86Assembler::addl_im):
(JSC::X86Assembler::addb_im):
(JSC::X86Assembler::addw_im):
(JSC::X86Assembler::addq_rr):
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::addr):
(JSC::B3::Air::LowerToAir::loadPromiseAnyOpcode):
(JSC::B3::Air::LowerToAir::loadPromise):
(JSC::B3::Air::LowerToAir::tryAppendStoreBinOp):
(JSC::B3::Air::LowerToAir::lower):
* b3/B3PureCSE.cpp:
(JSC::B3::PureCSE::clear):
(JSC::B3::PureCSE::findMatch):
(JSC::B3::PureCSE::process):
* b3/B3PureCSE.h:
* b3/B3ReduceStrength.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.cpp:
(JSC::B3::testNegPtr):
(JSC::B3::testStoreAddLoad32):
(JSC::B3::testStoreAddLoadImm32):
(JSC::B3::testStoreAddLoad8):
(JSC::B3::testStoreAddLoadImm8):
(JSC::B3::testStoreAddLoad16):
(JSC::B3::testStoreAddLoadImm16):
(JSC::B3::testStoreAddLoad64):
(JSC::B3::testStoreAddLoadImm64):
(JSC::B3::testStoreAddLoad32Index):
(JSC::B3::testStoreAddLoadImm32Index):
(JSC::B3::testStoreAddLoad8Index):
(JSC::B3::testStoreAddLoadImm8Index):
(JSC::B3::testStoreAddLoad16Index):
(JSC::B3::testStoreAddLoadImm16Index):
(JSC::B3::testStoreAddLoad64Index):
(JSC::B3::testStoreAddLoadImm64Index):
(JSC::B3::testStoreSubLoad):
(JSC::B3::run):
(JSC::B3::testStoreAddLoad): Deleted.
* disassembler/udis86/udis86_syn-att.c:
(ud_translate_att):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@195549 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
Source/JavaScriptCore/assembler/X86Assembler.h
Source/JavaScriptCore/b3/B3LowerToAir.cpp
Source/JavaScriptCore/b3/B3PureCSE.cpp
Source/JavaScriptCore/b3/B3PureCSE.h
Source/JavaScriptCore/b3/B3ReduceStrength.cpp
Source/JavaScriptCore/b3/air/AirOpcode.opcodes
Source/JavaScriptCore/b3/testb3.cpp
Source/JavaScriptCore/disassembler/udis86/udis86_syn-att.c

index dbc1225..4325fd1 100644 (file)
@@ -1,3 +1,77 @@
+2016-01-22  Filip Pizlo  <fpizlo@apple.com>
+
+        mandreel should run just as fast in FTL B3 as FTL LLVM
+        https://bugs.webkit.org/show_bug.cgi?id=153394
+
+        Reviewed by Gavin Barraclough.
+
+        This fixes two performance bugs and one disassembler bug.
+
+        - B3 now turns Branches into Jumps when they are dominated by a Check on the same
+          condition. This is like the opposite of foldPathConstants() was doing.
+
+        - Air now supports adding to 8-bit or 16-bit memory locations on x86. B3 now knows how to
+          lower Store8(Add(Load8Z(...))) and various other things to these new instructions.
+
+        - Disassembler now knows to print out the instruction's width, whenever it has one.
+          Previously, we'd print movb, movw, movl, and movq as "mov", which is unhelpful if
+          you're storing an immediate, for example.
+
+        This adds a bunch of tests for the new instruction forms. This is a big speed-up on
+        mandreel. It makes us just as fast as LLVM on that benchmark.
+
+        * assembler/MacroAssemblerX86Common.h:
+        (JSC::MacroAssemblerX86Common::add32):
+        (JSC::MacroAssemblerX86Common::add8):
+        (JSC::MacroAssemblerX86Common::add16):
+        (JSC::MacroAssemblerX86Common::add32AndSetFlags):
+        (JSC::MacroAssemblerX86Common::clz32AfterBsr):
+        * assembler/X86Assembler.h:
+        (JSC::X86Assembler::addl_rm):
+        (JSC::X86Assembler::addb_rm):
+        (JSC::X86Assembler::addw_rm):
+        (JSC::X86Assembler::addl_ir):
+        (JSC::X86Assembler::addl_im):
+        (JSC::X86Assembler::addb_im):
+        (JSC::X86Assembler::addw_im):
+        (JSC::X86Assembler::addq_rr):
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::addr):
+        (JSC::B3::Air::LowerToAir::loadPromiseAnyOpcode):
+        (JSC::B3::Air::LowerToAir::loadPromise):
+        (JSC::B3::Air::LowerToAir::tryAppendStoreBinOp):
+        (JSC::B3::Air::LowerToAir::lower):
+        * b3/B3PureCSE.cpp:
+        (JSC::B3::PureCSE::clear):
+        (JSC::B3::PureCSE::findMatch):
+        (JSC::B3::PureCSE::process):
+        * b3/B3PureCSE.h:
+        * b3/B3ReduceStrength.cpp:
+        * b3/air/AirOpcode.opcodes:
+        * b3/testb3.cpp:
+        (JSC::B3::testNegPtr):
+        (JSC::B3::testStoreAddLoad32):
+        (JSC::B3::testStoreAddLoadImm32):
+        (JSC::B3::testStoreAddLoad8):
+        (JSC::B3::testStoreAddLoadImm8):
+        (JSC::B3::testStoreAddLoad16):
+        (JSC::B3::testStoreAddLoadImm16):
+        (JSC::B3::testStoreAddLoad64):
+        (JSC::B3::testStoreAddLoadImm64):
+        (JSC::B3::testStoreAddLoad32Index):
+        (JSC::B3::testStoreAddLoadImm32Index):
+        (JSC::B3::testStoreAddLoad8Index):
+        (JSC::B3::testStoreAddLoadImm8Index):
+        (JSC::B3::testStoreAddLoad16Index):
+        (JSC::B3::testStoreAddLoadImm16Index):
+        (JSC::B3::testStoreAddLoad64Index):
+        (JSC::B3::testStoreAddLoadImm64Index):
+        (JSC::B3::testStoreSubLoad):
+        (JSC::B3::run):
+        (JSC::B3::testStoreAddLoad): Deleted.
+        * disassembler/udis86/udis86_syn-att.c:
+        (ud_translate_att):
+
 2016-01-25  Alex Christensen  <achristensen@webkit.org>
 
         [Win] Copy forwarding headers before building a project
index 951c465..472f846 100644 (file)
@@ -129,6 +129,33 @@ public:
         add32AndSetFlags(imm, address);
     }
 
+    void add32(TrustedImm32 imm, BaseIndex address)
+    {
+        if (!imm.m_value)
+            return;
+        add32AndSetFlags(imm, address);
+    }
+
+    void add8(TrustedImm32 imm, Address address)
+    {
+        m_assembler.addb_im(imm.m_value, address.offset, address.base);
+    }
+
+    void add8(TrustedImm32 imm, BaseIndex address)
+    {
+        m_assembler.addb_im(imm.m_value, address.offset, address.base, address.index, address.scale);
+    }
+
+    void add16(TrustedImm32 imm, Address address)
+    {
+        m_assembler.addw_im(imm.m_value, address.offset, address.base);
+    }
+
+    void add16(TrustedImm32 imm, BaseIndex address)
+    {
+        m_assembler.addw_im(imm.m_value, address.offset, address.base, address.index, address.scale);
+    }
+
     void add32(TrustedImm32 imm, RegisterID dest)
     {
         if (!imm.m_value)
@@ -146,6 +173,31 @@ public:
         m_assembler.addl_rm(src, dest.offset, dest.base);
     }
 
+    void add32(RegisterID src, BaseIndex dest)
+    {
+        m_assembler.addl_rm(src, dest.offset, dest.base, dest.index, dest.scale);
+    }
+
+    void add8(RegisterID src, Address dest)
+    {
+        m_assembler.addb_rm(src, dest.offset, dest.base);
+    }
+
+    void add8(RegisterID src, BaseIndex dest)
+    {
+        m_assembler.addb_rm(src, dest.offset, dest.base, dest.index, dest.scale);
+    }
+
+    void add16(RegisterID src, Address dest)
+    {
+        m_assembler.addw_rm(src, dest.offset, dest.base);
+    }
+
+    void add16(RegisterID src, BaseIndex dest)
+    {
+        m_assembler.addw_rm(src, dest.offset, dest.base, dest.index, dest.scale);
+    }
+
     void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
     {
         if (!imm.m_value) {
@@ -2015,6 +2067,11 @@ private:
         m_assembler.addl_im(imm.m_value, address.offset, address.base);
     }
 
+    void add32AndSetFlags(TrustedImm32 imm, BaseIndex address)
+    {
+        m_assembler.addl_im(imm.m_value, address.offset, address.base, address.index, address.scale);
+    }
+
     // If lzcnt is not available, use this after BSR
     // to count the leading zeros.
     void clz32AfterBsr(RegisterID dst)
index e443de2..131f139 100644 (file)
@@ -187,6 +187,7 @@ private:
     //     -v: 32 or 64bit depending on the operand-size attribute.
     //     -z: 32bit in both 32bit and 64bit mode. Common for immediate values.
     typedef enum {
+        OP_ADD_EbGb                     = 0x00,
         OP_ADD_EvGv                     = 0x01,
         OP_ADD_GvEv                     = 0x03,
         OP_ADD_EAXIv                    = 0x05,
@@ -433,6 +434,33 @@ public:
         m_formatter.oneByteOp(OP_ADD_EvGv, src, base, offset);
     }
 
+    void addl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        m_formatter.oneByteOp(OP_ADD_EvGv, src, base, index, scale, offset);
+    }
+
+    void addb_rm(RegisterID src, int offset, RegisterID base)
+    {
+        m_formatter.oneByteOp8(OP_ADD_EbGb, src, base, offset);
+    }
+
+    void addb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        m_formatter.oneByteOp8(OP_ADD_EbGb, src, base, index, scale, offset);
+    }
+
+    void addw_rm(RegisterID src, int offset, RegisterID base)
+    {
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.oneByteOp8(OP_ADD_EvGv, src, base, offset);
+    }
+
+    void addw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.oneByteOp8(OP_ADD_EvGv, src, base, index, scale, offset);
+    }
+
     void addl_ir(int imm, RegisterID dst)
     {
         if (CAN_SIGN_EXTEND_8_32(imm)) {
@@ -458,6 +486,53 @@ public:
         }
     }
 
+    void addl_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, index, scale, offset);
+            m_formatter.immediate8(imm);
+        } else {
+            m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, index, scale, offset);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void addb_im(int imm, int offset, RegisterID base)
+    {
+        m_formatter.oneByteOp8(OP_GROUP1_EbIb, GROUP1_OP_ADD, base, offset);
+        m_formatter.immediate8(imm);
+    }
+
+    void addb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        m_formatter.oneByteOp8(OP_GROUP1_EbIb, GROUP1_OP_ADD, base, index, scale, offset);
+        m_formatter.immediate8(imm);
+    }
+
+    void addw_im(int imm, int offset, RegisterID base)
+    {
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp8(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset);
+            m_formatter.immediate8(imm);
+        } else {
+            m_formatter.oneByteOp8(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset);
+            m_formatter.immediate16(imm);
+        }
+    }
+
+    void addw_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp8(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, index, scale, offset);
+            m_formatter.immediate8(imm);
+        } else {
+            m_formatter.oneByteOp8(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, index, scale, offset);
+            m_formatter.immediate16(imm);
+        }
+    }
+
 #if CPU(X86_64)
     void addq_rr(RegisterID src, RegisterID dst)
     {
@@ -1564,6 +1639,9 @@ public:
     void movw_rm(RegisterID src, int offset, RegisterID base)
     {
         m_formatter.prefix(PRE_OPERAND_SIZE);
+
+        // FIXME: We often use oneByteOp8 for 16-bit operations. It's not clear that this is
+        // necessary. https://bugs.webkit.org/show_bug.cgi?id=153433
         m_formatter.oneByteOp8(OP_MOV_EvGv, src, base, offset);
     }
 
index fbcb2fd..a3ff13b 100644 (file)
@@ -458,10 +458,8 @@ private:
         return result;
     }
 
-    ArgPromise loadPromise(Value* loadValue, B3::Opcode loadOpcode)
+    ArgPromise loadPromiseAnyOpcode(Value* loadValue)
     {
-        if (loadValue->opcode() != loadOpcode)
-            return Arg();
         if (!canBeInternal(loadValue))
             return Arg();
         if (crossesInterference(loadValue))
@@ -469,6 +467,13 @@ private:
         return ArgPromise(addr(loadValue), loadValue);
     }
 
+    ArgPromise loadPromise(Value* loadValue, B3::Opcode loadOpcode)
+    {
+        if (loadValue->opcode() != loadOpcode)
+            return Arg();
+        return loadPromiseAnyOpcode(loadValue);
+    }
+
     ArgPromise loadPromise(Value* loadValue)
     {
         return loadPromise(loadValue, Load);
@@ -744,14 +749,34 @@ private:
         Arg storeAddr = addr(m_value);
         ASSERT(storeAddr);
 
+        auto getLoadPromise = [&] (Value* load) -> ArgPromise {
+            switch (m_value->opcode()) {
+            case B3::Store:
+                if (load->opcode() != B3::Load)
+                    return ArgPromise();
+                break;
+            case B3::Store8:
+                if (load->opcode() != B3::Load8Z && load->opcode() != B3::Load8S)
+                    return ArgPromise();
+                break;
+            case B3::Store16:
+                if (load->opcode() != B3::Load16Z && load->opcode() != B3::Load16S)
+                    return ArgPromise();
+                break;
+            default:
+                return ArgPromise();
+            }
+            return loadPromiseAnyOpcode(load);
+        };
+        
         ArgPromise loadPromise;
         Value* otherValue = nullptr;
-        
-        loadPromise = this->loadPromise(left);
+
+        loadPromise = getLoadPromise(left);
         if (loadPromise.peek() == storeAddr)
             otherValue = right;
         else if (commutativity == Commutative) {
-            loadPromise = this->loadPromise(right);
+            loadPromise = getLoadPromise(right);
             if (loadPromise.peek() == storeAddr)
                 otherValue = left;
         }
@@ -1779,12 +1804,42 @@ private:
 
         case B3::Store8: {
             Value* valueToStore = m_value->child(0);
+            if (canBeInternal(valueToStore)) {
+                bool matched = false;
+                switch (valueToStore->opcode()) {
+                case Add:
+                    matched = tryAppendStoreBinOp<Add8, Air::Oops, Commutative>(
+                        valueToStore->child(0), valueToStore->child(1));
+                    break;
+                default:
+                    break;
+                }
+                if (matched) {
+                    commitInternal(valueToStore);
+                    return;
+                }
+            }
             m_insts.last().append(createStore(Air::Store8, valueToStore, addr(m_value)));
             return;
         }
 
         case B3::Store16: {
             Value* valueToStore = m_value->child(0);
+            if (canBeInternal(valueToStore)) {
+                bool matched = false;
+                switch (valueToStore->opcode()) {
+                case Add:
+                    matched = tryAppendStoreBinOp<Add16, Air::Oops, Commutative>(
+                        valueToStore->child(0), valueToStore->child(1));
+                    break;
+                default:
+                    break;
+                }
+                if (matched) {
+                    commitInternal(valueToStore);
+                    return;
+                }
+            }
             m_insts.last().append(createStore(Air::Store16, valueToStore, addr(m_value)));
             return;
         }
index f8a68e2..4030e28 100644 (file)
@@ -46,6 +46,23 @@ void PureCSE::clear()
     m_map.clear();
 }
 
+Value* PureCSE::findMatch(const ValueKey& key, BasicBlock* block, Dominators& dominators)
+{
+    if (!key)
+        return nullptr;
+
+    auto iter = m_map.find(key);
+    if (iter == m_map.end())
+        return nullptr;
+
+    for (Value* match : iter->value) {
+        if (dominators.dominates(match->owner, block))
+            return match;
+    }
+
+    return nullptr;
+}
+
 bool PureCSE::process(Value* value, Dominators& dominators)
 {
     if (value->opcode() == Identity)
index 67c1927..e22be3f 100644 (file)
@@ -34,6 +34,7 @@
 
 namespace JSC { namespace B3 {
 
+class BasicBlock;
 class Dominators;
 class Value;
 
@@ -48,6 +49,8 @@ public:
 
     void clear();
 
+    Value* findMatch(const ValueKey&, BasicBlock*, Dominators&);
+
     bool process(Value*, Dominators&);
     
 private:
index 38332b5..a985d2e 100644 (file)
@@ -42,7 +42,7 @@
 #include "B3PureCSE.h"
 #include "B3UpsilonValue.h"
 #include "B3UseCounts.h"
-#include "B3ValueKey.h"
+#include "B3ValueKeyInlines.h"
 #include "B3ValueInlines.h"
 #include <wtf/GraphNodeWorklist.h>
 #include <wtf/HashMap.h>
@@ -1643,7 +1643,19 @@ private:
                 m_changedCFG = true;
                 break;
             }
-            
+
+            // If a check for the same property dominates us, we can kill the branch. This sort
+            // of makes sense here because it's cheap, but hacks like this show that we're going
+            // to need SCCP.
+            Value* check = m_pureCSE.findMatch(
+                ValueKey(Check, Void, branch->child(0)), m_block, *m_dominators);
+            if (check) {
+                // The Check would have side-exited if child(0) was non-zero. So, it must be
+                // zero here.
+                branch->taken().block()->removePredecessor(m_block);
+                branch->convertToJump(branch->notTaken().block());
+                m_changedCFG = true;
+            }
             break;
         }
             
index 52e3e52..1dbb8dc 100644 (file)
@@ -110,9 +110,23 @@ Nop
 Add32 U:G:32, UZD:G:32
     Tmp, Tmp
     x86: Imm, Addr
+    x86: Imm, Index
     Imm, Tmp
     x86: Addr, Tmp
     x86: Tmp, Addr
+    x86: Tmp, Index
+
+x86: Add8 U:G:8, UD:G:8
+    Imm, Addr
+    Imm, Index
+    Tmp, Addr
+    Tmp, Index
+
+x86: Add16 U:G:16, UD:G:16
+    Imm, Addr
+    Imm, Index
+    Tmp, Addr
+    Tmp, Index
 
 Add32 U:G:32, U:G:32, ZD:G:32
     Imm, Tmp, Tmp
index 47d2109..1be1314 100644 (file)
@@ -4266,7 +4266,7 @@ void testNegPtr(intptr_t value)
     CHECK(compileAndRun<intptr_t>(proc, value) == -value);
 }
 
-void testStoreAddLoad(int amount)
+void testStoreAddLoad32(int amount)
 {
     Procedure proc;
     BasicBlock* root = proc.addBlock();
@@ -4277,6 +4277,221 @@ void testStoreAddLoad(int amount)
         root->appendNew<Value>(
             proc, Add, Origin(),
             root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr),
+            root->appendNew<Value>(
+                proc, Trunc, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoadImm32(int amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int slot = 37;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr),
+            root->appendNew<Const32Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoad8(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int8_t slot = 37;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    root->appendNew<MemoryValue>(
+        proc, Store8, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Value>(
+                proc, Trunc, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoadImm8(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int8_t slot = 37;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    root->appendNew<MemoryValue>(
+        proc, Store8, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Const32Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoad16(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int16_t slot = 37;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    root->appendNew<MemoryValue>(
+        proc, Store16, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Value>(
+                proc, Trunc, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoadImm16(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int16_t slot = 37;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    root->appendNew<MemoryValue>(
+        proc, Store16, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Const32Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoad64(int amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int64_t slot = 37000000000ll;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), slotPtr),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37000000000ll + amount);
+}
+
+void testStoreAddLoadImm64(int64_t amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int64_t slot = 370000000000ll;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), slotPtr),
+            root->appendNew<Const64Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc));
+    CHECK(slot == 370000000000ll + amount);
+}
+
+void testStoreAddLoad32Index(int amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int slot = 37;
+    int* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr),
+            root->appendNew<Value>(
+                proc, Trunc, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoadImm32Index(int amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int slot = 37;
+    int* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr),
             root->appendNew<Const32Value>(proc, Origin(), amount)),
         slotPtr);
     root->appendNew<ControlValue>(
@@ -4287,6 +4502,190 @@ void testStoreAddLoad(int amount)
     CHECK(slot == 37 + amount);
 }
 
+void testStoreAddLoad8Index(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int8_t slot = 37;
+    int8_t* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store8, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Value>(
+                proc, Trunc, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoadImm8Index(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int8_t slot = 37;
+    int8_t* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store8, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Const32Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoad16Index(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int16_t slot = 37;
+    int16_t* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store16, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Value>(
+                proc, Trunc, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoadImm16Index(int amount, B3::Opcode loadOpcode)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int16_t slot = 37;
+    int16_t* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store16, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, loadOpcode, Origin(), slotPtr),
+            root->appendNew<Const32Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc));
+    CHECK(slot == 37 + amount);
+}
+
+void testStoreAddLoad64Index(int amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int64_t slot = 37000000000ll;
+    int64_t* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), slotPtr),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, amount));
+    CHECK(slot == 37000000000ll + amount);
+}
+
+void testStoreAddLoadImm64Index(int64_t amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int64_t slot = 370000000000ll;
+    int64_t* ptr = &slot;
+    intptr_t zero = 0;
+    Value* slotPtr = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &ptr)),
+        root->appendNew<MemoryValue>(
+            proc, Load, pointerType(), Origin(),
+            root->appendNew<ConstPtrValue>(proc, Origin(), &zero)));
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<MemoryValue>(proc, Load, Int64, Origin(), slotPtr),
+            root->appendNew<Const64Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc));
+    CHECK(slot == 370000000000ll + amount);
+}
+
 void testStoreSubLoad(int amount)
 {
     Procedure proc;
@@ -10276,7 +10675,30 @@ void run(const char* filter)
     RUN(testAdd1Ptr(bitwise_cast<intptr_t>(vm)));
     RUN(testNeg32(52));
     RUN(testNegPtr(53));
-    RUN(testStoreAddLoad(46));
+    RUN(testStoreAddLoad32(46));
+    RUN(testStoreAddLoadImm32(46));
+    RUN(testStoreAddLoad64(4600));
+    RUN(testStoreAddLoadImm64(4600));
+    RUN(testStoreAddLoad8(4, Load8Z));
+    RUN(testStoreAddLoadImm8(4, Load8Z));
+    RUN(testStoreAddLoad8(4, Load8S));
+    RUN(testStoreAddLoadImm8(4, Load8S));
+    RUN(testStoreAddLoad16(6, Load16Z));
+    RUN(testStoreAddLoadImm16(6, Load16Z));
+    RUN(testStoreAddLoad16(6, Load16S));
+    RUN(testStoreAddLoadImm16(6, Load16S));
+    RUN(testStoreAddLoad32Index(46));
+    RUN(testStoreAddLoadImm32Index(46));
+    RUN(testStoreAddLoad64Index(4600));
+    RUN(testStoreAddLoadImm64Index(4600));
+    RUN(testStoreAddLoad8Index(4, Load8Z));
+    RUN(testStoreAddLoadImm8Index(4, Load8Z));
+    RUN(testStoreAddLoad8Index(4, Load8S));
+    RUN(testStoreAddLoadImm8Index(4, Load8S));
+    RUN(testStoreAddLoad16Index(6, Load16Z));
+    RUN(testStoreAddLoadImm16Index(6, Load16Z));
+    RUN(testStoreAddLoad16Index(6, Load16S));
+    RUN(testStoreAddLoadImm16Index(6, Load16S));
     RUN(testStoreSubLoad(46));
     RUN(testStoreAddLoadInterference(52));
     RUN(testStoreAddAndLoad(47, 0xffff));
index 7355763..4064b27 100644 (file)
@@ -167,6 +167,7 @@ extern void
 ud_translate_att(struct ud *u)
 {
   int size = 0;
+  unsigned i;
 
   /* check if P_OSO prefix is used */
   if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) {
@@ -229,19 +230,19 @@ ud_translate_att(struct ud *u)
                mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic));
   }
 
-  if (u->c1)
-       size = u->operand[0].size;
-  else if (u->c2)
-       size = u->operand[1].size;
-  else if (u->c3)
-       size = u->operand[2].size;
+  for (i = 3; i--;) {
+      if (u->operand[i].size > size)
+          size = u->operand[i].size;
+  }
 
   if (size == 8)
-       mkasm(u, "b");
+      mkasm(u, "b");
   else if (size == 16)
-       mkasm(u, "w");
+      mkasm(u, "w");
+  else if (size == 32)
+      mkasm(u, "l");
   else if (size == 64)
-       mkasm(u, "q");
+      mkasm(u, "q");
 
   mkasm(u, " ");