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 >= @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