Commit 1e001e71 authored by mbrandy's avatar mbrandy Committed by Commit bot

PPC: [generators] Decouple generator resume from fullcodegen.

Port 974721c6

Original commit message:
    Introduce a ResumeGeneratorTrampoline, which does the actual stack state
    reconstruction (currently always restores a fullcodegen frame), and
    introduce appropriate TurboFan builtins for %GeneratorPrototype%.next,
    %GeneratorPrototype%.return and %GeneratorPrototype%.throw based on
    this native builtin.

    Also unify the flooding in case of step-in to always work based on
    JSFunction and remove the special casing for JSGeneratorObject.

R=bmeurer@chromium.org, joransiu@ca.ibm.com, jyan@ca.ibm.com, michael_dawson@ca.ibm.com, bjaideep@ca.ibm.com
BUG=chromium:513471
LOG=n

Review URL: https://codereview.chromium.org/1868683002

Cr-Commit-Position: refs/heads/master@{#35314}
parent 55515c99
......@@ -1798,7 +1798,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
// this. It stays on the stack while we update the iterator.
VisitForStackValue(expr->expression());
Label suspend, continuation, post_runtime, resume;
Label suspend, continuation, post_runtime, resume, exception;
__ b(&suspend);
__ bind(&continuation);
......@@ -1807,12 +1807,18 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
// respective resume operation).
__ RecordGeneratorContinuation();
__ pop(r4);
__ CmpSmiLiteral(r4, Smi::FromInt(JSGeneratorObject::RETURN), r0);
__ bne(&resume);
__ push(result_register());
STATIC_ASSERT(JSGeneratorObject::kNext < JSGeneratorObject::kReturn);
STATIC_ASSERT(JSGeneratorObject::kThrow > JSGeneratorObject::kReturn);
__ CmpSmiLiteral(r4, Smi::FromInt(JSGeneratorObject::kReturn), r0);
__ blt(&resume);
__ Push(result_register());
__ bgt(&exception);
EmitCreateIteratorResult(true);
EmitUnwindAndReturn();
__ bind(&exception);
__ CallRuntime(Runtime::kThrow);
__ bind(&suspend);
OperandStackDepthIncrement(1); // Not popped on this path.
VisitForAccumulatorValue(expr->generator_object());
......@@ -1838,120 +1844,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
context()->Plug(result_register());
}
void FullCodeGenerator::EmitGeneratorResume(
Expression* generator, Expression* value,
JSGeneratorObject::ResumeMode resume_mode) {
// The value stays in r3, and is ultimately read by the resumed generator, as
// if CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. Or it
// is read to throw the value when the resumed generator is already closed.
// r4 will hold the generator object until the activation has been resumed.
VisitForStackValue(generator);
VisitForAccumulatorValue(value);
PopOperand(r4);
// Store input value into generator object.
__ StoreP(result_register(),
FieldMemOperand(r4, JSGeneratorObject::kInputOffset), r0);
__ mr(r5, result_register());
__ RecordWriteField(r4, JSGeneratorObject::kInputOffset, r5, r6,
kLRHasBeenSaved, kDontSaveFPRegs);
// Load suspended function and context.
__ LoadP(cp, FieldMemOperand(r4, JSGeneratorObject::kContextOffset));
__ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset));
// Load receiver and store as the first argument.
__ LoadP(r5, FieldMemOperand(r4, JSGeneratorObject::kReceiverOffset));
__ push(r5);
// Push holes for arguments to generator function. Since the parser forced
// context allocation for any variables in generators, the actual argument
// values have already been copied into the context and these dummy values
// will never be used.
__ LoadP(r6, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset));
__ LoadWordArith(
r6, FieldMemOperand(r6, SharedFunctionInfo::kFormalParameterCountOffset));
__ LoadRoot(r5, Heap::kTheHoleValueRootIndex);
Label argument_loop, push_frame;
#if V8_TARGET_ARCH_PPC64
__ cmpi(r6, Operand::Zero());
__ beq(&push_frame);
#else
__ SmiUntag(r6, SetRC);
__ beq(&push_frame, cr0);
#endif
__ mtctr(r6);
__ bind(&argument_loop);
__ push(r5);
__ bdnz(&argument_loop);
// Enter a new JavaScript frame, and initialize its slots as they were when
// the generator was suspended.
Label resume_frame, done;
__ bind(&push_frame);
__ b(&resume_frame, SetLK);
__ b(&done);
__ bind(&resume_frame);
// lr = return address.
// fp = caller's frame pointer.
// cp = callee's context,
// r7 = callee's JS function.
__ PushStandardFrame(r7);
// Load the operand stack size.
__ LoadP(r6, FieldMemOperand(r4, JSGeneratorObject::kOperandStackOffset));
__ LoadP(r6, FieldMemOperand(r6, FixedArray::kLengthOffset));
__ SmiUntag(r6, SetRC);
// If we are sending a value and there is no operand stack, we can jump back
// in directly.
Label call_resume;
if (resume_mode == JSGeneratorObject::NEXT) {
Label slow_resume;
__ bne(&slow_resume, cr0);
__ LoadP(ip, FieldMemOperand(r7, JSFunction::kCodeEntryOffset));
{
ConstantPoolUnavailableScope constant_pool_unavailable(masm_);
if (FLAG_enable_embedded_constant_pool) {
__ LoadConstantPoolPointerRegisterFromCodeTargetAddress(ip);
}
__ LoadP(r5, FieldMemOperand(r4, JSGeneratorObject::kContinuationOffset));
__ SmiUntag(r5);
__ add(ip, ip, r5);
__ LoadSmiLiteral(r5,
Smi::FromInt(JSGeneratorObject::kGeneratorExecuting));
__ StoreP(r5, FieldMemOperand(r4, JSGeneratorObject::kContinuationOffset),
r0);
__ Push(Smi::FromInt(resume_mode)); // Consumed in continuation.
__ Jump(ip);
__ bind(&slow_resume);
}
} else {
__ beq(&call_resume, cr0);
}
// Otherwise, we push holes for the operand stack and call the runtime to fix
// up the stack and the handlers.
Label operand_loop;
__ mtctr(r6);
__ bind(&operand_loop);
__ push(r5);
__ bdnz(&operand_loop);
__ bind(&call_resume);
__ Push(Smi::FromInt(resume_mode)); // Consumed in continuation.
DCHECK(!result_register().is(r4));
__ Push(r4, result_register());
__ Push(Smi::FromInt(resume_mode));
__ CallRuntime(Runtime::kResumeJSGeneratorObject);
// Not reached: the runtime call returns elsewhere.
__ stop("not-reached");
__ bind(&done);
context()->Plug(result_register());
}
void FullCodeGenerator::PushOperands(Register reg1, Register reg2) {
OperandStackDepthIncrement(2);
__ Push(reg1, reg2);
......
......@@ -705,6 +705,130 @@ void Builtins::Generate_JSBuiltinsConstructStubForDerived(
Generate_JSConstructStubHelper(masm, false, false, true);
}
// static
void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r3 : the value to pass to the generator
// -- r4 : the JSGeneratorObject to resume
// -- r5 : the resume mode (tagged)
// -- lr : return address
// -----------------------------------
__ AssertGeneratorObject(r4);
// Store input value into generator object.
__ StoreP(r3, FieldMemOperand(r4, JSGeneratorObject::kInputOffset), r0);
__ RecordWriteField(r4, JSGeneratorObject::kInputOffset, r3, r6,
kLRHasNotBeenSaved, kDontSaveFPRegs);
// Load suspended function and context.
__ LoadP(cp, FieldMemOperand(r4, JSGeneratorObject::kContextOffset));
__ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset));
// Flood function if we are stepping.
Label skip_flooding;
ExternalReference step_in_enabled =
ExternalReference::debug_step_in_enabled_address(masm->isolate());
__ mov(ip, Operand(step_in_enabled));
__ lbz(ip, MemOperand(ip));
__ cmpi(ip, Operand::Zero());
__ beq(&skip_flooding);
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r4, r5, r7);
__ CallRuntime(Runtime::kDebugPrepareStepInIfStepping);
__ Pop(r4, r5);
__ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset));
}
__ bind(&skip_flooding);
// Push receiver.
__ LoadP(ip, FieldMemOperand(r4, JSGeneratorObject::kReceiverOffset));
__ Push(ip);
// ----------- S t a t e -------------
// -- r4 : the JSGeneratorObject to resume
// -- r5 : the resume mode (tagged)
// -- r7 : generator function
// -- cp : generator context
// -- lr : return address
// -- sp[0] : generator receiver
// -----------------------------------
// Push holes for arguments to generator function. Since the parser forced
// context allocation for any variables in generators, the actual argument
// values have already been copied into the context and these dummy values
// will never be used.
__ LoadP(r6, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset));
__ LoadWordArith(
r6, FieldMemOperand(r6, SharedFunctionInfo::kFormalParameterCountOffset));
{
Label loop, done_loop;
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
#if V8_TARGET_ARCH_PPC64
__ cmpi(r6, Operand::Zero());
__ beq(&done_loop);
#else
__ SmiUntag(r6, SetRC);
__ beq(&done_loop, cr0);
#endif
__ mtctr(r6);
__ bind(&loop);
__ push(ip);
__ bdnz(&loop);
__ bind(&done_loop);
}
// Enter a new JavaScript frame, and initialize its slots as they were when
// the generator was suspended.
FrameScope scope(masm, StackFrame::MANUAL);
__ PushStandardFrame(r7);
// Restore the operand stack.
__ LoadP(r3, FieldMemOperand(r4, JSGeneratorObject::kOperandStackOffset));
__ LoadP(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
__ addi(r3, r3,
Operand(FixedArray::kHeaderSize - kHeapObjectTag - kPointerSize));
{
Label loop, done_loop;
__ SmiUntag(r6, SetRC);
__ beq(&done_loop, cr0);
__ mtctr(r6);
__ bind(&loop);
__ LoadPU(ip, MemOperand(r3, kPointerSize));
__ Push(ip);
__ bdnz(&loop);
__ bind(&done_loop);
}
// Push resume mode (consumed in continuation).
__ Push(r5);
// Reset operand stack so we don't leak.
__ LoadRoot(ip, Heap::kEmptyFixedArrayRootIndex);
__ StoreP(ip, FieldMemOperand(r4, JSGeneratorObject::kOperandStackOffset),
r0);
// Restore value.
__ LoadP(r3, FieldMemOperand(r4, JSGeneratorObject::kInputOffset));
// Resume the generator function at the continuation.
__ LoadP(r6, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset));
__ LoadP(r6, FieldMemOperand(r6, SharedFunctionInfo::kCodeOffset));
__ addi(r6, r6, Operand(Code::kHeaderSize - kHeapObjectTag));
{
ConstantPoolUnavailableScope constant_pool_unavailable(masm);
if (FLAG_enable_embedded_constant_pool) {
__ LoadConstantPoolPointerRegisterFromCodeTargetAddress(r6);
}
__ LoadP(r5, FieldMemOperand(r4, JSGeneratorObject::kContinuationOffset));
__ SmiUntag(r5);
__ add(r6, r6, r5);
__ LoadSmiLiteral(r5, Smi::FromInt(JSGeneratorObject::kGeneratorExecuting));
__ StoreP(r5, FieldMemOperand(r4, JSGeneratorObject::kContinuationOffset),
r0);
__ Jump(r6);
}
}
void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) {
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
......
......@@ -410,6 +410,16 @@ void InterpreterCEntryDescriptor::InitializePlatformSpecific(
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void ResumeGeneratorDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
r3, // the value to pass to the generator
r4, // the JSGeneratorObject to resume
r5 // the resume mode (tagged)
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8
......
......@@ -2994,6 +2994,18 @@ void MacroAssembler::AssertBoundFunction(Register object) {
}
}
void MacroAssembler::AssertGeneratorObject(Register object) {
if (emit_debug_code()) {
STATIC_ASSERT(kSmiTag == 0);
TestIfSmi(object, r0);
Check(ne, kOperandIsASmiAndNotAGeneratorObject, cr0);
push(object);
CompareObjectType(object, object, object, JS_GENERATOR_OBJECT_TYPE);
pop(object);
Check(eq, kOperandIsNotAGeneratorObject);
}
}
void MacroAssembler::AssertReceiver(Register object) {
if (emit_debug_code()) {
STATIC_ASSERT(kSmiTag == 0);
......
......@@ -1380,6 +1380,10 @@ class MacroAssembler : public Assembler {
// enabled via --debug-code.
void AssertBoundFunction(Register object);
// Abort execution if argument is not a JSGeneratorObject,
// enabled via --debug-code.
void AssertGeneratorObject(Register object);
// Abort execution if argument is not a JSReceiver, enabled via --debug-code.
void AssertReceiver(Register object);
......
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