Inline DataView accesses into DFG/FTL
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Aug 2018 06:05:21 +0000 (06:05 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Aug 2018 06:05:21 +0000 (06:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=188573
<rdar://problem/43286746>

Reviewed by Michael Saboff.

JSTests:

* microbenchmarks/data-view-accesses-2.js: Added.
(assert):
(let.p.of.Object.keys.let.str):
(let.p.of.Object.keys):
(test):
* microbenchmarks/data-view-accesses.js: Added.
(assert):
(let.p.of.Object.keys.let.str):
(let.p.of.Object.keys):
* stress/dataview-jit-bounds-checks.js: Added.
(assert):
(let.p.of.Object.keys.let.str):
(let.p.of.Object.keys):
* stress/dataview-jit-get.js: Added.
(assert):
(test1.bigEndian):
(test1.littleEndian):
(test1.biEndian):
(test1):
(test2.bigEndian):
(test2.littleEndian):
(test2.biEndian):
(test2):
(test3.bigEndian):
(test3.littleEndian):
(test3.biEndian):
(test3):
(test4.bigEndian):
(test4.littleEndian):
(test4.biEndian):
(test4):
(test5.bigEndian):
(test5.littleEndian):
(test5.biEndian):
(test5):
(test6.bigEndian):
(test6.littleEndian):
(test6.biEndian):
(test6):
(test7.load):
(test7):
(test8.load):
(test8):
* stress/dataview-jit-neuter.js: Added.
(assert):
(test.load):
(test):
(test2.load):
(test2):
* stress/dataview-jit-set.js: Added.
(assert):
(isLittleEndian):
(readByte):
(readHex):
(test.storeLittleEndian):
(test.storeBigEndian):
(test.store):
(test):
(test2.storeLittleEndian):
(test2.storeBigEndian):
(test2.store):
(test2):
(test3.storeLittleEndian):
(test3.storeBigEndian):
(test3.store):
(test3):
(test4.storeLittleEndian):
(test4.storeBigEndian):
(test4.store):
(test4):
(test5.storeLittleEndian):
(test5.storeBigEndian):
(test5.store):
(test5):
(test6.storeLittleEndian):
(test6.storeBigEndian):
(test6.store):
(test6):
(test7.store):
(test7):
(test8.store):
(test8):
* stress/dataview-jit-unaligned-accesses.js: Added.
(assert):
(let.p.of.Object.keys.let.str):
(let.p.of.Object.keys):

Source/JavaScriptCore:

This patch teaches the DFG/FTL to inline DataView accesses. The approach is
straight forward. We inline the various get*/set* operations as intrinsics.

This patch takes the most obvious approach for now. We OSR exit when:
- An isLittleEndian argument is provided, and is not a boolean.
- The index isn't an integer.
- The |this| isn't a DataView.
- We do an OOB access (or see a neutered array)

To implement this change in a performant way, this patch teaches the macro
assembler how to emit byte swap operations. The semantics of the added functions
are byteSwap + zero extend. This means for the 16bit byte swaps, we need
to actually emit zero extend instructions. For the 32/64bit byte swaps,
the instructions already have these semantics.

This patch is just a lightweight initial implementation. There are some easy
extensions we can do in future changes:
- Teach B3 how to byte swap: https://bugs.webkit.org/show_bug.cgi?id=188759
- CSE DataViewGet* nodes: https://bugs.webkit.org/show_bug.cgi?id=188768

* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::byteSwap16):
(JSC::MacroAssemblerARM64::byteSwap32):
(JSC::MacroAssemblerARM64::byteSwap64):
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::byteSwap32):
(JSC::MacroAssemblerX86Common::byteSwap16):
(JSC::MacroAssemblerX86Common::byteSwap64):
* assembler/X86Assembler.h:
(JSC::X86Assembler::bswapl_r):
(JSC::X86Assembler::bswapq_r):
(JSC::X86Assembler::shiftInstruction16):
(JSC::X86Assembler::rolw_i8r):
(JSC::X86Assembler::X86InstructionFormatter::SingleInstructionBufferWriter::memoryModRM):
* assembler/testmasm.cpp:
(JSC::testByteSwap):
(JSC::run):
* bytecode/DataFormat.h:
* bytecode/SpeculatedType.cpp:
(JSC::dumpSpeculation):
(JSC::speculationFromClassInfo):
(JSC::speculationFromJSType):
(JSC::speculationFromString):
* bytecode/SpeculatedType.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::dataViewData):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::SafeToExecuteEdge::operator()):
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::speculateDataViewObject):
(JSC::DFG::SpeculativeJIT::speculate):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGUseKind.cpp:
(WTF::printInternal):
* dfg/DFGUseKind.h:
(JSC::DFG::typeFilterFor):
(JSC::DFG::isCell):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::byteSwap32):
(JSC::FTL::DFG::LowerDFGToB3::byteSwap64):
(JSC::FTL::DFG::LowerDFGToB3::emitCodeBasedOnEndiannessBranch):
(JSC::FTL::DFG::LowerDFGToB3::compileDataViewGet):
(JSC::FTL::DFG::LowerDFGToB3::compileDataViewSet):
(JSC::FTL::DFG::LowerDFGToB3::lowDataViewObject):
(JSC::FTL::DFG::LowerDFGToB3::speculate):
(JSC::FTL::DFG::LowerDFGToB3::speculateDataViewObject):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSDataViewPrototype.cpp:

Source/WTF:

* wtf/TriState.h:

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

38 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/data-view-accesses-2.js [new file with mode: 0644]
JSTests/microbenchmarks/data-view-accesses.js [new file with mode: 0644]
JSTests/stress/dataview-jit-bounds-checks.js [new file with mode: 0644]
JSTests/stress/dataview-jit-get.js [new file with mode: 0644]
JSTests/stress/dataview-jit-neuter.js [new file with mode: 0644]
JSTests/stress/dataview-jit-set.js [new file with mode: 0644]
JSTests/stress/dataview-jit-unaligned-accesses.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/assembler/MacroAssemblerARM64.h
Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
Source/JavaScriptCore/assembler/X86Assembler.h
Source/JavaScriptCore/assembler/testmasm.cpp
Source/JavaScriptCore/bytecode/DataFormat.h
Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Source/JavaScriptCore/bytecode/SpeculatedType.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGUseKind.cpp
Source/JavaScriptCore/dfg/DFGUseKind.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSDataViewPrototype.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/TriState.h

index 7104839..6455ff4 100644 (file)
@@ -1,3 +1,98 @@
+2018-08-20  Saam barati  <sbarati@apple.com>
+
+        Inline DataView accesses into DFG/FTL
+        https://bugs.webkit.org/show_bug.cgi?id=188573
+        <rdar://problem/43286746>
+
+        Reviewed by Michael Saboff.
+
+        * microbenchmarks/data-view-accesses-2.js: Added.
+        (assert):
+        (let.p.of.Object.keys.let.str):
+        (let.p.of.Object.keys):
+        (test):
+        * microbenchmarks/data-view-accesses.js: Added.
+        (assert):
+        (let.p.of.Object.keys.let.str):
+        (let.p.of.Object.keys):
+        * stress/dataview-jit-bounds-checks.js: Added.
+        (assert):
+        (let.p.of.Object.keys.let.str):
+        (let.p.of.Object.keys):
+        * stress/dataview-jit-get.js: Added.
+        (assert):
+        (test1.bigEndian):
+        (test1.littleEndian):
+        (test1.biEndian):
+        (test1):
+        (test2.bigEndian):
+        (test2.littleEndian):
+        (test2.biEndian):
+        (test2):
+        (test3.bigEndian):
+        (test3.littleEndian):
+        (test3.biEndian):
+        (test3):
+        (test4.bigEndian):
+        (test4.littleEndian):
+        (test4.biEndian):
+        (test4):
+        (test5.bigEndian):
+        (test5.littleEndian):
+        (test5.biEndian):
+        (test5):
+        (test6.bigEndian):
+        (test6.littleEndian):
+        (test6.biEndian):
+        (test6):
+        (test7.load):
+        (test7):
+        (test8.load):
+        (test8):
+        * stress/dataview-jit-neuter.js: Added.
+        (assert):
+        (test.load):
+        (test):
+        (test2.load):
+        (test2):
+        * stress/dataview-jit-set.js: Added.
+        (assert):
+        (isLittleEndian):
+        (readByte):
+        (readHex):
+        (test.storeLittleEndian):
+        (test.storeBigEndian):
+        (test.store):
+        (test):
+        (test2.storeLittleEndian):
+        (test2.storeBigEndian):
+        (test2.store):
+        (test2):
+        (test3.storeLittleEndian):
+        (test3.storeBigEndian):
+        (test3.store):
+        (test3):
+        (test4.storeLittleEndian):
+        (test4.storeBigEndian):
+        (test4.store):
+        (test4):
+        (test5.storeLittleEndian):
+        (test5.storeBigEndian):
+        (test5.store):
+        (test5):
+        (test6.storeLittleEndian):
+        (test6.storeBigEndian):
+        (test6.store):
+        (test6):
+        (test7.store):
+        (test7):
+        (test8.store):
+        (test8):
+        * stress/dataview-jit-unaligned-accesses.js: Added.
+        (assert):
+        (let.p.of.Object.keys.let.str):
+        (let.p.of.Object.keys):
+
 2018-08-20  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [YARR] Extend size of fixed characters bulk matching in 64bit platform
diff --git a/JSTests/microbenchmarks/data-view-accesses-2.js b/JSTests/microbenchmarks/data-view-accesses-2.js
new file mode 100644 (file)
index 0000000..5433e24
--- /dev/null
@@ -0,0 +1,78 @@
+"use strict";
+
+function assert(b, m = "") {
+    if (!b)
+        throw new Error("Bad: " + m);
+}
+
+let getOps = {
+    getUint8: 1,
+    getUint16: 2,
+    getUint32: 4,
+    getInt8: 1,
+    getInt16: 2,
+    getInt32: 4,
+    getFloat32: 4,
+    getFloat64: 8,
+};
+
+let setOps = {
+    setUint8: 1,
+    setUint16: 2,
+    setUint32: 4,
+    setInt8: 1,
+    setInt16: 2,
+    setInt32: 4,
+    setFloat32: 4,
+    setFloat64: 8,
+};
+
+let getFuncs = [];
+for (let p of Object.keys(getOps)) {
+    let byteSize = getOps[p];
+    let str = `
+        (function (dv, byteLength) {
+            let sum = 0;
+            for (let i = 0; i < byteLength; i += ${byteSize}) {
+                sum += dv.${p}(i, false);
+            }
+            return sum;
+        });
+        `;
+       
+    let func = eval(str);
+    noInline(func);
+    getFuncs.push(func);
+}
+
+let setFuncs = [];
+for (let p of Object.keys(setOps)) {
+    let byteSize = setOps[p];
+    let value = 10;
+    if (p.indexOf("float") !== -1)
+        value = 10.5;
+    let str = `
+        (function (dv, byteLength) {
+            for (let i = 0; i < byteLength; i += ${byteSize}) {
+                dv.${p}(i, ${value}, false);
+            }
+        });
+        `;
+       
+    let func = eval(str);
+    noInline(func);
+    setFuncs.push(func);
+}
+
+function test() {
+    const size = 16*1024;
+    let ab = new ArrayBuffer(size);
+    let dv = new DataView(ab);
+    for (let i = 0; i < 1000; ++i) {
+        for (let f of getFuncs)
+            f(dv, size);
+        for (let f of setFuncs)
+            f(dv, size);
+    }
+}
+test();
diff --git a/JSTests/microbenchmarks/data-view-accesses.js b/JSTests/microbenchmarks/data-view-accesses.js
new file mode 100644 (file)
index 0000000..dcd8e70
--- /dev/null
@@ -0,0 +1,75 @@
+"use strict";
+
+function assert(b, m = "") {
+    if (!b)
+        throw new Error("Bad: " + m);
+}
+
+let getOps = {
+    getUint8: 1,
+    getUint16: 2,
+    getUint32: 4,
+    getInt8: 1,
+    getInt16: 2,
+    getInt32: 4,
+    getFloat32: 4,
+    getFloat64: 8,
+};
+
+let setOps = {
+    setUint8: 1,
+    setUint16: 2,
+    setUint32: 4,
+    setInt8: 1,
+    setInt16: 2,
+    setInt32: 4,
+    setFloat32: 4,
+    setFloat64: 8,
+};
+
+let getFuncs = [];
+for (let p of Object.keys(getOps)) {
+    let endOfCall = getOps[p] === 1 ? ");" : ", true);";
+    let str = `
+        (function ${p}(dv, index) {
+            return dv.${p}(index${endOfCall}
+        })
+    `;
+       
+    let func = eval(str);
+    noInline(func);
+    getFuncs.push(func);
+}
+
+let setFuncs = [];
+for (let p of Object.keys(setOps)) {
+    let endOfCall = setOps[p] === 1 ? ");" : ", true);";
+    let str = `
+        (function ${p}(dv, index, value) {
+            dv.${p}(index, value${endOfCall}
+        })
+    `;
+
+    let func = eval(str);
+    noInline(func);
+    setFuncs.push(func);
+}
+
+function test() {
+    const size = 16*1024;
+    let ab = new ArrayBuffer(size);
+    let dv = new DataView(ab);
+    for (let i = 0; i < 1000000; ++i) {
+        let index = (Math.random() * size) >>> 0;
+        index = Math.max(index - 8, 0);
+        for (let f of getFuncs) {
+            f(dv, index);
+        }
+
+        for (let f of setFuncs) {
+            f(dv, index, 10);
+        }
+    }
+
+}
+test();
diff --git a/JSTests/stress/dataview-jit-bounds-checks.js b/JSTests/stress/dataview-jit-bounds-checks.js
new file mode 100644 (file)
index 0000000..bd9a6da
--- /dev/null
@@ -0,0 +1,114 @@
+"use strict";
+
+function assert(b, m = "") {
+    if (!b)
+        throw new Error("Bad: " + m);
+}
+
+let getOps = {
+    getUint8: 1,
+    getUint16: 2,
+    getUint32: 4,
+    getInt8: 1,
+    getInt16: 2,
+    getInt32: 4,
+    getFloat32: 4,
+    getFloat64: 8,
+};
+
+let setOps = {
+    setUint8: 1,
+    setUint16: 2,
+    setUint32: 4,
+    setInt8: 1,
+    setInt16: 2,
+    setInt32: 4,
+    setFloat32: 4,
+    setFloat64: 8,
+};
+
+let getFuncs = [];
+for (let p of Object.keys(getOps)) {
+    let endOfCall = getOps[p] === 1 ? ");" : ", true);";
+    let str = `
+        (function ${p}(dv, index) {
+            return dv.${p}(index${endOfCall}
+        })
+    `;
+       
+    let func = eval(str);
+    noInline(func);
+    getFuncs.push(func);
+}
+
+let setFuncs = [];
+for (let p of Object.keys(setOps)) {
+    let endOfCall = setOps[p] === 1 ? ");" : ", true);";
+    let str = `
+        (function ${p}(dv, index, value) {
+            return dv.${p}(index, value${endOfCall}
+        })
+    `;
+
+    let func = eval(str);
+    noInline(func);
+    setFuncs.push(func);
+}
+
+function assertThrowsRangeError(f) {
+    let e = null;
+    try {
+        f();
+    } catch(err) {
+        e = err;
+    }
+    assert(e instanceof RangeError, e);
+}
+
+function test(warmup) {
+    const size = 16*1024;
+    let ab = new ArrayBuffer(size);
+    let dv = new DataView(ab);
+    for (let i = 0; i < warmup; ++i) {
+        for (let f of getFuncs) {
+            f(dv, 0);
+        }
+
+        for (let f of setFuncs) {
+            f(dv, 0, 10);
+        }
+    }
+
+    for (let f of getFuncs) {
+        assertThrowsRangeError(() => {
+            let index = size - getOps[f.name] + 1;
+            f(dv, index);
+        });
+        assertThrowsRangeError(() => {
+            let index = -1;
+            f(dv, index);
+        });
+        assertThrowsRangeError(() => {
+            let index = -2147483648;
+            f(dv, index);
+        });
+    } 
+
+    for (let f of setFuncs) {
+        assertThrowsRangeError(() => {
+            let index = size - setOps[f.name] + 1;
+            f(dv, index, 10);
+        });
+        assertThrowsRangeError(() => {
+            let index = -1;
+            f(dv, index, 10);
+        });
+        assertThrowsRangeError(() => {
+            let index = -2147483648;
+            f(dv, index, 10);
+        });
+    }
+}
+
+test(2000);
+test(10000);
diff --git a/JSTests/stress/dataview-jit-get.js b/JSTests/stress/dataview-jit-get.js
new file mode 100644 (file)
index 0000000..e0665af
--- /dev/null
@@ -0,0 +1,305 @@
+"use strict";
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad!");
+}
+
+function test1() {
+    function bigEndian(o, i) {
+        return o.getInt32(i, false);
+    }
+    noInline(bigEndian);
+    function littleEndian(o, i) {
+        return o.getInt32(i, true);
+    }
+    noInline(littleEndian);
+    function biEndian(o, i, b) {
+        return o.getInt32(i, b);
+    }
+    noInline(biEndian);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Int32Array(ab);
+    ta[0] = 0x01020304;
+    let dv = new DataView(ab);
+
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === 0x04030201);
+        assert(littleEndian(dv, 0) === 0x01020304);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === 0x01020304);
+        else
+            assert(biEndian(dv, 0, false) === 0x04030201);
+    }
+
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === 0x04030201);
+        assert(littleEndian(dv, 0) === 0x01020304);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === 0x01020304);
+        else
+            assert(biEndian(dv, 0, false) === 0x04030201);
+    }
+
+    // Make sure we get the right sign.
+    ta[0] = -32361386; // 0xfe123456
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === 0x563412fe);
+        assert(littleEndian(dv, 0) === -32361386);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === -32361386);
+        else
+            assert(biEndian(dv, 0, false) === 0x563412fe);
+    }
+
+    // -2146290602 == (int)0x80123456
+    ta[0] = 0x56341280;
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === -2146290602);
+        assert(littleEndian(dv, 0) === 0x56341280);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === 0x56341280);
+        else
+            assert(biEndian(dv, 0, false) === -2146290602);
+    }
+}
+test1();
+
+function test2() {
+    function bigEndian(o, i) {
+        return o.getInt16(i, false);
+    }
+    noInline(bigEndian);
+    function littleEndian(o, i) {
+        return o.getInt16(i, true);
+    }
+    noInline(littleEndian);
+    function biEndian(o, i, b) {
+        return o.getInt16(i, b);
+    }
+    noInline(biEndian);
+
+    let ab = new ArrayBuffer(2);
+    let ta = new Int16Array(ab);
+    ta[0] = 0x0102;
+    let dv = new DataView(ab);
+
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === 0x0201);
+        assert(littleEndian(dv, 0) === 0x0102);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === 0x0102);
+        else
+            assert(biEndian(dv, 0, false) === 0x0201);
+    }
+
+    // Check sign.
+    ta[0] = -512; // 0xfe00
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === 0x00fe);
+        assert(littleEndian(dv, 0) === -512);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === -512);
+        else
+            assert(biEndian(dv, 0, false) === 0x00fe);
+    }
+
+    // Check sign extension.
+    ta[0] = 0x00fe;
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === -512);
+        assert(littleEndian(dv, 0) === 0x00fe);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === 0x00fe);
+        else
+            assert(biEndian(dv, 0, false) === -512);
+    }
+}
+test2();
+
+function test3() {
+    function bigEndian(o, i) {
+        return o.getFloat32(i, false);
+    }
+    noInline(bigEndian);
+    function littleEndian(o, i) {
+        return o.getFloat32(i, true);
+    }
+    noInline(littleEndian);
+    function biEndian(o, i, b) {
+        return o.getFloat32(i, b);
+    }
+    noInline(biEndian);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Float32Array(ab);
+    const normal = 12912.403; // 0x4649c19d
+    const normalAsDouble = 12912.403320312500;
+
+    const flipped = -5.1162437589918884e-21;
+    ta[0] = normal;
+
+    let dv = new DataView(ab);
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === flipped);
+        assert(littleEndian(dv, 0) === 12912.403320312500);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === normalAsDouble);
+        else
+            assert(biEndian(dv, 0, false) === flipped);
+    }
+}
+test3();
+
+function test4() {
+    function bigEndian(o, i) {
+        return o.getUint32(i, false);
+    }
+    noInline(bigEndian);
+    function littleEndian(o, i) {
+        return o.getUint32(i, true);
+    }
+    noInline(littleEndian);
+    function biEndian(o, i, b) {
+        return o.getUint32(i, b);
+    }
+    noInline(biEndian);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Uint32Array(ab);
+    ta[0] = 0xa0b0d0f0;
+
+    let dv = new DataView(ab);
+    for (let i = 0; i < 10000; ++i) {
+        assert(bigEndian(dv, 0) === 0xf0d0b0a0);
+        assert(littleEndian(dv, 0) === 0xa0b0d0f0);
+        if (i % 2)
+            assert(biEndian(dv, 0, true) === 0xa0b0d0f0);
+        else
+            assert(biEndian(dv, 0, false) === 0xf0d0b0a0);
+    }
+}
+test4();
+
+function test5() {
+    function bigEndian(o, i) {
+        return o.getUint16(i, false);
+    }
+    noInline(bigEndian);
+    function littleEndian(o, i) {
+        return o.getUint16(i, true);
+    }
+    noInline(littleEndian);
+    function biEndian(o, i, b) {
+        return o.getUint16(i, b);
+    }
+    noInline(biEndian);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Uint32Array(ab);
+    ta[0] = 0xa0b0d0f0;
+
+    let dv = new DataView(ab);
+    for (let i = 0; i < 10000; ++i) {
+        assert(littleEndian(dv, 0) == 0xd0f0);
+        assert(bigEndian(dv, 0) == 0xf0d0);
+
+        assert(littleEndian(dv, 1) == 0xb0d0);
+        assert(bigEndian(dv, 1) == 0xd0b0);
+
+        assert(littleEndian(dv, 2) == 0xa0b0);
+        assert(bigEndian(dv, 2) == 0xb0a0);
+
+        assert(biEndian(dv, 0, true) == 0xd0f0);
+        assert(biEndian(dv, 0, false) == 0xf0d0);
+
+        assert(biEndian(dv, 1, true) == 0xb0d0);
+        assert(biEndian(dv, 1, false) == 0xd0b0);
+
+        assert(biEndian(dv, 2, true) == 0xa0b0);
+        assert(biEndian(dv, 2, false) == 0xb0a0);
+    }
+}
+test5();
+
+function test6() {
+    function bigEndian(o, i) {
+        return o.getInt16(i, false);
+    }
+    noInline(bigEndian);
+    function littleEndian(o, i) {
+        return o.getInt16(i, true);
+    }
+    noInline(littleEndian);
+    function biEndian(o, i, b) {
+        return o.getInt16(i, b);
+    }
+    noInline(biEndian);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Uint32Array(ab);
+    ta[0] = 0xa070fa01;
+
+    let dv = new DataView(ab);
+    for (let i = 0; i < 10000; ++i) {
+        assert(littleEndian(dv, 0) == -1535);
+        assert(bigEndian(dv, 0) == 0x01fa);
+
+        assert(littleEndian(dv, 1) == 0x70fa);
+        assert(bigEndian(dv, 1) == -1424);
+
+        assert(littleEndian(dv, 2) == -24464);
+        assert(bigEndian(dv, 2) == 0x70a0);
+
+        assert(biEndian(dv, 0, true) == -1535);
+        assert(biEndian(dv, 0, false) == 0x01fa);
+
+        assert(biEndian(dv, 1, true) == 0x70fa);
+        assert(biEndian(dv, 1, false) == -1424);
+
+        assert(biEndian(dv, 2, true) == -24464);
+        assert(biEndian(dv, 2, false) == 0x70a0);
+    }
+}
+test6();
+
+function test7() {
+    function load(o, i) {
+        return o.getInt8(i);
+    }
+    noInline(load);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Uint32Array(ab);
+    ta[0] = 0xa070fa01;
+
+    let dv = new DataView(ab);
+    for (let i = 0; i < 10000; ++i) {
+        assert(load(dv, 0) === 0x01);
+        assert(load(dv, 1) === -6);
+        assert(load(dv, 2) === 0x70);
+        assert(load(dv, 3) === -96);
+    }
+}
+test7();
+
+function test8() {
+    function load(o, i) {
+        return o.getUint8(i);
+    }
+    noInline(load);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Uint32Array(ab);
+    ta[0] = 0xa070fa01;
+
+    let dv = new DataView(ab);
+    for (let i = 0; i < 10000; ++i) {
+        assert(load(dv, 0) === 0x01);
+        assert(load(dv, 1) === 0xfa);
+        assert(load(dv, 2) === 0x70);
+        assert(load(dv, 3) === 0xa0)
+    }
+}
+test8();
diff --git a/JSTests/stress/dataview-jit-neuter.js b/JSTests/stress/dataview-jit-neuter.js
new file mode 100644 (file)
index 0000000..635fc4c
--- /dev/null
@@ -0,0 +1,59 @@
+"use strict";
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad!");
+}
+
+function test() {
+    function load(o, i) {
+        return o.getUint8(i);
+    }
+    noInline(load);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Uint32Array(ab);
+    ta[0] = 0xa070fa01;
+    let dv = new DataView(ab);
+
+    for (let i = 0; i < 1000; ++i) {
+        assert(load(dv, 0) === 0x01);
+    }
+
+    transferArrayBuffer(ab);
+    let e = null;
+    try {
+        load(dv, 0);
+    } catch(err) {
+        e = err;
+    }
+    assert(e instanceof RangeError);
+}
+test();
+
+
+function test2() {
+    function load(o, i) {
+        return o.getUint8(i);
+    }
+    noInline(load);
+
+    let ab = new ArrayBuffer(4);
+    let ta = new Uint32Array(ab);
+    ta[0] = 0xa070fa01;
+    let dv = new DataView(ab);
+
+    for (let i = 0; i < 10000; ++i) {
+        assert(load(dv, 0) === 0x01);
+    }
+
+    transferArrayBuffer(ab);
+    let e = null;
+    try {
+        load(dv, 0);
+    } catch(err) {
+        e = err;
+    }
+    assert(e instanceof RangeError);
+}
+test2();
diff --git a/JSTests/stress/dataview-jit-set.js b/JSTests/stress/dataview-jit-set.js
new file mode 100644 (file)
index 0000000..b32ec89
--- /dev/null
@@ -0,0 +1,440 @@
+"use strict";
+
+function assert(b) {
+    if (!b)
+        throw new Error;
+}
+
+function readHex(dv, bytes) {
+    function isLittleEndian() { 
+        let b = new ArrayBuffer(4);
+        let dv = new DataView(b);
+        dv.setInt32(0, 0x00112233, true);
+        return dv.getUint8(0) === 0x33;
+    }
+    let str = "";
+    function readByte(i) {
+        let b = dv.getUint8(i).toString(16);
+        if (b.length === 1)
+            b = "0" + b;
+        else
+            assert(b.length === 2)
+        return b;
+    }
+    if (isLittleEndian()) {
+        for (let i = bytes; i--;)
+            str = str + readByte(i);
+    } else {
+        for (let i = 0; i < bytes; ++i)
+            str = str + readByte(i);
+    }
+
+    return "0x" + str;
+}
+
+{
+    let b = new ArrayBuffer(4);
+    let dv = new DataView(b);
+    dv.setInt32(0, 0x00112233, true);
+    assert(readHex(dv, 4) === "0x00112233");
+}
+
+function test() {
+    function storeLittleEndian(dv, index, value) {
+        dv.setInt16(index, value, true);
+    }
+    noInline(storeLittleEndian);
+
+    function storeBigEndian(dv, index, value) {
+        dv.setInt16(index, value, false);
+    }
+    noInline(storeBigEndian);
+
+    function store(dv, index, value, littleEndian) {
+        dv.setInt16(index, value, littleEndian);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(2);
+    let arr = new Uint16Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        storeLittleEndian(dv, 0, 0xfaba);
+        assert(arr[0] === 0xfaba);
+
+        store(dv, 0, 0xabcd, true);
+        assert(arr[0] === 0xabcd);
+
+        store(dv, 0, 0xbadbeef, true);
+        assert(arr[0] === 0xbeef);
+
+        storeLittleEndian(dv, 0, 0xbb4db33f, true);
+        assert(arr[0] === 0xb33f);
+
+        storeBigEndian(dv, 0, 0xfada);
+        assert(arr[0] === 0xdafa);
+
+        storeBigEndian(dv, 0, 0x12ab);
+        assert(arr[0] === 0xab12);
+
+        store(dv, 0, 0x1234, false);
+        assert(arr[0] === 0x3412);
+
+        store(dv, 0, 0x0102, false);
+        assert(arr[0] === 0x0201);
+
+        store(dv, 0, -1, false);
+        assert(arr[0] === 0xffff);
+
+        store(dv, 0, -2, false);
+        assert(arr[0] === 0xfeff);
+
+        storeBigEndian(dv, 0, -1);
+        assert(arr[0] === 0xffff);
+
+        storeBigEndian(dv, 0, -2);
+        assert(arr[0] === 0xfeff);
+
+        storeBigEndian(dv, 0, -2147483648); 
+        assert(arr[0] === 0x0000);
+
+        storeLittleEndian(dv, 0, -2147483648); 
+        assert(arr[0] === 0x0000);
+
+        storeLittleEndian(dv, 0, -2147478988); 
+        assert(arr[0] === 0x1234);
+
+        storeBigEndian(dv, 0, -2147478988); 
+        assert(arr[0] === 0x3412);
+    }
+}
+test();
+
+function test2() {
+    function storeLittleEndian(dv, index, value) {
+        dv.setUint16(index, value, true);
+    }
+    noInline(storeLittleEndian);
+
+    function storeBigEndian(dv, index, value) {
+        dv.setUint16(index, value, false);
+    }
+    noInline(storeBigEndian);
+
+    function store(dv, index, value, littleEndian) {
+        dv.setUint16(index, value, littleEndian);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(2);
+    let arr = new Uint16Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        storeLittleEndian(dv, 0, 0xfaba);
+        assert(arr[0] === 0xfaba);
+
+        store(dv, 0, 0xabcd, true);
+        assert(arr[0] === 0xabcd);
+
+        store(dv, 0, 0xbadbeef, true);
+        assert(arr[0] === 0xbeef);
+
+        storeLittleEndian(dv, 0, 0xbb4db33f, true);
+        assert(arr[0] === 0xb33f);
+
+        storeBigEndian(dv, 0, 0xfada);
+        assert(arr[0] === 0xdafa);
+
+        storeBigEndian(dv, 0, 0x12ab);
+        assert(arr[0] === 0xab12);
+
+        store(dv, 0, 0x1234, false);
+        assert(arr[0] === 0x3412);
+
+        store(dv, 0, 0x0102, false);
+        assert(arr[0] === 0x0201);
+
+        store(dv, 0, -1, false);
+        assert(arr[0] === 0xffff);
+
+        store(dv, 0, -2, false);
+        assert(arr[0] === 0xfeff);
+
+        storeBigEndian(dv, 0, -1);
+        assert(arr[0] === 0xffff);
+
+        storeBigEndian(dv, 0, -2);
+        assert(arr[0] === 0xfeff);
+
+        storeBigEndian(dv, 0, -2147483648); 
+        assert(arr[0] === 0x0000);
+
+        storeLittleEndian(dv, 0, -2147483648); 
+        assert(arr[0] === 0x0000);
+
+        storeLittleEndian(dv, 0, -2147478988); 
+        assert(arr[0] === 0x1234);
+
+        storeBigEndian(dv, 0, -2147478988); 
+        assert(arr[0] === 0x3412);
+    }
+}
+test2();
+
+function test3() {
+    function storeLittleEndian(dv, index, value) {
+        dv.setUint32(index, value, true);
+    }
+    noInline(storeLittleEndian);
+
+    function storeBigEndian(dv, index, value) {
+        dv.setUint32(index, value, false);
+    }
+    noInline(storeBigEndian);
+
+    function store(dv, index, value, littleEndian) {
+        dv.setUint32(index, value, littleEndian);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(4);
+    let arr = new Uint32Array(buffer);
+    let arr2 = new Int32Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        storeLittleEndian(dv, 0, 0xffffffff);
+        assert(arr[0] === 0xffffffff);
+        assert(arr2[0] === -1);
+
+        storeLittleEndian(dv, 0, 0xffaabbcc);
+        assert(arr[0] === 0xffaabbcc);
+
+        storeBigEndian(dv, 0, 0x12345678);
+        assert(arr[0] === 0x78563412);
+
+        storeBigEndian(dv, 0, 0xffaabbcc);
+        assert(arr[0] === 0xccbbaaff);
+
+        store(dv, 0, 0xfaeadaca, false);
+        assert(arr[0] === 0xcadaeafa);
+
+        store(dv, 0, 0xcadaeafa, false);
+        assert(arr2[0] === -85271862);
+
+        store(dv, 0, 0x12345678, false);
+        assert(arr[0] === 0x78563412);
+
+        storeBigEndian(dv, 0, 0xbeeffeeb);
+        assert(arr2[0] === -335614018);
+    }
+}
+test3();
+
+function test4() {
+    function storeLittleEndian(dv, index, value) {
+        dv.setInt32(index, value, true);
+    }
+    noInline(storeLittleEndian);
+
+    function storeBigEndian(dv, index, value) {
+        dv.setInt32(index, value, false);
+    }
+    noInline(storeBigEndian);
+
+    function store(dv, index, value, littleEndian) {
+        dv.setInt32(index, value, littleEndian);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(4);
+    let arr = new Uint32Array(buffer);
+    let arr2 = new Int32Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        storeLittleEndian(dv, 0, 0xffffffff);
+        assert(arr[0] === 0xffffffff);
+        assert(arr2[0] === -1);
+
+        storeLittleEndian(dv, 0, 0xffaabbcc);
+        assert(arr[0] === 0xffaabbcc);
+
+        storeBigEndian(dv, 0, 0x12345678);
+        assert(arr[0] === 0x78563412);
+
+        storeBigEndian(dv, 0, 0xffaabbcc);
+        assert(arr[0] === 0xccbbaaff);
+
+        store(dv, 0, 0xfaeadaca, false);
+        assert(arr[0] === 0xcadaeafa);
+
+        store(dv, 0, 0xcadaeafa, false);
+        assert(arr2[0] === -85271862);
+
+        store(dv, 0, 0x12345678, false);
+        assert(arr[0] === 0x78563412);
+
+        storeBigEndian(dv, 0, 0xbeeffeeb);
+        assert(arr2[0] === -335614018);
+    }
+}
+test4();
+
+function test5() {
+    function storeLittleEndian(dv, index, value) {
+        dv.setFloat32(index, value, true);
+    }
+    noInline(storeLittleEndian);
+
+    function storeBigEndian(dv, index, value) {
+        dv.setFloat32(index, value, false);
+    }
+    noInline(storeBigEndian);
+
+    function store(dv, index, value, littleEndian) {
+        dv.setFloat32(index, value, littleEndian);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(4);
+    let arr = new Float32Array(buffer);
+    let bits = new Uint32Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        storeLittleEndian(dv, 0, 1.5);
+        assert(arr[0] === 1.5);
+
+        storeLittleEndian(dv, 0, 12912.124123215122);
+        assert(arr[0] === 12912.1240234375);
+        assert(bits[0] === 0x4649c07f);
+
+        storeLittleEndian(dv, 0, NaN);
+        assert(isNaN(arr[0]));
+        assert(bits[0] === 0x7FC00000);
+
+        storeLittleEndian(dv, 0, 2.3879393e-38);
+        assert(arr[0] === 2.387939260590663e-38);
+        assert(bits[0] === 0x01020304);
+
+        storeBigEndian(dv, 0, 2.3879393e-38);
+        assert(arr[0] === 1.539989614439558e-36);
+        assert(bits[0] === 0x04030201);
+    }
+}
+test5();
+
+function test6() {
+    function storeLittleEndian(dv, index, value) {
+        dv.setFloat64(index, value, true);
+    }
+    noInline(storeLittleEndian);
+
+    function storeBigEndian(dv, index, value) {
+        dv.setFloat64(index, value, false);
+    }
+    noInline(storeBigEndian);
+
+    function store(dv, index, value, littleEndian) {
+        dv.setFloat64(index, value, littleEndian);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(8);
+    let arr = new Float64Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        storeLittleEndian(dv, 0, NaN);
+        assert(isNaN(arr[0]));
+
+        storeLittleEndian(dv, 0, -2.5075187084135162e+284);
+        assert(arr[0] === -2.5075187084135162e+284);
+        assert(readHex(dv, 8) === "0xfafafafafafafafa");
+
+        store(dv, 0, 124.553, true);
+        assert(readHex(dv, 8) === "0x405f23645a1cac08");
+
+        store(dv, 0, Infinity, true);
+        assert(readHex(dv, 8) === "0x7ff0000000000000");
+
+        store(dv, 0, Infinity, false);
+        assert(readHex(dv, 8) === "0x000000000000f07f");
+
+        store(dv, 0, -Infinity, true);
+        assert(readHex(dv, 8) === "0xfff0000000000000");
+
+        storeBigEndian(dv, 0, -2.5075187084135162e+284);
+        assert(arr[0] === -2.5075187084135162e+284);
+        assert(readHex(dv, 8) === "0xfafafafafafafafa");
+
+        storeBigEndian(dv, 0, 124.553);
+        assert(readHex(dv, 8) === "0x08ac1c5a64235f40");
+    }
+}
+test6();
+
+function test7() {
+    function store(dv, index, value) {
+        dv.setInt8(index, value);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(1);
+    let arr = new Uint8Array(buffer);
+    let arr2 = new Int8Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        store(dv, 0, 0xff);
+        assert(arr[0] === 0xff);
+        assert(arr2[0] === -1);
+
+        store(dv, 0, 0xff00);
+        assert(arr[0] === 0);
+        assert(arr2[0] === 0);
+
+        store(dv, 0, -1);
+        assert(arr[0] === 0xff);
+        assert(arr2[0] === -1);
+
+        store(dv, 0, 0x0badbeef);
+        assert(arr[0] === 0xef);
+        assert(arr2[0] === -17);
+    }
+}
+test7();
+
+function test8() {
+    function store(dv, index, value) {
+        dv.setInt8(index, value);
+    }
+    noInline(store);
+
+    let buffer = new ArrayBuffer(1);
+    let arr = new Uint8Array(buffer);
+    let arr2 = new Int8Array(buffer);
+    let dv = new DataView(buffer);
+
+    for (let i = 0; i < 10000; ++i) {
+        store(dv, 0, 0xff);
+        assert(arr[0] === 0xff);
+        assert(arr2[0] === -1);
+
+        store(dv, 0, 0xff00);
+        assert(arr[0] === 0);
+        assert(arr2[0] === 0);
+
+        store(dv, 0, -1);
+        assert(arr[0] === 0xff);
+        assert(arr2[0] === -1);
+
+        store(dv, 0, 0x0badbeef);
+        assert(arr[0] === 0xef);
+        assert(arr2[0] === -17);
+    }
+}
+test8();
diff --git a/JSTests/stress/dataview-jit-unaligned-accesses.js b/JSTests/stress/dataview-jit-unaligned-accesses.js
new file mode 100644 (file)
index 0000000..70918a3
--- /dev/null
@@ -0,0 +1,75 @@
+"use strict";
+
+function assert(b, m = "") {
+    if (!b)
+        throw new Error("Bad: " + m);
+}
+
+let getOps = {
+    getUint8: 1,
+    getUint16: 2,
+    getUint32: 4,
+    getInt8: 1,
+    getInt16: 2,
+    getInt32: 4,
+    getFloat32: 4,
+    getFloat64: 8,
+};
+
+let setOps = {
+    setUint8: 1,
+    setUint16: 2,
+    setUint32: 4,
+    setInt8: 1,
+    setInt16: 2,
+    setInt32: 4,
+    setFloat32: 4,
+    setFloat64: 8,
+};
+
+let getFuncs = [];
+for (let p of Object.keys(getOps)) {
+    let endOfCall = getOps[p] === 1 ? ");" : ", true);";
+    let str = `
+        (function ${p}(dv, index) {
+            return dv.${p}(index${endOfCall}
+        })
+    `;
+       
+    let func = eval(str);
+    noInline(func);
+    getFuncs.push(func);
+}
+
+let setFuncs = [];
+for (let p of Object.keys(setOps)) {
+    let endOfCall = setOps[p] === 1 ? ");" : ", true);";
+    let str = `
+        (function ${p}(dv, index, value) {
+            dv.${p}(index, value${endOfCall}
+        })
+    `;
+
+    let func = eval(str);
+    noInline(func);
+    setFuncs.push(func);
+}
+
+function test() {
+    const size = 16*1024;
+    let ab = new ArrayBuffer(size);
+    let dv = new DataView(ab);
+    for (let i = 0; i < 100000; ++i) {
+        let index = (Math.random() * size) >>> 0;
+        index = Math.max(index - 8, 0);
+        for (let f of getFuncs) {
+            f(dv, index);
+        }
+
+        for (let f of setFuncs) {
+            f(dv, index, 10);
+        }
+    }
+
+}
+test();
index 6ed2c6b..d0aacf3 100644 (file)
@@ -1,3 +1,103 @@
+2018-08-20  Saam barati  <sbarati@apple.com>
+
+        Inline DataView accesses into DFG/FTL
+        https://bugs.webkit.org/show_bug.cgi?id=188573
+        <rdar://problem/43286746>
+
+        Reviewed by Michael Saboff.
+
+        This patch teaches the DFG/FTL to inline DataView accesses. The approach is
+        straight forward. We inline the various get*/set* operations as intrinsics.
+        
+        This patch takes the most obvious approach for now. We OSR exit when:
+        - An isLittleEndian argument is provided, and is not a boolean.
+        - The index isn't an integer.
+        - The |this| isn't a DataView.
+        - We do an OOB access (or see a neutered array)
+        
+        To implement this change in a performant way, this patch teaches the macro
+        assembler how to emit byte swap operations. The semantics of the added functions
+        are byteSwap + zero extend. This means for the 16bit byte swaps, we need
+        to actually emit zero extend instructions. For the 32/64bit byte swaps,
+        the instructions already have these semantics.
+        
+        This patch is just a lightweight initial implementation. There are some easy
+        extensions we can do in future changes:
+        - Teach B3 how to byte swap: https://bugs.webkit.org/show_bug.cgi?id=188759
+        - CSE DataViewGet* nodes: https://bugs.webkit.org/show_bug.cgi?id=188768
+
+        * assembler/MacroAssemblerARM64.h:
+        (JSC::MacroAssemblerARM64::byteSwap16):
+        (JSC::MacroAssemblerARM64::byteSwap32):
+        (JSC::MacroAssemblerARM64::byteSwap64):
+        * assembler/MacroAssemblerX86Common.h:
+        (JSC::MacroAssemblerX86Common::byteSwap32):
+        (JSC::MacroAssemblerX86Common::byteSwap16):
+        (JSC::MacroAssemblerX86Common::byteSwap64):
+        * assembler/X86Assembler.h:
+        (JSC::X86Assembler::bswapl_r):
+        (JSC::X86Assembler::bswapq_r):
+        (JSC::X86Assembler::shiftInstruction16):
+        (JSC::X86Assembler::rolw_i8r):
+        (JSC::X86Assembler::X86InstructionFormatter::SingleInstructionBufferWriter::memoryModRM):
+        * assembler/testmasm.cpp:
+        (JSC::testByteSwap):
+        (JSC::run):
+        * bytecode/DataFormat.h:
+        * bytecode/SpeculatedType.cpp:
+        (JSC::dumpSpeculation):
+        (JSC::speculationFromClassInfo):
+        (JSC::speculationFromJSType):
+        (JSC::speculationFromString):
+        * bytecode/SpeculatedType.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        (JSC::DFG::Node::dataViewData):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::SafeToExecuteEdge::operator()):
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::speculateDataViewObject):
+        (JSC::DFG::SpeculativeJIT::speculate):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGUseKind.cpp:
+        (WTF::printInternal):
+        * dfg/DFGUseKind.h:
+        (JSC::DFG::typeFilterFor):
+        (JSC::DFG::isCell):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::byteSwap32):
+        (JSC::FTL::DFG::LowerDFGToB3::byteSwap64):
+        (JSC::FTL::DFG::LowerDFGToB3::emitCodeBasedOnEndiannessBranch):
+        (JSC::FTL::DFG::LowerDFGToB3::compileDataViewGet):
+        (JSC::FTL::DFG::LowerDFGToB3::compileDataViewSet):
+        (JSC::FTL::DFG::LowerDFGToB3::lowDataViewObject):
+        (JSC::FTL::DFG::LowerDFGToB3::speculate):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateDataViewObject):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSDataViewPrototype.cpp:
+
 2018-08-20  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [YARR] Extend size of fixed characters bulk matching in 64bit platform
index 9c72c7f..42d0884 100644 (file)
@@ -451,6 +451,22 @@ public:
         m_assembler.clz<64>(dest, dest);
     }
 
+    void byteSwap16(RegisterID dst)
+    {
+        m_assembler.rev16<32>(dst, dst);
+        zeroExtend16To32(dst, dst);
+    }
+
+    void byteSwap32(RegisterID dst)
+    {
+        m_assembler.rev<32>(dst, dst);
+    }
+
+    void byteSwap64(RegisterID dst)
+    {
+        m_assembler.rev<64>(dst, dst);
+    }
+
     // Only used for testing purposes.
     void illegalInstruction()
     {
index 9e27723..ec52181 100644 (file)
@@ -408,6 +408,24 @@ public:
         m_assembler.popcnt_rr(src, dst);
     }
 
+    void byteSwap32(RegisterID dst)
+    {
+        m_assembler.bswapl_r(dst);
+    }
+
+    void byteSwap16(RegisterID dst)
+    {
+        m_assembler.rolw_i8r(8, dst);
+        zeroExtend16To32(dst, dst);
+    }
+
+#if CPU(X86_64)
+    void byteSwap64(RegisterID dst)
+    {
+        m_assembler.bswapq_r(dst);
+    }
+#endif
+
     // Only used for testing purposes.
     void illegalInstruction()
     {
index c34cde9..6edba1a 100644 (file)
@@ -339,6 +339,7 @@ private:
         OP2_XADDb           = 0xC0,
         OP2_XADD            = 0xC1,
         OP2_PEXTRW_GdUdIb   = 0xC5,
+        OP2_BSWAP           = 0xC8,
         OP2_PSLLQ_UdqIb     = 0x73,
         OP2_PSRLQ_UdqIb     = 0x73,
         OP2_POR_VdqWdq      = 0XEB,
@@ -1640,6 +1641,18 @@ public:
     }
 #endif
 
+    void bswapl_r(RegisterID dst)
+    {
+        m_formatter.twoByteOp(OP2_BSWAP, dst);
+    }
+
+#if CPU(X86_64)
+    void bswapq_r(RegisterID dst)
+    {
+        m_formatter.twoByteOp64(OP2_BSWAP, dst);
+    }
+#endif
+
     void tzcnt_rr(RegisterID src, RegisterID dst)
     {
         m_formatter.prefix(PRE_SSE_F3);
@@ -1703,6 +1716,18 @@ private:
             m_formatter.immediate8(imm);
         }
     }
+
+    template<GroupOpcodeID op>
+    void shiftInstruction16(int imm, RegisterID dst)
+    {
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        if (imm == 1)
+            m_formatter.oneByteOp(OP_GROUP2_Ev1, op, dst);
+        else {
+            m_formatter.oneByteOp(OP_GROUP2_EvIb, op, dst);
+            m_formatter.immediate8(imm);
+        }
+    }
 public:
 
     void sarl_i8r(int imm, RegisterID dst)
@@ -1755,6 +1780,11 @@ public:
         m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_ROL, dst);
     }
 
+    void rolw_i8r(int imm, RegisterID dst)
+    {
+        shiftInstruction16<GROUP2_OP_ROL>(imm, dst);
+    }
+
 #if CPU(X86_64)
 private:
     template<GroupOpcodeID op>
@@ -4334,6 +4364,14 @@ private:
             writer.putByteUnchecked(opcode);
         }
 
+        void twoByteOp(TwoByteOpcodeID opcode, int reg)
+        {
+            SingleInstructionBufferWriter writer(m_buffer);
+            writer.emitRexIfNeeded(0, 0, reg);
+            writer.putByteUnchecked(OP_2BYTE_ESCAPE);
+            writer.putByteUnchecked(opcode + (reg & 7));
+        }
+
         void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID rm)
         {
             SingleInstructionBufferWriter writer(m_buffer);
@@ -4507,6 +4545,14 @@ private:
             writer.memoryModRMAddr(reg, address);
         }
 
+        void twoByteOp64(TwoByteOpcodeID opcode, int reg)
+        {
+            SingleInstructionBufferWriter writer(m_buffer);
+            writer.emitRexW(0, 0, reg);
+            writer.putByteUnchecked(OP_2BYTE_ESCAPE);
+            writer.putByteUnchecked(opcode + (reg & 7));
+        }
+
         void twoByteOp64(TwoByteOpcodeID opcode, int reg, RegisterID rm)
         {
             SingleInstructionBufferWriter writer(m_buffer);
index 7fef2db..1d77d80 100644 (file)
@@ -863,6 +863,41 @@ void testProbeModifiesStackValues()
 }
 #endif // ENABLE(MASM_PROBE)
 
+void testByteSwap()
+{
+#if CPU(X86_64) || CPU(ARM64)
+    auto byteSwap16 = compile([] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+        jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
+        jit.byteSwap16(GPRInfo::returnValueGPR);
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+    CHECK_EQ(invoke<uint64_t>(byteSwap16, 0xaabbccddee001122), static_cast<uint64_t>(0x2211));
+    CHECK_EQ(invoke<uint64_t>(byteSwap16, 0xaabbccddee00ffaa), static_cast<uint64_t>(0xaaff));
+
+    auto byteSwap32 = compile([] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+        jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
+        jit.byteSwap32(GPRInfo::returnValueGPR);
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+    CHECK_EQ(invoke<uint64_t>(byteSwap32, 0xaabbccddee001122), static_cast<uint64_t>(0x221100ee));
+    CHECK_EQ(invoke<uint64_t>(byteSwap32, 0xaabbccddee00ffaa), static_cast<uint64_t>(0xaaff00ee));
+
+    auto byteSwap64 = compile([] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+        jit.move(GPRInfo::argumentGPR0, GPRInfo::returnValueGPR);
+        jit.byteSwap64(GPRInfo::returnValueGPR);
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+    CHECK_EQ(invoke<uint64_t>(byteSwap64, 0xaabbccddee001122), static_cast<uint64_t>(0x221100eeddccbbaa));
+    CHECK_EQ(invoke<uint64_t>(byteSwap64, 0xaabbccddee00ffaa), static_cast<uint64_t>(0xaaff00eeddccbbaa));
+#endif
+}
+
 #define RUN(test) do {                          \
         if (!shouldRun(#test))                  \
             break;                              \
@@ -947,6 +982,8 @@ void run(const char* filter)
     RUN(testProbeModifiesStackValues());
 #endif // ENABLE(MASM_PROBE)
 
+    RUN(testByteSwap());
+
     if (tasks.isEmpty())
         usage();
 
index 22c6492..fc2db59 100644 (file)
@@ -38,7 +38,7 @@ namespace JSC {
 enum DataFormat {
     DataFormatNone = 0,
     DataFormatInt32 = 1,
-    DataFormatInt52 = 2, // Int52's are left-shifted by 16 by default.
+    DataFormatInt52 = 2, // Int52's are left-shifted by 12 by default.
     DataFormatStrictInt52 = 3, // "Strict" Int52 means it's not shifted.
     DataFormatDouble = 4,
     DataFormatBoolean = 5,
index 709519d..a888b3b 100644 (file)
@@ -203,6 +203,11 @@ void dumpSpeculation(PrintStream& outStream, SpeculatedType value)
                 strOut.print("DerivedArray");
             else
                 isTop = false;
+
+            if (value & SpecDataViewObject)
+                strOut.print("DataView");
+            else
+                isTop = false;
         }
 
         if ((value & SpecString) == SpecString)
@@ -432,6 +437,9 @@ SpeculatedType speculationFromClassInfo(const ClassInfo* classInfo)
 
     if (classInfo == ProxyObject::info())
         return SpecProxyObject;
+
+    if (classInfo == JSDataView::info())
+        return SpecDataViewObject;
     
     if (classInfo->isSubClassOf(JSFunction::info())) {
         if (classInfo == JSBoundFunction::info())
@@ -559,6 +567,8 @@ SpeculatedType speculationFromJSType(JSType type)
         return SpecWeakMapObject;
     case JSWeakSetType:
         return SpecWeakSetObject;
+    case DataViewType:
+        return SpecDataViewObject;
     default:
         ASSERT_NOT_REACHED();
     }
@@ -750,6 +760,8 @@ SpeculatedType speculationFromString(const char* speculation)
         return SpecProxyObject;
     if (!strncmp(speculation, "SpecDerivedArray", strlen("SpecDerivedArray")))
         return SpecDerivedArray;
+    if (!strncmp(speculation, "SpecDataViewObject", strlen("SpecDataViewObject")))
+        return SpecDataViewObject;
     if (!strncmp(speculation, "SpecObjectOther", strlen("SpecObjectOther")))
         return SpecObjectOther;
     if (!strncmp(speculation, "SpecObject", strlen("SpecObject")))
index 1fe5b35..f2bc0e6 100644 (file)
@@ -65,7 +65,6 @@ static const SpeculatedType SpecWeakSetObject                     = 1ull << 20;
 static const SpeculatedType SpecProxyObject                       = 1ull << 21; // It's definitely a Proxy object or one of its subclasses.
 static const SpeculatedType SpecDerivedArray                      = 1ull << 22; // It's definitely a DerivedArray object.
 static const SpeculatedType SpecObjectOther                       = 1ull << 23; // It's definitely an object but not JSFinalObject, JSArray, or JSFunction.
-static const SpeculatedType SpecObject                            = SpecFinalObject | SpecArray | SpecFunction | SpecTypedArrayView | SpecDirectArguments | SpecScopedArguments | SpecStringObject | SpecRegExpObject | SpecMapObject | SpecSetObject | SpecWeakMapObject | SpecWeakSetObject | SpecProxyObject | SpecDerivedArray | SpecObjectOther; // Bitmask used for testing for any kind of object prediction.
 static const SpeculatedType SpecStringIdent                       = 1ull << 24; // It's definitely a JSString, and it's an identifier.
 static const SpeculatedType SpecStringVar                         = 1ull << 25; // It's definitely a JSString, and it's not an identifier.
 static const SpeculatedType SpecString                            = SpecStringIdent | SpecStringVar; // It's definitely a JSString.
@@ -93,7 +92,9 @@ static const SpeculatedType SpecOther                             = 1ull << 36;
 static const SpeculatedType SpecMisc                              = SpecBoolean | SpecOther; // It's definitely either a boolean, Null, or Undefined.
 static const SpeculatedType SpecEmpty                             = 1ull << 37; // It's definitely an empty value marker.
 static const SpeculatedType SpecBigInt                            = 1ull << 38; // It's definitely a BigInt.
+static const SpeculatedType SpecDataViewObject                    = 1ull << 39; // It's definitely a JSDataView.
 static const SpeculatedType SpecPrimitive                         = SpecString | SpecSymbol | SpecBytecodeNumber | SpecMisc | SpecBigInt; // It's any non-Object JSValue.
+static const SpeculatedType SpecObject                            = SpecFinalObject | SpecArray | SpecFunction | SpecTypedArrayView | SpecDirectArguments | SpecScopedArguments | SpecStringObject | SpecRegExpObject | SpecMapObject | SpecSetObject | SpecWeakMapObject | SpecWeakSetObject | SpecProxyObject | SpecDerivedArray | SpecObjectOther | SpecDataViewObject; // Bitmask used for testing for any kind of object prediction.
 static const SpeculatedType SpecCell                              = SpecObject | SpecString | SpecSymbol | SpecCellOther | SpecBigInt; // It's definitely a JSCell.
 static const SpeculatedType SpecHeapTop                           = SpecCell | SpecBytecodeNumber | SpecMisc; // It can be any of the above, except for SpecInt52Only and SpecDoubleImpureNaN.
 static const SpeculatedType SpecBytecodeTop                       = SpecHeapTop | SpecEmpty; // It can be any of the above, except for SpecInt52Only and SpecDoubleImpureNaN. Corresponds to what could be found in a bytecode local.
index 940faf6..1938312 100644 (file)
@@ -3770,9 +3770,30 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         filter(node->child1(), SpecCell);
         break;
     }
-        
+
+    case DataViewGetInt: {
+        DataViewData data = node->dataViewData();
+        if (data.byteSize < 4)
+            setNonCellTypeForNode(node, SpecInt32Only);
+        else {
+            ASSERT(data.byteSize == 4);
+            if (data.isSigned)
+                setNonCellTypeForNode(node, SpecInt32Only);
+            else
+                setNonCellTypeForNode(node, SpecAnyInt);
+        }
         break;
+    }
+
+    case DataViewGetFloat: {
+        setNonCellTypeForNode(node, SpecFullDouble);
+        break;
+    }
 
+    case DataViewSet: {
+        break;
+    }
+        
     case Unreachable:
         // It may be that during a previous run of AI we proved that something was unreachable, but
         // during this run of AI we forget that it's unreachable. AI's proofs don't have to get
index 54d7156..d1e6d19 100644 (file)
@@ -3079,6 +3079,180 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case DataViewGetInt8:
+    case DataViewGetUint8:
+    case DataViewGetInt16:
+    case DataViewGetUint16:
+    case DataViewGetInt32:
+    case DataViewGetUint32:
+    case DataViewGetFloat32:
+    case DataViewGetFloat64: {
+        if (!is64Bit())
+            return false;
+
+        // To inline data view accesses, we assume the architecture we're running on:
+        // - Is little endian.
+        // - Allows unaligned loads/stores without crashing. 
+
+        if (argumentCountIncludingThis < 2)
+            return false;
+        if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+            return false;
+
+        insertChecks();
+
+        uint8_t byteSize;
+        NodeType op = DataViewGetInt;
+        bool isSigned = false;
+        switch (intrinsic) {
+        case DataViewGetInt8:
+            isSigned = true;
+            FALLTHROUGH;
+        case DataViewGetUint8:
+            byteSize = 1;
+            break;
+
+        case DataViewGetInt16:
+            isSigned = true;
+            FALLTHROUGH;
+        case DataViewGetUint16:
+            byteSize = 2;
+            break;
+
+        case DataViewGetInt32:
+            isSigned = true;
+            FALLTHROUGH;
+        case DataViewGetUint32:
+            byteSize = 4;
+            break;
+
+        case DataViewGetFloat32:
+            byteSize = 4;
+            op = DataViewGetFloat;
+            break;
+        case DataViewGetFloat64:
+            byteSize = 8;
+            op = DataViewGetFloat;
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        TriState isLittleEndian = MixedTriState;
+        Node* littleEndianChild = nullptr;
+        if (byteSize > 1) {
+            if (argumentCountIncludingThis < 3)
+                isLittleEndian = FalseTriState;
+            else {
+                littleEndianChild = get(virtualRegisterForArgument(2, registerOffset));
+                if (littleEndianChild->hasConstant()) {
+                    JSValue constant = littleEndianChild->constant()->value();
+                    isLittleEndian = constant.pureToBoolean();
+                    if (isLittleEndian != MixedTriState)
+                        littleEndianChild = nullptr;
+                } else
+                    isLittleEndian = MixedTriState;
+            }
+        }
+
+        DataViewData data { };
+        data.isLittleEndian = isLittleEndian;
+        data.isSigned = isSigned;
+        data.byteSize = byteSize;
+
+        set(VirtualRegister(resultOperand),
+            addToGraph(op, OpInfo(data.asQuadWord), OpInfo(prediction), get(virtualRegisterForArgument(0, registerOffset)), get(virtualRegisterForArgument(1, registerOffset)), littleEndianChild));
+        return true;
+    }
+
+    case DataViewSetInt8:
+    case DataViewSetUint8:
+    case DataViewSetInt16:
+    case DataViewSetUint16:
+    case DataViewSetInt32:
+    case DataViewSetUint32:
+    case DataViewSetFloat32:
+    case DataViewSetFloat64: {
+        if (!is64Bit())
+            return false;
+
+        if (argumentCountIncludingThis < 3)
+            return false;
+
+        if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+            return false;
+
+        insertChecks();
+
+        uint8_t byteSize;
+        bool isFloatingPoint = false;
+        bool isSigned = false;
+        switch (intrinsic) {
+        case DataViewSetInt8:
+            isSigned = true;
+            FALLTHROUGH;
+        case DataViewSetUint8:
+            byteSize = 1;
+            break;
+
+        case DataViewSetInt16:
+            isSigned = true;
+            FALLTHROUGH;
+        case DataViewSetUint16:
+            byteSize = 2;
+            break;
+
+        case DataViewSetInt32:
+            isSigned = true;
+            FALLTHROUGH;
+        case DataViewSetUint32:
+            byteSize = 4;
+            break;
+
+        case DataViewSetFloat32:
+            isFloatingPoint = true;
+            byteSize = 4;
+            break;
+        case DataViewSetFloat64:
+            isFloatingPoint = true;
+            byteSize = 8;
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        TriState isLittleEndian = MixedTriState;
+        Node* littleEndianChild = nullptr;
+        if (byteSize > 1) {
+            if (argumentCountIncludingThis < 4)
+                isLittleEndian = FalseTriState;
+            else {
+                littleEndianChild = get(virtualRegisterForArgument(3, registerOffset));
+                if (littleEndianChild->hasConstant()) {
+                    JSValue constant = littleEndianChild->constant()->value();
+                    isLittleEndian = constant.pureToBoolean();
+                    if (isLittleEndian != MixedTriState)
+                        littleEndianChild = nullptr;
+                } else
+                    isLittleEndian = MixedTriState;
+            }
+        }
+
+        DataViewData data { };
+        data.isLittleEndian = isLittleEndian;
+        data.isSigned = isSigned;
+        data.byteSize = byteSize;
+        data.isFloatingPoint = isFloatingPoint;
+
+        addVarArgChild(get(virtualRegisterForArgument(0, registerOffset)));
+        addVarArgChild(get(virtualRegisterForArgument(1, registerOffset)));
+        addVarArgChild(get(virtualRegisterForArgument(2, registerOffset)));
+        addVarArgChild(littleEndianChild);
+
+        addToGraph(Node::VarArg, DataViewSet, OpInfo(data.asQuadWord), OpInfo());
+        return true;
+    }
+
     case HasOwnPropertyIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;
index 4563481..804d4d4 100644 (file)
@@ -1758,7 +1758,21 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case NumberToStringWithValidRadixConstant:
         def(PureValue(node, node->validRadixConstant()));
         return;
-        
+
+    case DataViewGetFloat:
+    case DataViewGetInt: {
+        read(MiscFields);
+        read(TypedArrayProperties);
+        return;
+    }
+
+    case DataViewSet: {
+        read(MiscFields);
+        read(TypedArrayProperties);
+        write(TypedArrayProperties);
+        return;
+    }
+
     case LastNodeType:
         RELEASE_ASSERT_NOT_REACHED();
         return;
index 789483f..2b0d4df 100644 (file)
@@ -318,6 +318,9 @@ bool doesGC(Graph& graph, Node* node)
     case FilterGetByIdStatus:
     case FilterPutByIdStatus:
     case FilterInByIdStatus:
+    case DataViewGetInt:
+    case DataViewGetFloat:
+    case DataViewSet:
         return false;
 
     case PushWithScope:
index a9f0b25..fb2c06d 100644 (file)
@@ -2104,6 +2104,60 @@ private:
             fixEdge<CellUse>(node->child1());
             break;
 
+        case DataViewGetInt:
+        case DataViewGetFloat: {
+            fixEdge<DataViewObjectUse>(node->child1());
+            fixEdge<Int32Use>(node->child2());
+            if (node->child3())
+                fixEdge<BooleanUse>(node->child3());
+
+            if (node->op() == DataViewGetInt) {
+                DataViewData data = node->dataViewData();
+                switch (data.byteSize) {
+                case 1:
+                case 2:
+                    node->setResult(NodeResultInt32);
+                    break;
+                case 4:
+                    if (data.isSigned) 
+                        node->setResult(NodeResultInt32);
+                    else
+                        node->setResult(NodeResultInt52);
+                    break;
+                default:
+                    RELEASE_ASSERT_NOT_REACHED();
+                }
+            }
+            break;
+        }
+
+        case DataViewSet: {
+            fixEdge<DataViewObjectUse>(m_graph.varArgChild(node, 0));
+            fixEdge<Int32Use>(m_graph.varArgChild(node, 1));
+            if (m_graph.varArgChild(node, 3))
+                fixEdge<BooleanUse>(m_graph.varArgChild(node, 3));
+            
+            DataViewData data = node->dataViewData();
+            Edge& valueToStore = m_graph.varArgChild(node, 2);
+            if (data.isFloatingPoint)
+                fixEdge<DoubleRepUse>(valueToStore);
+            else {
+                switch (data.byteSize) {
+                case 1:
+                case 2:
+                    fixEdge<Int32Use>(valueToStore);
+                    break;
+                case 4:
+                    if (data.isSigned)
+                        fixEdge<Int32Use>(valueToStore);
+                    else
+                        fixEdge<Int52RepUse>(valueToStore);
+                    break;
+                }
+            }
+            break;
+        }
+
 #if !ASSERT_DISABLED
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
         case SetArgument:
index b042993..11c3d75 100644 (file)
@@ -117,6 +117,19 @@ struct NewArrayBufferData {
 static_assert(sizeof(IndexingType) <= sizeof(unsigned), "");
 static_assert(sizeof(NewArrayBufferData) == sizeof(uint64_t), "");
 
+struct DataViewData {
+    union {
+        struct {
+            uint8_t byteSize;
+            bool isSigned;
+            bool isFloatingPoint; // Used for the DataViewSet node.
+            TriState isLittleEndian;
+        };
+        uint64_t asQuadWord;
+    };
+};
+static_assert(sizeof(DataViewData) == sizeof(uint64_t), "");
+
 struct BranchTarget {
     BranchTarget()
         : block(0)
@@ -1659,6 +1672,8 @@ public:
         case GetDynamicVar:
         case ExtractValueFromWeakMapGet:
         case ToThis:
+        case DataViewGetInt:
+        case DataViewGetFloat:
             return true;
         default:
             return false;
@@ -2143,6 +2158,12 @@ public:
         return m_opInfo.as<unsigned>();
     }
 
+    DataViewData dataViewData()
+    {
+        ASSERT(op() == DataViewGetInt || op() == DataViewGetFloat || op() == DataViewSet);
+        return bitwise_cast<DataViewData>(m_opInfo.as<uint64_t>());
+    }
+
     bool shouldGenerate()
     {
         return m_refCount;
index 14edfdb..af3db34 100644 (file)
@@ -483,6 +483,10 @@ namespace JSC { namespace DFG {
     macro(FilterGetByIdStatus, NodeMustGenerate) \
     macro(FilterInByIdStatus, NodeMustGenerate) \
     macro(FilterPutByIdStatus, NodeMustGenerate) \
+    /* Data view access */ \
+    macro(DataViewGetInt, NodeMustGenerate | NodeResultJS) /* The gets are must generate for now because they do bounds checks */ \
+    macro(DataViewGetFloat, NodeMustGenerate | NodeResultDouble) \
+    macro(DataViewSet, NodeMustGenerate | NodeMustGenerate | NodeHasVarArgs) \
 
 
 // This enum generates a monotonically increasing id for all Node types,
index 3791088..f4dff03 100644 (file)
@@ -621,7 +621,14 @@ private:
             }
             break;
         }
-            
+        
+        case DataViewSet: {
+            DataViewData data = node->dataViewData();
+            if (data.isFloatingPoint)
+                m_graph.voteNode(m_graph.varArgChild(node, 2), VoteValue, weight);
+            break;
+        }
+
         case MovHint:
             // Ignore these since they have no effect on in-DFG execution.
             break;
@@ -750,7 +757,9 @@ private:
         case CallDOMGetter:
         case GetDynamicVar:
         case GetPrototypeOf:
-        case ExtractValueFromWeakMapGet: {
+        case ExtractValueFromWeakMapGet: 
+        case DataViewGetInt:
+        case DataViewGetFloat: {
             setPrediction(m_currentNode->getHeapPrediction());
             break;
         }
@@ -1220,6 +1229,7 @@ private:
         case FilterPutByIdStatus:
         case FilterInByIdStatus:
         case ClearCatchLocals:
+        case DataViewSet:
             break;
             
         // This gets ignored because it only pretends to produce a value.
index 43eb97b..4bfb615 100644 (file)
@@ -65,6 +65,7 @@ public:
         case SetObjectUse:
         case WeakMapObjectUse:
         case WeakSetObjectUse:
+        case DataViewObjectUse:
         case ObjectOrOtherUse:
         case StringIdentUse:
         case StringUse:
@@ -462,6 +463,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case AtomicsIsLockFree:
     case InitializeEntrypointArguments:
     case MatchStructure:
+    case DataViewGetInt:
+    case DataViewGetFloat:
         return true;
 
     case ArraySlice:
@@ -602,6 +605,9 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
         return true;
     }
 
+    case DataViewSet:
+        return false;
+
     case SetAdd:
     case MapSet:
         return false;
index 41d5ed8..bd18f9d 100644 (file)
@@ -9903,6 +9903,20 @@ void SpeculativeJIT::speculateWeakSetObject(Edge edge)
     speculateWeakSetObject(edge, operand.gpr());
 }
 
+void SpeculativeJIT::speculateDataViewObject(Edge edge, GPRReg cell)
+{
+    speculateCellType(edge, cell, SpecDataViewObject, DataViewType);
+}
+
+void SpeculativeJIT::speculateDataViewObject(Edge edge)
+{
+    if (!needsTypeCheck(edge, SpecDataViewObject))
+        return;
+
+    SpeculateCellOperand operand(this, edge);
+    speculateDataViewObject(edge, operand.gpr());
+}
+
 void SpeculativeJIT::speculateObjectOrOther(Edge edge)
 {
     if (!needsTypeCheck(edge, SpecObject | SpecOther))
@@ -10271,6 +10285,9 @@ void SpeculativeJIT::speculate(Node*, Edge edge)
     case WeakSetObjectUse:
         speculateWeakSetObject(edge);
         break;
+    case DataViewObjectUse:
+        speculateDataViewObject(edge);
+        break;
     case ObjectOrOtherUse:
         speculateObjectOrOther(edge);
         break;
index 8bb6a54..7fefd12 100644 (file)
@@ -1610,6 +1610,8 @@ public:
     void speculateWeakMapObject(Edge, GPRReg cell);
     void speculateWeakSetObject(Edge);
     void speculateWeakSetObject(Edge, GPRReg cell);
+    void speculateDataViewObject(Edge);
+    void speculateDataViewObject(Edge, GPRReg cell);
     void speculateObjectOrOther(Edge);
     void speculateString(Edge edge, GPRReg cell);
     void speculateStringIdentAndLoadStorage(Edge edge, GPRReg string, GPRReg storage);
index d7ed865..75f42ee 100644 (file)
@@ -4107,6 +4107,9 @@ void SpeculativeJIT::compile(Node* node)
     case EntrySwitch:
     case CPUIntrinsic:
     case AssertNotEmpty:
+    case DataViewGetInt:
+    case DataViewGetFloat:
+    case DataViewSet:
         DFG_CRASH(m_jit.graph(), node, "unexpected node in DFG backend");
         break;
     }
index c5e1d44..7e1cbcf 100644 (file)
@@ -4553,6 +4553,365 @@ void SpeculativeJIT::compile(Node* node)
         compileClearCatchLocals(node);
         break;
 
+    case DataViewGetFloat:
+    case DataViewGetInt: {
+        SpeculateCellOperand dataView(this, node->child1());
+        GPRReg dataViewGPR = dataView.gpr();
+        speculateDataViewObject(node->child1(), dataViewGPR);
+
+        SpeculateInt32Operand index(this, node->child2());
+        GPRReg indexGPR = index.gpr();
+
+        GPRTemporary temp1(this);
+        GPRReg t1 = temp1.gpr();
+        GPRTemporary temp2(this);
+        GPRReg t2 = temp2.gpr();
+
+        std::optional<SpeculateBooleanOperand> isLittleEndianOperand;
+        if (node->child3())
+            isLittleEndianOperand.emplace(this, node->child3());
+        GPRReg isLittleEndianGPR = isLittleEndianOperand ? isLittleEndianOperand->gpr() : InvalidGPRReg;
+
+        DataViewData data = node->dataViewData();
+
+        m_jit.zeroExtend32ToPtr(indexGPR, t2);
+        if (data.byteSize > 1)
+            m_jit.add64(TrustedImm32(data.byteSize - 1), t2);
+        m_jit.load32(MacroAssembler::Address(dataViewGPR, JSArrayBufferView::offsetOfLength()), t1);
+        speculationCheck(OutOfBounds, JSValueRegs(), node,
+            m_jit.branch64(MacroAssembler::AboveOrEqual, t2, t1));
+
+        m_jit.loadPtr(JITCompiler::Address(dataViewGPR, JSArrayBufferView::offsetOfVector()), t2);
+        cageTypedArrayStorage(t2);
+
+        m_jit.zeroExtend32ToPtr(indexGPR, t1);
+        auto baseIndex = JITCompiler::BaseIndex(t2, t1, MacroAssembler::TimesOne);
+
+        if (node->op() == DataViewGetInt) {
+            switch (data.byteSize) {
+            case 1:
+                if (data.isSigned)
+                    m_jit.load8SignedExtendTo32(baseIndex, t2);
+                else
+                    m_jit.load8(baseIndex, t2);
+                int32Result(t2, node);
+                break;
+            case 2: {
+                auto emitLittleEndianLoad = [&] {
+                    if (data.isSigned)
+                        m_jit.load16SignedExtendTo32(baseIndex, t2);
+                    else
+                        m_jit.load16(baseIndex, t2);
+                };
+                auto emitBigEndianLoad = [&] {
+                    m_jit.load16(baseIndex, t2);
+                    m_jit.byteSwap16(t2);
+                    if (data.isSigned)
+                        m_jit.signExtend16To32(t2, t2);
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianLoad();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianLoad();
+                else {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isBigEndian = m_jit.branchTest32(MacroAssembler::Zero, isLittleEndianGPR, TrustedImm32(1));
+                    emitLittleEndianLoad();
+                    auto done = m_jit.jump();
+                    isBigEndian.link(&m_jit);
+                    emitBigEndianLoad();
+                    done.link(&m_jit);
+                }
+                int32Result(t2, node);
+                break;
+            }
+            case 4: {
+                m_jit.load32(baseIndex, t2);
+
+                if (data.isLittleEndian == FalseTriState)
+                    m_jit.byteSwap32(t2);
+                else if (data.isLittleEndian == MixedTriState) {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isLittleEndian = m_jit.branchTest32(MacroAssembler::NonZero, isLittleEndianGPR, TrustedImm32(1));
+                    m_jit.byteSwap32(t2);
+                    isLittleEndian.link(&m_jit);
+                }
+
+                if (data.isSigned)
+                    int32Result(t2, node);
+                else
+                    strictInt52Result(t2, node);
+                break;
+            }
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        } else {
+            FPRTemporary result(this);
+            FPRReg resultFPR = result.fpr();
+
+            switch (data.byteSize) {
+            case 4: {
+                auto emitLittleEndianCode = [&] {
+                    m_jit.loadFloat(baseIndex, resultFPR);
+                    m_jit.convertFloatToDouble(resultFPR, resultFPR);
+                };
+
+                auto emitBigEndianCode = [&] {
+                    m_jit.load32(baseIndex, t2);
+                    m_jit.byteSwap32(t2);
+                    m_jit.move32ToFloat(t2, resultFPR);
+                    m_jit.convertFloatToDouble(resultFPR, resultFPR);
+                };
+
+                if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isBigEndian = m_jit.branchTest32(MacroAssembler::Zero, isLittleEndianGPR, TrustedImm32(1));
+                    emitLittleEndianCode();
+                    auto done = m_jit.jump();
+                    isBigEndian.link(&m_jit);
+                    emitBigEndianCode();
+                    done.link(&m_jit);
+                }
+
+                break;
+            }
+            case 8: {
+                auto emitLittleEndianCode = [&] {
+                    m_jit.loadDouble(baseIndex, resultFPR);
+                };
+
+                auto emitBigEndianCode = [&] {
+                    m_jit.load64(baseIndex, t2);
+                    m_jit.byteSwap64(t2);
+                    m_jit.move64ToDouble(t2, resultFPR);
+                };
+
+                if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isBigEndian = m_jit.branchTest32(MacroAssembler::Zero, isLittleEndianGPR, TrustedImm32(1));
+                    emitLittleEndianCode();
+                    auto done = m_jit.jump();
+                    isBigEndian.link(&m_jit);
+                    emitBigEndianCode();
+                    done.link(&m_jit);
+                }
+
+                break;
+            }
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+
+            doubleResult(resultFPR, node);
+        }
+
+        break;
+    }
+
+    case DataViewSet: {
+        SpeculateCellOperand dataView(this, m_graph.varArgChild(node, 0));
+        GPRReg dataViewGPR = dataView.gpr();
+        speculateDataViewObject(m_graph.varArgChild(node, 0), dataViewGPR);
+
+        SpeculateInt32Operand index(this, m_graph.varArgChild(node, 1));
+        GPRReg indexGPR = index.gpr();
+
+        std::optional<SpeculateStrictInt52Operand> int52Value;
+        std::optional<SpeculateDoubleOperand> doubleValue;
+        std::optional<SpeculateInt32Operand> int32Value;
+        std::optional<FPRTemporary> fprTemporary;
+        GPRReg valueGPR = InvalidGPRReg;
+        FPRReg valueFPR = InvalidFPRReg;
+        FPRReg tempFPR = InvalidFPRReg;
+
+        DataViewData data = node->dataViewData();
+
+        Edge& valueEdge = m_graph.varArgChild(node, 2);
+        switch (valueEdge.useKind()) {
+        case Int32Use:
+            int32Value.emplace(this, valueEdge);
+            valueGPR = int32Value->gpr();
+            break;
+        case DoubleRepUse:
+            doubleValue.emplace(this, valueEdge);
+            valueFPR = doubleValue->fpr();
+            if (data.byteSize == 4) {
+                fprTemporary.emplace(this);
+                tempFPR = fprTemporary->fpr();
+            }
+            break;
+        case Int52RepUse:
+            int52Value.emplace(this, valueEdge);
+            valueGPR = int52Value->gpr();
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        GPRTemporary temp1(this);
+        GPRReg t1 = temp1.gpr();
+        GPRTemporary temp2(this);
+        GPRReg t2 = temp2.gpr();
+        GPRTemporary temp3(this);
+        GPRReg t3 = temp3.gpr();
+
+        std::optional<SpeculateBooleanOperand> isLittleEndianOperand;
+        if (m_graph.varArgChild(node, 3))
+            isLittleEndianOperand.emplace(this, m_graph.varArgChild(node, 3));
+        GPRReg isLittleEndianGPR = isLittleEndianOperand ? isLittleEndianOperand->gpr() : InvalidGPRReg;
+
+        m_jit.zeroExtend32ToPtr(indexGPR, t2);
+        if (data.byteSize > 1)
+            m_jit.add64(TrustedImm32(data.byteSize - 1), t2);
+        m_jit.load32(MacroAssembler::Address(dataViewGPR, JSArrayBufferView::offsetOfLength()), t1);
+        speculationCheck(OutOfBounds, JSValueRegs(), node,
+            m_jit.branch64(MacroAssembler::AboveOrEqual, t2, t1));
+
+        m_jit.loadPtr(JITCompiler::Address(dataViewGPR, JSArrayBufferView::offsetOfVector()), t2);
+        cageTypedArrayStorage(t2);
+
+        m_jit.zeroExtend32ToPtr(indexGPR, t1);
+        auto baseIndex = JITCompiler::BaseIndex(t2, t1, MacroAssembler::TimesOne);
+
+        if (data.isFloatingPoint) {
+            RELEASE_ASSERT(valueFPR != InvalidFPRReg);
+            if (data.byteSize == 4) {
+                RELEASE_ASSERT(tempFPR != InvalidFPRReg);
+                m_jit.convertDoubleToFloat(valueFPR, tempFPR);
+
+                auto emitLittleEndianCode = [&] {
+                    m_jit.storeFloat(tempFPR, baseIndex);
+                };
+
+                auto emitBigEndianCode = [&] {
+                    m_jit.moveFloatTo32(tempFPR, t3);
+                    m_jit.byteSwap32(t3);
+                    m_jit.store32(t3, baseIndex);
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isBigEndian = m_jit.branchTest32(MacroAssembler::Zero, isLittleEndianGPR, TrustedImm32(1));
+                    emitLittleEndianCode();
+                    auto done = m_jit.jump();
+                    isBigEndian.link(&m_jit);
+                    emitBigEndianCode();
+                    done.link(&m_jit);
+                }
+            } else {
+                RELEASE_ASSERT(data.byteSize == 8);
+                RELEASE_ASSERT(valueFPR != InvalidFPRReg);
+
+                auto emitLittleEndianCode = [&] {
+                    m_jit.storeDouble(valueFPR, baseIndex);
+                };
+                auto emitBigEndianCode = [&] {
+                    m_jit.moveDoubleTo64(valueFPR, t3);
+                    m_jit.byteSwap64(t3);
+                    m_jit.store64(t3, baseIndex);
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isBigEndian = m_jit.branchTest32(MacroAssembler::Zero, isLittleEndianGPR, TrustedImm32(1));
+                    emitLittleEndianCode();
+                    auto done = m_jit.jump();
+                    isBigEndian.link(&m_jit);
+                    emitBigEndianCode();
+                    done.link(&m_jit);
+                }
+            }
+        } else {
+            switch (data.byteSize) {
+            case 1:
+                RELEASE_ASSERT(valueEdge.useKind() == Int32Use);
+                RELEASE_ASSERT(valueGPR != InvalidGPRReg);
+                m_jit.store8(valueGPR, baseIndex);
+                break;
+            case 2: {
+                RELEASE_ASSERT(valueEdge.useKind() == Int32Use);
+                RELEASE_ASSERT(valueGPR != InvalidGPRReg);
+
+                auto emitLittleEndianCode = [&] {
+                    m_jit.store16(valueGPR, baseIndex);
+                };
+                auto emitBigEndianCode = [&] {
+                    m_jit.move(valueGPR, t3);
+                    m_jit.byteSwap16(t3);
+                    m_jit.store16(t3, baseIndex);
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isBigEndian = m_jit.branchTest32(MacroAssembler::Zero, isLittleEndianGPR, TrustedImm32(1));
+                    emitLittleEndianCode();
+                    auto done = m_jit.jump();
+                    isBigEndian.link(&m_jit);
+                    emitBigEndianCode();
+                    done.link(&m_jit);
+                }
+                break;
+            }
+            case 4: {
+                RELEASE_ASSERT(valueEdge.useKind() == Int32Use || valueEdge.useKind() == Int52RepUse);
+
+                auto emitLittleEndianCode = [&] {
+                    m_jit.store32(valueGPR, baseIndex);
+                };
+
+                auto emitBigEndianCode = [&] {
+                    m_jit.zeroExtend32ToPtr(valueGPR, t3);
+                    m_jit.byteSwap32(t3);
+                    m_jit.store32(t3, baseIndex);
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else {
+                    RELEASE_ASSERT(isLittleEndianGPR != InvalidGPRReg);
+                    auto isBigEndian = m_jit.branchTest32(MacroAssembler::Zero, isLittleEndianGPR, TrustedImm32(1));
+                    emitLittleEndianCode();
+                    auto done = m_jit.jump();
+                    isBigEndian.link(&m_jit);
+                    emitBigEndianCode();
+                    done.link(&m_jit);
+                }
+
+                break;
+            }
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        }
+
+        noResult(node);
+        break;
+    }
+
 #if ENABLE(FTL_JIT)        
     case CheckTierUpInLoop: {
         MacroAssembler::Jump callTierUp = m_jit.branchAdd32(
index c56429c..4e5f5a8 100644 (file)
@@ -104,16 +104,19 @@ void printInternal(PrintStream& out, UseKind useKind)
         out.print("DerivedArray");
         return;
     case MapObjectUse:
-        out.print("MapObjectUse");
+        out.print("MapObject");
         return;
     case SetObjectUse:
-        out.print("SetObjectUse");
+        out.print("SetObject");
         return;
     case WeakMapObjectUse:
-        out.print("WeakMapObjectUse");
+        out.print("WeakMapObject");
         return;
     case WeakSetObjectUse:
-        out.print("WeakSetObjectUse");
+        out.print("WeakSetObject");
+        return;
+    case DataViewObjectUse:
+        out.print("DataViewObject");
         return;
     case ObjectOrOtherUse:
         out.print("ObjectOrOther");
index acd1280..d898ecb 100644 (file)
@@ -70,6 +70,7 @@ enum UseKind {
     SetObjectUse,
     WeakMapObjectUse,
     WeakSetObjectUse,
+    DataViewObjectUse,
     StringObjectUse,
     StringOrStringObjectUse,
     NotStringVarUse,
@@ -160,6 +161,8 @@ inline SpeculatedType typeFilterFor(UseKind useKind)
         return SpecWeakMapObject;
     case WeakSetObjectUse:
         return SpecWeakSetObject;
+    case DataViewObjectUse:
+        return SpecDataViewObject;
     case StringObjectUse:
         return SpecStringObject;
     case StringOrStringObjectUse:
@@ -259,6 +262,7 @@ inline bool isCell(UseKind kind)
     case SetObjectUse:
     case WeakMapObjectUse:
     case WeakSetObjectUse:
+    case DataViewObjectUse:
         return true;
     default:
         return false;
index 1051563..f1822a4 100644 (file)
@@ -359,6 +359,9 @@ inline CapabilityLevel canCompile(Node* node)
     case FilterPutByIdStatus:
     case FilterInByIdStatus:
     case CreateThis:
+    case DataViewGetInt:
+    case DataViewGetFloat:
+    case DataViewSet:
         // These are OK.
         break;
 
@@ -445,6 +448,7 @@ CapabilityLevel canCompile(Graph& graph)
                 case SetObjectUse:
                 case WeakMapObjectUse:
                 case WeakSetObjectUse:
+                case DataViewObjectUse:
                 case FinalObjectUse:
                 case RegExpObjectUse:
                 case ProxyObjectUse:
index 761687b..e0298d3 100644 (file)
@@ -1307,6 +1307,13 @@ private:
         case FilterInByIdStatus:
             compileFilterICStatus();
             break;
+        case DataViewGetInt:
+        case DataViewGetFloat:
+            compileDataViewGet();
+            break;
+        case DataViewSet:
+            compileDataViewSet();
+            break;
 
         case PhantomLocal:
         case LoopHint:
@@ -12238,6 +12245,357 @@ private:
     {
         m_interpreter.filterICStatus(m_node);
     }
+
+    LValue byteSwap32(LValue value)
+    {
+        // FIXME: teach B3 byteswap
+        // https://bugs.webkit.org/show_bug.cgi?id=188759
+
+        RELEASE_ASSERT(value->type() == Int32);
+        PatchpointValue* patchpoint = m_out.patchpoint(Int32);
+        patchpoint->appendSomeRegister(value);
+        patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+            jit.move(params[1].gpr(), params[0].gpr());
+            jit.byteSwap32(params[0].gpr());
+        });
+        patchpoint->effects = Effects::none();
+        return patchpoint;
+    }
+
+    LValue byteSwap64(LValue value)
+    {
+        // FIXME: teach B3 byteswap
+        // https://bugs.webkit.org/show_bug.cgi?id=188759
+
+        RELEASE_ASSERT(value->type() == Int64);
+        PatchpointValue* patchpoint = m_out.patchpoint(Int64);
+        patchpoint->appendSomeRegister(value);
+        patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+            jit.move(params[1].gpr(), params[0].gpr());
+            jit.byteSwap64(params[0].gpr());
+        });
+        patchpoint->effects = Effects::none();
+        return patchpoint;
+    }
+
+    template <typename F1, typename F2>
+    LValue emitCodeBasedOnEndiannessBranch(LValue isLittleEndian, const F1& emitLittleEndianCode, const F2& emitBigEndianCode)
+    {
+        LType type;
+
+        LBasicBlock bigEndianCase = m_out.newBlock();
+        LBasicBlock littleEndianCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        m_out.branch(m_out.testIsZero32(isLittleEndian, m_out.constInt32(1)),
+            unsure(bigEndianCase), unsure(littleEndianCase));
+
+        LBasicBlock lastNext = m_out.appendTo(bigEndianCase, littleEndianCase);
+        LValue bigEndianValue = emitBigEndianCode();
+        type = bigEndianValue ? bigEndianValue->type() : Void;
+        ValueFromBlock bigEndianResult = bigEndianValue ? m_out.anchor(bigEndianValue) : ValueFromBlock();
+        m_out.jump(continuation);
+
+        m_out.appendTo(littleEndianCase, continuation);
+        LValue littleEndianValue = emitLittleEndianCode();
+        ValueFromBlock littleEndianResult = littleEndianValue ? m_out.anchor(littleEndianValue) : ValueFromBlock();
+        RELEASE_ASSERT((!littleEndianValue && !bigEndianValue) || type == littleEndianValue->type());
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        RELEASE_ASSERT(!!bigEndianResult == !!littleEndianResult);
+        if (bigEndianResult)
+            return m_out.phi(type, bigEndianResult, littleEndianResult);
+        return nullptr;
+    }
+
+    void compileDataViewGet()
+    {
+        LValue dataView = lowDataViewObject(m_node->child1());
+        LValue index = lowInt32(m_node->child2());
+        LValue isLittleEndian = nullptr;
+        if (m_node->child3())
+            isLittleEndian = lowBoolean(m_node->child3());
+
+        DataViewData data = m_node->dataViewData();
+
+        LValue length = m_out.zeroExtPtr(m_out.load32NonNegative(dataView, m_heaps.JSArrayBufferView_length));
+        LValue indexToCheck = m_out.zeroExtPtr(index);
+        if (data.byteSize > 1)
+            indexToCheck = m_out.add(indexToCheck, m_out.constInt64(data.byteSize - 1));
+        speculate(OutOfBounds, noValue(), nullptr, m_out.aboveOrEqual(indexToCheck, length));
+
+        LValue vector = caged(Gigacage::Primitive, m_out.loadPtr(dataView, m_heaps.JSArrayBufferView_vector));
+
+        TypedPointer pointer(m_heaps.typedArrayProperties, m_out.add(vector, m_out.zeroExtPtr(index)));
+
+        if (m_node->op() == DataViewGetInt) {
+            switch (data.byteSize) {
+            case 1:
+                if (data.isSigned)
+                    setInt32(m_out.load8SignExt32(pointer));
+                else
+                    setInt32(m_out.load8ZeroExt32(pointer));
+                break;
+            case 2: {
+                auto emitLittleEndianLoad = [&] {
+                    if (data.isSigned)
+                        return m_out.load16SignExt32(pointer);
+                    return m_out.load16ZeroExt32(pointer);
+                };
+
+                auto emitBigEndianLoad = [&] {
+                    LValue val = m_out.load16ZeroExt32(pointer);
+
+                    PatchpointValue* patchpoint = m_out.patchpoint(Int32);
+                    patchpoint->appendSomeRegister(val);
+                    patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                        jit.move(params[1].gpr(), params[0].gpr());
+                        jit.byteSwap16(params[0].gpr());
+                        if (data.isSigned)
+                            jit.signExtend16To32(params[0].gpr(), params[0].gpr());
+                    });
+                    patchpoint->effects = Effects::none();
+
+                    return patchpoint;
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    setInt32(emitBigEndianLoad());
+                else if (data.isLittleEndian == TrueTriState)
+                    setInt32(emitLittleEndianLoad());
+                else
+                    setInt32(emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianLoad, emitBigEndianLoad));
+
+                break;
+            }
+            case 4: {
+                LValue loadedValue = m_out.load32(pointer);
+
+                if (data.isLittleEndian == FalseTriState)
+                    loadedValue = byteSwap32(loadedValue);
+                else if (data.isLittleEndian == MixedTriState) {
+                    auto emitLittleEndianCode = [&] {
+                        return loadedValue;
+                    };
+                    auto emitBigEndianCode = [&] {
+                        return byteSwap32(loadedValue);
+                    };
+
+                    loadedValue = emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianCode, emitBigEndianCode);
+                }
+
+                if (data.isSigned)
+                    setInt32(loadedValue);
+                else
+                    setStrictInt52(m_out.zeroExt(loadedValue, Int64));
+
+                break;
+            }
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        } else {
+            switch (data.byteSize) {
+            case 4: {
+                auto emitLittleEndianCode = [&] {
+                    return m_out.floatToDouble(m_out.loadFloat(pointer));
+                };
+
+                auto emitBigEndianCode = [&] {
+                    LValue loadedValue = m_out.load32(pointer);
+                    PatchpointValue* patchpoint = m_out.patchpoint(Double);
+                    patchpoint->appendSomeRegister(loadedValue);
+                    patchpoint->numGPScratchRegisters = 1;
+                    patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                        jit.move(params[1].gpr(), params.gpScratch(0));
+                        jit.byteSwap32(params.gpScratch(0));
+                        jit.move32ToFloat(params.gpScratch(0), params[0].fpr());
+                        jit.convertFloatToDouble(params[0].fpr(), params[0].fpr());
+                    });
+                    patchpoint->effects = Effects::none();
+                    return patchpoint;
+                };
+
+                if (data.isLittleEndian == TrueTriState)
+                    setDouble(emitLittleEndianCode());
+                else if (data.isLittleEndian == FalseTriState)
+                    setDouble(emitBigEndianCode());
+                else
+                    setDouble(emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianCode, emitBigEndianCode));
+
+                break;
+            }
+            case 8: {
+                auto emitLittleEndianCode = [&] {
+                    return m_out.loadDouble(pointer);
+                };
+
+                auto emitBigEndianCode = [&] {
+                    LValue loadedValue = m_out.load64(pointer);
+                    loadedValue = byteSwap64(loadedValue);
+                    return m_out.bitCast(loadedValue, Double);
+                };
+
+                if (data.isLittleEndian == TrueTriState)
+                    setDouble(emitLittleEndianCode());
+                else if (data.isLittleEndian == FalseTriState)
+                    setDouble(emitBigEndianCode());
+                else
+                    setDouble(emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianCode, emitBigEndianCode));
+
+                break;
+            }
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        }
+    }
+
+    void compileDataViewSet()
+    {
+        LValue dataView = lowDataViewObject(m_graph.varArgChild(m_node, 0));
+        LValue index = lowInt32(m_graph.varArgChild(m_node, 1));
+        LValue isLittleEndian = nullptr;
+        if (m_graph.varArgChild(m_node, 3))
+            isLittleEndian = lowBoolean(m_graph.varArgChild(m_node, 3));
+
+        DataViewData data = m_node->dataViewData();
+
+        LValue length = m_out.zeroExtPtr(m_out.load32NonNegative(dataView, m_heaps.JSArrayBufferView_length));
+        LValue indexToCheck = m_out.zeroExtPtr(index);
+        if (data.byteSize > 1)
+            indexToCheck = m_out.add(indexToCheck, m_out.constInt64(data.byteSize - 1));
+        speculate(OutOfBounds, noValue(), nullptr, m_out.aboveOrEqual(indexToCheck, length));
+
+        Edge& valueEdge = m_graph.varArgChild(m_node, 2);
+        LValue valueToStore;
+        switch (valueEdge.useKind()) {
+        case Int32Use:
+            valueToStore = lowInt32(valueEdge);
+            break;
+        case DoubleRepUse:
+            valueToStore = lowDouble(valueEdge);
+            break;
+        case Int52RepUse:
+            valueToStore = lowStrictInt52(valueEdge);
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        LValue vector = caged(Gigacage::Primitive, m_out.loadPtr(dataView, m_heaps.JSArrayBufferView_vector));
+        TypedPointer pointer(m_heaps.typedArrayProperties, m_out.add(vector, m_out.zeroExtPtr(index)));
+
+        if (data.isFloatingPoint) {
+            if (data.byteSize == 4) {
+                valueToStore = m_out.doubleToFloat(valueToStore);
+
+                auto emitLittleEndianCode = [&] () -> LValue {
+                    m_out.storeFloat(valueToStore, pointer);
+                    return nullptr;
+                };
+
+                auto emitBigEndianCode = [&] () -> LValue {
+                    PatchpointValue* patchpoint = m_out.patchpoint(Int32);
+                    patchpoint->appendSomeRegister(valueToStore);
+                    patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                        jit.moveFloatTo32(params[1].fpr(), params[0].gpr());
+                        jit.byteSwap32(params[0].gpr());
+                    });
+                    patchpoint->effects = Effects::none();
+                    m_out.store32(patchpoint, pointer);
+                    return nullptr;
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else
+                    emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianCode, emitBigEndianCode);
+
+            } else {
+                RELEASE_ASSERT(data.byteSize == 8);
+                auto emitLittleEndianCode = [&] () -> LValue {
+                    m_out.storeDouble(valueToStore, pointer);
+                    return nullptr;
+                };
+                auto emitBigEndianCode = [&] () -> LValue {
+                    m_out.store64(byteSwap64(m_out.bitCast(valueToStore, Int64)), pointer);
+                    return nullptr;
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else
+                    emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianCode, emitBigEndianCode);
+            }
+        } else {
+            switch (data.byteSize) {
+            case 1:
+                RELEASE_ASSERT(valueEdge.useKind() == Int32Use);
+                m_out.store32As8(valueToStore, pointer);
+                break;
+            case 2: {
+                RELEASE_ASSERT(valueEdge.useKind() == Int32Use);
+
+                auto emitLittleEndianCode = [&] () -> LValue {
+                    m_out.store32As16(valueToStore, pointer);
+                    return nullptr;
+                };
+                auto emitBigEndianCode = [&] () -> LValue {
+                    PatchpointValue* patchpoint = m_out.patchpoint(Int32);
+                    patchpoint->appendSomeRegister(valueToStore);
+                    patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                        jit.move(params[1].gpr(), params[0].gpr());
+                        jit.byteSwap16(params[0].gpr());
+                    });
+                    patchpoint->effects = Effects::none();
+
+                    m_out.store32As16(patchpoint, pointer);
+                    return nullptr;
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else
+                    emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianCode, emitBigEndianCode);
+                break;
+            }
+            case 4: {
+                RELEASE_ASSERT(valueEdge.useKind() == Int32Use || valueEdge.useKind() == Int52RepUse);
+
+                if (valueEdge.useKind() == Int52RepUse)
+                    valueToStore = m_out.castToInt32(valueToStore);
+
+                auto emitLittleEndianCode = [&] () -> LValue {
+                    m_out.store32(valueToStore, pointer);
+                    return nullptr;
+                };
+                auto emitBigEndianCode = [&] () -> LValue {
+                    m_out.store32(byteSwap32(valueToStore), pointer);
+                    return nullptr;
+                };
+
+                if (data.isLittleEndian == FalseTriState)
+                    emitBigEndianCode();
+                else if (data.isLittleEndian == TrueTriState)
+                    emitLittleEndianCode();
+                else
+                    emitCodeBasedOnEndiannessBranch(isLittleEndian, emitLittleEndianCode, emitBigEndianCode);
+
+                break;
+            }
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        }
+    }
     
     void emitSwitchForMultiByOffset(LValue base, bool structuresChecked, Vector<SwitchCase, 2>& cases, LBasicBlock exit)
     {
@@ -14468,6 +14826,13 @@ private:
         speculateWeakSetObject(edge, result);
         return result;
     }
+
+    LValue lowDataViewObject(Edge edge)
+    {
+        LValue result = lowCell(edge);
+        speculateDataViewObject(edge, result);
+        return result;
+    }
     
     LValue lowString(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
     {
@@ -14943,6 +15308,9 @@ private:
         case WeakSetObjectUse:
             speculateWeakSetObject(edge);
             break;
+        case DataViewObjectUse:
+            speculateDataViewObject(edge);
+            break;
         case StringUse:
             speculateString(edge);
             break;
@@ -15437,6 +15805,17 @@ private:
     {
         speculateWeakSetObject(edge, lowCell(edge));
     }
+
+    void speculateDataViewObject(Edge edge, LValue cell)
+    {
+        FTL_TYPE_CHECK(
+            jsValueValue(cell), edge, SpecDataViewObject, isNotType(cell, DataViewType));
+    }
+
+    void speculateDataViewObject(Edge edge)
+    {
+        speculateDataViewObject(edge, lowCell(edge));
+    }
     
     void speculateString(Edge edge, LValue cell)
     {
index 801ed54..abe6f17 100644 (file)
@@ -239,6 +239,38 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "CPUCpuidIntrinsic";
     case CPUPauseIntrinsic:
         return "CPUPauseIntrinsic";
+    case DataViewGetInt8:
+        return "DataViewGetInt8";
+    case DataViewGetUint8:
+        return "DataViewGetUint8";
+    case DataViewGetInt16:
+        return "DataViewGetInt16";
+    case DataViewGetUint16:
+        return "DataViewGetUint16";
+    case DataViewGetInt32:
+        return "DataViewGetInt32";
+    case DataViewGetUint32:
+        return "DataViewGetUint32";
+    case DataViewGetFloat32:
+        return "DataViewGetFloat32";
+    case DataViewGetFloat64:
+        return "DataViewGetFloat64";
+    case DataViewSetInt8:
+        return "DataViewSetInt8";
+    case DataViewSetUint8:
+        return "DataViewSetUint8";
+    case DataViewSetInt16:
+        return "DataViewSetInt16";
+    case DataViewSetUint16:
+        return "DataViewSetUint16";
+    case DataViewSetInt32:
+        return "DataViewSetInt32";
+    case DataViewSetUint32:
+        return "DataViewSetUint32";
+    case DataViewSetFloat32:
+        return "DataViewSetFloat32";
+    case DataViewSetFloat64:
+        return "DataViewSetFloat64";
     }
     RELEASE_ASSERT_NOT_REACHED();
     return nullptr;
index 757df05..cfbd184 100644 (file)
@@ -139,6 +139,23 @@ enum Intrinsic {
     CPURdtscIntrinsic,
     CPUCpuidIntrinsic,
     CPUPauseIntrinsic,
+
+    DataViewGetInt8,
+    DataViewGetUint8,
+    DataViewGetInt16,
+    DataViewGetUint16,
+    DataViewGetInt32,
+    DataViewGetUint32,
+    DataViewGetFloat32,
+    DataViewGetFloat64,
+    DataViewSetInt8,
+    DataViewSetUint8,
+    DataViewSetInt16,
+    DataViewSetUint16,
+    DataViewSetInt32,
+    DataViewSetUint32,
+    DataViewSetFloat32,
+    DataViewSetFloat64,
 };
 
 const char* intrinsicName(Intrinsic);
index a062944..f05cff5 100644 (file)
@@ -39,22 +39,22 @@ namespace JSC {
 
 /* Source for JSDataViewPrototype.lut.h
 @begin dataViewTable
-  getInt8               dataViewProtoFuncGetInt8             DontEnum|Function       1
-  getUint8              dataViewProtoFuncGetUint8            DontEnum|Function       1
-  getInt16              dataViewProtoFuncGetInt16            DontEnum|Function       1
-  getUint16             dataViewProtoFuncGetUint16           DontEnum|Function       1
-  getInt32              dataViewProtoFuncGetInt32            DontEnum|Function       1
-  getUint32             dataViewProtoFuncGetUint32           DontEnum|Function       1
-  getFloat32            dataViewProtoFuncGetFloat32          DontEnum|Function       1
-  getFloat64            dataViewProtoFuncGetFloat64          DontEnum|Function       1
-  setInt8               dataViewProtoFuncSetInt8             DontEnum|Function       2
-  setUint8              dataViewProtoFuncSetUint8            DontEnum|Function       2
-  setInt16              dataViewProtoFuncSetInt16            DontEnum|Function       2
-  setUint16             dataViewProtoFuncSetUint16           DontEnum|Function       2
-  setInt32              dataViewProtoFuncSetInt32            DontEnum|Function       2
-  setUint32             dataViewProtoFuncSetUint32           DontEnum|Function       2
-  setFloat32            dataViewProtoFuncSetFloat32          DontEnum|Function       2
-  setFloat64            dataViewProtoFuncSetFloat64          DontEnum|Function       2
+  getInt8               dataViewProtoFuncGetInt8             DontEnum|Function       1  DataViewGetInt8
+  getUint8              dataViewProtoFuncGetUint8            DontEnum|Function       1  DataViewGetUint8
+  getInt16              dataViewProtoFuncGetInt16            DontEnum|Function       1  DataViewGetInt16
+  getUint16             dataViewProtoFuncGetUint16           DontEnum|Function       1  DataViewGetUint16
+  getInt32              dataViewProtoFuncGetInt32            DontEnum|Function       1  DataViewGetInt32
+  getUint32             dataViewProtoFuncGetUint32           DontEnum|Function       1  DataViewGetUint32
+  getFloat32            dataViewProtoFuncGetFloat32          DontEnum|Function       1  DataViewGetFloat32
+  getFloat64            dataViewProtoFuncGetFloat64          DontEnum|Function       1  DataViewGetFloat64
+  setInt8               dataViewProtoFuncSetInt8             DontEnum|Function       2  DataViewSetInt8
+  setUint8              dataViewProtoFuncSetUint8            DontEnum|Function       2  DataViewSetUint8
+  setInt16              dataViewProtoFuncSetInt16            DontEnum|Function       2  DataViewSetInt16
+  setUint16             dataViewProtoFuncSetUint16           DontEnum|Function       2  DataViewSetUint16
+  setInt32              dataViewProtoFuncSetInt32            DontEnum|Function       2  DataViewSetInt32
+  setUint32             dataViewProtoFuncSetUint32           DontEnum|Function       2  DataViewSetUint32
+  setFloat32            dataViewProtoFuncSetFloat32          DontEnum|Function       2  DataViewSetFloat32
+  setFloat64            dataViewProtoFuncSetFloat64          DontEnum|Function       2  DataViewSetFloat64
   buffer                dataViewProtoGetterBuffer            DontEnum|Accessor       0
   byteLength            dataViewProtoGetterByteLength        DontEnum|Accessor       0
   byteOffset            dataViewProtoGetterByteOffset        DontEnum|Accessor       0
index eac1685..d5ead37 100644 (file)
@@ -1,3 +1,13 @@
+2018-08-20  Saam barati  <sbarati@apple.com>
+
+        Inline DataView accesses into DFG/FTL
+        https://bugs.webkit.org/show_bug.cgi?id=188573
+        <rdar://problem/43286746>
+
+        Reviewed by Michael Saboff.
+
+        * wtf/TriState.h:
+
 2018-08-19  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
 
         [WTF] Add WTF::unalignedLoad and WTF::unalignedStore
index 93a496f..f853349 100644 (file)
@@ -28,7 +28,7 @@
 
 namespace WTF {
 
-enum TriState {
+enum TriState : int8_t {
     FalseTriState,
     TrueTriState,
     MixedTriState