Title: [207039] trunk/Source/_javascript_Core
Revision
207039
Author
[email protected]
Date
2016-10-10 20:39:05 -0700 (Mon, 10 Oct 2016)

Log Message

B3->Air lowering should be able to emit complex leas on x86
https://bugs.webkit.org/show_bug.cgi?id=163234

Reviewed by Saam Barati.
        
This adds comprehensive support for emitting lea on x86.
        
When adding this, I found that it was useful to also finally add more reassociation. That
reduces the amount of patterns that the instruction selector has to deal with.

* assembler/MacroAssembler.h:
(JSC::MacroAssembler::lea32):
(JSC::MacroAssembler::lea64):
(JSC::MacroAssembler::lea): Deleted.
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::commitInternal):
(JSC::B3::Air::LowerToAir::tryAppendLea):
(JSC::B3::Air::LowerToAir::lower):
(JSC::B3::Air::LowerToAir::createSelect): Deleted.
* b3/B3ReduceStrength.cpp:
* b3/B3Value.h:
* b3/B3ValueInlines.h:
(JSC::B3::Value::isRepresentableAs):
(JSC::B3::Value::representableAs): Deleted.
* b3/air/AirOpcode.opcodes:
* b3/testb3.cpp: Lots of tests for lea and reassociation.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (207038 => 207039)


--- trunk/Source/_javascript_Core/ChangeLog	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-10-11 03:39:05 UTC (rev 207039)
@@ -1,3 +1,32 @@
+2016-10-10  Filip Pizlo  <[email protected]>
+
+        B3->Air lowering should be able to emit complex leas on x86
+        https://bugs.webkit.org/show_bug.cgi?id=163234
+
+        Reviewed by Saam Barati.
+        
+        This adds comprehensive support for emitting lea on x86.
+        
+        When adding this, I found that it was useful to also finally add more reassociation. That
+        reduces the amount of patterns that the instruction selector has to deal with.
+
+        * assembler/MacroAssembler.h:
+        (JSC::MacroAssembler::lea32):
+        (JSC::MacroAssembler::lea64):
+        (JSC::MacroAssembler::lea): Deleted.
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::commitInternal):
+        (JSC::B3::Air::LowerToAir::tryAppendLea):
+        (JSC::B3::Air::LowerToAir::lower):
+        (JSC::B3::Air::LowerToAir::createSelect): Deleted.
+        * b3/B3ReduceStrength.cpp:
+        * b3/B3Value.h:
+        * b3/B3ValueInlines.h:
+        (JSC::B3::Value::isRepresentableAs):
+        (JSC::B3::Value::representableAs): Deleted.
+        * b3/air/AirOpcode.opcodes:
+        * b3/testb3.cpp: Lots of tests for lea and reassociation.
+
 2016-10-10  Mark Lam  <[email protected]>
 
         Change ArrayPrototype.cpp's putLength() and setLength() to take a VM& so that we can use vm.propertyNames.

Modified: trunk/Source/_javascript_Core/assembler/MacroAssembler.h (207038 => 207039)


--- trunk/Source/_javascript_Core/assembler/MacroAssembler.h	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/assembler/MacroAssembler.h	2016-10-11 03:39:05 UTC (rev 207039)
@@ -1358,11 +1358,18 @@
     }
 #endif
 
-    void lea(Address address, RegisterID dest)
+    void lea32(Address address, RegisterID dest)
     {
-        addPtr(TrustedImm32(address.offset), address.base, dest);
+        add32(TrustedImm32(address.offset), address.base, dest);
     }
 
+#if CPU(X86_64) || CPU(ARM64)
+    void lea64(Address address, RegisterID dest)
+    {
+        add64(TrustedImm32(address.offset), address.base, dest);
+    }
+#endif // CPU(X86_64) || CPU(ARM64)
+
     bool shouldBlind(Imm32 imm)
     {
 #if ENABLE(FORCED_JIT_BLINDING)

Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (207038 => 207039)


--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-10-11 03:39:05 UTC (rev 207039)
@@ -399,7 +399,8 @@
     // short, you should avoid this by using the pattern matcher to match patterns.
     void commitInternal(Value* value)
     {
-        m_locked.add(value);
+        if (value)
+            m_locked.add(value);
     }
 
     bool crossesInterference(Value* value)
@@ -1798,6 +1799,148 @@
             },
             false);
     }
+    
+    bool tryAppendLea()
+    {
+        Air::Opcode leaOpcode = tryOpcodeForType(Lea32, Lea64, m_value->type());
+        if (!isValidForm(leaOpcode, Arg::Index, Arg::Tmp))
+            return false;
+        
+        // This lets us turn things like this:
+        //
+        //     Add(Add(@x, Shl(@y, $2)), $100)
+        //
+        // Into this:
+        //
+        //     lea 100(%rdi,%rsi,4), %rax
+        //
+        // We have a choice here between committing the internal bits of an index or sharing
+        // them. There are solid arguments for both.
+        //
+        // Sharing: The word on the street is that the cost of a lea is one cycle no matter
+        // what it does. Every experiment I've ever seen seems to confirm this. So, sharing
+        // helps us in situations where Wasm input did this:
+        //
+        //     x = a[i].x;
+        //     y = a[i].y;
+        //
+        // With sharing we would do:
+        //
+        //     leal (%a,%i,4), %tmp
+        //     cmp (%size, %tmp)
+        //     ja _fail
+        //     movl (%base, %tmp), %x
+        //     leal 4(%a,%i,4), %tmp
+        //     cmp (%size, %tmp)
+        //     ja _fail
+        //     movl (%base, %tmp), %y
+        //
+        // In the absence of sharing, we may find ourselves needing separate registers for
+        // the innards of the index. That's relatively unlikely to be a thing due to other
+        // optimizations that we already have, but it could happen
+        //
+        // Committing: The worst case is that there is a complicated graph of additions and
+        // shifts, where each value has multiple uses. In that case, it's better to compute
+        // each one separately from the others since that way, each calculation will use a
+        // relatively nearby tmp as its input. That seems uncommon, but in those cases,
+        // committing is a clear winner: it would result in a simple interference graph
+        // while sharing would result in a complex one. Interference sucks because it means
+        // more time in IRC and it means worse code.
+        //
+        // It's not super clear if any of these corner cases would ever arise. Committing
+        // has the benefit that it's easier to reason about, and protects a much darker
+        // corner case (more interference).
+                
+        // Here are the things we want to match:
+        // Add(Add(@x, @y), $c)
+        // Add(Shl(@x, $c), @y)
+        // Add(@x, Shl(@y, $c))
+        // Add(Add(@x, Shl(@y, $c)), $d)
+        // Add(Add(Shl(@x, $c), @y), $d)
+        //
+        // Note that if you do Add(Shl(@x, $c), $d) then we will treat $d as a non-constant and
+        // force it to materialize. You'll get something like this:
+        //
+        // movl $d, %tmp
+        // leal (%tmp,%x,1<<c), %result
+        //
+        // Which is pretty close to optimal and has the nice effect of being able to handle large
+        // constants gracefully.
+        
+        Value* innerAdd = nullptr;
+        
+        Value* value = m_value;
+        
+        // We're going to consume Add(Add(_), $c). If we succeed at consuming it then we have these
+        // patterns left (i.e. in the Add(_)):
+        //
+        // Add(Add(@x, @y), $c)
+        // Add(Add(@x, Shl(@y, $c)), $d)
+        // Add(Add(Shl(@x, $c), @y), $d)
+        //
+        // Otherwise we are looking at these patterns:
+        //
+        // Add(Shl(@x, $c), @y)
+        // Add(@x, Shl(@y, $c))
+        //
+        // This means that the subsequent code only has to worry about three patterns:
+        //
+        // Add(Shl(@x, $c), @y)
+        // Add(@x, Shl(@y, $c))
+        // Add(@x, @y) (only if offset != 0)
+        int32_t offset = 0;
+        if (value->child(1)->isRepresentableAs<int32_t>()
+            && canBeInternal(value->child(0))
+            && value->child(0)->opcode() == Add) {
+            innerAdd = value->child(0);
+            offset = value->child(1)->asInt32();
+            value = value->child(0);
+        }
+        
+        auto tryShl = [&] (Value* shl, Value* other) -> bool {
+            if (shl->opcode() != Shl)
+                return false;
+            if (!canBeInternal(shl))
+                return false;
+            if (!shl->child(1)->hasInt32())
+                return false;
+            unsigned logScale = shl->child(1)->asInt32();
+            if (m_value->type() == Int32)
+                logScale &= 31;
+            else
+                logScale &= 63;
+            // Use 64-bit math to perform the shift so that <<32 does the right thing.
+            int64_t bigScale = static_cast<uint64_t>(1) << static_cast<uint64_t>(logScale);
+            if (!isRepresentableAs<int32_t>(bigScale))
+                return false;
+            unsigned scale = static_cast<int32_t>(bigScale);
+            if (!Arg::isValidIndexForm(scale, offset))
+                return false;
+            
+            ASSERT(!m_locked.contains(shl->child(0)));
+            ASSERT(!m_locked.contains(other));
+            
+            append(leaOpcode, Arg::index(tmp(other), tmp(shl->child(0)), scale, offset), tmp(m_value));
+            commitInternal(innerAdd);
+            commitInternal(shl);
+            return true;
+        };
+        
+        if (tryShl(value->child(0), value->child(1)))
+            return true;
+        if (tryShl(value->child(1), value->child(0)))
+            return true;
+        
+        // The remaining pattern is just:
+        // Add(@x, @y) (only if offset != 0)
+        if (!offset)
+            return false;
+        ASSERT(!m_locked.contains(value->child(0)));
+        ASSERT(!m_locked.contains(value->child(1)));
+        append(leaOpcode, Arg::index(tmp(value->child(0)), tmp(value->child(1)), 1, offset), tmp(m_value));
+        commitInternal(innerAdd);
+        return true;
+    }
 
     void lower()
     {
@@ -1834,9 +1977,11 @@
         }
 
         case Add: {
+            if (tryAppendLea())
+                return;
+            
             Air::Opcode multiplyAddOpcode = tryOpcodeForType(MultiplyAdd32, MultiplyAdd64, m_value->type());
-            if (multiplyAddOpcode != Air::Oops
-                && isValidForm(multiplyAddOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
+            if (isValidForm(multiplyAddOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
                 Value* left = m_value->child(0);
                 Value* right = m_value->child(1);
                 if (!imm(right) || m_valueToTmp[right]) {
@@ -1846,7 +1991,7 @@
 
                         Value* multiplyLeft = left->child(0);
                         Value* multiplyRight = left->child(1);
-                        if (m_locked.contains(multiplyLeft) || m_locked.contains(multiplyRight))
+                        if (canBeInternal(multiplyLeft) || canBeInternal(multiplyRight))
                             return false;
 
                         append(multiplyAddOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(right), tmp(m_value));
@@ -2215,7 +2360,7 @@
 
         case SlotBase: {
             append(
-                Lea,
+                pointerType() == Int64 ? Lea64 : Lea32,
                 Arg::stack(m_stackToStack.get(m_value->as<SlotBaseValue>()->slot())),
                 tmp(m_value));
             return;

Modified: trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp (207038 => 207039)


--- trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp	2016-10-11 03:39:05 UTC (rev 207039)
@@ -482,10 +482,10 @@
         switch (m_value->opcode()) {
         case Add:
             handleCommutativity();
-
-            // Turn this: Add(Add(value, constant1), constant2)
-            // Into this: Add(value, constant1 + constant2)
+            
             if (m_value->child(0)->opcode() == Add && isInt(m_value->type())) {
+                // Turn this: Add(Add(value, constant1), constant2)
+                // Into this: Add(value, constant1 + constant2)
                 Value* newSum = m_value->child(1)->addConstant(m_proc, m_value->child(0)->child(1));
                 if (newSum) {
                     m_insertionSet.insertValue(m_index, newSum);
@@ -492,9 +492,46 @@
                     m_value->child(0) = m_value->child(0)->child(0);
                     m_value->child(1) = newSum;
                     m_changed = true;
+                    break;
                 }
+                
+                // Turn this: Add(Add(value, constant), otherValue)
+                // Into this: Add(Add(value, otherValue), constant)
+                if (!m_value->child(1)->hasInt() && m_value->child(0)->child(1)->hasInt()) {
+                    Value* value = m_value->child(0)->child(0);
+                    Value* constant = m_value->child(0)->child(1);
+                    Value* otherValue = m_value->child(1);
+                    // This could create duplicate code if Add(value, constant) is used elsewhere.
+                    // However, we already model adding a constant as if it was free in other places
+                    // so let's just roll with it. The alternative would mean having to do good use
+                    // counts, which reduceStrength() currently doesn't have.
+                    m_value->child(0) =
+                        m_insertionSet.insert<Value>(
+                            m_index, Add, m_value->origin(), value, otherValue);
+                    m_value->child(1) = constant;
+                    m_changed = true;
+                    break;
+                }
             }
-
+            
+            // Turn this: Add(otherValue, Add(value, constant))
+            // Into this: Add(Add(value, otherValue), constant)
+            if (isInt(m_value->type())
+                && !m_value->child(0)->hasInt()
+                && m_value->child(1)->opcode() == Add
+                && m_value->child(1)->child(1)->hasInt()) {
+                Value* value = m_value->child(1)->child(0);
+                Value* constant = m_value->child(1)->child(1);
+                Value* otherValue = m_value->child(0);
+                // This creates a duplicate add. That's dangerous but probably fine, see above.
+                m_value->child(0) =
+                    m_insertionSet.insert<Value>(
+                        m_index, Add, m_value->origin(), value, otherValue);
+                m_value->child(1) = constant;
+                m_changed = true;
+                break;
+            }
+            
             // Turn this: Add(constant1, constant2)
             // Into this: constant1 + constant2
             if (Value* constantAdd = m_value->child(0)->addConstant(m_proc, m_value->child(1))) {

Modified: trunk/Source/_javascript_Core/b3/B3Value.h (207038 => 207039)


--- trunk/Source/_javascript_Core/b3/B3Value.h	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/b3/B3Value.h	2016-10-11 03:39:05 UTC (rev 207039)
@@ -225,7 +225,7 @@
     float asFloat() const;
 
     bool hasNumber() const;
-    template<typename T> bool representableAs() const;
+    template<typename T> bool isRepresentableAs() const;
     template<typename T> T asNumber() const;
 
     // Booleans in B3 are Const32(0) or Const32(1). So this is true if the type is Int32 and the only

Modified: trunk/Source/_javascript_Core/b3/B3ValueInlines.h (207038 => 207039)


--- trunk/Source/_javascript_Core/b3/B3ValueInlines.h	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/b3/B3ValueInlines.h	2016-10-11 03:39:05 UTC (rev 207039)
@@ -186,17 +186,17 @@
 }
 
 template<typename T>
-inline bool Value::representableAs() const
+inline bool Value::isRepresentableAs() const
 {
     switch (opcode()) {
     case Const32:
-        return isRepresentableAs<T>(asInt32());
+        return B3::isRepresentableAs<T>(asInt32());
     case Const64:
-        return isRepresentableAs<T>(asInt64());
+        return B3::isRepresentableAs<T>(asInt64());
     case ConstDouble:
-        return isRepresentableAs<T>(asDouble());
+        return B3::isRepresentableAs<T>(asDouble());
     case ConstFloat:
-        return isRepresentableAs<T>(asFloat());
+        return B3::isRepresentableAs<T>(asFloat());
     default:
         return false;
     }

Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (207038 => 207039)


--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2016-10-11 03:39:05 UTC (rev 207039)
@@ -300,9 +300,14 @@
 x86_64: X86Div64 UZD:G:64, UZD:G:64, U:G:64
     Tmp*, Tmp*, Tmp
 
-Lea UA:G:Ptr, D:G:Ptr
+Lea32 UA:G:32, D:G:32
     Addr, Tmp
+    x86: Index, Tmp as x86Lea32
 
+Lea64 UA:G:64, D:G:64
+    Addr, Tmp
+    x86: Index, Tmp as x86Lea64
+
 And32 U:G:32, U:G:32, ZD:G:32
     Tmp, Tmp, Tmp
     arm64: BitImm, Tmp, Tmp

Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (207038 => 207039)


--- trunk/Source/_javascript_Core/b3/testb3.cpp	2016-10-11 02:55:26 UTC (rev 207038)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp	2016-10-11 03:39:05 UTC (rev 207039)
@@ -154,28 +154,37 @@
     Air::validate(proc.code());
 }
 
-void checkUsesInstruction(Compilation& compilation, const char* text)
+template<typename Func>
+void checkDisassembly(Compilation& compilation, const Func& func, CString failText)
 {
     CString disassembly = compilation.disassembly();
-    if (strstr(disassembly.data(), text))
+    if (func(disassembly.data()))
         return;
-
+    
     crashLock.lock();
-    dataLog("Bad lowering!  Expected to find ", text, " but didn't:\n");
+    dataLog("Bad lowering!  ", failText, "\n");
     dataLog(disassembly);
     CRASH();
 }
 
+void checkUsesInstruction(Compilation& compilation, const char* text)
+{
+    checkDisassembly(
+        compilation,
+        [&] (const char* disassembly) -> bool {
+            return strstr(disassembly, text);
+        },
+        toCString("Expected to find ", text, " but didnt!"));
+}
+
 void checkDoesNotUseInstruction(Compilation& compilation, const char* text)
 {
-    CString disassembly = compilation.disassembly();
-    if (!strstr(disassembly.data(), text))
-        return;
-
-    crashLock.lock();
-    dataLog("Bad lowering!  Did not expected to find ", text, " but it's there:\n");
-    dataLog(disassembly);
-    CRASH();
+    checkDisassembly(
+        compilation,
+        [&] (const char* disassembly) -> bool {
+            return !strstr(disassembly, text);
+        },
+        toCString("Did not expected to find ", text, " but it's there!"));
 }
 
 template<typename Type>
@@ -13381,6 +13390,275 @@
     go(false);
 }
 
+void testX86LeaAddAddShlLeft()
+{
+    // Add(Add(Shl(@x, $c), @y), $d)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<Value>(
+                proc, Shl, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+                root->appendNew<Const32Value>(proc, Origin(), 2)),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+        root->appendNew<ConstPtrValue>(proc, Origin(), 100));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkUsesInstruction(*code, "lea 0x64(%rdi,%rsi,4), %rax");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + (2 << 2)) + 100);
+}
+
+void testX86LeaAddAddShlRight()
+{
+    // Add(Add(@x, Shl(@y, $c)), $d)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+            root->appendNew<Value>(
+                proc, Shl, Origin(),
+                root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+                root->appendNew<Const32Value>(proc, Origin(), 2))),
+        root->appendNew<ConstPtrValue>(proc, Origin(), 100));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkUsesInstruction(*code, "lea 0x64(%rdi,%rsi,4), %rax");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + (2 << 2)) + 100);
+}
+
+void testX86LeaAddAdd()
+{
+    // Add(Add(@x, @y), $c)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+        root->appendNew<ConstPtrValue>(proc, Origin(), 100));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkDisassembly(
+        *code,
+        [&] (const char* disassembly) -> bool {
+            return strstr(disassembly, "lea 0x64(%rdi,%rsi), %rax")
+                || strstr(disassembly, "lea 0x64(%rsi,%rdi), %rax");
+        },
+        "Expected to find something like lea 0x64(%rdi,%rsi), %rax but didn't!");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + 2) + 100);
+}
+
+void testX86LeaAddShlRight()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 2)));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkUsesInstruction(*code, "lea (%rdi,%rsi,4), %rax");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 2));
+}
+
+void testX86LeaAddShlLeftScale1()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 0)));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkDisassembly(
+        *code,
+        [&] (const char* disassembly) -> bool {
+            return strstr(disassembly, "lea (%rdi,%rsi), %rax")
+                || strstr(disassembly, "lea (%rsi,%rdi), %rax");
+        },
+        "Expected to find something like lea (%rdi,%rsi), %rax but didn't!");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + 2);
+}
+
+void testX86LeaAddShlLeftScale2()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 1)));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkUsesInstruction(*code, "lea (%rdi,%rsi,2), %rax");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 1));
+}
+
+void testX86LeaAddShlLeftScale4()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 2)),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkUsesInstruction(*code, "lea (%rdi,%rsi,4), %rax");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 2));
+}
+
+void testX86LeaAddShlLeftScale8()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 3)));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    checkUsesInstruction(*code, "lea (%rdi,%rsi,8), %rax");
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 3));
+}
+
+void testAddShl32()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 32)));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (static_cast<intptr_t>(2) << static_cast<intptr_t>(32)));
+}
+
+void testAddShl64()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 64)));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + 2);
+}
+
+void testAddShl65()
+{
+    // Add(Shl(@x, $c), @y)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* result = root->appendNew<Value>(
+        proc, Add, Origin(),
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+        root->appendNew<Value>(
+            proc, Shl, Origin(),
+            root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
+            root->appendNew<Const32Value>(proc, Origin(), 65)));
+    root->appendNew<Value>(proc, Return, Origin(), result);
+    
+    auto code = compile(proc);
+    CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 1));
+}
+
+void testReduceStrengthReassociation(bool flip)
+{
+    // Add(Add(@x, $c), @y) -> Add(Add(@x, @y), $c)
+    // and
+    // Add(@y, Add(@x, $c)) -> Add(Add(@x, @y), $c)
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+    
+    Value* innerAdd = root->appendNew<Value>(
+        proc, Add, Origin(), arg1,
+        root->appendNew<ConstPtrValue>(proc, Origin(), 42));
+    
+    Value* outerAdd;
+    if (flip)
+        outerAdd = root->appendNew<Value>(proc, Add, Origin(), arg2, innerAdd);
+    else
+        outerAdd = root->appendNew<Value>(proc, Add, Origin(), innerAdd, arg2);
+    
+    root->appendNew<Value>(proc, Return, Origin(), outerAdd);
+    
+    proc.resetReachability();
+
+    if (shouldBeVerbose()) {
+        dataLog("IR before reduceStrength:\n");
+        dataLog(proc);
+    }
+    
+    reduceStrength(proc);
+    
+    if (shouldBeVerbose()) {
+        dataLog("IR after reduceStrength:\n");
+        dataLog(proc);
+    }
+    
+    CHECK_EQ(root->last()->opcode(), Return);
+    CHECK_EQ(root->last()->child(0)->opcode(), Add);
+    CHECK(root->last()->child(0)->child(1)->isIntPtr(42));
+    CHECK_EQ(root->last()->child(0)->child(0)->opcode(), Add);
+    CHECK(
+        (root->last()->child(0)->child(0)->child(0) == arg1 && root->last()->child(0)->child(0)->child(1) == arg2) ||
+        (root->last()->child(0)->child(0)->child(0) == arg2 && root->last()->child(0)->child(0)->child(1) == arg1));
+}
+
 // Make sure the compiler does not try to optimize anything out.
 NEVER_INLINE double zero()
 {
@@ -14801,6 +15079,23 @@
     RUN(testPatchpointTerminalReturnValue(true));
     RUN(testPatchpointTerminalReturnValue(false));
     
+    RUN(testMemoryFence());
+    RUN(testStoreFence());
+    RUN(testLoadFence());
+    RUN(testTrappingLoad());
+    RUN(testTrappingStore());
+    RUN(testTrappingLoadAddStore());
+    RUN(testTrappingLoadDCE());
+    RUN(testTrappingStoreElimination());
+    RUN(testMoveConstants());
+    RUN(testPCOriginMapDoesntInsertNops());
+    RUN(testPinRegisters());
+    RUN(testReduceStrengthReassociation(true));
+    RUN(testReduceStrengthReassociation(false));
+    RUN(testAddShl32());
+    RUN(testAddShl64());
+    RUN(testAddShl65());
+    
     if (isX86()) {
         RUN(testBranchBitAndImmFusion(Identity, Int64, 1, Air::BranchTest32, Air::Arg::Tmp));
         RUN(testBranchBitAndImmFusion(Identity, Int64, 0xff, Air::BranchTest32, Air::Arg::Tmp));
@@ -14810,7 +15105,14 @@
         RUN(testBranchBitAndImmFusion(Load8Z, Int32, 1, Air::BranchTest8, Air::Arg::Addr));
         RUN(testBranchBitAndImmFusion(Load, Int32, 1, Air::BranchTest32, Air::Arg::Addr));
         RUN(testBranchBitAndImmFusion(Load, Int64, 1, Air::BranchTest32, Air::Arg::Addr));
-        
+        RUN(testX86LeaAddAddShlLeft());
+        RUN(testX86LeaAddAddShlRight());
+        RUN(testX86LeaAddAdd());
+        RUN(testX86LeaAddShlRight());
+        RUN(testX86LeaAddShlLeftScale1());
+        RUN(testX86LeaAddShlLeftScale2());
+        RUN(testX86LeaAddShlLeftScale4());
+        RUN(testX86LeaAddShlLeftScale8());
     }
 
     if (isARM64()) {
@@ -14818,18 +15120,6 @@
         RUN(testTernarySubInstructionSelection(Trunc, Int32, Air::Sub32));
     }
 
-    RUN(testMemoryFence());
-    RUN(testStoreFence());
-    RUN(testLoadFence());
-    RUN(testTrappingLoad());
-    RUN(testTrappingStore());
-    RUN(testTrappingLoadAddStore());
-    RUN(testTrappingLoadDCE());
-    RUN(testTrappingStoreElimination());
-    RUN(testMoveConstants());
-    RUN(testPCOriginMapDoesntInsertNops());
-    RUN(testPinRegisters());
-    
     if (tasks.isEmpty())
         usage();
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to