Title: [207266] trunk
Revision
207266
Author
keith_mil...@apple.com
Date
2016-10-12 18:42:53 -0700 (Wed, 12 Oct 2016)

Log Message

B3 needs a special WasmBoundsCheck Opcode
https://bugs.webkit.org/show_bug.cgi?id=163246

Reviewed by Filip Pizlo.

Source/_javascript_Core:

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:
* _javascript_Core.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:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/CMakeLists.txt (207265 => 207266)


--- trunk/Source/_javascript_Core/CMakeLists.txt	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/CMakeLists.txt	2016-10-13 01:42:53 UTC (rev 207266)
@@ -172,6 +172,7 @@
     b3/B3ValueRep.cpp
     b3/B3Variable.cpp
     b3/B3VariableValue.cpp
+    b3/B3WasmBoundsCheckValue.cpp
 
     bindings/ScriptFunctionCall.cpp
     bindings/ScriptObject.cpp

Modified: trunk/Source/_javascript_Core/ChangeLog (207265 => 207266)


--- trunk/Source/_javascript_Core/ChangeLog	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-10-13 01:42:53 UTC (rev 207266)
@@ -1,3 +1,65 @@
+2016-10-12  Keith Miller  <keith_mil...@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:
+        * _javascript_Core.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  <fpi...@apple.com>
 
         The blackening of CellState is a bad way of tracking if the object is being marked for the first time

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (207265 => 207266)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-10-13 01:42:53 UTC (rev 207266)
@@ -1206,6 +1206,8 @@
 		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 */; };
@@ -3441,6 +3443,8 @@
 		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>"; };
@@ -4857,6 +4861,8 @@
 				0F338E181BF286EA0013C88F /* B3BlockInsertionSet.h */,
 				0FEC84BA1BDACDAC0080FF74 /* B3BlockWorklist.h */,
 				DCFDFBD71D1F5D9800FE3D72 /* B3BottomProvider.h */,
+				5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */,
+				5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */,
 				0F6B8ADE1C4EFE1700969052 /* B3BreakCriticalEdges.cpp */,
 				0F6B8ADF1C4EFE1700969052 /* B3BreakCriticalEdges.h */,
 				DC9A0C1C1D2D94EF0085124E /* B3CaseCollection.cpp */,
@@ -7650,6 +7656,7 @@
 				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 */,
@@ -9365,6 +9372,7 @@
 				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 */,

Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -568,13 +568,17 @@
         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 @@
             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(

Modified: trunk/Source/_javascript_Core/b3/B3Opcode.cpp (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/B3Opcode.cpp	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/B3Opcode.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -272,6 +272,9 @@
     case Check:
         out.print("Check");
         return;
+    case WasmBoundsCheck:
+        out.print("WasmBoundsCheck");
+        return;
     case Upsilon:
         out.print("Upsilon");
         return;

Modified: trunk/Source/_javascript_Core/b3/B3Opcode.h (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/B3Opcode.h	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/B3Opcode.h	2016-10-13 01:42:53 UTC (rev 207266)
@@ -209,6 +209,12 @@
     // 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,

Modified: trunk/Source/_javascript_Core/b3/B3Procedure.cpp (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/B3Procedure.cpp	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/B3Procedure.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -348,6 +348,11 @@
     }
 }
 
+void Procedure::setWasmBoundsCheckGenerator(RefPtr<WasmBoundsCheckGenerator> generator)
+{
+    code().setWasmBoundsCheckGenerator(generator);
+}
+
 } } // namespace JSC::B3
 
 #endif // ENABLE(B3_JIT)

Modified: trunk/Source/_javascript_Core/b3/B3Procedure.h (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/B3Procedure.h	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/B3Procedure.h	2016-10-13 01:42:53 UTC (rev 207266)
@@ -58,6 +58,9 @@
 
 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 @@
     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;
 

Modified: trunk/Source/_javascript_Core/b3/B3Validate.cpp (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/B3Validate.cpp	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/B3Validate.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -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 @@
                 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));

Modified: trunk/Source/_javascript_Core/b3/B3Value.cpp (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/B3Value.cpp	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/B3Value.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -616,6 +616,10 @@
     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 @@
     case Return:
     case Oops:
     case EntrySwitch:
+    case WasmBoundsCheck:
         return Void;
     case Select:
         ASSERT(secondChild);

Added: trunk/Source/_javascript_Core/b3/B3WasmBoundsCheckValue.cpp (0 => 207266)


--- trunk/Source/_javascript_Core/b3/B3WasmBoundsCheckValue.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3WasmBoundsCheckValue.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -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)

Added: trunk/Source/_javascript_Core/b3/B3WasmBoundsCheckValue.h (0 => 207266)


--- trunk/Source/_javascript_Core/b3/B3WasmBoundsCheckValue.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3WasmBoundsCheckValue.h	2016-10-13 01:42:53 UTC (rev 207266)
@@ -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)

Modified: trunk/Source/_javascript_Core/b3/air/AirCode.h (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/air/AirCode.h	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/air/AirCode.h	2016-10-13 01:42:53 UTC (rev 207266)
@@ -52,6 +52,9 @@
 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 @@
 
     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 @@
     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;
 };
 

Modified: trunk/Source/_javascript_Core/b3/air/AirCustom.cpp (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/air/AirCustom.cpp	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/air/AirCustom.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -178,6 +178,17 @@
     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)

Modified: trunk/Source/_javascript_Core/b3/air/AirCustom.h (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/air/AirCustom.h	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/air/AirCustom.h	2016-10-13 01:42:53 UTC (rev 207266)
@@ -27,9 +27,12 @@
 
 #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 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)

Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes	2016-10-13 01:42:53 UTC (rev 207266)
@@ -889,4 +889,7 @@
 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
 

Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (207265 => 207266)


--- trunk/Source/_javascript_Core/b3/testb3.cpp	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp	2016-10-13 01:42:53 UTC (rev 207266)
@@ -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 @@
     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 @@
     RUN(testSomeEarlyRegister());
     RUN(testPatchpointTerminalReturnValue(true));
     RUN(testPatchpointTerminalReturnValue(false));
-    
+
     RUN(testMemoryFence());
     RUN(testStoreFence());
     RUN(testLoadFence());
@@ -15168,7 +15200,12 @@
     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));

Modified: trunk/Websites/webkit.org/ChangeLog (207265 => 207266)


--- trunk/Websites/webkit.org/ChangeLog	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Websites/webkit.org/ChangeLog	2016-10-13 01:42:53 UTC (rev 207266)
@@ -1,3 +1,14 @@
+2016-10-12  Keith Miller  <keith_mil...@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.fra...@apple.com>
 
         Convert contributors.json to a flat list

Modified: trunk/Websites/webkit.org/docs/b3/intermediate-representation.html (207265 => 207266)


--- trunk/Websites/webkit.org/docs/b3/intermediate-representation.html	2016-10-13 01:14:15 UTC (rev 207265)
+++ trunk/Websites/webkit.org/docs/b3/intermediate-representation.html	2016-10-13 01:42:53 UTC (rev 207266)
@@ -537,6 +537,16 @@
         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
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to