B3 needs a special WasmBoundsCheck Opcode
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Oct 2016 01:42:53 +0000 (01:42 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Oct 2016 01:42:53 +0000 (01:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=163246

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

This patch adds a new Opcode, WasmBoundsCheck, as well as a B3::Value subclass for it,
WasmBoundsCheckValue. WasmBoundsCheckValue takes three pieces of information. The first is
the Int32 pointer value used to be used by the Load.  Next is the pinned register. The
pinned register must be pinned by calling proc.setPinned() prior to compiling the
Procedure. Lastly, the WasmBoundsCheckValue takes an offset. The WasmBoundsCheckValue is
will then emit code that side-exits if the Int64 sum of the offset and pointer is greater
than or equal to the value in the pinnedRegister. Instead of taking a generator for each
value like Check/Patchpoint, WasmBoundsCheck gets its generator directly off Air::Code. In
Air this patch adds a new Custom opcode, WasmBoundsCheck.

In the future we should add WasmBoundsCheck to CSE so it can eliminate redundant bounds
checks. At the first cut, we can remove any WasmBoundsCheck dominated by another
WasmBoundsCheck with the same pointer and pinnedGPR, and a larger offset.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::imm):
(JSC::B3::Air::LowerToAir::lower):
* b3/B3Opcode.cpp:
(WTF::printInternal):
* b3/B3Opcode.h:
* b3/B3Procedure.cpp:
(JSC::B3::Procedure::setWasmBoundsCheckGenerator):
* b3/B3Procedure.h:
(JSC::B3::Procedure::setWasmBoundsCheckGenerator):
* b3/B3Validate.cpp:
* b3/B3Value.cpp:
(JSC::B3::Value::effects):
(JSC::B3::Value::typeFor):
* b3/B3WasmBoundsCheckValue.cpp: Added.
(JSC::B3::WasmBoundsCheckValue::~WasmBoundsCheckValue):
(JSC::B3::WasmBoundsCheckValue::WasmBoundsCheckValue):
(JSC::B3::WasmBoundsCheckValue::dumpMeta):
* b3/B3WasmBoundsCheckValue.h: Added.
(JSC::B3::WasmBoundsCheckValue::accepts):
(JSC::B3::WasmBoundsCheckValue::pinnedGPR):
(JSC::B3::WasmBoundsCheckValue::offset):
* b3/air/AirCode.h:
(JSC::B3::Air::Code::setWasmBoundsCheckGenerator):
(JSC::B3::Air::Code::wasmBoundsCheckGenerator):
* b3/air/AirCustom.cpp:
(JSC::B3::Air::WasmBoundsCheckCustom::isValidForm):
* b3/air/AirCustom.h:
(JSC::B3::Air::WasmBoundsCheckCustom::forEachArg):
(JSC::B3::Air::WasmBoundsCheckCustom::isValidFormStatic):
(JSC::B3::Air::WasmBoundsCheckCustom::admitsStack):
(JSC::B3::Air::WasmBoundsCheckCustom::isTerminal):
(JSC::B3::Air::WasmBoundsCheckCustom::hasNonArgNonControlEffects):
(JSC::B3::Air::WasmBoundsCheckCustom::generate):
* b3/air/AirOpcode.opcodes:
* b3/testb3.cpp:
(JSC::B3::testWasmBoundsCheck):
(JSC::B3::run):

Websites/webkit.org:

Update the docs for the new WasmBoundsCheck opcode.

* docs/b3/intermediate-representation.html:

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

19 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/b3/B3LowerToAir.cpp
Source/JavaScriptCore/b3/B3Opcode.cpp
Source/JavaScriptCore/b3/B3Opcode.h
Source/JavaScriptCore/b3/B3Procedure.cpp
Source/JavaScriptCore/b3/B3Procedure.h
Source/JavaScriptCore/b3/B3Validate.cpp
Source/JavaScriptCore/b3/B3Value.cpp
Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.cpp [new file with mode: 0644]
Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.h [new file with mode: 0644]
Source/JavaScriptCore/b3/air/AirCode.h
Source/JavaScriptCore/b3/air/AirCustom.cpp
Source/JavaScriptCore/b3/air/AirCustom.h
Source/JavaScriptCore/b3/air/AirOpcode.opcodes
Source/JavaScriptCore/b3/testb3.cpp
Websites/webkit.org/ChangeLog
Websites/webkit.org/docs/b3/intermediate-representation.html

index 57ceff6..aae71b4 100644 (file)
@@ -172,6 +172,7 @@ set(JavaScriptCore_SOURCES
     b3/B3ValueRep.cpp
     b3/B3Variable.cpp
     b3/B3VariableValue.cpp
+    b3/B3WasmBoundsCheckValue.cpp
 
     bindings/ScriptFunctionCall.cpp
     bindings/ScriptObject.cpp
index 5273a19..728639d 100644 (file)
@@ -1,3 +1,65 @@
+2016-10-12  Keith Miller  <keith_miller@apple.com>
+
+        B3 needs a special WasmBoundsCheck Opcode
+        https://bugs.webkit.org/show_bug.cgi?id=163246
+
+        Reviewed by Filip Pizlo.
+
+        This patch adds a new Opcode, WasmBoundsCheck, as well as a B3::Value subclass for it,
+        WasmBoundsCheckValue. WasmBoundsCheckValue takes three pieces of information. The first is
+        the Int32 pointer value used to be used by the Load.  Next is the pinned register. The
+        pinned register must be pinned by calling proc.setPinned() prior to compiling the
+        Procedure. Lastly, the WasmBoundsCheckValue takes an offset. The WasmBoundsCheckValue is
+        will then emit code that side-exits if the Int64 sum of the offset and pointer is greater
+        than or equal to the value in the pinnedRegister. Instead of taking a generator for each
+        value like Check/Patchpoint, WasmBoundsCheck gets its generator directly off Air::Code. In
+        Air this patch adds a new Custom opcode, WasmBoundsCheck.
+
+        In the future we should add WasmBoundsCheck to CSE so it can eliminate redundant bounds
+        checks. At the first cut, we can remove any WasmBoundsCheck dominated by another
+        WasmBoundsCheck with the same pointer and pinnedGPR, and a larger offset.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::imm):
+        (JSC::B3::Air::LowerToAir::lower):
+        * b3/B3Opcode.cpp:
+        (WTF::printInternal):
+        * b3/B3Opcode.h:
+        * b3/B3Procedure.cpp:
+        (JSC::B3::Procedure::setWasmBoundsCheckGenerator):
+        * b3/B3Procedure.h:
+        (JSC::B3::Procedure::setWasmBoundsCheckGenerator):
+        * b3/B3Validate.cpp:
+        * b3/B3Value.cpp:
+        (JSC::B3::Value::effects):
+        (JSC::B3::Value::typeFor):
+        * b3/B3WasmBoundsCheckValue.cpp: Added.
+        (JSC::B3::WasmBoundsCheckValue::~WasmBoundsCheckValue):
+        (JSC::B3::WasmBoundsCheckValue::WasmBoundsCheckValue):
+        (JSC::B3::WasmBoundsCheckValue::dumpMeta):
+        * b3/B3WasmBoundsCheckValue.h: Added.
+        (JSC::B3::WasmBoundsCheckValue::accepts):
+        (JSC::B3::WasmBoundsCheckValue::pinnedGPR):
+        (JSC::B3::WasmBoundsCheckValue::offset):
+        * b3/air/AirCode.h:
+        (JSC::B3::Air::Code::setWasmBoundsCheckGenerator):
+        (JSC::B3::Air::Code::wasmBoundsCheckGenerator):
+        * b3/air/AirCustom.cpp:
+        (JSC::B3::Air::WasmBoundsCheckCustom::isValidForm):
+        * b3/air/AirCustom.h:
+        (JSC::B3::Air::WasmBoundsCheckCustom::forEachArg):
+        (JSC::B3::Air::WasmBoundsCheckCustom::isValidFormStatic):
+        (JSC::B3::Air::WasmBoundsCheckCustom::admitsStack):
+        (JSC::B3::Air::WasmBoundsCheckCustom::isTerminal):
+        (JSC::B3::Air::WasmBoundsCheckCustom::hasNonArgNonControlEffects):
+        (JSC::B3::Air::WasmBoundsCheckCustom::generate):
+        * b3/air/AirOpcode.opcodes:
+        * b3/testb3.cpp:
+        (JSC::B3::testWasmBoundsCheck):
+        (JSC::B3::run):
+
 2016-10-12  Filip Pizlo  <fpizlo@apple.com>
 
         The blackening of CellState is a bad way of tracking if the object is being marked for the first time
index d44b2c4..108fe8a 100644 (file)
                52C952B919A28A1C0069B386 /* TypeProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52C952B819A28A1C0069B386 /* TypeProfiler.cpp */; };
                531374BD1D5CE67600AF7A0B /* WASMPlan.h in Headers */ = {isa = PBXBuildFile; fileRef = 531374BC1D5CE67600AF7A0B /* WASMPlan.h */; };
                531374BF1D5CE95000AF7A0B /* WASMPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 531374BE1D5CE95000AF7A0B /* WASMPlan.cpp */; };
+               5341FC701DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */; };
+               5341FC721DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */; };
                53486BB71C1795C300F6F3AF /* JSTypedArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 53486BB61C1795C300F6F3AF /* JSTypedArray.h */; settings = {ATTRIBUTES = (Public, ); }; };
                53486BBB1C18E84500F6F3AF /* JSTypedArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53486BBA1C18E84500F6F3AF /* JSTypedArray.cpp */; };
                534902851C7276B70012BCB8 /* TypedArrayCTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 534902821C7242C80012BCB8 /* TypedArrayCTest.cpp */; };
                52C952B819A28A1C0069B386 /* TypeProfiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TypeProfiler.cpp; sourceTree = "<group>"; };
                531374BC1D5CE67600AF7A0B /* WASMPlan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WASMPlan.h; sourceTree = "<group>"; };
                531374BE1D5CE95000AF7A0B /* WASMPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WASMPlan.cpp; sourceTree = "<group>"; };
+               5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3WasmBoundsCheckValue.cpp; path = b3/B3WasmBoundsCheckValue.cpp; sourceTree = "<group>"; };
+               5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3WasmBoundsCheckValue.h; path = b3/B3WasmBoundsCheckValue.h; sourceTree = "<group>"; };
                53486BB61C1795C300F6F3AF /* JSTypedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTypedArray.h; sourceTree = "<group>"; };
                53486BBA1C18E84500F6F3AF /* JSTypedArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTypedArray.cpp; sourceTree = "<group>"; };
                534902821C7242C80012BCB8 /* TypedArrayCTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypedArrayCTest.cpp; path = API/tests/TypedArrayCTest.cpp; sourceTree = "<group>"; };
                                0F338E181BF286EA0013C88F /* B3BlockInsertionSet.h */,
                                0FEC84BA1BDACDAC0080FF74 /* B3BlockWorklist.h */,
                                DCFDFBD71D1F5D9800FE3D72 /* B3BottomProvider.h */,
+                               5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */,
+                               5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */,
                                0F6B8ADE1C4EFE1700969052 /* B3BreakCriticalEdges.cpp */,
                                0F6B8ADF1C4EFE1700969052 /* B3BreakCriticalEdges.h */,
                                DC9A0C1C1D2D94EF0085124E /* B3CaseCollection.cpp */,
                                72AAF7CE1D0D31B3005E60BE /* JSCustomGetterSetterFunction.h in Headers */,
                                0F1E3A67153A21E2000F9456 /* DFGSilentRegisterSavePlan.h in Headers */,
                                0FFB921D16D02F300055A5DB /* DFGSlowPathGenerator.h in Headers */,
+                               5341FC721DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h in Headers */,
                                86EC9DD31328DF82002B2AD7 /* DFGSpeculativeJIT.h in Headers */,
                                0F682FB319BCB36400FA3BAD /* DFGSSACalculator.h in Headers */,
                                A7D89D0017A0B8CC00773AD8 /* DFGSSAConversionPhase.h in Headers */,
                                2AACE63C18CA5A0300ED0191 /* GCActivityCallback.cpp in Sources */,
                                0F766D2F15A8DCE0008F363E /* GCAwareJITStubRoutine.cpp in Sources */,
                                2ADFA26318EF3540004F9FCC /* GCLogging.cpp in Sources */,
+                               5341FC701DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp in Sources */,
                                0F93329F14CA7DCA0085F3C6 /* GetByIdStatus.cpp in Sources */,
                                0F0332C318B01763005F979A /* GetByIdVariant.cpp in Sources */,
                                14280855107EC0E70013E7B2 /* GetterSetter.cpp in Sources */,
index d61a076..5c89cbd 100644 (file)
@@ -568,13 +568,17 @@ private:
         return loadPromise(loadValue, Load);
     }
 
+    Arg imm(int64_t intValue)
+    {
+        if (Arg::isValidImmForm(intValue))
+            return Arg::imm(intValue);
+        return Arg();
+    }
+
     Arg imm(Value* value)
     {
-        if (value->hasInt()) {
-            int64_t intValue = value->asInt();
-            if (Arg::isValidImmForm(intValue))
-                return Arg::imm(intValue);
-        }
+        if (value->hasInt())
+            return imm(value->asInt());
         return Arg();
     }
 
@@ -2627,6 +2631,26 @@ private:
             return;
         }
 
+        case B3::WasmBoundsCheck: {
+            WasmBoundsCheckValue* value = m_value->as<WasmBoundsCheckValue>();
+
+            Value* ptr = value->child(0);
+
+            Arg temp = m_code.newTmp(Arg::GP);
+            append(Inst(Move32, value, tmp(ptr), temp));
+            if (value->offset()) {
+                if (imm(value->offset()))
+                    append(Add64, imm(value->offset()), temp);
+                else {
+                    Arg bigImm = m_code.newTmp(Arg::GP);
+                    append(Move, Arg::bigImm(value->offset()), bigImm);
+                    append(Add64, bigImm, temp);
+                }
+            }
+            append(Inst(Air::WasmBoundsCheck, value, temp, Arg(value->pinnedGPR())));
+            return;
+        }
+
         case Upsilon: {
             Value* value = m_value->child(0);
             append(
index f03a94c..e100d3b 100644 (file)
@@ -272,6 +272,9 @@ void printInternal(PrintStream& out, Opcode opcode)
     case Check:
         out.print("Check");
         return;
+    case WasmBoundsCheck:
+        out.print("WasmBoundsCheck");
+        return;
     case Upsilon:
         out.print("Upsilon");
         return;
index c977347..e32e9d3 100644 (file)
@@ -209,6 +209,12 @@ enum Opcode : int16_t {
     // WarmAny. It will not have an output constraint.
     Check,
 
+    // Special Wasm opcode that takes a Int32, a special pinned gpr and an offset. This node exists
+    // to allow us to CSE WasmBoundsChecks if both use the same pointer and one dominates the other.
+    // Without some such node B3 would not have enough information about the inner workings of wasm
+    // to be able to perform such optimizations.
+    WasmBoundsCheck,
+
     // SSA support, in the style of DFG SSA.
     Upsilon, // This uses the UpsilonValue class.
     Phi,
index 29fc1c4..6cde2f1 100644 (file)
@@ -348,6 +348,11 @@ void Procedure::setBlockOrderImpl(Vector<BasicBlock*>& blocks)
     }
 }
 
+void Procedure::setWasmBoundsCheckGenerator(RefPtr<WasmBoundsCheckGenerator> generator)
+{
+    code().setWasmBoundsCheckGenerator(generator);
+}
+
 } } // namespace JSC::B3
 
 #endif // ENABLE(B3_JIT)
index d1c722f..2236145 100644 (file)
@@ -58,6 +58,9 @@ class Variable;
 
 namespace Air { class Code; }
 
+typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg, unsigned);
+typedef SharedTask<WasmBoundsCheckGeneratorFunction> WasmBoundsCheckGenerator;
+
 // This represents B3's view of a piece of code. Note that this object must exist in a 1:1
 // relationship with Air::Code. B3::Procedure and Air::Code are just different facades of the B3
 // compiler's knowledge about a piece of code. Some kinds of state aren't perfect fits for either
@@ -221,6 +224,14 @@ public:
     PCToOriginMap& pcToOriginMap() { return m_pcToOriginMap; }
     PCToOriginMap releasePCToOriginMap() { return WTFMove(m_pcToOriginMap); }
 
+    JS_EXPORT_PRIVATE void setWasmBoundsCheckGenerator(RefPtr<WasmBoundsCheckGenerator>);
+
+    template<typename Functor>
+    void setWasmBoundsCheckGenerator(const Functor& functor)
+    {
+        setWasmBoundsCheckGenerator(RefPtr<WasmBoundsCheckGenerator>(createSharedTask<WasmBoundsCheckGeneratorFunction>(functor)));
+    }
+
 private:
     friend class BlockInsertionSet;
 
index afc00a2..b06344e 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(B3_JIT)
 
+#include "AirCode.h"
 #include "B3ArgumentRegValue.h"
 #include "B3BasicBlockInlines.h"
 #include "B3Dominators.h"
@@ -40,6 +41,7 @@
 #include "B3ValueInlines.h"
 #include "B3Variable.h"
 #include "B3VariableValue.h"
+#include "B3WasmBoundsCheckValue.h"
 #include <wtf/HashSet.h>
 #include <wtf/StringPrintStream.h>
 #include <wtf/text/CString.h>
@@ -413,6 +415,13 @@ public:
                 VALIDATE(value->as<StackmapValue>()->constrainedChild(0).rep() == ValueRep::WarmAny, ("At ", *value));
                 validateStackmap(value);
                 break;
+            case WasmBoundsCheck:
+                VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
+                VALIDATE(value->numChildren() == 1, ("At ", *value));
+                VALIDATE(value->child(0)->type() == Int32, ("At ", *value));
+                VALIDATE(m_procedure.code().isPinned(value->as<WasmBoundsCheckValue>()->pinnedGPR()), ("At ", *value));
+                VALIDATE(m_procedure.code().wasmBoundsCheckGenerator(), ("At ", *value));
+                break;
             case Upsilon:
                 VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
                 VALIDATE(value->numChildren() == 1, ("At ", *value));
index a087f51..5b41198 100644 (file)
@@ -616,6 +616,10 @@ Effects Value::effects() const
     case Check:
         result = Effects::forCheck();
         break;
+    case WasmBoundsCheck:
+        result.readsPinned = true;
+        result.exitsSideways = true;
+        break;
     case Upsilon:
     case Set:
         result.writesLocalState = true;
@@ -809,6 +813,7 @@ Type Value::typeFor(Kind kind, Value* firstChild, Value* secondChild)
     case Return:
     case Oops:
     case EntrySwitch:
+    case WasmBoundsCheck:
         return Void;
     case Select:
         ASSERT(secondChild);
diff --git a/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.cpp b/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.cpp
new file mode 100644 (file)
index 0000000..b3a3290
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 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 "B3WasmBoundsCheckValue.h"
+
+#if ENABLE(B3_JIT)
+
+namespace JSC { namespace B3 {
+
+WasmBoundsCheckValue::~WasmBoundsCheckValue()
+{
+}
+
+WasmBoundsCheckValue::WasmBoundsCheckValue(Origin origin, Value* ptr, GPRReg pinnedGPR, unsigned offset)
+    : Value(CheckedOpcode, WasmBoundsCheck, origin, ptr)
+    , m_pinnedGPR(pinnedGPR)
+    , m_offset(offset)
+{
+}
+
+Value* WasmBoundsCheckValue::cloneImpl() const
+{
+    return new WasmBoundsCheckValue(*this);
+}
+
+void WasmBoundsCheckValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const
+{
+    out.print(comma, "sizeRegister = ", m_pinnedGPR, ", offset = ", m_offset);
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
diff --git a/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.h b/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.h
new file mode 100644 (file)
index 0000000..ccc54b8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015-2016 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)
+
+#include "B3Value.h"
+#include "CCallHelpers.h"
+
+namespace JSC { namespace B3 {
+
+class WasmBoundsCheckValue : public Value {
+public:
+    static bool accepts(Kind kind)
+    {
+        switch (kind.opcode()) {
+        case WasmBoundsCheck:
+            return true;
+        default:
+            return false;
+        }
+    }
+    
+    ~WasmBoundsCheckValue();
+
+    GPRReg pinnedGPR() const { return m_pinnedGPR; }
+    unsigned offset() const { return m_offset; }
+
+protected:
+    void dumpMeta(CommaPrinter&, PrintStream&) const override;
+
+    Value* cloneImpl() const override;
+
+private:
+    friend class Procedure;
+
+    JS_EXPORT_PRIVATE WasmBoundsCheckValue(Origin, Value* ptr, GPRReg pinnedGPR, unsigned offset);
+
+    GPRReg m_pinnedGPR;
+    unsigned m_offset;
+};
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
index 83cb2e1..6d4a147 100644 (file)
@@ -52,6 +52,9 @@ namespace Air {
 class BlockInsertionSet;
 class CCallSpecial;
 
+typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg, unsigned);
+typedef SharedTask<WasmBoundsCheckGeneratorFunction> WasmBoundsCheckGenerator;
+
 // This is an IR that is very close to the bare metal. It requires about 40x more bytes than the
 // generated machine code - for example if you're generating 1MB of machine code, you need about
 // 40MB of Air.
@@ -261,6 +264,13 @@ public:
 
     const char* lastPhaseName() const { return m_lastPhaseName; }
 
+    void setWasmBoundsCheckGenerator(RefPtr<WasmBoundsCheckGenerator> generator)
+    {
+        m_wasmBoundsCheckGenerator = generator;
+    }
+
+    RefPtr<WasmBoundsCheckGenerator> wasmBoundsCheckGenerator() const { return m_wasmBoundsCheckGenerator; }
+
     // This is a hash of the code. You can use this if you want to put code into a hashtable, but
     // it's mainly for validating the results from JSAir.
     unsigned jsHash() const;
@@ -298,6 +308,7 @@ private:
     RegisterAtOffsetList m_calleeSaveRegisters;
     Vector<FrequentedBlock> m_entrypoints; // This is empty until after lowerEntrySwitch().
     Vector<CCallHelpers::Label> m_entrypointLabels; // This is empty until code generation.
+    RefPtr<WasmBoundsCheckGenerator> m_wasmBoundsCheckGenerator;
     const char* m_lastPhaseName;
 };
 
index f6eb5f3..2a2df2f 100644 (file)
@@ -178,6 +178,17 @@ CCallHelpers::Jump ShuffleCustom::generate(Inst& inst, CCallHelpers&, Generation
     return CCallHelpers::Jump();
 }
 
+bool WasmBoundsCheckCustom::isValidForm(Inst& inst)
+{
+    if (inst.args.size() != 2)
+        return false;
+    if (!inst.args[0].isTmp() && !inst.args[0].isSomeImm())
+        return false;
+
+    return inst.args[1].isReg();
+}
+
+
 } } } // namespace JSC::B3::Air
 
 #endif // ENABLE(B3_JIT)
index 8b90fef..b0d55cd 100644 (file)
 
 #if ENABLE(B3_JIT)
 
+#include "AirCode.h"
+#include "AirGenerationContext.h"
 #include "AirInst.h"
 #include "AirSpecial.h"
-#include "B3Value.h"
+#include "B3ValueInlines.h"
+#include "B3WasmBoundsCheckValue.h"
 
 namespace JSC { namespace B3 { namespace Air {
 
@@ -273,6 +276,53 @@ struct EntrySwitchCustom : public CommonCustomBase<EntrySwitchCustom> {
     }
 };
 
+struct WasmBoundsCheckCustom : public CommonCustomBase<WasmBoundsCheckCustom> {
+    template<typename Func>
+    static void forEachArg(Inst& inst, const Func& functor)
+    {
+        functor(inst.args[0], Arg::Use, Arg::GP, Arg::Width64);
+        functor(inst.args[1], Arg::Use, Arg::GP, Arg::Width64);
+    }
+
+    template<typename... Arguments>
+    static bool isValidFormStatic(Arguments...)
+    {
+        return false;
+    }
+
+    static bool isValidForm(Inst&);
+
+    static bool admitsStack(Inst&, unsigned)
+    {
+        return false;
+    }
+
+    static bool isTerminal(Inst&)
+    {
+        return false;
+    }
+    
+    static bool hasNonArgNonControlEffects(Inst&)
+    {
+        return true;
+    }
+
+    static CCallHelpers::Jump generate(Inst& inst, CCallHelpers& jit, GenerationContext& context)
+    {
+        WasmBoundsCheckValue* value = inst.origin->as<WasmBoundsCheckValue>();
+        CCallHelpers::Jump outOfBounds = Inst(Air::Branch64, value, Arg::relCond(CCallHelpers::AboveOrEqual), inst.args[0], inst.args[1]).generate(jit, context);
+
+        context.latePaths.append(createSharedTask<GenerationContext::LatePathFunction>(
+            [=] (CCallHelpers& jit, Air::GenerationContext&) {
+                outOfBounds.link(&jit);
+                context.code->wasmBoundsCheckGenerator()->run(jit, value->pinnedGPR(), value->offset());
+            }));
+
+        // We said we were not a terminal.
+        return CCallHelpers::Jump();
+    }
+};
+
 } } } // namespace JSC::B3::Air
 
 #endif // ENABLE(B3_JIT)
index e94672e..17972a3 100644 (file)
@@ -889,4 +889,7 @@ custom Patch
 custom CCall
 custom ColdCCall
 
+# This is a special wasm opcode that branches to a trap handler. This uses the generator located to Air::Code
+# to produce the side-exit code.
+custom WasmBoundsCheck
 
index ec24e4d..ac31c3c 100644 (file)
@@ -54,6 +54,7 @@
 #include "B3Validate.h"
 #include "B3ValueInlines.h"
 #include "B3VariableValue.h"
+#include "B3WasmBoundsCheckValue.h"
 #include "CCallHelpers.h"
 #include "FPRInfo.h"
 #include "GPRInfo.h"
@@ -13729,6 +13730,37 @@ void testOptimizeMaterialization()
     CHECK(found);
 }
 
+void testWasmBoundsCheck(unsigned offset)
+{
+    Procedure proc;
+    GPRReg pinned = GPRInfo::argumentGPR1;
+    proc.pinRegister(pinned);
+
+    proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned actualOffset) {
+        CHECK_EQ(pinnedGPR, pinned);
+        CHECK_EQ(actualOffset, offset);
+
+        // This should always work because a function this simple should never have callee
+        // saves.
+        jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+
+    BasicBlock* root = proc.addBlock();
+    Value* left = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    if (pointerType() != Int32)
+        left = root->appendNew<Value>(proc, Trunc, Origin(), left);
+    root->appendNew<WasmBoundsCheckValue>(proc, Origin(), left, pinned, offset);
+    Value* result = root->appendNew<Const32Value>(proc, Origin(), 0x42);
+    root->appendNewControlValue(proc, Return, Origin(), result);
+
+    auto code = compile(proc);
+    CHECK_EQ(invoke<int32_t>(*code, 1, 2 + offset), 0x42);
+    CHECK_EQ(invoke<int32_t>(*code, 3, 2 + offset), 42);
+    CHECK_EQ(invoke<int32_t>(*code, 2, 2 + offset), 42);
+}
+
 // Make sure the compiler does not try to optimize anything out.
 NEVER_INLINE double zero()
 {
@@ -15148,7 +15180,7 @@ void run(const char* filter)
     RUN(testSomeEarlyRegister());
     RUN(testPatchpointTerminalReturnValue(true));
     RUN(testPatchpointTerminalReturnValue(false));
-    
+
     RUN(testMemoryFence());
     RUN(testStoreFence());
     RUN(testLoadFence());
@@ -15168,7 +15200,12 @@ void run(const char* filter)
     RUN(testLoadBaseIndexShift2());
     RUN(testLoadBaseIndexShift32());
     RUN(testOptimizeMaterialization());
-    
+
+    RUN(testWasmBoundsCheck(0));
+    RUN(testWasmBoundsCheck(100));
+    RUN(testWasmBoundsCheck(10000));
+    RUN(testWasmBoundsCheck(std::numeric_limits<unsigned>::max() - 5));
+
     if (isX86()) {
         RUN(testBranchBitAndImmFusion(Identity, Int64, 1, Air::BranchTest32, Air::Arg::Tmp));
         RUN(testBranchBitAndImmFusion(Identity, Int64, 0xff, Air::BranchTest32, Air::Arg::Tmp));
index 4c70f79..6ce13d8 100644 (file)
@@ -1,3 +1,14 @@
+2016-10-12  Keith Miller  <keith_miller@apple.com>
+
+        B3 needs a special WasmBoundsCheck Opcode
+        https://bugs.webkit.org/show_bug.cgi?id=163246
+
+        Reviewed by Filip Pizlo.
+
+        Update the docs for the new WasmBoundsCheck opcode.
+
+        * docs/b3/intermediate-representation.html:
+
 2016-10-09  Simon Fraser  <simon.fraser@apple.com>
 
         Convert contributors.json to a flat list
index bdc57a1..62ac97c 100644 (file)
@@ -537,6 +537,16 @@ patchpoint->setGenerator(
         to an instruction that branches to the exit if @a &gt;= @b or if either @a or @b are
         NaN.  Must use the CheckValue class.</dd>
 
+      <dt>Void WasmBoundsCheck(Int32, pinnedGPR, offset)</dt>
+      <dd>Special Wasm opcode. This branches on the first child. If the first child plus the offset
+        produces a Int64 less than to the pinnedGPR this falls through. Otherwise, it branches to
+        the exit path generated by the passed generator. Unlike the Patch/Check family, the
+        generator used by WasmBoundsCheck sould be set on the Procuder itself. The GRPReg passed in
+        pinnedGPR must also be marked as pinned by calling the Procedure's pinning API. B3 assumes
+        the WasmBoundsCheck will side-exit when the it branches, so the generator must do some kind
+        of termination. In Wasm this is used to trap and unwind back to JS. Must use the
+        WasmBoundsCheckValue class.</dd>
+
       <dt>Void Upsilon(T, ^phi)</dt>
       <dd>B3 uses SSA.  SSA requires that each variable gets assigned to only once anywhere in
         the procedure.  But that doesn't work if you have a variable that is supposed to be the