B3 should be able to compile a program with a double constant
https://bugs.webkit.org/show_bug.cgi?id=151002

Reviewed by Benjamin Poulain.

This implements a bunch of annoying stuff that is necessary to support constants that need a
data section, such as double constants on X86_64:

- B3::Procedure can now tell you what to keep alive in addition to the MacroAssemblerCodeRef.
  We call this the B3::OpaqueByproducts. It's the client's responsibility to keep this alive
  after calling B3::generate().

- Added a new helper for compiling B3 code, called B3::Compilation. Constructing a
  Compilation runs the compiler. Then you can pass around a Compilation the way you would
  have passed around a MacroAssemblerCodeRef.

- Added a constant motion phase, called moveConstants(). This does very simple constant
  hoisting/sinking: it makes sure that each constant is only materialized in one place in
  each basic block. It uses a DataSection, which is a kind of OpaqueByproduct, to store
  double constants.

- The way I wanted to do constant motion is to basically track what constants are of interest
  and then recreate them as needed, so the original Values become irrelevant in the process.
  To do that, I needed an abstraction that is almost identical to the DFG PureValue
  abstraction that we use for CSE. So, I created such a thing, and called it ValueKey. It
  can be used to compare and hash pure Values, and to recreate them as needed.

- Fixed the lowering's handling of constants so that we don't perturb the placement of the
  constant materializations.

* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::branchConvertDoubleToInt32):
(JSC::MacroAssemblerX86Common::moveZeroToDouble):
(JSC::MacroAssemblerX86Common::branchDoubleNonZero):
* b3/B3Common.h:
(JSC::B3::isIdentical):
(JSC::B3::isRepresentableAsImpl):
* b3/B3Compilation.cpp: Added.
(JSC::B3::Compilation::Compilation):
(JSC::B3::Compilation::~Compilation):
* b3/B3Compilation.h: Added.
(JSC::B3::Compilation::code):
* b3/B3ConstDoubleValue.h:
(JSC::B3::ConstDoubleValue::accepts): Deleted.
* b3/B3DataSection.cpp: Added.
(JSC::B3::DataSection::DataSection):
(JSC::B3::DataSection::~DataSection):
(JSC::B3::DataSection::dump):
* b3/B3DataSection.h: Added.
(JSC::B3::DataSection::data):
(JSC::B3::DataSection::size):
* b3/B3Generate.cpp:
(JSC::B3::generate):
(JSC::B3::generateToAir):
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::imm):
(JSC::B3::Air::LowerToAir::immOrTmp):
(JSC::B3::Air::LowerToAir::fillStackmap):
(JSC::B3::Air::LowerToAir::lower):
(JSC::B3::Air::LowerToAir::immForMove): Deleted.
(JSC::B3::Air::LowerToAir::immOrTmpForMove): Deleted.
* b3/B3MoveConstants.cpp: Added.
(JSC::B3::moveConstants):
* b3/B3MoveConstants.h: Added.
* b3/B3OpaqueByproduct.h: Added.
(JSC::B3::OpaqueByproduct::OpaqueByproduct):
(JSC::B3::OpaqueByproduct::~OpaqueByproduct):
* b3/B3OpaqueByproducts.cpp: Added.
(JSC::B3::OpaqueByproducts::OpaqueByproducts):
(JSC::B3::OpaqueByproducts::~OpaqueByproducts):
(JSC::B3::OpaqueByproducts::add):
(JSC::B3::OpaqueByproducts::dump):
* b3/B3OpaqueByproducts.h: Added.
(JSC::B3::OpaqueByproducts::count):
* b3/B3Opcode.h:
(JSC::B3::constPtrOpcode):
* b3/B3Procedure.cpp:
(JSC::B3::Procedure::Procedure):
(JSC::B3::Procedure::dump):
(JSC::B3::Procedure::blocksInPreOrder):
(JSC::B3::Procedure::deleteValue):
(JSC::B3::Procedure::addDataSection):
(JSC::B3::Procedure::addValueIndex):
* b3/B3Procedure.h:
(JSC::B3::Procedure::lastPhaseName):
(JSC::B3::Procedure::byproducts):
(JSC::B3::Procedure::takeByproducts):
* b3/B3Type.h:
* b3/B3Value.cpp:
(JSC::B3::Value::effects):
(JSC::B3::Value::key):
(JSC::B3::Value::performSubstitution):
* b3/B3Value.h:
* b3/B3ValueKey.cpp: Added.
(JSC::B3::ValueKey::dump):
(JSC::B3::ValueKey::materialize):
* b3/B3ValueKey.h: Added.
(JSC::B3::ValueKey::ValueKey):
(JSC::B3::ValueKey::opcode):
(JSC::B3::ValueKey::type):
(JSC::B3::ValueKey::childIndex):
(JSC::B3::ValueKey::value):
(JSC::B3::ValueKey::doubleValue):
(JSC::B3::ValueKey::operator==):
(JSC::B3::ValueKey::operator!=):
(JSC::B3::ValueKey::hash):
(JSC::B3::ValueKey::operator bool):
(JSC::B3::ValueKey::canMaterialize):
(JSC::B3::ValueKey::isHashTableDeletedValue):
(JSC::B3::ValueKeyHash::hash):
(JSC::B3::ValueKeyHash::equal):
* b3/B3ValueKeyInlines.h: Added.
(JSC::B3::ValueKey::ValueKey):
(JSC::B3::ValueKey::child):
* b3/air/AirCode.cpp:
(JSC::B3::Air::Code::Code):
* b3/air/AirCode.h:
(JSC::B3::Air::Code::proc):
(JSC::B3::Air::Code::lastPhaseName):
* b3/air/AirOpcode.opcodes:
* b3/testb3.cpp:
(JSC::B3::compile):
(JSC::B3::invoke):
(JSC::B3::compileAndRun):
(JSC::B3::test42):
(JSC::B3::testBranch):
(JSC::B3::testBranchPtr):
(JSC::B3::testDiamond):
(JSC::B3::testBranchNotEqual):
(JSC::B3::testBranchNotEqualCommute):
(JSC::B3::testBranchNotEqualNotEqual):
(JSC::B3::testBranchEqual):
(JSC::B3::testBranchEqualEqual):
(JSC::B3::testBranchEqualCommute):
(JSC::B3::testBranchEqualEqual1):
(JSC::B3::testBranchFold):
(JSC::B3::testSimpleCheck):
(JSC::B3::testCompare):
(JSC::B3::testReturnDouble):
(JSC::B3::run):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@192183 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/b3/B3MoveConstants.cpp b/Source/JavaScriptCore/b3/B3MoveConstants.cpp
new file mode 100644
index 0000000..2ddd1a7
--- /dev/null
+++ b/Source/JavaScriptCore/b3/B3MoveConstants.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 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 "B3MoveConstants.h"
+
+#if ENABLE(B3_JIT)
+
+#include "B3BasicBlockInlines.h"
+#include "B3InsertionSetInlines.h"
+#include "B3MemoryValue.h"
+#include "B3PhaseScope.h"
+#include "B3ProcedureInlines.h"
+#include "B3ValueInlines.h"
+#include "B3ValueKeyInlines.h"
+
+namespace JSC { namespace B3 {
+
+namespace {
+
+class MoveConstants {
+public:
+    MoveConstants(Procedure& proc)
+        : m_proc(proc)
+        , m_insertionSet(proc)
+    {
+    }
+
+    void run()
+    {
+        // Eventually this phase will do smart things. For now, it uses a super simple heuristic: it
+        // places large constants in the block that uses them, and makes sure that each block has
+        // only one materialization for each large constant.
+
+        // FIXME: Implement a better story for constants. At a minimum this should allow the B3
+        // client to specify important constants that always get hoisted. Also, the table used to
+        // hold double constants should have a pointer to it that is hoisted. If we wanted to be more
+        // aggressive, we could make constant materialization be a feature of Air: we could label
+        // some Tmps as being unmaterialized constants and have a late Air phase - post register
+        // allocation - that creates materializations of those constant Tmps by scavenging leftover
+        // registers.
+
+        // First we need to figure out which constants go into the data section. These are non-zero
+        // double constants.
+        for (Value* value : m_proc.values()) {
+            if (!needsMotion(value))
+                continue;
+            m_toRemove.append(value);
+            ValueKey key = value->key();
+            if (goesInTable(key))
+                m_constTable.add(key, m_constTable.size());
+        }
+        
+        m_dataSection = static_cast<int64_t*>(m_proc.addDataSection(m_constTable.size() * sizeof(int64_t)));
+        for (auto& entry : m_constTable)
+            m_dataSection[entry.value] = entry.key.value();
+        
+        for (BasicBlock* block : m_proc) {
+            m_constants.clear();
+            
+            for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
+                Value* value = block->at(valueIndex);
+                for (Value*& child : value->children()) {
+                    if (!needsMotion(child))
+                        continue;
+
+                    child = materialize(valueIndex, child->key(), value->origin());
+                }
+            }
+            
+            m_insertionSet.execute(block);
+        }
+
+        for (Value* toRemove : m_toRemove)
+            toRemove->replaceWithNop();
+    }
+
+private:
+    Value* materialize(unsigned valueIndex, const ValueKey& key, const Origin& origin)
+    {
+        if (Value* result = m_constants.get(key))
+            return result;
+
+        // Note that we deliberately don't do this in one add() because this is a recursive function
+        // that may rehash the map.
+
+        Value* result;
+        if (goesInTable(key)) {
+            Value* tableBase = materialize(
+                valueIndex,
+                ValueKey(
+                    constPtrOpcode(), pointerType(),
+                    static_cast<int64_t>(bitwise_cast<intptr_t>(m_dataSection))),
+                origin);
+            result = m_insertionSet.insert<MemoryValue>(
+                valueIndex, Load, key.type(), origin, tableBase,
+                sizeof(intptr_t) * m_constTable.get(key));
+        } else
+            result = m_insertionSet.insertValue(valueIndex, key.materialize(m_proc, origin));
+        m_constants.add(key, result);
+        return result;
+    }
+
+    bool goesInTable(const ValueKey& key)
+    {
+        return key.opcode() == ConstDouble && key != doubleZero();
+    }
+
+    bool needsMotion(const Value* value)
+    {
+        if (!value->isConstant())
+            return false;
+        
+        // We currently assume that 32-bit int constants are always cheap to materialize.
+        // This is wrong for ARM. We need some abstract query like "isImmediate(int64_t)". On
+        // ARM64 this would take into account the way that ARM64 can encode large constants.
+        if (value->hasInt() && value->representableAs<int32_t>())
+            return false;
+
+        return true;
+    }
+
+    static ValueKey doubleZero()
+    {
+        return ValueKey(ConstDouble, Double, 0.0);
+    }
+
+    Procedure& m_proc;
+    Vector<Value*> m_toRemove;
+    HashMap<ValueKey, unsigned> m_constTable;
+    int64_t* m_dataSection;
+    HashMap<ValueKey, Value*> m_constants;
+    InsertionSet m_insertionSet;
+};
+
+} // anonymous namespace
+
+void moveConstants(Procedure& proc)
+{
+    PhaseScope phaseScope(proc, "moveConstants");
+    MoveConstants moveConstants(proc);
+    moveConstants.run();
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+