Commit f0645612 authored by jarin's avatar jarin Committed by Commit Bot

This is a first step towards reducing the number of stores/loads when...

This is a first step towards reducing the number of stores/loads when suspending/resuming a generator.

Unfortunately, even for an empty generator, we still use 8 register for various things (try-finally, copies of generator object, parser-introduced temporaries). I will try to get rid of these in separate CLs.

Changes:

- SuspendGenerator bytecode now takes register list to save.
- ResumeGenerator was split into two bytecodes:
  * Resume generator reads the state out and marks the generator as
      'executing'.
  * RestoreGeneratorRegisters reloads the registers from
      the generator.
    + this required adding support for output register list.

- Introduced generator_object_ register in the bytecode generator.
  * in subsequent CLs, I will make better use of it, the goal is
      to get rid if the .generator_object local variable.

- Taught register optimizer to flush unassigned registers.

BUG=v8:6379

Review-Url: https://codereview.chromium.org/2894293003
Cr-Commit-Position: refs/heads/master@{#45675}
parent 19f7cc49
......@@ -28,31 +28,17 @@ void BytecodeLoopAssignments::Add(interpreter::Register r) {
}
}
void BytecodeLoopAssignments::AddPair(interpreter::Register r) {
void BytecodeLoopAssignments::AddList(interpreter::Register r, uint32_t count) {
if (r.is_parameter()) {
DCHECK(interpreter::Register(r.index() + 1).is_parameter());
bit_vector_->Add(r.ToParameterIndex(parameter_count_));
bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
} else {
DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
bit_vector_->Add(parameter_count_ + r.index());
bit_vector_->Add(parameter_count_ + r.index() + 1);
}
}
void BytecodeLoopAssignments::AddTriple(interpreter::Register r) {
if (r.is_parameter()) {
DCHECK(interpreter::Register(r.index() + 1).is_parameter());
DCHECK(interpreter::Register(r.index() + 2).is_parameter());
bit_vector_->Add(r.ToParameterIndex(parameter_count_));
bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 2);
for (uint32_t i = 0; i < count; i++) {
DCHECK(interpreter::Register(r.index() + i).is_parameter());
bit_vector_->Add(r.ToParameterIndex(parameter_count_) + i);
}
} else {
DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
bit_vector_->Add(parameter_count_ + r.index());
bit_vector_->Add(parameter_count_ + r.index() + 1);
bit_vector_->Add(parameter_count_ + r.index() + 2);
for (uint32_t i = 0; i < count; i++) {
DCHECK(!interpreter::Register(r.index() + i).is_parameter());
bit_vector_->Add(parameter_count_ + r.index() + i);
}
}
}
......@@ -112,6 +98,17 @@ void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness,
}
break;
}
case OperandType::kRegOutList: {
interpreter::Register r = accessor.GetRegisterOperand(i++);
uint32_t reg_count = accessor.GetRegisterCountOperand(i);
if (!r.is_parameter()) {
for (uint32_t j = 0; j < reg_count; ++j) {
DCHECK(!interpreter::Register(r.index() + j).is_parameter());
in_liveness.MarkRegisterDead(r.index() + j);
}
}
break;
}
case OperandType::kRegOutPair: {
interpreter::Register r = accessor.GetRegisterOperand(i);
if (!r.is_parameter()) {
......@@ -227,12 +224,18 @@ void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments,
assignments.Add(accessor.GetRegisterOperand(i));
break;
}
case OperandType::kRegOutList: {
interpreter::Register r = accessor.GetRegisterOperand(i++);
uint32_t reg_count = accessor.GetRegisterCountOperand(i);
assignments.AddList(r, reg_count);
break;
}
case OperandType::kRegOutPair: {
assignments.AddPair(accessor.GetRegisterOperand(i));
assignments.AddList(accessor.GetRegisterOperand(i), 2);
break;
}
case OperandType::kRegOutTriple: {
assignments.AddTriple(accessor.GetRegisterOperand(i));
assignments.AddList(accessor.GetRegisterOperand(i), 3);
break;
}
default:
......
......@@ -24,8 +24,7 @@ class V8_EXPORT_PRIVATE BytecodeLoopAssignments {
BytecodeLoopAssignments(int parameter_count, int register_count, Zone* zone);
void Add(interpreter::Register r);
void AddPair(interpreter::Register r);
void AddTriple(interpreter::Register r);
void AddList(interpreter::Register r, uint32_t count);
void AddAll();
void Union(const BytecodeLoopAssignments& other);
......
......@@ -2277,15 +2277,20 @@ void BytecodeGraphBuilder::VisitSuspendGenerator() {
Node* state = environment()->LookupAccumulator();
Node* generator = environment()->LookupRegister(
bytecode_iterator().GetRegisterOperand(0));
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
// We assume we are storing a range starting from index 0.
CHECK_EQ(0, first_reg.index());
int register_count =
static_cast<int>(bytecode_iterator().GetRegisterCountOperand(2));
SuspendFlags flags = interpreter::SuspendGeneratorBytecodeFlags::Decode(
bytecode_iterator().GetFlagOperand(1));
bytecode_iterator().GetFlagOperand(3));
// The offsets used by the bytecode iterator are relative to a different base
// than what is used in the interpreter, hence the addition.
Node* offset =
jsgraph()->Constant(bytecode_iterator().current_offset() +
(BytecodeArray::kHeaderSize - kHeapObjectTag));
int register_count = environment()->register_count();
int value_input_count = 3 + register_count;
Node** value_inputs = local_zone()->NewArray<Node*>(value_input_count);
......@@ -2301,21 +2306,31 @@ void BytecodeGraphBuilder::VisitSuspendGenerator() {
value_input_count, value_inputs, false);
}
void BytecodeGraphBuilder::VisitResumeGenerator() {
void BytecodeGraphBuilder::VisitRestoreGeneratorState() {
Node* generator = environment()->LookupRegister(
bytecode_iterator().GetRegisterOperand(0));
Node* state =
NewNode(javascript()->GeneratorRestoreContinuation(), generator);
environment()->BindAccumulator(state, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitRestoreGeneratorRegisters() {
Node* generator =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
// We assume we are restoring registers starting fromm index 0.
CHECK_EQ(0, first_reg.index());
int register_count =
static_cast<int>(bytecode_iterator().GetRegisterCountOperand(2));
// Bijection between registers and array indices must match that used in
// InterpreterAssembler::ExportRegisterFile.
for (int i = 0; i < environment()->register_count(); ++i) {
for (int i = 0; i < register_count; ++i) {
Node* value = NewNode(javascript()->GeneratorRestoreRegister(i), generator);
environment()->BindRegister(interpreter::Register(i), value);
}
Node* state =
NewNode(javascript()->GeneratorRestoreContinuation(), generator);
environment()->BindAccumulator(state);
}
void BytecodeGraphBuilder::VisitWide() {
......
......@@ -770,8 +770,9 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* GeneratorStore(int register_count,
SuspendFlags suspend_flags);
// Used to implement Ignition's ResumeGenerator bytecode.
// Used to implement Ignition's RestoreGeneratorState bytecode.
const Operator* GeneratorRestoreContinuation();
// Used to implement Ignition's RestoreGeneratorRegisters bytecode.
const Operator* GeneratorRestoreRegister(int index);
const Operator* StackCheck();
......
......@@ -261,6 +261,15 @@ class OperandHelper<OperandType::kRegOut> {
}
};
template <>
class OperandHelper<OperandType::kRegOutList> {
public:
INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder,
RegisterList reg_list)) {
return builder->GetOutputRegisterListOperand(reg_list);
}
};
template <>
class OperandHelper<OperandType::kRegOutPair> {
public:
......@@ -1235,15 +1244,22 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadModuleVariable(int cell_index,
}
BytecodeArrayBuilder& BytecodeArrayBuilder::SuspendGenerator(
Register generator, SuspendFlags flags) {
OutputSuspendGenerator(generator,
Register generator, RegisterList registers, SuspendFlags flags) {
OutputSuspendGenerator(generator, registers, registers.register_count(),
SuspendGeneratorBytecodeFlags::Encode(flags));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator(
BytecodeArrayBuilder& BytecodeArrayBuilder::RestoreGeneratorState(
Register generator) {
OutputResumeGenerator(generator);
OutputRestoreGeneratorState(generator);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::RestoreGeneratorRegisters(
Register generator, RegisterList registers) {
OutputRestoreGeneratorRegisters(generator, registers,
registers.register_count());
return *this;
}
......
......@@ -401,8 +401,11 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// Generators.
BytecodeArrayBuilder& SuspendGenerator(Register generator,
RegisterList registers,
SuspendFlags flags);
BytecodeArrayBuilder& ResumeGenerator(Register generator);
BytecodeArrayBuilder& RestoreGeneratorState(Register generator);
BytecodeArrayBuilder& RestoreGeneratorRegisters(Register generator,
RegisterList registers);
// Exception handling.
BytecodeArrayBuilder& MarkHandler(int handler_id,
......
......@@ -174,6 +174,7 @@ std::ostream& BytecodeDecoder::Decode(std::ostream& os,
<< reg_list.last_register().ToString(parameter_count);
break;
}
case interpreter::OperandType::kRegOutList:
case interpreter::OperandType::kRegList: {
DCHECK_LT(i, number_of_operands - 1);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, i + 1),
......
......@@ -776,6 +776,7 @@ BytecodeGenerator::BytecodeGenerator(CompilationInfo* info)
execution_context_(nullptr),
execution_result_(nullptr),
generator_jump_table_(nullptr),
generator_object_(),
generator_state_(),
loop_depth_(0) {
DCHECK_EQ(closure_scope(), closure_scope()->GetClosureScope());
......@@ -962,6 +963,7 @@ void BytecodeGenerator::VisitIterationHeader(IterationStatement* stmt,
void BytecodeGenerator::BuildGeneratorPrologue() {
DCHECK_GT(info()->literal()->suspend_count(), 0);
generator_object_ = register_allocator()->NewRegister();
generator_state_ = register_allocator()->NewRegister();
generator_jump_table_ =
builder()->AllocateJumpTable(info()->literal()->suspend_count(), 0);
......@@ -970,19 +972,20 @@ void BytecodeGenerator::BuildGeneratorPrologue() {
// indicate that this is a resume call and to pass in the generator object.
// In ordinary calls, new.target is always undefined because generator
// functions are non-constructable.
Register generator_object = Register::new_target();
builder()->MoveRegister(Register::new_target(), generator_object_);
BytecodeLabel regular_call;
builder()
->LoadAccumulatorWithRegister(generator_object)
->LoadAccumulatorWithRegister(generator_object_)
.JumpIfUndefined(&regular_call);
// This is a resume call. Restore the current context and the registers,
// then perform state dispatch.
Register generator_context = register_allocator()->NewRegister();
builder()
->CallRuntime(Runtime::kInlineGeneratorGetContext, generator_object)
->CallRuntime(Runtime::kInlineGeneratorGetContext, generator_object_)
.PushContext(generator_context)
.ResumeGenerator(generator_object)
.RestoreGeneratorState(generator_object_)
.StoreAccumulatorInRegister(generator_state_)
.SwitchOnSmiNoFeedback(generator_jump_table_);
// We fall through when the generator state is not in the jump table.
......@@ -2518,8 +2521,8 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
}
}
void BytecodeGenerator::BuildGeneratorSuspend(Suspend* expr,
Register generator) {
void BytecodeGenerator::BuildGeneratorSuspend(Suspend* expr, Register generator,
RegisterList registers_to_save) {
RegisterAllocationScope register_scope(this);
builder()->SetExpressionPosition(expr);
......@@ -2528,7 +2531,7 @@ void BytecodeGenerator::BuildGeneratorSuspend(Suspend* expr,
// Save context, registers, and state. Then return.
builder()
->LoadLiteral(Smi::FromInt(expr->suspend_id()))
.SuspendGenerator(generator, expr->flags());
.SuspendGenerator(generator, registers_to_save, expr->flags());
if (expr->IsNonInitialAsyncGeneratorYield()) {
// AsyncGenerator yields (with the exception of the initial yield) delegate
......@@ -2549,10 +2552,13 @@ void BytecodeGenerator::BuildGeneratorSuspend(Suspend* expr,
builder()->Return(); // Hard return (ignore any finally blocks).
}
void BytecodeGenerator::BuildGeneratorResume(Suspend* expr,
Register generator) {
void BytecodeGenerator::BuildGeneratorResume(
Suspend* expr, Register generator, RegisterList registers_to_restore) {
RegisterAllocationScope register_scope(this);
// Clobbers all registers.
builder()->RestoreGeneratorRegisters(generator_object_, registers_to_restore);
// Update state to indicate that we have finished resuming. Loop headers
// rely on this.
builder()
......@@ -2623,10 +2629,11 @@ void BytecodeGenerator::BuildGeneratorResume(Suspend* expr,
void BytecodeGenerator::VisitSuspend(Suspend* expr) {
Register generator = VisitForRegisterValue(expr->generator_object());
BuildGeneratorSuspend(expr, generator);
RegisterList registers(0, register_allocator()->next_register_index());
BuildGeneratorSuspend(expr, generator, registers);
builder()->Bind(generator_jump_table_, static_cast<int>(expr->suspend_id()));
// Upon resume, we continue here.
BuildGeneratorResume(expr, generator);
BuildGeneratorResume(expr, generator, registers);
}
void BytecodeGenerator::VisitThrow(Throw* expr) {
......@@ -3728,7 +3735,8 @@ void BytecodeGenerator::BuildGeneratorObjectVariableInitialization() {
builder()
->MoveRegister(Register::function_closure(), args[0])
.MoveRegister(builder()->Receiver(), args[1])
.CallRuntime(Runtime::kInlineCreateJSGeneratorObject, args);
.CallRuntime(Runtime::kInlineCreateJSGeneratorObject, args)
.StoreAccumulatorInRegister(generator_object_);
BuildVariableAssignment(closure_scope()->generator_object_var(), Token::INIT,
FeedbackSlot::Invalid(), HoleCheckMode::kElided);
}
......
......@@ -136,8 +136,10 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildNewLocalWithContext(Scope* scope);
void BuildGeneratorPrologue();
void BuildGeneratorSuspend(Suspend* expr, Register generator);
void BuildGeneratorResume(Suspend* expr, Register generator);
void BuildGeneratorSuspend(Suspend* expr, Register generator,
RegisterList registers_to_save);
void BuildGeneratorResume(Suspend* expr, Register generator,
RegisterList registers_to_restore);
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest);
......@@ -256,6 +258,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
ExpressionResultScope* execution_result_;
BytecodeJumpTable* generator_jump_table_;
Register generator_object_;
Register generator_state_;
int loop_depth_;
};
......
......@@ -20,6 +20,7 @@ namespace interpreter {
#define REGISTER_OUTPUT_OPERAND_TYPE_LIST(V) \
V(RegOut, OperandTypeInfo::kScalableSignedByte) \
V(RegOutList, OperandTypeInfo::kScalableSignedByte) \
V(RegOutPair, OperandTypeInfo::kScalableSignedByte) \
V(RegOutTriple, OperandTypeInfo::kScalableSignedByte)
......
......@@ -304,6 +304,7 @@ BytecodeRegisterOptimizer::GetMaterializedEquivalentNotAccumulator(
void BytecodeRegisterOptimizer::Materialize(RegisterInfo* info) {
if (!info->materialized()) {
RegisterInfo* materialized = info->GetMaterializedEquivalent();
DCHECK_NOT_NULL(materialized);
OutputRegisterTransfer(materialized, info);
}
}
......@@ -410,7 +411,7 @@ void BytecodeRegisterOptimizer::GrowRegisterMap(Register reg) {
for (size_t i = old_size; i < new_size; ++i) {
register_info_table_[i] =
new (zone()) RegisterInfo(RegisterFromRegisterInfoTableIndex(i),
NextEquivalenceId(), false, false);
NextEquivalenceId(), true, false);
}
}
}
......
......@@ -67,7 +67,7 @@ class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
if (Bytecodes::IsJump(bytecode) || Bytecodes::IsSwitch(bytecode) ||
bytecode == Bytecode::kDebugger ||
bytecode == Bytecode::kSuspendGenerator ||
bytecode == Bytecode::kResumeGenerator) {
bytecode == Bytecode::kRestoreGeneratorRegisters) {
// All state must be flushed before emitting
// - a jump bytecode (as the register equivalents at the jump target
// aren't known)
......@@ -75,7 +75,7 @@ class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
// aren't known)
// - a call to the debugger (as it can manipulate locals and parameters),
// - a generator suspend (as this involves saving all registers).
// - a generator resume (as this involves restoring all registers).
// - a generator register restore.
Flush();
}
......
......@@ -315,9 +315,11 @@ namespace interpreter {
V(Return, AccumulatorUse::kRead) \
\
/* Generators */ \
V(RestoreGeneratorState, AccumulatorUse::kWrite, OperandType::kReg) \
V(SuspendGenerator, AccumulatorUse::kRead, OperandType::kReg, \
OperandType::kFlag8) \
V(ResumeGenerator, AccumulatorUse::kWrite, OperandType::kReg) \
OperandType::kRegList, OperandType::kRegCount, OperandType::kFlag8) \
V(RestoreGeneratorRegisters, AccumulatorUse::kNone, OperandType::kReg, \
OperandType::kRegOutList, OperandType::kRegCount) \
\
/* Debugger */ \
V(Debugger, AccumulatorUse::kNone) \
......@@ -821,6 +823,7 @@ class V8_EXPORT_PRIVATE Bytecodes final {
case OperandType::kRegOutTriple:
return 3;
case OperandType::kRegList:
case OperandType::kRegOutList:
UNREACHABLE();
default:
return 0;
......
......@@ -1390,20 +1390,24 @@ bool InterpreterAssembler::TargetSupportsUnalignedAccess() {
#endif
}
Node* InterpreterAssembler::RegisterCount() {
Node* bytecode_array = LoadRegister(Register::bytecode_array());
Node* frame_size = LoadObjectField(
bytecode_array, BytecodeArray::kFrameSizeOffset, MachineType::Uint32());
return WordShr(ChangeUint32ToWord(frame_size),
IntPtrConstant(kPointerSizeLog2));
void InterpreterAssembler::AbortIfRegisterCountInvalid(Node* register_file,
Node* register_count) {
Node* array_size = LoadAndUntagFixedArrayBaseLength(register_file);
Label ok(this), abort(this, Label::kDeferred);
Branch(UintPtrLessThanOrEqual(register_count, array_size), &ok, &abort);
BIND(&abort);
Abort(kInvalidRegisterFileInGenerator);
Goto(&ok);
BIND(&ok);
}
Node* InterpreterAssembler::ExportRegisterFile(Node* array) {
Node* register_count = RegisterCount();
Node* InterpreterAssembler::ExportRegisterFile(Node* array,
Node* register_count) {
if (FLAG_debug_code) {
Node* array_size = LoadAndUntagFixedArrayBaseLength(array);
AbortIfWordNotEqual(array_size, register_count,
kInvalidRegisterFileInGenerator);
AbortIfRegisterCountInvalid(array, register_count);
}
Variable var_index(this, MachineType::PointerRepresentation());
......@@ -1432,12 +1436,10 @@ Node* InterpreterAssembler::ExportRegisterFile(Node* array) {
return array;
}
Node* InterpreterAssembler::ImportRegisterFile(Node* array) {
Node* register_count = RegisterCount();
Node* InterpreterAssembler::ImportRegisterFile(Node* array,
Node* register_count) {
if (FLAG_debug_code) {
Node* array_size = LoadAndUntagFixedArrayBaseLength(array);
AbortIfWordNotEqual(array_size, register_count,
kInvalidRegisterFileInGenerator);
AbortIfRegisterCountInvalid(array, register_count);
}
Variable var_index(this, MachineType::PointerRepresentation());
......
......@@ -81,12 +81,11 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
void GotoIfHasContextExtensionUpToDepth(compiler::Node* context,
compiler::Node* depth, Label* target);
// Number of registers.
compiler::Node* RegisterCount();
// Backup/restore register file to/from a fixed array of the correct length.
compiler::Node* ExportRegisterFile(compiler::Node* array);
compiler::Node* ImportRegisterFile(compiler::Node* array);
compiler::Node* ExportRegisterFile(compiler::Node* array,
compiler::Node* register_count);
compiler::Node* ImportRegisterFile(compiler::Node* array,
compiler::Node* register_count);
// Loads from and stores to the interpreter register file.
compiler::Node* LoadRegister(Register reg);
......@@ -221,6 +220,9 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
void Abort(BailoutReason bailout_reason);
void AbortIfWordNotEqual(compiler::Node* lhs, compiler::Node* rhs,
BailoutReason bailout_reason);
// Abort if |register_count| is invalid for given register file array.
void AbortIfRegisterCountInvalid(compiler::Node* register_file,
compiler::Node* register_count);
// Dispatch to frame dropper trampoline if necessary.
void MaybeDropFrames(compiler::Node* context);
......
......@@ -3612,14 +3612,15 @@ IGNITION_HANDLER(Illegal, InterpreterAssembler) { Abort(kInvalidBytecode); }
// No operation.
IGNITION_HANDLER(Nop, InterpreterAssembler) { Dispatch(); }
// SuspendGenerator <generator>
// SuspendGenerator <generator> <first input register> <register count> <flags>
//
// Exports the register file and stores it into the generator. Also stores the
// current context, the state given in the accumulator, and the current bytecode
// offset (for debugging purposes) into the generator.
IGNITION_HANDLER(SuspendGenerator, InterpreterAssembler) {
Node* generator_reg = BytecodeOperandReg(0);
Node* flags = BytecodeOperandFlag(1);
Node* flags = BytecodeOperandFlag(3);
Node* generator = LoadRegister(generator_reg);
Label if_stepping(this, Label::kDeferred), ok(this);
......@@ -3637,7 +3638,13 @@ IGNITION_HANDLER(SuspendGenerator, InterpreterAssembler) {
Node* context = GetContext();
Node* state = GetAccumulator();
ExportRegisterFile(array);
// Bytecode operand 1 should be always 0 (we are always store registers
// from the beginning).
CSA_ASSERT(this, WordEqual(BytecodeOperandReg(1),
IntPtrConstant(Register(0).ToOperand())));
// Bytecode operand 2 is the number of registers to store to the generator.
Node* register_count = ChangeUint32ToWord(BytecodeOperandCount(2));
ExportRegisterFile(array, register_count);
StoreObjectField(generator, JSGeneratorObject::kContextOffset, context);
StoreObjectField(generator, JSGeneratorObject::kContinuationOffset, state);
......@@ -3686,18 +3693,14 @@ IGNITION_HANDLER(SuspendGenerator, InterpreterAssembler) {
}
}
// ResumeGenerator <generator>
// RestoreGeneratorState <generator>
//
// Imports the register file stored in the generator. Also loads the
// generator's state and stores it in the accumulator, before overwriting it
// with kGeneratorExecuting.
IGNITION_HANDLER(ResumeGenerator, InterpreterAssembler) {
// Loads the generator's state and stores it in the accumulator,
// before overwriting it with kGeneratorExecuting.
IGNITION_HANDLER(RestoreGeneratorState, InterpreterAssembler) {
Node* generator_reg = BytecodeOperandReg(0);
Node* generator = LoadRegister(generator_reg);
ImportRegisterFile(
LoadObjectField(generator, JSGeneratorObject::kRegisterFileOffset));
Node* old_state =
LoadObjectField(generator, JSGeneratorObject::kContinuationOffset);
Node* new_state = Int32Constant(JSGeneratorObject::kGeneratorExecuting);
......@@ -3708,6 +3711,28 @@ IGNITION_HANDLER(ResumeGenerator, InterpreterAssembler) {
Dispatch();
}
// RestoreGeneratorRegisters <generator> <first output register> <register
// count>
//
// Imports the register file stored in the generator.
IGNITION_HANDLER(RestoreGeneratorRegisters, InterpreterAssembler) {
Node* generator_reg = BytecodeOperandReg(0);
// Bytecode operand 1 is the start register. It should always be 0, so let's
// ignore it.
CSA_ASSERT(this, WordEqual(BytecodeOperandReg(1),
IntPtrConstant(Register(0).ToOperand())));
// Bytecode operand 2 is the number of registers to store to the generator.
Node* register_count = ChangeUint32ToWord(BytecodeOperandCount(2));
Node* generator = LoadRegister(generator_reg);
ImportRegisterFile(
LoadObjectField(generator, JSGeneratorObject::kRegisterFileOffset),
register_count);
Dispatch();
}
} // namespace
Handle<Code> GenerateBytecodeHandler(Isolate* isolate, Bytecode bytecode,
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function* h() { try {yield 42} finally {yield 43} };
function* g() { yield* h(); };
let x = g();
x.next();
%OptimizeFunctionOnNextCall(g);
x.throw(670);
try { x.next() } catch (e) { }
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function* foo() {
for (let i = 0; i < 10; i++) {
yield 1;
}
return 0;
}
g = foo();
%OptimizeFunctionOnNextCall(foo);
g.next();
g.next();
......@@ -358,7 +358,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.StoreModuleVariable(1, 42);
// Emit generator operations.
builder.SuspendGenerator(reg, SuspendFlags::kYield).ResumeGenerator(reg);
builder.SuspendGenerator(reg, reg_list, SuspendFlags::kYield)
.RestoreGeneratorState(reg)
.RestoreGeneratorRegisters(reg, reg_list);
// Intrinsics handled by the interpreter.
builder.CallRuntime(Runtime::kInlineIsArray, reg_list);
......
......@@ -392,6 +392,7 @@ TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) {
case interpreter::OperandType::kRegList:
case interpreter::OperandType::kReg:
case interpreter::OperandType::kRegOut:
case interpreter::OperandType::kRegOutList:
case interpreter::OperandType::kRegOutPair:
case interpreter::OperandType::kRegOutTriple:
case interpreter::OperandType::kRegPair:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment