Ignore:
Timestamp:
Sep 17, 2019, 10:02:45 PM (6 years ago)
Author:
[email protected]
Message:

[JSC] Generator should have internal fields
https://bugs.webkit.org/show_bug.cgi?id=201159

Reviewed by Keith Miller.

JSTests:

  • stress/create-generator.js: Added.

(shouldBe):
(test.generator):
(test):

  • stress/generator-construct-failure.js: Added.

(shouldThrow):
(TypeError):

  • stress/generator-prototype-change.js: Added.

(shouldBe):
(gen):

  • stress/generator-prototype-closure.js: Added.

(shouldBe):
(test.gen):
(test):

  • stress/object-assign-fast-path.js:

Source/JavaScriptCore:

This patch makes generator's internal states InternalField instead of private properties.
Each generator function produces a generator with different Prototype, which makes generators have different Structures.
As a result, Generator.prototype.next etc.'s implementation becomes megamorphic even if it is not necessary.

If we make these structures adaptively poly-proto, some generators get poly-proto structures while others are not, resulting
in megamorphic lookup in Generator.prototype.next. If we make all the generator's structure poly-proto, it makes Generator.prototype.next
lookup suboptimal for now.

In this patch, we start with a relatively simple solution. This patch introduces JSGenerator class, and it has internal fields for generator's internal
states. We extend promise-internal-field access bytecodes to access to these fields from bytecode so that Generator.prototype.next can access
these fields without using megamorphic get_by_id_direct.

And we attach JSGeneratorType to JSGenerator so that we can efficiently implement @isGenerator() check in bytecode.

We reserve the offset = 0 slot for the future poly-proto extension for JSGenerator. By reserving this slot, non-poly-proto JSGenerator and poly-proto
JSGenerator still can offer the way to access to the same Generator internal fields with the same offset while poly-proto JSGenerator can get offset = 0
inline-storage slot for PolyProto implementation.

This patch adds op_create_generator since it is distinct from op_create_promise once we add PolyProto support.
In the future when we introduce some kind of op_create_async_generator we will probably share only one bytecode for both generator and async generator.

This patch offers around 10% improvement in JetStream2/Basic. And this patch is the basis of optimization of JetStream2/async-fs which leverages async generators significantly.

This patch includes several design decisions.

  1. We add a new JSGenerator instead of leveraging JSFinalObject. The main reason is that we would like to have JSGeneratorType to quickly query @isGenerator.
  2. This patch currently does not include object-allocation-sinking support for JSGenerator, but it is trivial, and will be added. And this patch also does not include poly-proto support for JSGenerator. The main reason is simply because this patch is already large enough, and I do not want to make this patch larger and larger.
  3. We can support arbitrary sized inline-storage: Reserving 0-5 offsets for internal fields, and start putting all the other things to the subsequent internal fields. But for now, we are not taking this approach just because I'm not sure this is necessary. If we found such a pattern, we can easily extend the current one but for now, I would like to keep this patch simple.
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • Sources.txt:
  • builtins/AsyncFunctionPrototype.js:

(globalPrivate.asyncFunctionResume):

  • builtins/GeneratorPrototype.js:

(globalPrivate.generatorResume):
(next):
(return):
(throw):

  • bytecode/BytecodeGeneratorification.cpp:

(JSC::BytecodeGeneratorification::run):

  • bytecode/BytecodeIntrinsicRegistry.cpp:

(JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):

  • bytecode/BytecodeIntrinsicRegistry.h:
  • bytecode/BytecodeList.rb:
  • bytecode/BytecodeUseDef.h:

(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::finalizeLLIntInlineCaches):

  • bytecode/SpeculatedType.cpp:

(JSC::speculationFromJSType):

  • bytecode/SpeculatedType.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::emitPutGeneratorFields):
(JSC::BytecodeGenerator::emitCreateGenerator):
(JSC::BytecodeGenerator::emitNewGenerator):
(JSC::BytecodeGenerator::emitYield):
(JSC::BytecodeGenerator::emitDelegateYield):
(JSC::BytecodeGenerator::emitGeneratorStateChange):

  • bytecompiler/BytecodeGenerator.h:

(JSC::BytecodeGenerator::emitIsGenerator):
(JSC::BytecodeGenerator::generatorStateRegister):
(JSC::BytecodeGenerator::generatorValueRegister):
(JSC::BytecodeGenerator::generatorResumeModeRegister):
(JSC::BytecodeGenerator::generatorFrameRegister):

  • bytecompiler/NodesCodegen.cpp:

(JSC::generatorInternalFieldIndex):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_getGeneratorInternalField):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_putGeneratorInternalField):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_isGenerator):
(JSC::FunctionNode::emitBytecode):

  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGClobbersExitState.cpp:

(JSC::DFG::clobbersExitState):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::ConstantFoldingPhase::foldConstants):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupIsCellWithType):

  • dfg/DFGGraph.cpp:

(JSC::DFG::Graph::dump):

  • dfg/DFGNode.h:

(JSC::DFG::Node::convertToNewGenerator):
(JSC::DFG::Node::speculatedTypeForQuery):
(JSC::DFG::Node::hasStructure):

  • dfg/DFGNodeType.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileCreatePromise):
(JSC::DFG::SpeculativeJIT::compileCreateGenerator):
(JSC::DFG::SpeculativeJIT::compileNewGenerator):

  • dfg/DFGSpeculativeJIT.h:
  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGStoreBarrierInsertionPhase.cpp:
  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileNewGenerator):
(JSC::FTL::DFG::LowerDFGToB3::compileCreatePromise):
(JSC::FTL::DFG::LowerDFGToB3::compileCreateGenerator):
(JSC::FTL::DFG::LowerDFGToB3::isCellWithType):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):

  • jit/JITOperations.cpp:
  • jit/JITOperations.h:
  • jit/JITPropertyAccess.cpp:

(JSC::JIT::emit_op_get_internal_field):
(JSC::JIT::emit_op_put_internal_field):

  • llint/LowLevelInterpreter.asm:
  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/CommonSlowPaths.h:
  • runtime/InternalFunction.cpp:

(JSC::InternalFunction::createSubclassStructureSlow):

  • runtime/InternalFunction.h:

(JSC::InternalFunction::createSubclassStructure):

  • runtime/JSGenerator.cpp: Added.

(JSC::JSGenerator::create):
(JSC::JSGenerator::createStructure):
(JSC::JSGenerator::JSGenerator):
(JSC::JSGenerator::finishCreation):
(JSC::JSGenerator::visitChildren):

  • runtime/JSGenerator.h: Copied from Source/JavaScriptCore/runtime/JSGeneratorFunction.h.
  • runtime/JSGeneratorFunction.h:
  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):

  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::generatorStructure const):

  • runtime/JSType.cpp:

(WTF::printInternal):

  • runtime/JSType.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r250005 r250025  
    3535#include "JSCInlines.h"
    3636#include "JSFunction.h"
    37 #include "JSGeneratorFunction.h"
     37#include "JSGenerator.h"
    3838#include "JSGlobalObject.h"
    3939#include "JSImmutableButterfly.h"
     
    10211021}
    10221022
     1023static JSGenerator::Field generatorInternalFieldIndex(BytecodeIntrinsicNode* node)
     1024{
     1025    if (node->emitter() == &BytecodeIntrinsicNode::emit_intrinsic_generatorFieldState)
     1026        return JSGenerator::Field::State;
     1027    if (node->emitter() == &BytecodeIntrinsicNode::emit_intrinsic_generatorFieldNext)
     1028        return JSGenerator::Field::Next;
     1029    if (node->emitter() == &BytecodeIntrinsicNode::emit_intrinsic_generatorFieldThis)
     1030        return JSGenerator::Field::This;
     1031    if (node->emitter() == &BytecodeIntrinsicNode::emit_intrinsic_generatorFieldFrame)
     1032        return JSGenerator::Field::Frame;
     1033    RELEASE_ASSERT_NOT_REACHED();
     1034    return JSGenerator::Field::State;
     1035}
     1036
    10231037RegisterID* BytecodeIntrinsicNode::emit_intrinsic_getPromiseInternalField(BytecodeGenerator& generator, RegisterID* dst)
    10241038{
     
    10291043    unsigned index = static_cast<unsigned>(promiseInternalFieldIndex(static_cast<BytecodeIntrinsicNode*>(node->m_expr)));
    10301044    ASSERT(index < JSPromise::numberOfInternalFields);
     1045    ASSERT(!node->m_next);
     1046
     1047    return generator.emitGetInternalField(generator.finalDestination(dst), base.get(), index);
     1048}
     1049
     1050RegisterID* BytecodeIntrinsicNode::emit_intrinsic_getGeneratorInternalField(BytecodeGenerator& generator, RegisterID* dst)
     1051{
     1052    ArgumentListNode* node = m_args->m_listNode;
     1053    RefPtr<RegisterID> base = generator.emitNode(node);
     1054    node = node->m_next;
     1055    RELEASE_ASSERT(node->m_expr->isBytecodeIntrinsicNode());
     1056    unsigned index = static_cast<unsigned>(generatorInternalFieldIndex(static_cast<BytecodeIntrinsicNode*>(node->m_expr)));
     1057    ASSERT(index < JSGenerator::numberOfInternalFields);
    10311058    ASSERT(!node->m_next);
    10321059
     
    11111138    unsigned index = static_cast<unsigned>(promiseInternalFieldIndex(static_cast<BytecodeIntrinsicNode*>(node->m_expr)));
    11121139    ASSERT(index < JSPromise::numberOfInternalFields);
     1140    node = node->m_next;
     1141    RefPtr<RegisterID> value = generator.emitNode(node);
     1142
     1143    ASSERT(!node->m_next);
     1144
     1145    return generator.move(dst, generator.emitPutInternalField(base.get(), index, value.get()));
     1146}
     1147
     1148RegisterID* BytecodeIntrinsicNode::emit_intrinsic_putGeneratorInternalField(BytecodeGenerator& generator, RegisterID* dst)
     1149{
     1150    ArgumentListNode* node = m_args->m_listNode;
     1151    RefPtr<RegisterID> base = generator.emitNode(node);
     1152    node = node->m_next;
     1153    RELEASE_ASSERT(node->m_expr->isBytecodeIntrinsicNode());
     1154    unsigned index = static_cast<unsigned>(generatorInternalFieldIndex(static_cast<BytecodeIntrinsicNode*>(node->m_expr)));
     1155    ASSERT(index < JSGenerator::numberOfInternalFields);
    11131156    node = node->m_next;
    11141157    RefPtr<RegisterID> value = generator.emitNode(node);
     
    12311274
    12321275    return generator.move(dst, generator.emitIdWithProfile(idValue.get(), speculation));
     1276}
     1277
     1278RegisterID* BytecodeIntrinsicNode::emit_intrinsic_isGenerator(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst)
     1279{
     1280    ArgumentListNode* node = m_args->m_listNode;
     1281    RefPtr<RegisterID> src = generator.emitNode(node);
     1282    ASSERT(!node->m_next);
     1283
     1284    return generator.move(dst, generator.emitIsGenerator(generator.tempDestination(dst), src.get()));
    12331285}
    12341286   
     
    39944046        generator.move(args.argumentRegister(argumentCount++), generator.promiseRegister());
    39954047        generator.emitLoad(args.argumentRegister(argumentCount++), jsUndefined());
    3996         generator.emitLoad(args.argumentRegister(argumentCount++), jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::NormalMode)));
     4048        generator.emitLoad(args.argumentRegister(argumentCount++), jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::NormalMode)));
    39974049        // JSTextPosition(int _line, int _offset, int _lineStartOffset)
    39984050        JSTextPosition divot(firstLine(), startOffset(), lineStartOffset());
     
    40114063        {
    40124064            RefPtr<RegisterID> condition = generator.newTemporary();
    4013             generator.emitEqualityOp<OpStricteq>(condition.get(), generator.generatorResumeModeRegister(), generator.emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::NormalMode))));
     4065            generator.emitEqualityOp<OpStricteq>(condition.get(), generator.generatorResumeModeRegister(), generator.emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::NormalMode))));
    40144066            generator.emitJumpIfTrue(condition.get(), generatorBodyLabel.get());
    40154067
    40164068            Ref<Label> throwLabel = generator.newLabel();
    4017             generator.emitEqualityOp<OpStricteq>(condition.get(), generator.generatorResumeModeRegister(), generator.emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::ThrowMode))));
     4069            generator.emitEqualityOp<OpStricteq>(condition.get(), generator.generatorResumeModeRegister(), generator.emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::ThrowMode))));
    40184070            generator.emitJumpIfTrue(condition.get(), throwLabel.get());
    40194071
Note: See TracChangeset for help on using the changeset viewer.