[JSC] Introduce op_get_by_id_direct
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Apr 2018 16:21:38 +0000 (16:21 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Apr 2018 16:21:38 +0000 (16:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=183970

Reviewed by Filip Pizlo.

JSTests:

* stress/generator-prototype-copy.js: Added.
(gen):
(catch):
Adopted JF's tests.

* stress/generator-type-check.js: Added.
(shouldThrow):
(foo2):
(i.shouldThrow):
* stress/get-by-id-direct-getter.js: Added.
(shouldBe):
(shouldThrow):
(obj.get hello):
(builtin.createBuiltin):
(obj2.get length):
* stress/get-by-id-direct.js: Added.
(shouldBe):
(shouldThrow):
(builtin.createBuiltin):
* test262.yaml:
We fixed long-standing spec compatibility issue.
As a result, this patch makes several test262 tests passed!

Source/JavaScriptCore:

This patch introduces op_get_by_id_direct bytecode. This is super similar to op_get_by_id.
But it just performs [[GetOwnProperty]] operation instead of [[Get]]. We support this
in all the tiers, so using this opcode does not lead to inefficiency.

Main purpose of this op_get_by_id_direct is using it for private properties. We are using
properties indexed with private symbols to implement ECMAScript internal fields. Before this
patch, we just use get and put operations. However, it is not the correct semantics: accessing
to the internal fields should not traverse prototype chain, which is specified in the spec.
We use op_get_by_id_direct to access to properties which are used internal fields, so that
prototype chains are not traversed.

To emit op_get_by_id_direct, we introduce a new bytecode intrinsic @getByIdDirectPrivate().
When you write `@getByIdDirectPrivate(object, "name")`, the bytecode generator emits the
bytecode `op_get_by_id_direct, object, @name`.

* builtins/ArrayIteratorPrototype.js:
(next):
(globalPrivate.arrayIteratorValueNext):
(globalPrivate.arrayIteratorKeyNext):
(globalPrivate.arrayIteratorKeyValueNext):
* builtins/AsyncFromSyncIteratorPrototype.js:
* builtins/AsyncFunctionPrototype.js:
(globalPrivate.asyncFunctionResume):
* builtins/AsyncGeneratorPrototype.js:
(globalPrivate.asyncGeneratorQueueIsEmpty):
(globalPrivate.asyncGeneratorQueueEnqueue):
(globalPrivate.asyncGeneratorQueueDequeue):
(globalPrivate.asyncGeneratorDequeue):
(globalPrivate.isExecutionState):
(globalPrivate.isSuspendYieldState):
(globalPrivate.asyncGeneratorReject):
(globalPrivate.asyncGeneratorResolve):
(globalPrivate.doAsyncGeneratorBodyCall):
(globalPrivate.asyncGeneratorEnqueue):
* builtins/GeneratorPrototype.js:
(globalPrivate.generatorResume):
(next):
(return):
(throw):
* builtins/MapIteratorPrototype.js:
(next):
* builtins/PromiseOperations.js:
(globalPrivate.isPromise):
(globalPrivate.rejectPromise):
(globalPrivate.fulfillPromise):
* builtins/PromisePrototype.js:
(then):
* builtins/SetIteratorPrototype.js:
(next):
* builtins/StringIteratorPrototype.js:
(next):
* builtins/TypedArrayConstructor.js:
(of):
(from):
* bytecode/BytecodeDumper.cpp:
(JSC::BytecodeDumper<Block>::dumpBytecode):
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::finalizeLLIntInlineCaches):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFromLLInt):
(JSC::GetByIdStatus::computeFor):
* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::reset):
* bytecode/StructureStubInfo.h:
(JSC::appropriateOptimizingGetByIdFunction):
(JSC::appropriateGenericGetByIdFunction):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitDirectGetById):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_getByIdDirect):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_getByIdDirectPrivate):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToGetByOffset):
(JSC::DFG::Node::convertToMultiGetByOffset):
(JSC::DFG::Node::hasIdentifier):
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetById):
(JSC::DFG::SpeculativeJIT::compileGetByIdFlush):
(JSC::DFG::SpeculativeJIT::compileTryGetById): Deleted.
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileGetById):
(JSC::FTL::DFG::LowerDFGToB3::compileGetByIdWithThis):
(JSC::FTL::DFG::LowerDFGToB3::getById):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_get_by_id_direct):
(JSC::JIT::emitSlow_op_get_by_id_direct):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_get_by_id_direct):
(JSC::JIT::emitSlow_op_get_by_id_direct):
* jit/Repatch.cpp:
(JSC::appropriateOptimizingGetByIdFunction):
(JSC::appropriateGetByIdFunction):
(JSC::tryCacheGetByID):
(JSC::repatchGetByID):
(JSC::appropriateGenericGetByIdFunction): Deleted.
* jit/Repatch.h:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/JSCJSValue.h:
* runtime/JSCJSValueInlines.h:
(JSC::JSValue::getOwnPropertySlot const):
* runtime/JSObject.h:
* runtime/JSObjectInlines.h:
(JSC::JSObject::getOwnPropertySlotInline):

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

64 files changed:
JSTests/ChangeLog
JSTests/stress/generator-prototype-copy.js [new file with mode: 0644]
JSTests/stress/generator-type-check.js [new file with mode: 0644]
JSTests/stress/get-by-id-direct-getter.js [new file with mode: 0644]
JSTests/stress/get-by-id-direct.js [new file with mode: 0644]
JSTests/test262.yaml
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js
Source/JavaScriptCore/builtins/AsyncFromSyncIteratorPrototype.js
Source/JavaScriptCore/builtins/AsyncFunctionPrototype.js
Source/JavaScriptCore/builtins/AsyncGeneratorPrototype.js
Source/JavaScriptCore/builtins/GeneratorPrototype.js
Source/JavaScriptCore/builtins/MapIteratorPrototype.js
Source/JavaScriptCore/builtins/PromiseOperations.js
Source/JavaScriptCore/builtins/PromisePrototype.js
Source/JavaScriptCore/builtins/SetIteratorPrototype.js
Source/JavaScriptCore/builtins/StringIteratorPrototype.js
Source/JavaScriptCore/builtins/TypedArrayConstructor.js
Source/JavaScriptCore/bytecode/BytecodeDumper.cpp
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.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/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/jit/Repatch.h
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/JSCJSValueInlines.h
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSObjectInlines.h

index 84fbe23..0ef5fb0 100644 (file)
@@ -1,3 +1,34 @@
+2018-04-08  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Introduce op_get_by_id_direct
+        https://bugs.webkit.org/show_bug.cgi?id=183970
+
+        Reviewed by Filip Pizlo.
+
+        * stress/generator-prototype-copy.js: Added.
+        (gen):
+        (catch):
+        Adopted JF's tests.
+
+        * stress/generator-type-check.js: Added.
+        (shouldThrow):
+        (foo2):
+        (i.shouldThrow):
+        * stress/get-by-id-direct-getter.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (obj.get hello):
+        (builtin.createBuiltin):
+        (obj2.get length):
+        * stress/get-by-id-direct.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (builtin.createBuiltin):
+        * test262.yaml:
+        We fixed long-standing spec compatibility issue.
+        As a result, this patch makes several test262 tests passed!
+
+
 2018-04-07  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Unreviewed, annotate test with @skip if $memoryLimited
diff --git a/JSTests/stress/generator-prototype-copy.js b/JSTests/stress/generator-prototype-copy.js
new file mode 100644 (file)
index 0000000..977308f
--- /dev/null
@@ -0,0 +1,12 @@
+function* gen() { yield; }
+let foo = gen();
+let obj = {};
+obj.__proto__ = foo;
+
+try {
+    obj.next().value;
+    throw "bad";
+} catch (e) {
+    if (!(e instanceof TypeError))
+        throw e;
+}
diff --git a/JSTests/stress/generator-type-check.js b/JSTests/stress/generator-type-check.js
new file mode 100644 (file)
index 0000000..37432c7
--- /dev/null
@@ -0,0 +1,54 @@
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+var iterator;
+
+var a = [];
+
+function* foo(index) {
+    while (1) {
+        var q = a.pop();
+        if(q){
+            q.__proto__ = iterator;
+            q.next();
+        }
+        yield index++;
+    }
+}
+
+function* foo2(){
+    yield;
+}
+
+var temp = foo2(0);
+
+for(var i = 0; i < 10; i++) { // make a few objects with @generatorState set
+    var q = {};
+    q.__proto__ = temp;
+    shouldThrow(() => {
+        q.next();
+    }, `TypeError: |this| should be a generator`);
+    q.__proto__ = {};
+    a.push(q);
+
+}
+
+iterator = foo(0);
+
+var q = {};
+q.__proto__ = iterator;
+shouldThrow(() => {
+    q.next();
+}, `TypeError: |this| should be a generator`);
diff --git a/JSTests/stress/get-by-id-direct-getter.js b/JSTests/stress/get-by-id-direct-getter.js
new file mode 100644 (file)
index 0000000..b11a570
--- /dev/null
@@ -0,0 +1,112 @@
+var createBuiltin = $vm.createBuiltin;
+
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+(function () {
+    var builtin = createBuiltin(`(function (obj) {
+        return @getByIdDirect(obj, "hello");
+    })`);
+    noInline(builtin);
+
+    var obj = { get hello() { return 42; }, world:33 };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj), 42);
+
+    var obj2 = { hello: 22 };
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(obj), 42);
+        shouldBe(builtin(obj2), 22);
+    }
+
+    var obj3 = { };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj3), undefined);
+
+    var obj4 = {
+        __proto__: { hello: 33 }
+    };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj4), undefined);
+
+    var target5 = "Hello";
+    var target6 = 42;
+    var target7 = false;
+    var target8 = Symbol("Cocoa");
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(target5), undefined);
+        shouldBe(builtin(target6), undefined);
+        shouldBe(builtin(target7), undefined);
+        shouldBe(builtin(target8), undefined);
+    }
+
+    shouldThrow(() => {
+        builtin(null);
+    }, `TypeError: null is not an object`);
+
+    shouldThrow(() => {
+        builtin(undefined);
+    }, `TypeError: undefined is not an object`);
+
+    shouldBe(builtin(obj), 42);
+    shouldBe(builtin(obj2), 22);
+    shouldBe(builtin(obj3), undefined);
+    shouldBe(builtin(obj4), undefined);
+    shouldBe(builtin(target5), undefined);
+    shouldBe(builtin(target6), undefined);
+    shouldBe(builtin(target7), undefined);
+    shouldBe(builtin(target8), undefined);
+}());
+
+(function () {
+    var builtin = createBuiltin(`(function (obj) {
+        return @getByIdDirect(obj, "hello");
+    })`);
+    noInline(builtin);
+
+    var obj = { };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj), undefined);
+    shouldBe(builtin(obj), undefined);
+    obj.hello = 42;
+    shouldBe(builtin(obj), 42);
+}());
+
+
+(function () {
+    var builtin = createBuiltin(`(function (obj) {
+        return @getByIdDirect(obj, "length");
+    })`);
+    noInline(builtin);
+
+    var array = [0, 1, 2];
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(array), 3);
+    shouldBe(builtin({}), undefined);
+
+    var obj = { length:2 };
+    var obj2 = { get length() { return 2; } };
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(array), 3);
+        shouldBe(builtin(obj), 2);
+        shouldBe(builtin(obj2), 2);
+        shouldBe(builtin("Cocoa"), 5);
+    }
+}());
diff --git a/JSTests/stress/get-by-id-direct.js b/JSTests/stress/get-by-id-direct.js
new file mode 100644 (file)
index 0000000..e0e4864
--- /dev/null
@@ -0,0 +1,110 @@
+var createBuiltin = $vm.createBuiltin;
+
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+(function () {
+    var builtin = createBuiltin(`(function (obj) {
+        return @getByIdDirect(obj, "hello");
+    })`);
+    noInline(builtin);
+
+    var obj = { hello: 42, world:33 };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj), 42);
+
+    var obj2 = { hello: 22 };
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(obj), 42);
+        shouldBe(builtin(obj2), 22);
+    }
+
+    var obj3 = { };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj3), undefined);
+
+    var obj4 = {
+        __proto__: { hello: 33 }
+    };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj4), undefined);
+
+    var target5 = "Hello";
+    var target6 = 42;
+    var target7 = false;
+    var target8 = Symbol("Cocoa");
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(target5), undefined);
+        shouldBe(builtin(target6), undefined);
+        shouldBe(builtin(target7), undefined);
+        shouldBe(builtin(target8), undefined);
+    }
+
+    shouldThrow(() => {
+        builtin(null);
+    }, `TypeError: null is not an object`);
+
+    shouldThrow(() => {
+        builtin(undefined);
+    }, `TypeError: undefined is not an object`);
+
+    shouldBe(builtin(obj), 42);
+    shouldBe(builtin(obj2), 22);
+    shouldBe(builtin(obj3), undefined);
+    shouldBe(builtin(obj4), undefined);
+    shouldBe(builtin(target5), undefined);
+    shouldBe(builtin(target6), undefined);
+    shouldBe(builtin(target7), undefined);
+    shouldBe(builtin(target8), undefined);
+}());
+
+(function () {
+    var builtin = createBuiltin(`(function (obj) {
+        return @getByIdDirect(obj, "hello");
+    })`);
+    noInline(builtin);
+
+    var obj = { };
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(obj), undefined);
+    shouldBe(builtin(obj), undefined);
+    obj.hello = 42;
+    shouldBe(builtin(obj), 42);
+}());
+
+
+(function () {
+    var builtin = createBuiltin(`(function (obj) {
+        return @getByIdDirect(obj, "length");
+    })`);
+    noInline(builtin);
+
+    var array = [0, 1, 2];
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(builtin(array), 3);
+    shouldBe(builtin({}), undefined);
+
+    var obj = { length:2 };
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(builtin(array), 3);
+        shouldBe(builtin(obj), 2);
+        shouldBe(builtin("Cocoa"), 5);
+    }
+}());
index 763633f..4b04946 100644 (file)
 - path: test262/test/built-ins/ArrayIteratorPrototype/next/name.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js", "../../../../harness/propertyHelper.js"], [:strict]
 - path: test262/test/built-ins/ArrayIteratorPrototype/next/non-own-slots.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/built-ins/ArrayIteratorPrototype/next/non-own-slots.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/built-ins/ArrayIteratorPrototype/next/property-descriptor.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js", "../../../../harness/propertyHelper.js"], []
 - path: test262/test/built-ins/ArrayIteratorPrototype/next/property-descriptor.js
 - path: test262/test/built-ins/StringIteratorPrototype/next/next-iteration.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/built-ins/StringIteratorPrototype/next/next-missing-internal-slots.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/built-ins/StringIteratorPrototype/next/next-missing-internal-slots.js
-  cmd: runTest262 :fail, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/built-ins/Symbol/auto-boxing-non-strict.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], []
 - path: test262/test/built-ins/Symbol/auto-boxing-strict.js
index cb40fcb..38f2554 100644 (file)
@@ -1,3 +1,163 @@
+2018-04-08  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Introduce op_get_by_id_direct
+        https://bugs.webkit.org/show_bug.cgi?id=183970
+
+        Reviewed by Filip Pizlo.
+
+        This patch introduces op_get_by_id_direct bytecode. This is super similar to op_get_by_id.
+        But it just performs [[GetOwnProperty]] operation instead of [[Get]]. We support this
+        in all the tiers, so using this opcode does not lead to inefficiency.
+
+        Main purpose of this op_get_by_id_direct is using it for private properties. We are using
+        properties indexed with private symbols to implement ECMAScript internal fields. Before this
+        patch, we just use get and put operations. However, it is not the correct semantics: accessing
+        to the internal fields should not traverse prototype chain, which is specified in the spec.
+        We use op_get_by_id_direct to access to properties which are used internal fields, so that
+        prototype chains are not traversed.
+
+        To emit op_get_by_id_direct, we introduce a new bytecode intrinsic @getByIdDirectPrivate().
+        When you write `@getByIdDirectPrivate(object, "name")`, the bytecode generator emits the
+        bytecode `op_get_by_id_direct, object, @name`.
+
+        * builtins/ArrayIteratorPrototype.js:
+        (next):
+        (globalPrivate.arrayIteratorValueNext):
+        (globalPrivate.arrayIteratorKeyNext):
+        (globalPrivate.arrayIteratorKeyValueNext):
+        * builtins/AsyncFromSyncIteratorPrototype.js:
+        * builtins/AsyncFunctionPrototype.js:
+        (globalPrivate.asyncFunctionResume):
+        * builtins/AsyncGeneratorPrototype.js:
+        (globalPrivate.asyncGeneratorQueueIsEmpty):
+        (globalPrivate.asyncGeneratorQueueEnqueue):
+        (globalPrivate.asyncGeneratorQueueDequeue):
+        (globalPrivate.asyncGeneratorDequeue):
+        (globalPrivate.isExecutionState):
+        (globalPrivate.isSuspendYieldState):
+        (globalPrivate.asyncGeneratorReject):
+        (globalPrivate.asyncGeneratorResolve):
+        (globalPrivate.doAsyncGeneratorBodyCall):
+        (globalPrivate.asyncGeneratorEnqueue):
+        * builtins/GeneratorPrototype.js:
+        (globalPrivate.generatorResume):
+        (next):
+        (return):
+        (throw):
+        * builtins/MapIteratorPrototype.js:
+        (next):
+        * builtins/PromiseOperations.js:
+        (globalPrivate.isPromise):
+        (globalPrivate.rejectPromise):
+        (globalPrivate.fulfillPromise):
+        * builtins/PromisePrototype.js:
+        (then):
+        * builtins/SetIteratorPrototype.js:
+        (next):
+        * builtins/StringIteratorPrototype.js:
+        (next):
+        * builtins/TypedArrayConstructor.js:
+        (of):
+        (from):
+        * bytecode/BytecodeDumper.cpp:
+        (JSC::BytecodeDumper<Block>::dumpBytecode):
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::finishCreation):
+        (JSC::CodeBlock::finalizeLLIntInlineCaches):
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeFromLLInt):
+        (JSC::GetByIdStatus::computeFor):
+        * bytecode/StructureStubInfo.cpp:
+        (JSC::StructureStubInfo::reset):
+        * bytecode/StructureStubInfo.h:
+        (JSC::appropriateOptimizingGetByIdFunction):
+        (JSC::appropriateGenericGetByIdFunction):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitDirectGetById):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_getByIdDirect):
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_getByIdDirectPrivate):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleGetById):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToGetByOffset):
+        (JSC::DFG::Node::convertToMultiGetByOffset):
+        (JSC::DFG::Node::hasIdentifier):
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetById):
+        (JSC::DFG::SpeculativeJIT::compileGetByIdFlush):
+        (JSC::DFG::SpeculativeJIT::compileTryGetById): Deleted.
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetById):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetByIdWithThis):
+        (JSC::FTL::DFG::LowerDFGToB3::getById):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JIT.h:
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_get_by_id_direct):
+        (JSC::JIT::emitSlow_op_get_by_id_direct):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_get_by_id_direct):
+        (JSC::JIT::emitSlow_op_get_by_id_direct):
+        * jit/Repatch.cpp:
+        (JSC::appropriateOptimizingGetByIdFunction):
+        (JSC::appropriateGetByIdFunction):
+        (JSC::tryCacheGetByID):
+        (JSC::repatchGetByID):
+        (JSC::appropriateGenericGetByIdFunction): Deleted.
+        * jit/Repatch.h:
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LLIntSlowPaths.h:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/JSCJSValue.h:
+        * runtime/JSCJSValueInlines.h:
+        (JSC::JSValue::getOwnPropertySlot const):
+        * runtime/JSObject.h:
+        * runtime/JSObjectInlines.h:
+        (JSC::JSObject::getOwnPropertySlotInline):
+
 2018-04-07  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Remove several asXXX functions
index 92640f7..6dc1e5a 100644 (file)
@@ -31,7 +31,7 @@ function next()
     if (this == null)
         @throwTypeError("%ArrayIteratorPrototype%.next requires that |this| not be null or undefined");
 
-    let next = this.@arrayIteratorNext;
+    let next = @getByIdDirectPrivate(this, "arrayIteratorNext");
     if (next === @undefined)
         @throwTypeError("%ArrayIteratorPrototype%.next requires that |this| be an Array Iterator instance");
 
@@ -45,9 +45,9 @@ function arrayIteratorValueNext()
     var done = true;
     var value;
 
-    var array = this.@iteratedObject;
-    if (!this.@arrayIteratorIsDone) {
-        var index = this.@arrayIteratorNextIndex;
+    var array = @getByIdDirectPrivate(this, "iteratedObject");
+    if (!@getByIdDirectPrivate(this, "arrayIteratorIsDone")) {
+        var index = @getByIdDirectPrivate(this, "arrayIteratorNextIndex");
         var length = array.length >>> 0;
         if (index >= length) {
             this.@arrayIteratorIsDone = true;
@@ -68,9 +68,9 @@ function arrayIteratorKeyNext()
     var done = true;
     var value;
 
-    var array = this.@iteratedObject;
-    if (!this.@arrayIteratorIsDone) {
-        var index = this.@arrayIteratorNextIndex;
+    var array = @getByIdDirectPrivate(this, "iteratedObject");
+    if (!@getByIdDirectPrivate(this, "arrayIteratorIsDone")) {
+        var index = @getByIdDirectPrivate(this, "arrayIteratorNextIndex");
         var length = array.length >>> 0;
         if (index >= length) {
             this.@arrayIteratorIsDone = true;
@@ -91,9 +91,9 @@ function arrayIteratorKeyValueNext()
     var done = true;
     var value;
 
-    var array = this.@iteratedObject;
-    if (!this.@arrayIteratorIsDone) {
-        var index = this.@arrayIteratorNextIndex;
+    var array = @getByIdDirectPrivate(this, "iteratedObject");
+    if (!@getByIdDirectPrivate(this, "arrayIteratorIsDone")) {
+        var index = @getByIdDirectPrivate(this, "arrayIteratorNextIndex");
         var length = array.length >>> 0;
         if (index >= length) {
             this.@arrayIteratorIsDone = true;
index 7fc1186..0421f55 100644 (file)
@@ -29,15 +29,15 @@ function next(value)
 
     const promiseCapability = @newPromiseCapability(@Promise);
 
-    if (!@isObject(this) || !@isObject(this.@syncIterator)) {
+    if (!@isObject(this) || !@isObject(@getByIdDirectPrivate(this, "syncIterator"))) {
         promiseCapability.@reject.@call(@undefined, new @TypeError('Iterator is not an object.'));
         return promiseCapability.@promise;
     }
 
-    const syncIterator = this.@syncIterator;
+    const syncIterator = @getByIdDirectPrivate(this, "syncIterator");
 
     try {
-        const { done: nextDone, value: nextValue } = this.@nextMethod.@call(syncIterator, value);
+        const { done: nextDone, value: nextValue } = @getByIdDirectPrivate(this, "nextMethod").@call(syncIterator, value);
         const valueWrapperCapability = @newPromiseCapability(@Promise);
         valueWrapperCapability.@resolve.@call(@undefined, nextValue);
         valueWrapperCapability.@promise.@then(
@@ -56,12 +56,12 @@ function return(value)
 
     const promiseCapability = @newPromiseCapability(@Promise);
 
-    if (!@isObject(this) || !@isObject(this.@syncIterator)) {
+    if (!@isObject(this) || !@isObject(@getByIdDirectPrivate(this, "syncIterator"))) {
         promiseCapability.@reject.@call(@undefined, new @TypeError('Iterator is not an object.'));
         return promiseCapability.@promise;
     }
 
-    const syncIterator = this.@syncIterator;
+    const syncIterator = @getByIdDirectPrivate(this, "syncIterator");
 
     let returnMethod;
 
@@ -105,12 +105,12 @@ function throw(exception)
 
     const promiseCapability = @newPromiseCapability(@Promise);
 
-    if (!@isObject(this) || !@isObject(this.@syncIterator)) {
+    if (!@isObject(this) || !@isObject(@getByIdDirectPrivate(this, "syncIterator"))) {
         promiseCapability.@reject.@call(@undefined, new @TypeError('Iterator is not an object.'));
         return promiseCapability.@promise;
     }
 
-    const syncIterator = this.@syncIterator;
+    const syncIterator = @getByIdDirectPrivate(this, "syncIterator");
 
     let throwMethod;
 
index 88cfb01..a9a7150 100644 (file)
@@ -27,7 +27,7 @@
 function asyncFunctionResume(generator, promiseCapability, sentValue, resumeMode)
 {
     "use strict";
-    let state = generator.@generatorState;
+    let state = @getByIdDirectPrivate(generator, "generatorState");
     let value = @undefined;
 
     if (state === @GeneratorStateCompleted || (resumeMode !== @GeneratorResumeModeNormal && resumeMode !== @GeneratorResumeModeThrow))
@@ -35,8 +35,8 @@ function asyncFunctionResume(generator, promiseCapability, sentValue, resumeMode
 
     try {
         generator.@generatorState = @GeneratorStateExecuting;
-        value = generator.@generatorNext.@call(generator.@generatorThis, generator, state, sentValue, resumeMode, generator.@generatorFrame);
-        if (generator.@generatorState === @GeneratorStateExecuting) {
+        value = @getByIdDirectPrivate(generator, "generatorNext").@call(@getByIdDirectPrivate(generator, "generatorThis"), generator, state, sentValue, resumeMode, @getByIdDirectPrivate(generator, "generatorFrame"));
+        if (@getByIdDirectPrivate(generator, "generatorState") === @GeneratorStateExecuting) {
             generator.@generatorState = @GeneratorStateCompleted;
             promiseCapability.@resolve(value);
             return promiseCapability.@promise;
index b4b2f8f..f8be92d 100644 (file)
@@ -28,7 +28,7 @@ function asyncGeneratorQueueIsEmpty(generator)
 {
     "use strict";
 
-    return generator.@asyncGeneratorQueueLast === null;
+    return @getByIdDirectPrivate(generator, "asyncGeneratorQueueLast") === null;
 }
 
 @globalPrivate
@@ -36,16 +36,17 @@ function asyncGeneratorQueueEnqueue(generator, item)
 {
     "use strict";
 
-    @assert(item.@asyncGeneratorQueueItemNext === null && item.@asyncGeneratorQueueItemPrevious === null);
+    @assert(@getByIdDirectPrivate(item, "asyncGeneratorQueueItemNext") === null && @getByIdDirectPrivate(item, "asyncGeneratorQueueItemPrevious") === null);
 
-    if (generator.@asyncGeneratorQueueFirst === null) {
-        @assert(generator.@asyncGeneratorQueueLast === null);
+    if (@getByIdDirectPrivate(generator, "asyncGeneratorQueueFirst") === null) {
+        @assert(@getByIdDirectPrivate(generator, "asyncGeneratorQueueLast") === null);
 
         generator.@asyncGeneratorQueueFirst = item;
         generator.@asyncGeneratorQueueLast = item;
     } else {
-        item.@asyncGeneratorQueueItemPrevious = generator.@asyncGeneratorQueueLast;
-        generator.@asyncGeneratorQueueLast.@asyncGeneratorQueueItemNext = item;
+        var last = @getByIdDirectPrivate(generator, "asyncGeneratorQueueLast");
+        item.@asyncGeneratorQueueItemPrevious = last;
+        last.@asyncGeneratorQueueItemNext = item;
         generator.@asyncGeneratorQueueLast = item;
     }
 }
@@ -55,13 +56,14 @@ function asyncGeneratorQueueDequeue(generator)
 {
     "use strict";
 
-    if (generator.@asyncGeneratorQueueFirst === null)
+    const result = @getByIdDirectPrivate(generator, "asyncGeneratorQueueFirst");
+    if (result === null)
         return null;
 
-    const result = generator.@asyncGeneratorQueueFirst;
-    generator.@asyncGeneratorQueueFirst = result.@asyncGeneratorQueueItemNext;
+    var updatedFirst = @getByIdDirectPrivate(result, "asyncGeneratorQueueItemNext");
+    generator.@asyncGeneratorQueueFirst = updatedFirst;
 
-    if (generator.@asyncGeneratorQueueFirst === null)
+    if (updatedFirst === null)
         generator.@asyncGeneratorQueueLast = null;
 
     return result;
@@ -72,7 +74,7 @@ function asyncGeneratorDequeue(generator)
 {
     "use strict";
 
-    const queue = generator.@asyncGeneratorQueue;
+    const queue = @getByIdDirectPrivate(generator, "asyncGeneratorQueue");
 
     @assert(!@asyncGeneratorQueueIsEmpty(generator), "Async genetator's Queue is an empty List.");
     
@@ -84,9 +86,11 @@ function isExecutionState(generator)
 {
     "use strict";
 
-    return (generator.@generatorState > 0 && generator.@asyncGeneratorSuspendReason === @AsyncGeneratorSuspendReasonNone)
-        || generator.@generatorState === @AsyncGeneratorStateExecuting
-        || generator.@asyncGeneratorSuspendReason === @AsyncGeneratorSuspendReasonAwait;
+    var state = @getByIdDirectPrivate(generator, "generatorState");
+    var reason = @getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason");
+    return (state > 0 && reason === @AsyncGeneratorSuspendReasonNone)
+        || state === @AsyncGeneratorStateExecuting
+        || reason === @AsyncGeneratorSuspendReasonAwait;
 }
 
 @globalPrivate
@@ -94,8 +98,9 @@ function isSuspendYieldState(generator)
 {
     "use strict";
 
-    return (generator.@generatorState > 0 && generator.@asyncGeneratorSuspendReason === @AsyncGeneratorSuspendReasonYield)
-        || generator.@generatorState === @AsyncGeneratorStateSuspendedYield;
+    var state = @getByIdDirectPrivate(generator, "generatorState");
+    return (state > 0 && @getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason") === @AsyncGeneratorSuspendReasonYield)
+        || state === @AsyncGeneratorStateSuspendedYield;
 }
 
 @globalPrivate
@@ -103,7 +108,7 @@ function asyncGeneratorReject(generator, exception)
 {
     "use strict";
 
-    @assert(typeof generator.@asyncGeneratorSuspendReason === "number", "Generator is not an AsyncGenerator instance.");
+    @assert(typeof @getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason") === "number", "Generator is not an AsyncGenerator instance.");
 
     const { promiseCapability } = @asyncGeneratorDequeue(generator);
     promiseCapability.@reject.@call(@undefined, exception);
@@ -116,7 +121,7 @@ function asyncGeneratorResolve(generator, value, done)
 {
     "use strict";
 
-    @assert(typeof generator.@asyncGeneratorSuspendReason === "number", "Generator is not an AsyncGenerator instance.");
+    @assert(typeof @getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason") === "number", "Generator is not an AsyncGenerator instance.");
 
     const { promiseCapability } = @asyncGeneratorDequeue(generator);
     promiseCapability.@resolve.@call(@undefined, { done, value: value });
@@ -163,14 +168,14 @@ function doAsyncGeneratorBodyCall(generator, resumeValue, resumeMode)
     "use strict";
 
     let value = @undefined;
-    let state = generator.@generatorState;
+    let state = @getByIdDirectPrivate(generator, "generatorState");
 
     generator.@generatorState = @AsyncGeneratorStateExecuting;
     generator.@asyncGeneratorSuspendReason = @AsyncGeneratorSuspendReasonNone;
 
     try {
-        value = generator.@generatorNext.@call(generator.@generatorThis, generator, state, resumeValue, resumeMode, generator.@generatorFrame);
-        if (generator.@generatorState === @AsyncGeneratorStateExecuting)
+        value = @getByIdDirectPrivate(generator, "generatorNext").@call(@getByIdDirectPrivate(generator, "generatorThis"), generator, state, resumeValue, resumeMode, @getByIdDirectPrivate(generator, "generatorFrame"));
+        if (@getByIdDirectPrivate(generator, "generatorState") === @AsyncGeneratorStateExecuting)
             generator.@generatorState = @AsyncGeneratorStateCompleted;
     } catch (error) {
         generator.@generatorState = @AsyncGeneratorStateCompleted;
@@ -179,7 +184,7 @@ function doAsyncGeneratorBodyCall(generator, resumeValue, resumeMode)
         return @asyncGeneratorReject(generator, error);
     }
 
-    if (generator.@asyncGeneratorSuspendReason === @AsyncGeneratorSuspendReasonAwait) {
+    if (@getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason") === @AsyncGeneratorSuspendReasonAwait) {
         const onFulfilled = function(result) { @doAsyncGeneratorBodyCall(generator, result, @GeneratorResumeModeNormal); };
 
         @awaitValue(generator, value, onFulfilled);
@@ -187,10 +192,10 @@ function doAsyncGeneratorBodyCall(generator, resumeValue, resumeMode)
         return @undefined;
     }
 
-    if (generator.@asyncGeneratorSuspendReason === @AsyncGeneratorSuspendReasonYield)
+    if (@getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason") === @AsyncGeneratorSuspendReasonYield)
         return @asyncGeneratorYield(generator, value, resumeMode);
 
-    if (generator.@generatorState === @AsyncGeneratorStateCompleted) {
+    if (@getByIdDirectPrivate(generator, "generatorState") === @AsyncGeneratorStateCompleted) {
         generator.@asyncGeneratorSuspendReason = @AsyncGeneratorSuspendReasonNone;
         return @asyncGeneratorResolve(generator, value, true);
     }
@@ -203,9 +208,9 @@ function asyncGeneratorResumeNext(generator)
 {
     "use strict";
 
-    @assert(typeof generator.@asyncGeneratorSuspendReason === "number", "Generator is not an AsyncGenerator instance.");
+    @assert(typeof @getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason") === "number", "Generator is not an AsyncGenerator instance.");
 
-    let state = generator.@generatorState;
+    let state = @getByIdDirectPrivate(generator, "generatorState");
 
     @assert(state !== @AsyncGeneratorStateExecuting, "Async generator should not be in executing state");
 
@@ -215,7 +220,7 @@ function asyncGeneratorResumeNext(generator)
     if (@asyncGeneratorQueueIsEmpty(generator))
         return @undefined;
 
-    const next = generator.@asyncGeneratorQueueFirst;
+    const next = @getByIdDirectPrivate(generator, "asyncGeneratorQueueFirst");
 
     if (next.resumeMode !== @GeneratorResumeModeNormal) {
         if (state === @AsyncGeneratorStateSuspendedStart) {
@@ -259,7 +264,7 @@ function asyncGeneratorEnqueue(generator, value, resumeMode)
     "use strict";
     
     const promiseCapability = @newPromiseCapability(@Promise);
-    if (!@isObject(generator) || typeof generator.@asyncGeneratorSuspendReason !== 'number') {
+    if (!@isObject(generator) || typeof @getByIdDirectPrivate(generator, "asyncGeneratorSuspendReason") !== 'number') {
         promiseCapability.@reject.@call(@undefined, new @TypeError('|this| should be an async generator'));
         return promiseCapability.@promise;
     }
index 945b2e9..75d5562 100644 (file)
@@ -35,8 +35,8 @@ function generatorResume(generator, state, generatorThis, sentValue, value, resu
     if (!done) {
         try {
             generator.@generatorState = @GeneratorStateExecuting;
-            value = generator.@generatorNext.@call(generatorThis, generator, state, sentValue, resumeMode, generator.@generatorFrame);
-            if (generator.@generatorState === @GeneratorStateExecuting) {
+            value = @getByIdDirectPrivate(generator, "generatorNext").@call(generatorThis, generator, state, sentValue, resumeMode, @getByIdDirectPrivate(generator, "generatorFrame"));
+            if (@getByIdDirectPrivate(generator, "generatorState") === @GeneratorStateExecuting) {
                 generator.@generatorState = @GeneratorStateCompleted;
                 done = true;
             }
@@ -52,35 +52,35 @@ function next(value)
 {
     "use strict";
 
-    var state = this.@generatorState;
+    var state = @getByIdDirectPrivate(this, "generatorState");
     if (typeof state !== "number")
         @throwTypeError("|this| should be a generator");
 
     if (state === @GeneratorStateExecuting)
         @throwTypeError("Generator is executing");
 
-    return @generatorResume(this, state, this.@generatorThis, value, @undefined, @GeneratorResumeModeNormal);
+    return @generatorResume(this, state, @getByIdDirectPrivate(this, "generatorThis"), value, @undefined, @GeneratorResumeModeNormal);
 }
 
 function return(value)
 {
     "use strict";
 
-    var state = this.@generatorState;
+    var state = @getByIdDirectPrivate(this, "generatorState");
     if (typeof state !== "number")
         @throwTypeError("|this| should be a generator");
 
     if (state === @GeneratorStateExecuting)
         @throwTypeError("Generator is executing");
 
-    return @generatorResume(this, state, this.@generatorThis, value, value, @GeneratorResumeModeReturn);
+    return @generatorResume(this, state, @getByIdDirectPrivate(this, "generatorThis"), value, value, @GeneratorResumeModeReturn);
 }
 
 function throw(exception)
 {
     "use strict";
 
-    var state = this.@generatorState;
+    var state = @getByIdDirectPrivate(this, "generatorState");
     if (typeof state !== "number")
         @throwTypeError("|this| should be a generator");
 
@@ -90,5 +90,5 @@ function throw(exception)
     if (state === @GeneratorStateCompleted)
         throw exception;
 
-    return @generatorResume(this, state, this.@generatorThis, exception, @undefined, @GeneratorResumeModeThrow);
+    return @generatorResume(this, state, @getByIdDirectPrivate(this, "generatorThis"), exception, @undefined, @GeneratorResumeModeThrow);
 }
index e65ede7..3dbf935 100644 (file)
@@ -51,8 +51,8 @@ function next()
     if (this == null)
         @throwTypeError("%MapIteratorPrototype%.next requires that |this| not be null or undefined");
 
-    var bucket = this.@mapBucket;
+    var bucket = @getByIdDirectPrivate(this, "mapBucket");
     if (bucket === @undefined)
         @throwTypeError("%MapIteratorPrototype%.next requires that |this| be a Map Iterator instance");
-    return @mapIteratorNext.@call(this, bucket, this.@mapIteratorKind);
+    return @mapIteratorNext.@call(this, bucket, @getByIdDirectPrivate(this, "mapIteratorKind"));
 }
index a10a5d6..29469b2 100644 (file)
@@ -31,7 +31,7 @@ function isPromise(promise)
 {
     "use strict";
 
-    return @isObject(promise) && !!promise.@promiseState;
+    return @isObject(promise) && !!@getByIdDirectPrivate(promise, "promiseState");
 }
 
 @globalPrivate
@@ -106,14 +106,14 @@ function rejectPromise(promise, reason)
 {
     "use strict";
 
-    var reactions = promise.@promiseReactions;
+    var reactions = @getByIdDirectPrivate(promise, "promiseReactions");
     promise.@promiseResult = reason;
     promise.@promiseReactions = @undefined;
     promise.@promiseState = @promiseStateRejected;
 
     @InspectorInstrumentation.promiseRejected(promise, reason, reactions);
 
-    if (!promise.@promiseIsHandled)
+    if (!@getByIdDirectPrivate(promise, "promiseIsHandled"))
         @hostPromiseRejectionTracker(promise, @promiseRejectionReject);
 
     @triggerPromiseReactions(@promiseStateRejected, reactions, reason);
@@ -124,7 +124,7 @@ function fulfillPromise(promise, value)
 {
     "use strict";
 
-    var reactions = promise.@promiseReactions;
+    var reactions = @getByIdDirectPrivate(promise, "promiseReactions");
     promise.@promiseResult = value;
     promise.@promiseReactions = @undefined;
     promise.@promiseState = @promiseStateFulfilled;
index 97e3354..6342b40 100644 (file)
@@ -49,13 +49,14 @@ function then(onFulfilled, onRejected)
 
     var reaction = @newPromiseReaction(resultCapability, onFulfilled, onRejected);
 
-    var state = this.@promiseState;
-    if (state === @promiseStatePending)
-        @putByValDirect(this.@promiseReactions, this.@promiseReactions.length, reaction);
-    else {
-        if (state === @promiseStateRejected && !this.@promiseIsHandled)
+    var state = @getByIdDirectPrivate(this, "promiseState");
+    if (state === @promiseStatePending) {
+        var reactions = @getByIdDirectPrivate(this, "promiseReactions");
+        @putByValDirect(reactions, reactions.length, reaction);
+    } else {
+        if (state === @promiseStateRejected && !@getByIdDirectPrivate(this, "promiseIsHandled"))
             @hostPromiseRejectionTracker(this, @promiseRejectionHandle);
-        @enqueueJob(@promiseReactionJob, [state, reaction, this.@promiseResult]);
+        @enqueueJob(@promiseReactionJob, [state, reaction, @getByIdDirectPrivate(this, "promiseResult")]);
     }
 
     this.@promiseIsHandled = true;
index c086601..a06b2a2 100644 (file)
@@ -48,8 +48,8 @@ function next()
     if (this == null)
         @throwTypeError("%SetIteratorPrototype%.next requires that |this| not be null or undefined");
 
-    var bucket = this.@setBucket;
+    var bucket = @getByIdDirectPrivate(this, "setBucket");
     if (bucket === @undefined)
         @throwTypeError("%SetIteratorPrototype%.next requires that |this| be a Set Iterator instance");
-    return @setIteratorNext.@call(this, bucket, this.@setIteratorKind);
+    return @setIteratorNext.@call(this, bucket, @getByIdDirectPrivate(this, "setIteratorKind"));
 }
index 52762db..3360d2c 100644 (file)
@@ -30,14 +30,14 @@ function next()
     if (this == null)
         @throwTypeError("%StringIteratorPrototype%.next requires that |this| not be null or undefined");
 
-    var position = this.@stringIteratorNextIndex;
+    var position = @getByIdDirectPrivate(this, "stringIteratorNextIndex");
     if (position === @undefined)
         @throwTypeError("%StringIteratorPrototype%.next requires that |this| be a String Iterator instance");
 
     var done = true;
     var value = @undefined;
 
-    var string = this.@iteratedString;
+    var string = @getByIdDirectPrivate(this, "iteratedString");
     if (string !== @undefined) {
         var length = string.length >>> 0;
         if (position >= length) {
index f338a5b..fa885d1 100644 (file)
@@ -32,7 +32,7 @@ function of(/* items... */)
 {
     "use strict";
     let len = arguments.length;
-    let constructFunction = this.@allocateTypedArray;
+    let constructFunction = @getByIdDirectPrivate(this, "allocateTypedArray");
     if (constructFunction === @undefined)
         @throwTypeError("TypedArray.of requires its this argument to subclass a TypedArray constructor");
 
@@ -85,7 +85,7 @@ function from(items /* [ , mapfn [ , thisArg ] ] */)
             k++;
         }
 
-        let constructFunction = this.@allocateTypedArray;
+        let constructFunction = @getByIdDirectPrivate(this, "allocateTypedArray");
         if (constructFunction === @undefined)
             @throwTypeError("TypedArray.from requires its this argument subclass a TypedArray constructor");
 
@@ -100,7 +100,7 @@ function from(items /* [ , mapfn [ , thisArg ] ] */)
 
     let arrayLikeLength = @toLength(arrayLike.length);
 
-    let constructFunction = this.@allocateTypedArray;
+    let constructFunction = @getByIdDirectPrivate(this, "allocateTypedArray");
     if (constructFunction === @undefined)
         @throwTypeError("this does not subclass a TypedArray constructor");
 
index b655efe..abe9a8a 100644 (file)
@@ -1026,6 +1026,17 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         dumpValueProfiling(out, it, hasPrintedProfiling);
         break;
     }
+    case op_get_by_id_direct: {
+        int r0 = (++it)->u.operand;
+        int r1 = (++it)->u.operand;
+        int id0 = (++it)->u.operand;
+        printLocationAndOp(out, location, it, "get_by_id_direct");
+        out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
+        it += 2; // Increment up to the value profiler.
+        printGetByIdCacheStatus(out, location, stubInfos);
+        dumpValueProfiling(out, it, hasPrintedProfiling);
+        break;
+    }
     case op_get_by_id:
     case op_get_by_id_proto_load:
     case op_get_by_id_unset:
index 3d25108..855016e 100644 (file)
@@ -40,6 +40,8 @@ class Identifier;
 #define JSC_COMMON_BYTECODE_INTRINSIC_FUNCTIONS_EACH_NAME(macro) \
     macro(argument) \
     macro(argumentCount) \
+    macro(getByIdDirect) \
+    macro(getByIdDirectPrivate) \
     macro(idWithProfile) \
     macro(isObject) \
     macro(isJSArray) \
index ff302ac..5628df5 100644 (file)
@@ -87,6 +87,7 @@
             { "name" : "op_get_by_id_unset", "length" : 9 },
             { "name" : "op_get_by_id_with_this", "length" : 6 },
             { "name" : "op_get_by_val_with_this", "length" : 6 },
+            { "name" : "op_get_by_id_direct", "length" : 7  },
             { "name" : "op_try_get_by_id", "length" : 5 },
             { "name" : "op_put_by_id", "length" : 9 },
             { "name" : "op_put_by_id_with_this", "length" : 5 },
index b250def..701d36c 100644 (file)
@@ -184,6 +184,7 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_get_by_id:
     case op_get_by_id_proto_load:
     case op_get_by_id_unset:
+    case op_get_by_id_direct:
     case op_get_array_length:
     case op_typeof:
     case op_is_empty:
@@ -435,6 +436,7 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_get_by_id:
     case op_get_by_id_proto_load:
     case op_get_by_id_unset:
+    case op_get_by_id_direct:
     case op_get_by_id_with_this:
     case op_get_by_val_with_this:
     case op_get_array_length:
index 01fdcc2..76c0381 100644 (file)
@@ -566,6 +566,7 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         case op_get_by_id:
         case op_get_by_id_with_this:
         case op_try_get_by_id:
+        case op_get_by_id_direct:
         case op_get_by_val_with_this:
         case op_get_from_arguments:
         case op_to_number:
@@ -1253,6 +1254,16 @@ void CodeBlock::finalizeLLIntInlineCaches()
             clearLLIntGetByIdCache(curInstruction);
             break;
         }
+        case op_get_by_id_direct: {
+            StructureID oldStructureID = curInstruction[4].u.structureID;
+            if (!oldStructureID || Heap::isMarked(vm.heap.structureIDTable().get(oldStructureID)))
+                break;
+            if (Options::verboseOSR())
+                dataLogF("Clearing LLInt property access.\n");
+            curInstruction[4].u.pointer = nullptr;
+            curInstruction[5].u.pointer = nullptr;
+            break;
+        }
         case op_put_by_id: {
             StructureID oldStructureID = curInstruction[4].u.structureID;
             StructureID newStructureID = curInstruction[6].u.structureID;
index 92ac782..c2b54a0 100644 (file)
@@ -29,6 +29,7 @@
 #include "CodeBlock.h"
 #include "ComplexGetStatus.h"
 #include "GetterSetterAccessCase.h"
+#include "InterpreterInlines.h"
 #include "IntrinsicGetterAccessCase.h"
 #include "JSCInlines.h"
 #include "JSScope.h"
@@ -76,40 +77,46 @@ bool GetByIdStatus::hasExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex
 
 GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid)
 {
-    UNUSED_PARAM(profiledBlock);
-    UNUSED_PARAM(bytecodeIndex);
-    UNUSED_PARAM(uid);
-
     VM& vm = *profiledBlock->vm();
     
     Instruction* instruction = &profiledBlock->instructions()[bytecodeIndex];
 
-    Opcode opcode = instruction[0].u.opcode;
+    switch (Interpreter::getOpcodeID(instruction[0].u.opcode)) {
+    case op_get_by_id:
+    case op_get_by_id_direct: {
+        StructureID structureID = instruction[4].u.structureID;
+        if (!structureID)
+            return GetByIdStatus(NoInformation, false);
 
-    ASSERT(opcode == LLInt::getOpcode(op_get_array_length) || opcode == LLInt::getOpcode(op_try_get_by_id) || opcode == LLInt::getOpcode(op_get_by_id_proto_load) || opcode == LLInt::getOpcode(op_get_by_id) || opcode == LLInt::getOpcode(op_get_by_id_unset));
+        Structure* structure = vm.heap.structureIDTable().get(structureID);
 
-    // FIXME: We should not just bail if we see a try_get_by_id or a get_by_id_proto_load.\ e
-    // https://bugs.webkit.org/show_bug.cgi?id=158039
-    if (opcode != LLInt::getOpcode(op_get_by_id))
-        return GetByIdStatus(NoInformation, false);
+        if (structure->takesSlowPathInDFGForImpureProperty())
+            return GetByIdStatus(NoInformation, false);
 
-    StructureID structureID = instruction[4].u.structureID;
-    if (!structureID)
-        return GetByIdStatus(NoInformation, false);
+        unsigned attributes;
+        PropertyOffset offset = structure->getConcurrently(uid, attributes);
+        if (!isValidOffset(offset))
+            return GetByIdStatus(NoInformation, false);
+        if (attributes & PropertyAttribute::CustomAccessor)
+            return GetByIdStatus(NoInformation, false);
 
-    Structure* structure = vm.heap.structureIDTable().get(structureID);
+        return GetByIdStatus(Simple, false, GetByIdVariant(StructureSet(structure), offset));
+    }
 
-    if (structure->takesSlowPathInDFGForImpureProperty())
+    case op_get_array_length:
+    case op_try_get_by_id:
+    case op_get_by_id_proto_load:
+    case op_get_by_id_unset: {
+        // FIXME: We should not just bail if we see a try_get_by_id or a get_by_id_proto_load.
+        // https://bugs.webkit.org/show_bug.cgi?id=158039
         return GetByIdStatus(NoInformation, false);
+    }
 
-    unsigned attributes;
-    PropertyOffset offset = structure->getConcurrently(uid, attributes);
-    if (!isValidOffset(offset))
-        return GetByIdStatus(NoInformation, false);
-    if (attributes & PropertyAttribute::CustomAccessor)
+    default: {
+        ASSERT_NOT_REACHED();
         return GetByIdStatus(NoInformation, false);
-    
-    return GetByIdStatus(Simple, false, GetByIdVariant(StructureSet(structure), offset));
+    }
+    }
 }
 
 GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
@@ -361,6 +368,10 @@ GetByIdStatus GetByIdStatus::computeFor(const StructureSet& set, UniquedStringIm
 {
     // For now we only handle the super simple self access case. We could handle the
     // prototype case in the future.
+    //
+    // Note that this code is also used for GetByIdDirect since this function only looks
+    // into direct properties. When supporting prototype chains, we should split this for
+    // GetById and GetByIdDirect.
     
     if (set.isEmpty())
         return GetByIdStatus();
index 4a57a38..d606db0 100644 (file)
@@ -228,6 +228,9 @@ void StructureStubInfo::reset(CodeBlock* codeBlock)
     case AccessType::GetWithThis:
         resetGetByID(codeBlock, *this, GetByIDKind::WithThis);
         break;
+    case AccessType::GetDirect:
+        resetGetByID(codeBlock, *this, GetByIDKind::Direct);
+        break;
     case AccessType::Put:
         resetPutByID(codeBlock, *this);
         break;
index d650612..b13ef09 100644 (file)
@@ -47,6 +47,7 @@ class PolymorphicAccess;
 enum class AccessType : int8_t {
     Get,
     GetWithThis,
+    GetDirect,
     TryGet,
     Put,
     In
@@ -219,6 +220,38 @@ inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStu
     return structureStubInfo.codeOrigin;
 }
 
+inline J_JITOperation_ESsiJI appropriateOptimizingGetByIdFunction(AccessType type)
+{
+    switch (type) {
+    case AccessType::Get:
+        return operationGetByIdOptimize;
+    case AccessType::TryGet:
+        return operationTryGetByIdOptimize;
+    case AccessType::GetDirect:
+        return operationGetByIdDirectOptimize;
+    case AccessType::GetWithThis:
+    default:
+        ASSERT_NOT_REACHED();
+        return nullptr;
+    }
+}
+
+inline J_JITOperation_EJI appropriateGenericGetByIdFunction(AccessType type)
+{
+    switch (type) {
+    case AccessType::Get:
+        return operationGetByIdGeneric;
+    case AccessType::TryGet:
+        return operationTryGetByIdGeneric;
+    case AccessType::GetDirect:
+        return operationGetByIdDirectGeneric;
+    case AccessType::GetWithThis:
+    default:
+        ASSERT_NOT_REACHED();
+        return nullptr;
+    }
+}
+
 #else
 
 class StructureStubInfo;
index 0f60d78..d1d4192 100644 (file)
@@ -2721,6 +2721,22 @@ RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, Re
     return dst;
 }
 
+RegisterID* BytecodeGenerator::emitDirectGetById(RegisterID* dst, RegisterID* base, const Identifier& property)
+{
+    ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties should be handled with get_by_val_direct.");
+
+    m_codeBlock->addPropertyAccessInstruction(instructions().size());
+
+    UnlinkedValueProfile profile = emitProfiledOpcode(op_get_by_id_direct);
+    instructions().append(kill(dst));
+    instructions().append(base->index());
+    instructions().append(addConstant(property));
+    instructions().append(0);
+    instructions().append(0);
+    instructions().append(profile);
+    return dst;
+}
+
 RegisterID* BytecodeGenerator::emitPutById(RegisterID* base, const Identifier& property, RegisterID* value)
 {
     ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties should be handled with put_by_val.");
index 0edfe3d..b7255ac 100644 (file)
@@ -699,6 +699,7 @@ namespace JSC {
         RegisterID* emitTryGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
         RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
         RegisterID* emitGetById(RegisterID* dst, RegisterID* base, RegisterID* thisVal, const Identifier& property);
+        RegisterID* emitDirectGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
         RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value);
         RegisterID* emitPutById(RegisterID* base, RegisterID* thisValue, const Identifier& property, RegisterID* value);
         RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType);
index 3de897a..6403942 100644 (file)
@@ -944,6 +944,29 @@ RegisterID* BytecodeIntrinsicNode::emitBytecode(BytecodeGenerator& generator, Re
     return (this->*m_emitter)(generator, dst);
 }
 
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_getByIdDirect(BytecodeGenerator& generator, RegisterID* dst)
+{
+    ArgumentListNode* node = m_args->m_listNode;
+    RefPtr<RegisterID> base = generator.emitNode(node);
+    node = node->m_next;
+    ASSERT(node->m_expr->isString());
+    const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
+    ASSERT(!node->m_next);
+    return generator.emitDirectGetById(generator.finalDestination(dst), base.get(), ident);
+}
+
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_getByIdDirectPrivate(BytecodeGenerator& generator, RegisterID* dst)
+{
+    ArgumentListNode* node = m_args->m_listNode;
+    RefPtr<RegisterID> base = generator.emitNode(node);
+    node = node->m_next;
+    ASSERT(node->m_expr->isString());
+    const Identifier* ident = generator.vm()->propertyNames->lookUpPrivateName(static_cast<StringNode*>(node->m_expr)->value());
+    ASSERT(ident);
+    ASSERT(!node->m_next);
+    return generator.emitDirectGetById(generator.finalDestination(dst), base.get(), *ident);
+}
+
 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_argument(BytecodeGenerator& generator, RegisterID* dst)
 {
     ArgumentListNode* node = m_args->m_listNode;
index 7c187e3..238c004 100644 (file)
@@ -2516,6 +2516,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).makeHeapTop();
         break;
 
+    case GetByIdDirect:
+    case GetByIdDirectFlush:
     case GetById:
     case GetByIdFlush: {
         if (!node->prediction()) {
index 64a3d1e..d43a103 100644 (file)
@@ -3954,8 +3954,10 @@ void ByteCodeParser::handleGetById(
     NodeType getById;
     if (type == AccessType::Get)
         getById = getByIdStatus.makesCalls() ? GetByIdFlush : GetById;
-    else
+    else if (type == AccessType::TryGet)
         getById = TryGetById;
+    else
+        getById = getByIdStatus.makesCalls() ? GetByIdDirectFlush : GetByIdDirect;
 
     if (getById != TryGetById && getByIdStatus.isModuleNamespace()) {
         if (handleModuleNamespaceLoad(destinationOperand, prediction, base, getByIdStatus)) {
@@ -3979,7 +3981,7 @@ void ByteCodeParser::handleGetById(
         }
     }
 
-    ASSERT(type == AccessType::Get || !getByIdStatus.makesCalls());
+    ASSERT(type == AccessType::Get || type == AccessType::GetDirect ||  !getByIdStatus.makesCalls());
     if (!getByIdStatus.isSimple() || !getByIdStatus.numVariants() || !Options::useAccessInlining()) {
         set(VirtualRegister(destinationOperand),
             addToGraph(getById, OpInfo(identifierNumber), OpInfo(prediction), base));
@@ -4049,7 +4051,7 @@ void ByteCodeParser::handleGetById(
     if (UNLIKELY(m_graph.compilation()))
         m_graph.compilation()->noticeInlinedGetById();
 
-    ASSERT(type == AccessType::Get || !variant.callLinkStatus());
+    ASSERT(type == AccessType::Get || type == AccessType::GetDirect || !variant.callLinkStatus());
     if (!variant.callLinkStatus() && variant.intrinsic() == NoIntrinsic) {
         set(VirtualRegister(destinationOperand), loadedValue);
         return;
@@ -5119,6 +5121,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_define_accessor_property);
         }
 
+        case op_get_by_id_direct:
         case op_try_get_by_id:
         case op_get_by_id:
         case op_get_by_id_proto_load:
@@ -5134,15 +5137,25 @@ void ByteCodeParser::parseBlock(unsigned limit)
                 m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
                 m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
                 currentCodeOrigin(), uid);
-            AccessType type = op_try_get_by_id == opcodeID ? AccessType::TryGet : AccessType::Get;
 
-            unsigned opcodeLength = opcodeID == op_try_get_by_id ? OPCODE_LENGTH(op_try_get_by_id) : OPCODE_LENGTH(op_get_by_id);
+            AccessType type = AccessType::Get;
+            unsigned opcodeLength = OPCODE_LENGTH(op_get_by_id);
+            if (opcodeID == op_try_get_by_id) {
+                type = AccessType::TryGet;
+                opcodeLength = OPCODE_LENGTH(op_try_get_by_id);
+            } else if (opcodeID == op_get_by_id_direct) {
+                type = AccessType::GetDirect;
+                opcodeLength = OPCODE_LENGTH(op_get_by_id_direct);
+            }
 
             handleGetById(
                 currentInstruction[1].u.operand, prediction, base, identifierNumber, getByIdStatus, type, opcodeLength);
 
-            if (op_try_get_by_id == opcodeID)
-                NEXT_OPCODE(op_try_get_by_id); // Opcode's length is different from others in this case.
+            // Opcode's length is different from others in try and direct cases.
+            if (opcodeID == op_try_get_by_id)
+                NEXT_OPCODE(op_try_get_by_id);
+            else if (opcodeID == op_get_by_id_direct)
+                NEXT_OPCODE(op_get_by_id_direct);
             else
                 NEXT_OPCODE(op_get_by_id);
         }
index e205863..5cee65b 100644 (file)
@@ -167,6 +167,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_get_by_id_proto_load:
     case op_get_by_id_unset:
     case op_get_by_id_with_this:
+    case op_get_by_id_direct:
     case op_get_by_val_with_this:
     case op_get_array_length:
     case op_put_by_id:
index 7b3d589..1ec4556 100644 (file)
@@ -585,6 +585,8 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case GetById:
     case GetByIdFlush:
     case GetByIdWithThis:
+    case GetByIdDirect:
+    case GetByIdDirectFlush:
     case GetByValWithThis:
     case PutById:
     case PutByIdWithThis:
index 0af6dfc..b051bf9 100644 (file)
@@ -488,6 +488,8 @@ private:
                 break;
             }
         
+            case GetByIdDirect:
+            case GetByIdDirectFlush:
             case GetById:
             case GetByIdFlush: {
                 Edge childEdge = node->child1();
index 0f015aa..390b4e3 100644 (file)
@@ -100,6 +100,8 @@ bool doesGC(Graph& graph, Node* node)
     case GetById:
     case GetByIdFlush:
     case GetByIdWithThis:
+    case GetByIdDirect:
+    case GetByIdDirectFlush:
     case PutById:
     case PutByIdFlush:
     case PutByIdWithThis:
index 2ba1720..36524b0 100644 (file)
@@ -1372,6 +1372,13 @@ private:
             break;
         }
 
+        case GetByIdDirect:
+        case GetByIdDirectFlush: {
+            if (node->child1()->shouldSpeculateCell())
+                fixEdge<CellUse>(node->child1());
+            break;
+        }
+
         case GetById:
         case GetByIdFlush: {
             // FIXME: This should be done in the ByteCodeParser based on reading the
index 921c506..01d25ba 100644 (file)
@@ -552,7 +552,7 @@ public:
     
     void convertToGetByOffset(StorageAccessData& data, Edge storage, Edge base)
     {
-        ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset);
+        ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush || m_op == MultiGetByOffset);
         m_opInfo = &data;
         children.setChild1(storage);
         children.setChild2(base);
@@ -562,7 +562,7 @@ public:
     
     void convertToMultiGetByOffset(MultiGetByOffsetData* data)
     {
-        ASSERT(m_op == GetById || m_op == GetByIdFlush);
+        ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush);
         m_opInfo = data;
         child1().setUseKind(CellUse);
         m_op = MultiGetByOffset;
@@ -1002,6 +1002,8 @@ public:
         case GetById:
         case GetByIdFlush:
         case GetByIdWithThis:
+        case GetByIdDirect:
+        case GetByIdDirectFlush:
         case PutById:
         case PutByIdFlush:
         case PutByIdDirect:
@@ -1578,6 +1580,8 @@ public:
         case GetById:
         case GetByIdFlush:
         case GetByIdWithThis:
+        case GetByIdDirect:
+        case GetByIdDirectFlush:
         case GetPrototypeOf:
         case TryGetById:
         case GetByVal:
index d755d30..5d0e639 100644 (file)
@@ -185,6 +185,8 @@ namespace JSC { namespace DFG {
     macro(GetById, NodeResultJS | NodeMustGenerate) \
     macro(GetByIdFlush, NodeResultJS | NodeMustGenerate) \
     macro(GetByIdWithThis, NodeResultJS | NodeMustGenerate) \
+    macro(GetByIdDirect, NodeResultJS | NodeMustGenerate) \
+    macro(GetByIdDirectFlush, NodeResultJS | NodeMustGenerate) \
     macro(PutById, NodeMustGenerate) \
     macro(PutByIdFlush, NodeMustGenerate) \
     macro(PutByIdDirect, NodeMustGenerate) \
index 5d7b697..1590431 100644 (file)
@@ -1259,18 +1259,6 @@ EncodedJSValue JIT_OPERATION operationToNumber(ExecState* exec, EncodedJSValue v
     return JSValue::encode(jsNumber(JSValue::decode(value).toNumber(exec)));
 }
 
-EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedThis, UniquedStringImpl* impl)
-{
-    VM& vm = exec->vm();
-    NativeCallFrameTracer tracer(&vm, exec);
-
-    JSValue baseValue = JSValue::decode(encodedBase);
-    JSValue thisVal = JSValue::decode(encodedThis);
-    PropertySlot slot(thisVal, PropertySlot::PropertySlot::InternalMethodType::Get);
-    JSValue result = baseValue.get(exec, Identifier::fromUid(exec, impl), slot);
-    return JSValue::encode(result);
-}
-
 EncodedJSValue JIT_OPERATION operationGetByValWithThis(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedThis, EncodedJSValue encodedSubscript)
 {
     VM& vm = exec->vm();
index a356d82..3f080cf 100644 (file)
@@ -76,7 +76,6 @@ EncodedJSValue JIT_OPERATION operationGetByValObjectString(ExecState*, JSCell*,
 EncodedJSValue JIT_OPERATION operationGetByValObjectSymbol(ExecState*, JSCell*, JSCell* symbol) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToPrimitive(ExecState*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToNumber(ExecState*, EncodedJSValue) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValWithThis(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetPrototypeOf(ExecState*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetPrototypeOfObject(ExecState*, JSObject*) WTF_INTERNAL;
index 8f222fd..ac528b9 100644 (file)
@@ -705,6 +705,8 @@ private:
         case GetById:
         case GetByIdFlush:
         case GetByIdWithThis:
+        case GetByIdDirect:
+        case GetByIdDirectFlush:
         case TryGetById:
         case GetByValWithThis:
         case GetByOffset:
index 6bf7b54..63a9125 100644 (file)
@@ -225,6 +225,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case GetByIdWithThis:
     case GetByValWithThis:
     case GetByIdFlush:
+    case GetByIdDirect:
+    case GetByIdDirectFlush:
     case PutById:
     case PutByIdFlush:
     case PutByIdWithThis:
index ab5c6b6..07f7184 100644 (file)
@@ -964,8 +964,10 @@ void SpeculativeJIT::useChildren(Node* node)
     }
 }
 
-void SpeculativeJIT::compileTryGetById(Node* node)
+void SpeculativeJIT::compileGetById(Node* node, AccessType accessType)
 {
+    ASSERT(accessType == AccessType::Get || accessType == AccessType::GetDirect || accessType == AccessType::TryGet);
+
     switch (node->child1().useKind()) {
     case CellUse: {
         SpeculateCellOperand base(this, node->child1());
@@ -976,7 +978,7 @@ void SpeculativeJIT::compileTryGetById(Node* node)
 
         base.use();
 
-        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), JITCompiler::Jump(), NeedToSpill, AccessType::TryGet);
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), JITCompiler::Jump(), NeedToSpill, accessType);
 
         jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
         break;
@@ -993,7 +995,52 @@ void SpeculativeJIT::compileTryGetById(Node* node)
 
         JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs);
 
-        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), notCell, NeedToSpill, AccessType::TryGet);
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), notCell, NeedToSpill, accessType);
+
+        jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
+        break;
+    }
+
+    default:
+        DFG_CRASH(m_jit.graph(), node, "Bad use kind");
+        break;
+    }
+}
+
+void SpeculativeJIT::compileGetByIdFlush(Node* node, AccessType accessType)
+{
+    switch (node->child1().useKind()) {
+    case CellUse: {
+        SpeculateCellOperand base(this, node->child1());
+        JSValueRegs baseRegs = JSValueRegs::payloadOnly(base.gpr());
+
+        JSValueRegsFlushedCallResult result(this);
+        JSValueRegs resultRegs = result.regs();
+
+        base.use();
+
+        flushRegisters();
+
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), JITCompiler::Jump(), DontSpill, accessType);
+
+        jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
+        break;
+    }
+
+    case UntypedUse: {
+        JSValueOperand base(this, node->child1());
+        JSValueRegs baseRegs = base.jsValueRegs();
+
+        JSValueRegsFlushedCallResult result(this);
+        JSValueRegs resultRegs = result.regs();
+
+        base.use();
+
+        flushRegisters();
+
+        JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs);
+
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), notCell, DontSpill, accessType);
 
         jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
         break;
@@ -1002,7 +1049,7 @@ void SpeculativeJIT::compileTryGetById(Node* node)
     default:
         DFG_CRASH(m_jit.graph(), node, "Bad use kind");
         break;
-    } 
+    }
 }
 
 void SpeculativeJIT::compileIn(Node* node)
index d72589f..312d14b 100644 (file)
@@ -724,14 +724,14 @@ public:
     void compileMovHint(Node*);
     void compileMovHintAndCheck(Node*);
 
-    void cachedGetById(CodeOrigin, JSValueRegs base, JSValueRegs result, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill, AccessType = AccessType::Get);
+    void cachedGetById(CodeOrigin, JSValueRegs base, JSValueRegs result, unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode, AccessType);
 
 #if USE(JSVALUE64)
-    void cachedGetById(CodeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill, AccessType = AccessType::Get);
+    void cachedGetById(CodeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode, AccessType);
     void cachedPutById(CodeOrigin, GPRReg base, GPRReg value, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
     void cachedGetByIdWithThis(CodeOrigin, GPRReg baseGPR, GPRReg thisGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::JumpList slowPathTarget = JITCompiler::JumpList());
 #elif USE(JSVALUE32_64)
-    void cachedGetById(CodeOrigin, GPRReg baseTagGPROrNone, GPRReg basePayloadGPR, GPRReg resultTagGPR, GPRReg resultPayloadGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill, AccessType = AccessType::Get);
+    void cachedGetById(CodeOrigin, GPRReg baseTagGPROrNone, GPRReg basePayloadGPR, GPRReg resultTagGPR, GPRReg resultPayloadGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode, AccessType);
     void cachedPutById(CodeOrigin, GPRReg basePayloadGPR, GPRReg valueTagGPR, GPRReg valuePayloadGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind, JITCompiler::Jump slowPathTarget = JITCompiler::Jump(), SpillRegistersMode = NeedToSpill);
     void cachedGetByIdWithThis(CodeOrigin, GPRReg baseTagGPROrNone, GPRReg basePayloadGPR, GPRReg thisTagGPROrNone, GPRReg thisPayloadGPR, GPRReg resultTagGPR, GPRReg resultPayloadGPR, unsigned identifierNumber, JITCompiler::JumpList slowPathTarget = JITCompiler::JumpList());
 #endif
@@ -739,7 +739,8 @@ public:
     void compileDeleteById(Node*);
     void compileDeleteByVal(Node*);
     void compilePushWithScope(Node*);
-    void compileTryGetById(Node*);
+    void compileGetById(Node*, AccessType);
+    void compileGetByIdFlush(Node*, AccessType);
     void compileIn(Node*);
     
     void nonSpeculativeNonPeepholeCompareNullOrUndefined(Edge operand);
index 3fe560e..a1e329c 100644 (file)
@@ -177,7 +177,7 @@ void SpeculativeJIT::cachedGetById(
     unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode, AccessType type)
 {
     // This is a hacky fix for when the register allocator decides to alias the base payload with the result tag. This only happens
-    // in the case of GetByIdFlush, which has a relatively expensive register allocation story already so we probably don't need to
+    // in the case of GetByIdFlush/GetByIdDirectFlush, which has a relatively expensive register allocation story already so we probably don't need to
     // trip over one move instruction.
     if (basePayloadGPR == resultTagGPR) {
         RELEASE_ASSERT(basePayloadGPR != resultPayloadGPR);
@@ -3502,55 +3502,27 @@ void SpeculativeJIT::compile(Node* node)
     }
 
     case TryGetById: {
-        compileTryGetById(node);
+        compileGetById(node, AccessType::TryGet);
+        break;
+    }
+
+    case GetByIdDirect: {
+        compileGetById(node, AccessType::GetDirect);
+        break;
+    }
+
+    case GetByIdDirectFlush: {
+        compileGetByIdFlush(node, AccessType::GetDirect);
         break;
     }
 
     case GetById: {
-        // FIXME https://bugs.webkit.org/show_bug.cgi?id=161158
-        // dedup with SpeculativeJIT::compileTryGetById and 64-bit version of this.
-        switch (node->child1().useKind()) {
-        case CellUse: {
-            SpeculateCellOperand base(this, node->child1());
-            GPRTemporary resultTag(this);
-            GPRTemporary resultPayload(this, Reuse, base);
-            
-            GPRReg baseGPR = base.gpr();
-            GPRReg resultTagGPR = resultTag.gpr();
-            GPRReg resultPayloadGPR = resultPayload.gpr();
+        compileGetById(node, AccessType::Get);
+        break;
+    }
 
-            base.use();
-            
-            cachedGetById(node->origin.semantic, InvalidGPRReg, baseGPR, resultTagGPR, resultPayloadGPR, node->identifierNumber());
-            
-            jsValueResult(resultTagGPR, resultPayloadGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-        
-        case UntypedUse: {
-            JSValueOperand base(this, node->child1());
-            GPRTemporary resultTag(this);
-            GPRTemporary resultPayload(this, Reuse, base, TagWord);
-        
-            GPRReg baseTagGPR = base.tagGPR();
-            GPRReg basePayloadGPR = base.payloadGPR();
-            GPRReg resultTagGPR = resultTag.gpr();
-            GPRReg resultPayloadGPR = resultPayload.gpr();
-        
-            base.use();
-        
-            JITCompiler::Jump notCell = m_jit.branchIfNotCell(base.jsValueRegs());
-        
-            cachedGetById(node->origin.semantic, baseTagGPR, basePayloadGPR, resultTagGPR, resultPayloadGPR, node->identifierNumber(), notCell);
-        
-            jsValueResult(resultTagGPR, resultPayloadGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-            
-        default:
-            RELEASE_ASSERT_NOT_REACHED();
-            break;
-        }
+    case GetByIdFlush: {
+        compileGetByIdFlush(node, AccessType::Get);
         break;
     }
 
@@ -3594,62 +3566,6 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
-    case GetByIdFlush: {
-        if (!node->prediction()) {
-            terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
-            break;
-        }
-        
-        switch (node->child1().useKind()) {
-        case CellUse: {
-            SpeculateCellOperand base(this, node->child1());
-            
-            GPRReg baseGPR = base.gpr();
-
-            GPRFlushedCallResult resultPayload(this);
-            GPRFlushedCallResult2 resultTag(this);
-            GPRReg resultPayloadGPR = resultPayload.gpr();
-            GPRReg resultTagGPR = resultTag.gpr();
-
-            base.use();
-            
-            flushRegisters();
-            
-            cachedGetById(node->origin.semantic, InvalidGPRReg, baseGPR, resultTagGPR, resultPayloadGPR, node->identifierNumber(), JITCompiler::Jump(), DontSpill);
-            
-            jsValueResult(resultTagGPR, resultPayloadGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-        
-        case UntypedUse: {
-            JSValueOperand base(this, node->child1());
-            GPRReg baseTagGPR = base.tagGPR();
-            GPRReg basePayloadGPR = base.payloadGPR();
-
-            GPRFlushedCallResult resultPayload(this);
-            GPRFlushedCallResult2 resultTag(this);
-            GPRReg resultPayloadGPR = resultPayload.gpr();
-            GPRReg resultTagGPR = resultTag.gpr();
-
-            base.use();
-        
-            flushRegisters();
-        
-            JITCompiler::Jump notCell = m_jit.branchIfNotCell(base.jsValueRegs());
-        
-            cachedGetById(node->origin.semantic, baseTagGPR, basePayloadGPR, resultTagGPR, resultPayloadGPR, node->identifierNumber(), notCell, DontSpill);
-        
-            jsValueResult(resultTagGPR, resultPayloadGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-            
-        default:
-            RELEASE_ASSERT_NOT_REACHED();
-            break;
-        }
-        break;
-    }
-
     case GetArrayLength:
         compileGetArrayLength(node);
         break;
index 54223bc..404333d 100644 (file)
@@ -178,9 +178,9 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg
     if (slowPathTarget.isSet())
         slowCases.append(slowPathTarget);
     slowCases.append(gen.slowPathJump());
-    
+
     auto slowPath = slowPathCall(
-        slowCases, this, type == AccessType::Get ? operationGetByIdOptimize : operationTryGetByIdOptimize, GetPropertyPtrTag,
+        slowCases, this, appropriateOptimizingGetByIdFunction(type), GetPropertyPtrTag,
         spillMode, ExceptionCheckRequirement::CheckNeeded,
         resultGPR, gen.stubInfo(), baseGPR, identifierUID(identifierNumber));
     
@@ -3708,100 +3708,27 @@ void SpeculativeJIT::compile(Node* node)
     }
 
     case TryGetById: {
-        compileTryGetById(node);
+        compileGetById(node, AccessType::TryGet);
         break;
     }
 
-    case GetById: {
-        // FIXME https://bugs.webkit.org/show_bug.cgi?id=161158
-        // dedup with SpeculativeJIT::compileTryGetById and 32-bit version of this.
-        switch (node->child1().useKind()) {
-        case CellUse: {
-            SpeculateCellOperand base(this, node->child1());
-            GPRTemporary result(this, Reuse, base);
-            
-            GPRReg baseGPR = base.gpr();
-            GPRReg resultGPR = result.gpr();
-            
-            base.use();
-            
-            cachedGetById(node->origin.semantic, baseGPR, resultGPR, node->identifierNumber());
-            
-            jsValueResult(resultGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-        
-        case UntypedUse: {
-            JSValueOperand base(this, node->child1());
-            GPRTemporary result(this, Reuse, base);
-        
-            GPRReg baseGPR = base.gpr();
-            GPRReg resultGPR = result.gpr();
-        
-            base.use();
-        
-            JITCompiler::Jump notCell = m_jit.branchIfNotCell(JSValueRegs(baseGPR));
-        
-            cachedGetById(node->origin.semantic, baseGPR, resultGPR, node->identifierNumber(), notCell);
-        
-            jsValueResult(resultGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-            
-        default:
-            DFG_CRASH(m_jit.graph(), node, "Bad use kind");
-            break;
-        }
+    case GetByIdDirect: {
+        compileGetById(node, AccessType::GetDirect);
         break;
     }
 
-    case GetByIdFlush: {
-        if (!node->prediction()) {
-            terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
-            break;
-        }
-
-        switch (node->child1().useKind()) {
-        case CellUse: {
-            SpeculateCellOperand base(this, node->child1());
-            GPRReg baseGPR = base.gpr();
+    case GetByIdDirectFlush: {
+        compileGetByIdFlush(node, AccessType::GetDirect);
+        break;
+    }
 
-            GPRFlushedCallResult result(this);
-            
-            GPRReg resultGPR = result.gpr();
-            
-            base.use();
-            
-            flushRegisters();
-            
-            cachedGetById(node->origin.semantic, baseGPR, resultGPR, node->identifierNumber(), JITCompiler::Jump(), DontSpill);
-            
-            jsValueResult(resultGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-        
-        case UntypedUse: {
-            JSValueOperand base(this, node->child1());
-            GPRReg baseGPR = base.gpr();
+    case GetById: {
+        compileGetById(node, AccessType::Get);
+        break;
+    }
 
-            GPRFlushedCallResult result(this);
-            GPRReg resultGPR = result.gpr();
-        
-            base.use();
-            flushRegisters();
-        
-            JITCompiler::Jump notCell = m_jit.branchIfNotCell(JSValueRegs(baseGPR));
-        
-            cachedGetById(node->origin.semantic, baseGPR, resultGPR, node->identifierNumber(), notCell, DontSpill);
-        
-            jsValueResult(resultGPR, node, UseChildrenCalledExplicitly);
-            break;
-        }
-            
-        default:
-            DFG_CRASH(m_jit.graph(), node, "Bad use kind");
-            break;
-        }
+    case GetByIdFlush: {
+        compileGetByIdFlush(node, AccessType::Get);
         break;
     }
 
index d57941a..5ee1653 100644 (file)
@@ -195,6 +195,8 @@ inline CapabilityLevel canCompile(Node* node)
     case GetById:
     case GetByIdFlush:
     case GetByIdWithThis:
+    case GetByIdDirect:
+    case GetByIdDirectFlush:
     case ToThis:
     case MultiGetByOffset:
     case MultiPutByOffset:
index 1ed4b9c..b69d736 100644 (file)
@@ -704,6 +704,10 @@ private:
         case GetByIdWithThis:
             compileGetByIdWithThis();
             break;
+        case GetByIdDirect:
+        case GetByIdDirectFlush:
+            compileGetById(AccessType::GetDirect);
+            break;
         case In:
             compileIn();
             break;
@@ -3043,7 +3047,7 @@ private:
     
     void compileGetById(AccessType type)
     {
-        ASSERT(type == AccessType::Get || type == AccessType::TryGet);
+        ASSERT(type == AccessType::Get || type == AccessType::TryGet || type == AccessType::GetDirect);
         switch (m_node->child1().useKind()) {
         case CellUse: {
             setJSValue(getById(lowCell(m_node->child1()), type));
@@ -3067,11 +3071,7 @@ private:
             ValueFromBlock cellResult = m_out.anchor(getById(value, type));
             m_out.jump(continuation);
 
-            J_JITOperation_EJI getByIdFunction;
-            if (type == AccessType::Get)
-                getByIdFunction = operationGetByIdGeneric;
-            else
-                getByIdFunction = operationTryGetByIdGeneric;
+            J_JITOperation_EJI getByIdFunction = appropriateGenericGetByIdFunction(type);
 
             m_out.appendTo(notCellCase, continuation);
             ValueFromBlock notCellResult = m_out.anchor(vmCall(
@@ -3118,7 +3118,7 @@ private:
 
             m_out.appendTo(notCellCase, continuation);
             ValueFromBlock notCellResult = m_out.anchor(vmCall(
-                Int64, m_out.operation(operationGetByIdWithThis),
+                Int64, m_out.operation(operationGetByIdWithThisGeneric),
                 m_callFrame, base, thisValue,
                 m_out.constIntPtr(m_graph.identifiers()[m_node->identifierNumber()])));
             m_out.jump(continuation);
@@ -11397,7 +11397,7 @@ private:
         patchpoint->append(m_tagMask, ValueRep::lateReg(GPRInfo::tagMaskRegister));
         patchpoint->append(m_tagTypeNumber, ValueRep::lateReg(GPRInfo::tagTypeNumberRegister));
 
-        // FIXME: If this is a GetByIdFlush, we might get some performance boost if we claim that it
+        // FIXME: If this is a GetByIdFlush/GetByIdDirectFlush, we might get some performance boost if we claim that it
         // clobbers volatile registers late. It's not necessary for correctness, though, since the
         // IC code is super smart about saving registers.
         // https://bugs.webkit.org/show_bug.cgi?id=152848
@@ -11436,11 +11436,7 @@ private:
                     [=] (CCallHelpers& jit) {
                         AllowMacroScratchRegisterUsage allowScratch(jit);
 
-                        J_JITOperation_ESsiJI optimizationFunction;
-                        if (type == AccessType::Get)
-                            optimizationFunction = operationGetByIdOptimize;
-                        else
-                            optimizationFunction = operationTryGetByIdOptimize;
+                        J_JITOperation_ESsiJI optimizationFunction = appropriateOptimizingGetByIdFunction(type);
 
                         generator->slowPathJump().link(&jit);
                         CCallHelpers::Label slowPathBegin = jit.label();
index ed05ec4..dd3f1a1 100644 (file)
@@ -334,6 +334,7 @@ void JIT::privateCompileMainPass()
         case op_get_by_id_unset:
         DEFINE_OP(op_get_by_id)
         DEFINE_OP(op_get_by_id_with_this)
+        DEFINE_OP(op_get_by_id_direct)
         DEFINE_OP(op_get_by_val)
         DEFINE_OP(op_overrides_has_instance)
         DEFINE_OP(op_instanceof)
@@ -512,6 +513,7 @@ void JIT::privateCompileSlowCases()
         case op_get_by_id_unset:
         DEFINE_SLOWCASE_OP(op_get_by_id)
         DEFINE_SLOWCASE_OP(op_get_by_id_with_this)
+        DEFINE_SLOWCASE_OP(op_get_by_id_direct)
         DEFINE_SLOWCASE_OP(op_get_by_val)
         DEFINE_SLOWCASE_OP(op_instanceof)
         DEFINE_SLOWCASE_OP(op_instanceof_custom)
index 7cb4353..bb6cf51 100644 (file)
@@ -500,6 +500,7 @@ namespace JSC {
         void emit_op_try_get_by_id(Instruction*);
         void emit_op_get_by_id(Instruction*);
         void emit_op_get_by_id_with_this(Instruction*);
+        void emit_op_get_by_id_direct(Instruction*);
         void emit_op_get_arguments_length(Instruction*);
         void emit_op_get_by_val(Instruction*);
         void emit_op_get_argument_by_val(Instruction*);
@@ -610,6 +611,7 @@ namespace JSC {
         void emitSlow_op_try_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_by_id_with_this(Instruction*, Vector<SlowCaseEntry>::iterator&);
+        void emitSlow_op_get_by_id_direct(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_arguments_length(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_argument_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&);
index 0718b84..355d25f 100644 (file)
@@ -221,6 +221,61 @@ EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState* exec, Struct
     return JSValue::encode(slot.getPureResult());
 }
 
+EncodedJSValue JIT_OPERATION operationGetByIdDirect(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    Identifier ident = Identifier::fromUid(&vm, uid);
+    stubInfo->tookSlowPath = true;
+
+    JSValue baseValue = JSValue::decode(base);
+    PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty);
+
+    bool found = baseValue.getOwnPropertySlot(exec, ident, slot);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    scope.release();
+    return JSValue::encode(found ? slot.getValue(exec, ident) : jsUndefined());
+}
+
+EncodedJSValue JIT_OPERATION operationGetByIdDirectGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    Identifier ident = Identifier::fromUid(&vm, uid);
+
+    JSValue baseValue = JSValue::decode(base);
+    PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty);
+
+    bool found = baseValue.getOwnPropertySlot(exec, ident, slot);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    scope.release();
+    return JSValue::encode(found ? slot.getValue(exec, ident) : jsUndefined());
+}
+
+EncodedJSValue JIT_OPERATION operationGetByIdDirectOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    Identifier ident = Identifier::fromUid(&vm, uid);
+
+    JSValue baseValue = JSValue::decode(base);
+    PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty);
+
+    bool found = baseValue.getOwnPropertySlot(exec, ident, slot);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    if (stubInfo->considerCaching(exec->codeBlock(), baseValue.structureOrNull()))
+        repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Direct);
+
+    scope.release();
+    return JSValue::encode(found ? slot.getValue(exec, ident) : jsUndefined());
+}
+
 EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
 {
     SuperSamplerScope superSamplerScope(false);
@@ -270,7 +325,7 @@ EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, Structure
     }));
 }
 
-EncodedJSValue JIT_OPERATION operationGetByIdWithThisGeneric(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, UniquedStringImpl* uid)
+EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, UniquedStringImpl* uid)
 {
     SuperSamplerScope superSamplerScope(false);
 
@@ -287,6 +342,21 @@ EncodedJSValue JIT_OPERATION operationGetByIdWithThisGeneric(ExecState* exec, St
     return JSValue::encode(baseValue.get(exec, ident, slot));
 }
 
+EncodedJSValue JIT_OPERATION operationGetByIdWithThisGeneric(ExecState* exec, EncodedJSValue base, EncodedJSValue thisEncoded, UniquedStringImpl* uid)
+{
+    SuperSamplerScope superSamplerScope(false);
+
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    Identifier ident = Identifier::fromUid(vm, uid);
+
+    JSValue baseValue = JSValue::decode(base);
+    JSValue thisValue = JSValue::decode(thisEncoded);
+    PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get);
+
+    return JSValue::encode(baseValue.get(exec, ident, slot));
+}
+
 EncodedJSValue JIT_OPERATION operationGetByIdWithThisOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, UniquedStringImpl* uid)
 {
     SuperSamplerScope superSamplerScope(false);
index d819241..653a8cb 100644 (file)
@@ -364,8 +364,12 @@ EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState*, StructureSt
 EncodedJSValue JIT_OPERATION operationGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationGetByIdWithThisGeneric(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByIdWithThisGeneric(ExecState*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdWithThisOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByIdDirect(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByIdDirectGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByIdDirectOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationInOptimize(ExecState*, StructureStubInfo*, JSCell*, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationIn(ExecState*, StructureStubInfo*, JSCell*, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGenericIn(ExecState*, JSCell*, EncodedJSValue) WTF_INTERNAL;
index 6bcbfb5..795f62c 100644 (file)
@@ -593,6 +593,43 @@ void JIT::emitSlow_op_try_get_by_id(Instruction* currentInstruction, Vector<Slow
     gen.reportSlowPathCall(coldPathBegin, call);
 }
 
+void JIT::emit_op_get_by_id_direct(Instruction* currentInstruction)
+{
+    int resultVReg = currentInstruction[1].u.operand;
+    int baseVReg = currentInstruction[2].u.operand;
+    const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+    emitGetVirtualRegister(baseVReg, regT0);
+
+    emitJumpSlowCaseIfNotJSCell(regT0, baseVReg);
+
+    JITGetByIdGenerator gen(
+        m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(m_bytecodeOffset), RegisterSet::stubUnavailableRegisters(),
+        ident->impl(), JSValueRegs(regT0), JSValueRegs(regT0), AccessType::GetDirect);
+    gen.generateFastPath(*this);
+    addSlowCase(gen.slowPathJump());
+    m_getByIds.append(gen);
+
+    emitValueProfilingSite();
+    emitPutVirtualRegister(resultVReg);
+}
+
+void JIT::emitSlow_op_get_by_id_direct(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkAllSlowCases(iter);
+
+    int resultVReg = currentInstruction[1].u.operand;
+    const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+    JITGetByIdGenerator& gen = m_getByIds[m_getByIdIndex++];
+
+    Label coldPathBegin = label();
+
+    Call call = callOperationWithProfile(operationGetByIdDirectOptimize, GetPropertyPtrTag, resultVReg, gen.stubInfo(), regT0, ident->impl());
+
+    gen.reportSlowPathCall(coldPathBegin, call);
+}
+
 void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 {
     int resultVReg = currentInstruction[1].u.operand;
index e774df7..0ceb957 100644 (file)
@@ -609,6 +609,43 @@ void JIT::emitSlow_op_try_get_by_id(Instruction* currentInstruction, Vector<Slow
 }
 
 
+void JIT::emit_op_get_by_id_direct(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int base = currentInstruction[2].u.operand;
+    const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+    emitLoad(base, regT1, regT0);
+    emitJumpSlowCaseIfNotJSCell(base, regT1);
+
+    JITGetByIdGenerator gen(
+        m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
+        ident->impl(), JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::GetDirect);
+    gen.generateFastPath(*this);
+    addSlowCase(gen.slowPathJump());
+    m_getByIds.append(gen);
+
+    emitValueProfilingSite();
+    emitStore(dst, regT1, regT0);
+}
+
+void JIT::emitSlow_op_get_by_id_direct(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    linkAllSlowCases(iter);
+
+    int resultVReg = currentInstruction[1].u.operand;
+    const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+    JITGetByIdGenerator& gen = m_getByIds[m_getByIdIndex++];
+
+    Label coldPathBegin = label();
+
+    Call call = callOperationWithProfile(operationGetByIdDirectOptimize, resultVReg, gen.stubInfo(), JSValueRegs(regT1, regT0), ident->impl());
+
+    gen.reportSlowPathCall(coldPathBegin, call);
+}
+
+
 void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
index ab5da23..343eb88 100644 (file)
@@ -149,20 +149,34 @@ ALWAYS_INLINE static void fireWatchpointsAndClearStubIfNeeded(VM& vm, StructureS
 
 inline FunctionPtr appropriateOptimizingGetByIdFunction(GetByIDKind kind)
 {
-    if (kind == GetByIDKind::Normal)
+    switch (kind) {
+    case GetByIDKind::Normal:
         return operationGetByIdOptimize;
-    else if (kind == GetByIDKind::WithThis)
+    case GetByIDKind::WithThis:
         return operationGetByIdWithThisOptimize;
-    return operationTryGetByIdOptimize;
+    case GetByIDKind::Try:
+        return operationTryGetByIdOptimize;
+    case GetByIDKind::Direct:
+        return operationGetByIdDirectOptimize;
+    }
+    ASSERT_NOT_REACHED();
+    return operationGetById;
 }
 
-inline FunctionPtr appropriateGenericGetByIdFunction(GetByIDKind kind)
+inline FunctionPtr appropriateGetByIdFunction(GetByIDKind kind)
 {
-    if (kind == GetByIDKind::Normal)
+    switch (kind) {
+    case GetByIDKind::Normal:
         return operationGetById;
-    else if (kind == GetByIDKind::WithThis)
-        return operationGetByIdWithThisGeneric;
-    return operationTryGetById;
+    case GetByIDKind::WithThis:
+        return operationGetByIdWithThis;
+    case GetByIDKind::Try:
+        return operationTryGetById;
+    case GetByIDKind::Direct:
+        return operationGetByIdDirect;
+    }
+    ASSERT_NOT_REACHED();
+    return operationGetById;
 }
 
 static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
@@ -273,28 +287,32 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
                 if (slot.isUnset() && structure->typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence())
                     return GiveUpOnCache;
 
-                bool usesPolyProto;
-                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot, usesPolyProto);
-                if (!prototypeAccessChain) {
-                    // It's invalid to access this prototype property.
-                    return GiveUpOnCache;
-                }
+                // If a kind is GetByIDKind::Direct, we do not need to investigate prototype chains further.
+                // Cacheability just depends on the head structure.
+                if (kind != GetByIDKind::Direct) {
+                    bool usesPolyProto;
+                    prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot, usesPolyProto);
+                    if (!prototypeAccessChain) {
+                        // It's invalid to access this prototype property.
+                        return GiveUpOnCache;
+                    }
 
-                if (!usesPolyProto) {
-                    // We use ObjectPropertyConditionSet instead for faster accesses.
-                    prototypeAccessChain = nullptr;
+                    if (!usesPolyProto) {
+                        // We use ObjectPropertyConditionSet instead for faster accesses.
+                        prototypeAccessChain = nullptr;
 
-                    if (slot.isUnset()) {
-                        conditionSet = generateConditionsForPropertyMiss(
-                            vm, codeBlock, exec, structure, propertyName.impl());
-                    } else {
-                        conditionSet = generateConditionsForPrototypePropertyHit(
-                            vm, codeBlock, exec, structure, slot.slotBase(),
-                            propertyName.impl());
-                    }
+                        if (slot.isUnset()) {
+                            conditionSet = generateConditionsForPropertyMiss(
+                                vm, codeBlock, exec, structure, propertyName.impl());
+                        } else {
+                            conditionSet = generateConditionsForPrototypePropertyHit(
+                                vm, codeBlock, exec, structure, slot.slotBase(),
+                                propertyName.impl());
+                        }
 
-                    if (!conditionSet.isValid())
-                        return GiveUpOnCache;
+                        if (!conditionSet.isValid())
+                            return GiveUpOnCache;
+                    }
                 }
 
                 offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
@@ -370,7 +388,7 @@ void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& proper
     
     if (tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo, kind) == GiveUpOnCache) {
         CodeBlock* codeBlock = exec->codeBlock();
-        ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), FunctionPtr(appropriateGenericGetByIdFunction(kind), GetPropertyPtrTag));
+        ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), FunctionPtr(appropriateGetByIdFunction(kind), GetPropertyPtrTag));
     }
 }
 
index c4464fc..3406773 100644 (file)
@@ -35,7 +35,8 @@ namespace JSC {
 enum class GetByIDKind {
     Normal,
     Try,
-    WithThis
+    WithThis,
+    Direct
 };
 
 void repatchGetByID(ExecState*, JSValue, const Identifier&, const PropertySlot&, StructureStubInfo&, GetByIDKind);
index aa025bd..133feb5 100644 (file)
@@ -594,6 +594,56 @@ LLINT_SLOW_PATH_DECL(slow_path_try_get_by_id)
     LLINT_RETURN_PROFILED(op_try_get_by_id, result);
 }
 
+LLINT_SLOW_PATH_DECL(slow_path_get_by_id_direct)
+{
+    LLINT_BEGIN();
+    CodeBlock* codeBlock = exec->codeBlock();
+    const Identifier& ident = codeBlock->identifier(pc[3].u.operand);
+    JSValue baseValue = LLINT_OP_C(2).jsValue();
+    PropertySlot slot(baseValue, PropertySlot::PropertySlot::InternalMethodType::GetOwnProperty);
+
+    bool found = baseValue.getOwnPropertySlot(exec, ident, slot);
+    LLINT_CHECK_EXCEPTION();
+    JSValue result = found ? slot.getValue(exec, ident) : jsUndefined();
+    LLINT_CHECK_EXCEPTION();
+
+    if (!LLINT_ALWAYS_ACCESS_SLOW && slot.isCacheable()) {
+        {
+            StructureID oldStructureID = pc[4].u.structureID;
+            if (oldStructureID) {
+                Structure* a = vm.heap.structureIDTable().get(oldStructureID);
+                Structure* b = baseValue.asCell()->structure(vm);
+
+                if (Structure::shouldConvertToPolyProto(a, b)) {
+                    ASSERT(a->rareData()->sharedPolyProtoWatchpoint().get() == b->rareData()->sharedPolyProtoWatchpoint().get());
+                    a->rareData()->sharedPolyProtoWatchpoint()->invalidate(vm, StringFireDetail("Detected poly proto opportunity."));
+                }
+            }
+        }
+
+        JSCell* baseCell = baseValue.asCell();
+        Structure* structure = baseCell->structure();
+        if (slot.isValue()) {
+            // Start out by clearing out the old cache.
+            pc[4].u.pointer = nullptr; // old structure
+            pc[5].u.pointer = nullptr; // offset
+
+            if (structure->propertyAccessesAreCacheable()
+                && !structure->needImpurePropertyWatchpoint()) {
+                vm.heap.writeBarrier(codeBlock);
+
+                ConcurrentJSLocker locker(codeBlock->m_lock);
+
+                pc[4].u.structureID = structure->id();
+                pc[5].u.operand = slot.cachedOffset();
+            }
+        }
+    }
+
+    LLINT_RETURN_PROFILED(op_get_by_id_direct, result);
+}
+
+
 static void setupGetByIdPrototypeCache(ExecState* exec, VM& vm, Instruction* pc, JSCell* baseCell, PropertySlot& slot, const Identifier& ident)
 {
     CodeBlock* codeBlock = exec->codeBlock();
index d241f71..104a522 100644 (file)
@@ -68,6 +68,7 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_regexp);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof_custom);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_try_get_by_id);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id_direct);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_arguments_length);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_id);
index 57bbf7b..fa58b2f 100644 (file)
@@ -1390,6 +1390,25 @@ end
 # convert opcode into a get_by_id_proto_load/get_by_id_unset, respectively, after an
 # execution counter hits zero.
 
+_llint_op_get_by_id_direct:
+    traceExecution()
+    loadi 8[PC], t0
+    loadi 16[PC], t1
+    loadConstantOrVariablePayload(t0, CellTag, t3, .opGetByIdDirectSlow)
+    loadi 20[PC], t2
+    bineq JSCell::m_structureID[t3], t1, .opGetByIdDirectSlow
+    loadPropertyAtVariableOffset(t2, t3, t0, t1)
+    loadi 4[PC], t2
+    storei t0, TagOffset[cfr, t2, 8]
+    storei t1, PayloadOffset[cfr, t2, 8]
+    valueProfile(t0, t1, 24, t2)
+    dispatch(constexpr op_get_by_id_direct_length)
+
+.opGetByIdDirectSlow:
+    callSlowPath(_llint_slow_path_get_by_id_direct)
+    dispatch(constexpr op_get_by_id_direct_length)
+
+
 _llint_op_get_by_id:
     traceExecution()
     loadi 8[PC], t0
index 060f9b9..cf8dabd 100644 (file)
@@ -1311,6 +1311,26 @@ macro storePropertyAtVariableOffset(propertyOffsetAsInt, objectAndStorage, value
     storeq value, (firstOutOfLineOffset - 2) * 8[objectAndStorage, propertyOffsetAsInt, 8]
 end
 
+
+_llint_op_get_by_id_direct:
+    traceExecution()
+    loadisFromInstruction(2, t0)
+    loadConstantOrVariableCell(t0, t3, .opGetByIdDirectSlow)
+    loadi JSCell::m_structureID[t3], t1
+    loadisFromInstruction(4, t2)
+    bineq t2, t1, .opGetByIdDirectSlow
+    loadisFromInstruction(5, t1)
+    loadisFromInstruction(1, t2)
+    loadPropertyAtVariableOffset(t1, t3, t0)
+    storeq t0, [cfr, t2, 8]
+    valueProfile(t0, 6, t1)
+    dispatch(constexpr op_get_by_id_direct_length)
+
+.opGetByIdDirectSlow:
+    callSlowPath(_llint_slow_path_get_by_id_direct)
+    dispatch(constexpr op_get_by_id_direct_length)
+
+
 _llint_op_get_by_id:
     traceExecution()
     loadisFromInstruction(2, t0)
index 366b0a2..21e80f8 100644 (file)
@@ -291,6 +291,8 @@ public:
     template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, CallbackWhenNoException) const;
     template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, PropertySlot&, CallbackWhenNoException) const;
 
+    bool getOwnPropertySlot(ExecState*, PropertyName, PropertySlot&) const;
+
     bool put(ExecState*, PropertyName, JSValue, PutPropertySlot&);
     bool putInline(ExecState*, PropertyName, JSValue, PutPropertySlot&);
     JS_EXPORT_PRIVATE bool putToPrimitive(ExecState*, PropertyName, JSValue, PutPropertySlot&);
index b5a4b4b..d0d3799 100644 (file)
@@ -859,6 +859,24 @@ ALWAYS_INLINE bool JSValue::getPropertySlot(ExecState* exec, PropertyName proper
     return object->getPropertySlot(exec, propertyName, slot);
 }
 
+ALWAYS_INLINE bool JSValue::getOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
+{
+    // If this is a primitive, we'll need to synthesize the prototype -
+    // and if it's a string there are special properties to check first.
+    auto scope = DECLARE_THROW_SCOPE(exec->vm());
+    if (UNLIKELY(!isObject())) {
+        if (isString()) {
+            scope.release();
+            return asString(*this)->getStringPropertySlot(exec, propertyName, slot);
+        }
+        if (isUndefinedOrNull())
+            throwException(exec, scope, createNotAnObjectError(exec, *this));
+        return false;
+    }
+    scope.release();
+    return asObject(asCell())->getOwnPropertySlotInline(exec, propertyName, slot);
+}
+
 ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
 {
     PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get);
index 058a7bb..c2eb8e6 100644 (file)
@@ -174,6 +174,7 @@ public:
 
     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
     JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
+    bool getOwnPropertySlotInline(ExecState*, PropertyName, PropertySlot&);
 
     // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot
     // currently returns incorrect results for the DOM window (with non-own properties)
index 29cb941..794ee20 100644 (file)
@@ -161,6 +161,14 @@ ALWAYS_INLINE bool JSObject::getNonIndexPropertySlot(ExecState* exec, PropertyNa
     }
 }
 
+inline bool JSObject::getOwnPropertySlotInline(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+    VM& vm = exec->vm();
+    if (UNLIKELY(TypeInfo::overridesGetOwnPropertySlot(inlineTypeFlags())))
+        return methodTable(vm)->getOwnPropertySlot(this, exec, propertyName, slot);
+    return JSObject::getOwnPropertySlot(this, exec, propertyName, slot);
+}
+
 inline void JSObject::putDirectWithoutTransition(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
 {
     ASSERT(!value.isGetterSetter() && !(attributes & PropertyAttribute::Accessor));