PPC/s390: [turbofan] [builtins] Unify construct builtins for JS functions and classes and add inlining and deoptimizer support
Port 2026d5cb79
R=tebbi@chromium.org, joransiu@ca.ibm.com, jyan@ca.ibm.com, michael_dawson@ca.ibm.com
BUG=v8:6180
LOG=N
Review-Url: https://codereview.chromium.org/2875073003
Cr-Commit-Position: refs/heads/master@{#45383}
This commit is contained in:
parent
d692f5e683
commit
6bd1aeee00
@ -446,9 +446,7 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
|
||||
|
||||
namespace {
|
||||
|
||||
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
|
||||
bool create_implicit_receiver,
|
||||
bool disallow_non_object_return) {
|
||||
void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) {
|
||||
Label post_instantiation_deopt_entry;
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3 : number of arguments
|
||||
@ -459,74 +457,46 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
|
||||
// -- sp[...]: constructor arguments
|
||||
// -----------------------------------
|
||||
|
||||
Isolate* isolate = masm->isolate();
|
||||
|
||||
// Enter a construct frame.
|
||||
{
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::CONSTRUCT);
|
||||
|
||||
// Preserve the incoming parameters on the stack.
|
||||
|
||||
if (!create_implicit_receiver) {
|
||||
__ SmiTag(r7, r3, SetRC);
|
||||
__ Push(cp, r7);
|
||||
__ PushRoot(Heap::kTheHoleValueRootIndex);
|
||||
} else {
|
||||
__ SmiTag(r3);
|
||||
__ Push(cp, r3);
|
||||
|
||||
// Allocate the new receiver object.
|
||||
__ Push(r4, r6);
|
||||
__ Call(CodeFactory::FastNewObject(masm->isolate()).code(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ mr(r7, r3);
|
||||
__ Pop(r4, r6);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r4: constructor function
|
||||
// -- r6: new target
|
||||
// -- r7: newly allocated object
|
||||
// -----------------------------------
|
||||
|
||||
// Retrieve smi-tagged arguments count from the stack.
|
||||
__ LoadP(r3, MemOperand(sp));
|
||||
__ SmiUntag(r3, SetRC);
|
||||
|
||||
// Push the allocated receiver to the stack. We need two copies
|
||||
// because we may have to return the original one and the calling
|
||||
// conventions dictate that the called function pops the receiver.
|
||||
__ Push(r7, r7);
|
||||
}
|
||||
|
||||
// Deoptimizer re-enters stub code here.
|
||||
__ bind(&post_instantiation_deopt_entry);
|
||||
|
||||
__ SmiTag(r3);
|
||||
__ Push(cp, r3);
|
||||
__ SmiUntag(r3, SetRC);
|
||||
// The receiver for the builtin/api call.
|
||||
__ PushRoot(Heap::kTheHoleValueRootIndex);
|
||||
// Set up pointer to last argument.
|
||||
__ addi(r5, fp, Operand(StandardFrameConstants::kCallerSPOffset));
|
||||
__ addi(r7, fp, Operand(StandardFrameConstants::kCallerSPOffset));
|
||||
|
||||
// Copy arguments and receiver to the expression stack.
|
||||
// r3: number of arguments
|
||||
// r4: constructor function
|
||||
// r5: address of last argument (caller sp)
|
||||
// r6: new target
|
||||
// cr0: condition indicating whether r3 is zero
|
||||
// sp[0]: receiver
|
||||
// sp[1]: receiver
|
||||
// sp[2]: number of arguments (smi-tagged)
|
||||
|
||||
Label loop, no_args;
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3: number of arguments (untagged)
|
||||
// -- r4: constructor function
|
||||
// -- r6: new target
|
||||
// -- r7: pointer to last argument
|
||||
// -- cr0: condition indicating whether r3 is zero
|
||||
// -- sp[0*kPointerSize]: the hole (receiver)
|
||||
// -- sp[1*kPointerSize]: number of arguments (tagged)
|
||||
// -- sp[2*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
__ beq(&no_args, cr0);
|
||||
__ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
|
||||
__ sub(sp, sp, ip);
|
||||
__ mtctr(r3);
|
||||
__ bind(&loop);
|
||||
__ subi(ip, ip, Operand(kPointerSize));
|
||||
__ LoadPX(r0, MemOperand(r5, ip));
|
||||
__ LoadPX(r0, MemOperand(r7, ip));
|
||||
__ StorePX(r0, MemOperand(sp, ip));
|
||||
__ bdnz(&loop);
|
||||
__ bind(&no_args);
|
||||
|
||||
// Call the function.
|
||||
// r3: number of arguments
|
||||
// r3: number of arguments (untagged)
|
||||
// r4: constructor function
|
||||
// r6: new target
|
||||
{
|
||||
@ -536,146 +506,231 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
|
||||
CheckDebugStepCallWrapper());
|
||||
}
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
if (create_implicit_receiver && !disallow_non_object_return &&
|
||||
!is_api_function) {
|
||||
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
}
|
||||
|
||||
// Restore context from the frame.
|
||||
// r3: result
|
||||
// sp[0]: receiver
|
||||
// sp[1]: number of arguments (smi-tagged)
|
||||
__ LoadP(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset));
|
||||
|
||||
if (create_implicit_receiver) {
|
||||
// If the result is an object (in the ECMA sense), we should get rid
|
||||
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
|
||||
// on page 74.
|
||||
Label use_receiver, return_value, do_throw;
|
||||
|
||||
// If the result is a smi, it is *not* an object in the ECMA sense.
|
||||
// r3: result
|
||||
// sp[0]: receiver
|
||||
// sp[1]: number of arguments (smi-tagged)
|
||||
// If the result is undefined, we jump out to using the implicit
|
||||
// receiver, otherwise we do a smi check and fall through to
|
||||
// check if the return value is a valid receiver.
|
||||
if (disallow_non_object_return) {
|
||||
__ CompareRoot(r3, Heap::kUndefinedValueRootIndex);
|
||||
__ beq(&use_receiver);
|
||||
__ JumpIfSmi(r3, &do_throw);
|
||||
} else {
|
||||
__ JumpIfSmi(r3, &use_receiver);
|
||||
}
|
||||
|
||||
// If the type of the result (stored in its map) is less than
|
||||
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
|
||||
__ CompareObjectType(r3, r4, r6, FIRST_JS_RECEIVER_TYPE);
|
||||
__ bge(&return_value);
|
||||
|
||||
if (disallow_non_object_return) {
|
||||
__ bind(&do_throw);
|
||||
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
|
||||
}
|
||||
|
||||
// Throw away the result of the constructor invocation and use the
|
||||
// on-stack receiver as the result.
|
||||
__ bind(&use_receiver);
|
||||
__ LoadP(r3, MemOperand(sp));
|
||||
|
||||
// Remove receiver from the stack, remove caller arguments, and
|
||||
// return.
|
||||
__ bind(&return_value);
|
||||
// r3: result
|
||||
// sp[0]: receiver (newly allocated object)
|
||||
// sp[1]: number of arguments (smi-tagged)
|
||||
__ LoadP(r4, MemOperand(sp, 1 * kPointerSize));
|
||||
} else {
|
||||
__ LoadP(r4, MemOperand(sp));
|
||||
}
|
||||
// Restore smi-tagged arguments count from the frame.
|
||||
__ LoadP(r4, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
|
||||
// Leave construct frame.
|
||||
}
|
||||
// Remove caller arguments from the stack and return.
|
||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
|
||||
// ES6 9.2.2. Step 13+
|
||||
// For derived class constructors, throw a TypeError here if the result
|
||||
// is not a JSReceiver. For the base constructor, we've already checked
|
||||
// the result, so we omit the check.
|
||||
if (disallow_non_object_return && !create_implicit_receiver) {
|
||||
Label do_throw, dont_throw;
|
||||
__ JumpIfSmi(r3, &do_throw);
|
||||
__ SmiToPtrArrayOffset(r4, r4);
|
||||
__ add(sp, sp, r4);
|
||||
__ addi(sp, sp, Operand(kPointerSize));
|
||||
__ blr();
|
||||
}
|
||||
|
||||
// The construct stub for ES5 constructor functions and ES6 class constructors.
|
||||
void Generate_JSConstructStubGeneric(MacroAssembler* masm,
|
||||
bool restrict_constructor_return) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3: number of arguments (untagged)
|
||||
// -- r4: constructor function
|
||||
// -- r6: new target
|
||||
// -- cp: context
|
||||
// -- lr: return address
|
||||
// -- sp[...]: constructor arguments
|
||||
// -----------------------------------
|
||||
|
||||
// Enter a construct frame.
|
||||
{
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::CONSTRUCT);
|
||||
Label post_instantiation_deopt_entry, not_create_implicit_receiver;
|
||||
|
||||
// Preserve the incoming parameters on the stack.
|
||||
__ SmiTag(r3);
|
||||
__ Push(cp, r3, r4, r6);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- sp[0*kPointerSize]: new target
|
||||
// -- r4 and sp[1*kPointerSize]: constructor function
|
||||
// -- sp[2*kPointerSize]: number of arguments (tagged)
|
||||
// -- sp[3*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
|
||||
__ LoadP(r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lwz(r7, FieldMemOperand(r7, SharedFunctionInfo::kCompilerHintsOffset));
|
||||
__ TestBitMask(r7,
|
||||
FunctionKind::kDerivedConstructor
|
||||
<< SharedFunctionInfo::kFunctionKindShift,
|
||||
r0);
|
||||
__ bne(¬_create_implicit_receiver, cr0);
|
||||
|
||||
// If not derived class constructor: Allocate the new receiver object.
|
||||
__ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1,
|
||||
r7, r8);
|
||||
__ Call(CodeFactory::FastNewObject(masm->isolate()).code(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ b(&post_instantiation_deopt_entry);
|
||||
|
||||
// Else: use TheHoleValue as receiver for constructor call
|
||||
__ bind(¬_create_implicit_receiver);
|
||||
__ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3: receiver
|
||||
// -- Slot 3 / sp[0*kPointerSize]: new target
|
||||
// -- Slot 2 / sp[1*kPointerSize]: constructor function
|
||||
// -- Slot 1 / sp[2*kPointerSize]: number of arguments (tagged)
|
||||
// -- Slot 0 / sp[3*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
// Deoptimizer enters here.
|
||||
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
__ bind(&post_instantiation_deopt_entry);
|
||||
|
||||
// Restore new target.
|
||||
__ Pop(r6);
|
||||
// Push the allocated receiver to the stack. We need two copies
|
||||
// because we may have to return the original one and the calling
|
||||
// conventions dictate that the called function pops the receiver.
|
||||
__ Push(r3, r3);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r6: new target
|
||||
// -- sp[0*kPointerSize]: implicit receiver
|
||||
// -- sp[1*kPointerSize]: implicit receiver
|
||||
// -- sp[2*kPointerSize]: constructor function
|
||||
// -- sp[3*kPointerSize]: number of arguments (tagged)
|
||||
// -- sp[4*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
|
||||
// Restore constructor function and argument count.
|
||||
__ LoadP(r4, MemOperand(fp, ConstructFrameConstants::kConstructorOffset));
|
||||
__ LoadP(r3, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(r3, SetRC);
|
||||
|
||||
// Set up pointer to last argument.
|
||||
__ addi(r7, fp, Operand(StandardFrameConstants::kCallerSPOffset));
|
||||
|
||||
// Copy arguments and receiver to the expression stack.
|
||||
Label loop, no_args;
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3: number of arguments (untagged)
|
||||
// -- r6: new target
|
||||
// -- r7: pointer to last argument
|
||||
// -- cr0: condition indicating whether r3 is zero
|
||||
// -- sp[0*kPointerSize]: implicit receiver
|
||||
// -- sp[1*kPointerSize]: implicit receiver
|
||||
// -- r4 and sp[2*kPointerSize]: constructor function
|
||||
// -- sp[3*kPointerSize]: number of arguments (tagged)
|
||||
// -- sp[4*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
__ beq(&no_args, cr0);
|
||||
__ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
|
||||
__ sub(sp, sp, ip);
|
||||
__ mtctr(r3);
|
||||
__ bind(&loop);
|
||||
__ subi(ip, ip, Operand(kPointerSize));
|
||||
__ LoadPX(r0, MemOperand(r7, ip));
|
||||
__ StorePX(r0, MemOperand(sp, ip));
|
||||
__ bdnz(&loop);
|
||||
__ bind(&no_args);
|
||||
|
||||
// Call the function.
|
||||
{
|
||||
ConstantPoolUnavailableScope constant_pool_unavailable(masm);
|
||||
ParameterCount actual(r3);
|
||||
__ InvokeFunction(r4, r6, actual, CALL_FUNCTION,
|
||||
CheckDebugStepCallWrapper());
|
||||
}
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0: constructor result
|
||||
// -- sp[0*kPointerSize]: implicit receiver
|
||||
// -- sp[1*kPointerSize]: constructor function
|
||||
// -- sp[2*kPointerSize]: number of arguments
|
||||
// -- sp[3*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
|
||||
// Restore the context from the frame.
|
||||
__ LoadP(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset));
|
||||
|
||||
// If the result is an object (in the ECMA sense), we should get rid
|
||||
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
|
||||
// on page 74.
|
||||
Label use_receiver, do_throw, other_result, leave_frame;
|
||||
|
||||
// If the result is undefined, we jump out to using the implicit receiver.
|
||||
__ JumpIfRoot(r3, Heap::kUndefinedValueRootIndex, &use_receiver);
|
||||
|
||||
// Otherwise we do a smi check and fall through to check if the return value
|
||||
// is a valid receiver.
|
||||
|
||||
// If the result is a smi, it is *not* an object in the ECMA sense.
|
||||
__ JumpIfSmi(r3, &other_result);
|
||||
|
||||
// If the type of the result (stored in its map) is less than
|
||||
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
|
||||
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
||||
__ CompareObjectType(r3, r6, r6, FIRST_JS_RECEIVER_TYPE);
|
||||
__ bge(&dont_throw);
|
||||
__ CompareObjectType(r3, r7, r7, FIRST_JS_RECEIVER_TYPE);
|
||||
__ bge(&leave_frame);
|
||||
|
||||
__ bind(&other_result);
|
||||
// The result is now neither undefined nor an object.
|
||||
if (restrict_constructor_return) {
|
||||
// Throw if constructor function is a class constructor
|
||||
__ LoadP(r7, MemOperand(fp, ConstructFrameConstants::kConstructorOffset));
|
||||
__ LoadP(r7, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lwz(r7, FieldMemOperand(r7, SharedFunctionInfo::kCompilerHintsOffset));
|
||||
__ TestBitMask(r7,
|
||||
FunctionKind::kClassConstructor
|
||||
<< SharedFunctionInfo::kFunctionKindShift,
|
||||
r0);
|
||||
__ beq(&use_receiver, cr0);
|
||||
|
||||
} else {
|
||||
__ b(&use_receiver);
|
||||
}
|
||||
|
||||
__ bind(&do_throw);
|
||||
{
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
|
||||
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
|
||||
}
|
||||
__ bind(&dont_throw);
|
||||
|
||||
// Throw away the result of the constructor invocation and use the
|
||||
// on-stack receiver as the result.
|
||||
__ bind(&use_receiver);
|
||||
__ LoadP(r3, MemOperand(sp));
|
||||
__ JumpIfRoot(r3, Heap::kTheHoleValueRootIndex, &do_throw);
|
||||
|
||||
__ bind(&leave_frame);
|
||||
// Restore smi-tagged arguments count from the frame.
|
||||
__ LoadP(r4, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
// Leave construct frame.
|
||||
}
|
||||
|
||||
// Remove caller arguments from the stack and return.
|
||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
|
||||
__ SmiToPtrArrayOffset(r4, r4);
|
||||
__ add(sp, sp, r4);
|
||||
__ addi(sp, sp, Operand(kPointerSize));
|
||||
if (create_implicit_receiver) {
|
||||
__ IncrementCounter(isolate->counters()->constructed_objects(), 1, r4, r5);
|
||||
}
|
||||
__ blr();
|
||||
// Store offset of trampoline address for deoptimizer. This is the bailout
|
||||
// point after the receiver instantiation but before the function invocation.
|
||||
// We need to restore some registers in order to continue the above code.
|
||||
if (create_implicit_receiver && !disallow_non_object_return &&
|
||||
!is_api_function) {
|
||||
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3 : newly allocated object
|
||||
// -- sp[0] : constructor function
|
||||
// -----------------------------------
|
||||
|
||||
__ pop(r4);
|
||||
__ Push(r3, r3);
|
||||
|
||||
// Retrieve smi-tagged arguments count from the stack.
|
||||
__ LoadP(r3, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(r3);
|
||||
|
||||
// Retrieve the new target value from the stack. This was placed into the
|
||||
// frame description in place of the receiver by the optimizing compiler.
|
||||
__ addi(r6, fp, Operand(StandardFrameConstants::kCallerSPOffset));
|
||||
__ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
|
||||
__ LoadPX(r6, MemOperand(r6, ip));
|
||||
|
||||
// Continue with constructor function invocation.
|
||||
__ b(&post_instantiation_deopt_entry);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, true, false);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, true, false, false);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, false, false);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, true, true);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
|
||||
void Builtins::Generate_JSConstructStubGenericRestrictedReturn(
|
||||
MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, false, true);
|
||||
Generate_JSConstructStubGeneric(masm, true);
|
||||
}
|
||||
void Builtins::Generate_JSConstructStubGenericUnrestrictedReturn(
|
||||
MacroAssembler* masm) {
|
||||
Generate_JSConstructStubGeneric(masm, false);
|
||||
}
|
||||
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
|
||||
Generate_JSBuiltinsConstructStubHelper(masm);
|
||||
}
|
||||
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
|
||||
Generate_JSBuiltinsConstructStubHelper(masm);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -442,9 +442,7 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
|
||||
|
||||
namespace {
|
||||
|
||||
void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
|
||||
bool create_implicit_receiver,
|
||||
bool disallow_non_object_return) {
|
||||
void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) {
|
||||
Label post_instantiation_deopt_entry;
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2 : number of arguments
|
||||
@ -455,52 +453,18 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
|
||||
// -- sp[...]: constructor arguments
|
||||
// -----------------------------------
|
||||
|
||||
Isolate* isolate = masm->isolate();
|
||||
|
||||
// Enter a construct frame.
|
||||
{
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::CONSTRUCT);
|
||||
|
||||
// Preserve the incoming parameters on the stack.
|
||||
|
||||
if (!create_implicit_receiver) {
|
||||
__ SmiTag(r6, r2);
|
||||
__ LoadAndTestP(r6, r6);
|
||||
__ Push(cp, r6);
|
||||
__ PushRoot(Heap::kTheHoleValueRootIndex);
|
||||
} else {
|
||||
__ SmiTag(r2);
|
||||
__ Push(cp, r2);
|
||||
|
||||
// Allocate the new receiver object.
|
||||
__ Push(r3, r5);
|
||||
__ Call(CodeFactory::FastNewObject(masm->isolate()).code(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ LoadRR(r6, r2);
|
||||
__ Pop(r3, r5);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r3: constructor function
|
||||
// -- r5: new target
|
||||
// -- r6: newly allocated object
|
||||
// -----------------------------------
|
||||
|
||||
// Retrieve smi-tagged arguments count from the stack.
|
||||
__ LoadP(r2, MemOperand(sp));
|
||||
__ SmiUntag(r2);
|
||||
__ LoadAndTestP(r2, r2);
|
||||
|
||||
// Push the allocated receiver to the stack. We need two copies
|
||||
// because we may have to return the original one and the calling
|
||||
// conventions dictate that the called function pops the receiver.
|
||||
__ Push(r6, r6);
|
||||
}
|
||||
|
||||
// Deoptimizer re-enters stub code here.
|
||||
__ bind(&post_instantiation_deopt_entry);
|
||||
|
||||
__ SmiTag(r2);
|
||||
__ Push(cp, r2);
|
||||
__ SmiUntag(r2);
|
||||
// The receiver for the builtin/api call.
|
||||
__ PushRoot(Heap::kTheHoleValueRootIndex);
|
||||
// Set up pointer to last argument.
|
||||
__ la(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset));
|
||||
__ la(r6, MemOperand(fp, StandardFrameConstants::kCallerSPOffset));
|
||||
|
||||
// Copy arguments and receiver to the expression stack.
|
||||
// r2: number of arguments
|
||||
@ -518,7 +482,7 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
|
||||
__ LoadRR(r1, r2);
|
||||
__ bind(&loop);
|
||||
__ lay(ip, MemOperand(ip, -kPointerSize));
|
||||
__ LoadP(r0, MemOperand(ip, r4));
|
||||
__ LoadP(r0, MemOperand(ip, r6));
|
||||
__ StoreP(r0, MemOperand(ip, sp));
|
||||
__ BranchOnCount(r1, &loop);
|
||||
__ bind(&no_args);
|
||||
@ -532,148 +496,228 @@ void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function,
|
||||
__ InvokeFunction(r3, r5, actual, CALL_FUNCTION,
|
||||
CheckDebugStepCallWrapper());
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
if (create_implicit_receiver && !disallow_non_object_return &&
|
||||
!is_api_function) {
|
||||
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
}
|
||||
|
||||
// Restore context from the frame.
|
||||
// r2: result
|
||||
// sp[0]: receiver
|
||||
// sp[1]: number of arguments (smi-tagged)
|
||||
__ LoadP(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset));
|
||||
|
||||
if (create_implicit_receiver) {
|
||||
// If the result is an object (in the ECMA sense), we should get rid
|
||||
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
|
||||
// on page 74.
|
||||
Label use_receiver, return_value, do_throw;
|
||||
|
||||
// If the result is a smi, it is *not* an object in the ECMA sense.
|
||||
// r2: result
|
||||
// sp[0]: receiver
|
||||
// sp[1]: new.target
|
||||
// sp[2]: number of arguments (smi-tagged)
|
||||
// If the result is undefined, we jump out to using the implicit
|
||||
// receiver, otherwise we do a smi check and fall through to
|
||||
// check if the return value is a valid receiver.
|
||||
if (disallow_non_object_return) {
|
||||
__ CompareRoot(r2, Heap::kUndefinedValueRootIndex);
|
||||
__ beq(&use_receiver);
|
||||
__ JumpIfSmi(r2, &do_throw);
|
||||
} else {
|
||||
__ JumpIfSmi(r2, &use_receiver);
|
||||
}
|
||||
|
||||
// If the type of the result (stored in its map) is less than
|
||||
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
|
||||
__ CompareObjectType(r2, r3, r5, FIRST_JS_RECEIVER_TYPE);
|
||||
__ bge(&return_value);
|
||||
|
||||
if (disallow_non_object_return) {
|
||||
__ bind(&do_throw);
|
||||
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
|
||||
}
|
||||
|
||||
// Throw away the result of the constructor invocation and use the
|
||||
// on-stack receiver as the result.
|
||||
__ bind(&use_receiver);
|
||||
__ LoadP(r2, MemOperand(sp));
|
||||
|
||||
// Remove receiver from the stack, remove caller arguments, and
|
||||
// return.
|
||||
__ bind(&return_value);
|
||||
// r2: result
|
||||
// sp[0]: receiver (newly allocated object)
|
||||
// sp[1]: number of arguments (smi-tagged)
|
||||
__ LoadP(r3, MemOperand(sp, 1 * kPointerSize));
|
||||
} else {
|
||||
__ LoadP(r3, MemOperand(sp));
|
||||
}
|
||||
// Restore smi-tagged arguments count from the frame.
|
||||
__ LoadP(r3, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
|
||||
// Leave construct frame.
|
||||
}
|
||||
// Remove caller arguments from the stack and return.
|
||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
|
||||
// ES6 9.2.2. Step 13+
|
||||
// For derived class constructors, throw a TypeError here if the result
|
||||
// is not a JSReceiver. For the base constructor, we've already checked
|
||||
// the result, so we omit the check.
|
||||
if (disallow_non_object_return && !create_implicit_receiver) {
|
||||
Label do_throw, dont_throw;
|
||||
__ JumpIfSmi(r2, &do_throw);
|
||||
__ SmiToPtrArrayOffset(r3, r3);
|
||||
__ AddP(sp, sp, r3);
|
||||
__ AddP(sp, sp, Operand(kPointerSize));
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
// The construct stub for ES5 constructor functions and ES6 class constructors.
|
||||
void Generate_JSConstructStubGeneric(MacroAssembler* masm,
|
||||
bool restrict_constructor_return) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2: number of arguments (untagged)
|
||||
// -- r3: constructor function
|
||||
// -- r5: new target
|
||||
// -- cp: context
|
||||
// -- lr: return address
|
||||
// -- sp[...]: constructor arguments
|
||||
// -----------------------------------
|
||||
|
||||
// Enter a construct frame.
|
||||
{
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::CONSTRUCT);
|
||||
Label post_instantiation_deopt_entry, not_create_implicit_receiver;
|
||||
|
||||
// Preserve the incoming parameters on the stack.
|
||||
__ SmiTag(r2);
|
||||
__ Push(cp, r2, r3, r5);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- sp[0*kPointerSize]: new target
|
||||
// -- r3 and sp[1*kPointerSize]: constructor function
|
||||
// -- sp[2*kPointerSize]: number of arguments (tagged)
|
||||
// -- sp[3*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
|
||||
__ LoadP(r6, FieldMemOperand(r3, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ LoadlW(r6,
|
||||
FieldMemOperand(r6, SharedFunctionInfo::kCompilerHintsOffset));
|
||||
__ TestBitMask(r6,
|
||||
FunctionKind::kDerivedConstructor
|
||||
<< SharedFunctionInfo::kFunctionKindShift,
|
||||
r0);
|
||||
__ bne(¬_create_implicit_receiver);
|
||||
|
||||
// If not derived class constructor: Allocate the new receiver object.
|
||||
__ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1,
|
||||
r6, r7);
|
||||
__ Call(CodeFactory::FastNewObject(masm->isolate()).code(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ b(&post_instantiation_deopt_entry);
|
||||
|
||||
// Else: use TheHoleValue as receiver for constructor call
|
||||
__ bind(¬_create_implicit_receiver);
|
||||
__ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2: receiver
|
||||
// -- Slot 3 / sp[0*kPointerSize]: new target
|
||||
// -- Slot 2 / sp[1*kPointerSize]: constructor function
|
||||
// -- Slot 1 / sp[2*kPointerSize]: number of arguments (tagged)
|
||||
// -- Slot 0 / sp[3*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
// Deoptimizer enters here.
|
||||
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
__ bind(&post_instantiation_deopt_entry);
|
||||
|
||||
// Restore new target.
|
||||
__ Pop(r5);
|
||||
// Push the allocated receiver to the stack. We need two copies
|
||||
// because we may have to return the original one and the calling
|
||||
// conventions dictate that the called function pops the receiver.
|
||||
__ Push(r2, r2);
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r5: new target
|
||||
// -- sp[0*kPointerSize]: implicit receiver
|
||||
// -- sp[1*kPointerSize]: implicit receiver
|
||||
// -- sp[2*kPointerSize]: constructor function
|
||||
// -- sp[3*kPointerSize]: number of arguments (tagged)
|
||||
// -- sp[4*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
|
||||
// Restore constructor function and argument count.
|
||||
__ LoadP(r3, MemOperand(fp, ConstructFrameConstants::kConstructorOffset));
|
||||
__ LoadP(r2, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(r2);
|
||||
|
||||
// Set up pointer to last argument.
|
||||
__ la(r6, MemOperand(fp, StandardFrameConstants::kCallerSPOffset));
|
||||
|
||||
// Copy arguments and receiver to the expression stack.
|
||||
Label loop, no_args;
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2: number of arguments (untagged)
|
||||
// -- r5: new target
|
||||
// -- r6: pointer to last argument
|
||||
// -- cr0: condition indicating whether r2 is zero
|
||||
// -- sp[0*kPointerSize]: implicit receiver
|
||||
// -- sp[1*kPointerSize]: implicit receiver
|
||||
// -- r3 and sp[2*kPointerSize]: constructor function
|
||||
// -- sp[3*kPointerSize]: number of arguments (tagged)
|
||||
// -- sp[4*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
|
||||
__ beq(&no_args);
|
||||
__ ShiftLeftP(ip, r2, Operand(kPointerSizeLog2));
|
||||
__ SubP(sp, sp, ip);
|
||||
__ LoadRR(r1, r2);
|
||||
__ bind(&loop);
|
||||
__ lay(ip, MemOperand(ip, -kPointerSize));
|
||||
__ LoadP(r0, MemOperand(ip, r6));
|
||||
__ StoreP(r0, MemOperand(ip, sp));
|
||||
__ BranchOnCount(r1, &loop);
|
||||
__ bind(&no_args);
|
||||
|
||||
// Call the function.
|
||||
ParameterCount actual(r2);
|
||||
__ InvokeFunction(r3, r5, actual, CALL_FUNCTION,
|
||||
CheckDebugStepCallWrapper());
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0: constructor result
|
||||
// -- sp[0*kPointerSize]: implicit receiver
|
||||
// -- sp[1*kPointerSize]: constructor function
|
||||
// -- sp[2*kPointerSize]: number of arguments
|
||||
// -- sp[3*kPointerSize]: context
|
||||
// -----------------------------------
|
||||
|
||||
// Store offset of return address for deoptimizer.
|
||||
masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
|
||||
// Restore the context from the frame.
|
||||
__ LoadP(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset));
|
||||
|
||||
// If the result is an object (in the ECMA sense), we should get rid
|
||||
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
|
||||
// on page 74.
|
||||
Label use_receiver, do_throw, other_result, leave_frame;
|
||||
|
||||
// If the result is undefined, we jump out to using the implicit receiver.
|
||||
__ JumpIfRoot(r2, Heap::kUndefinedValueRootIndex, &use_receiver);
|
||||
|
||||
// Otherwise we do a smi check and fall through to check if the return value
|
||||
// is a valid receiver.
|
||||
|
||||
// If the result is a smi, it is *not* an object in the ECMA sense.
|
||||
__ JumpIfSmi(r2, &other_result);
|
||||
|
||||
// If the type of the result (stored in its map) is less than
|
||||
// FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense.
|
||||
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
||||
__ CompareObjectType(r2, r5, r5, FIRST_JS_RECEIVER_TYPE);
|
||||
__ bge(&dont_throw);
|
||||
__ CompareObjectType(r2, r6, r6, FIRST_JS_RECEIVER_TYPE);
|
||||
__ bge(&leave_frame);
|
||||
|
||||
__ bind(&other_result);
|
||||
// The result is now neither undefined nor an object.
|
||||
if (restrict_constructor_return) {
|
||||
// Throw if constructor function is a class constructor
|
||||
__ LoadP(r6, MemOperand(fp, ConstructFrameConstants::kConstructorOffset));
|
||||
__ LoadP(r6, FieldMemOperand(r6, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ LoadlW(r6,
|
||||
FieldMemOperand(r6, SharedFunctionInfo::kCompilerHintsOffset));
|
||||
__ TestBitMask(r6,
|
||||
FunctionKind::kClassConstructor
|
||||
<< SharedFunctionInfo::kFunctionKindShift,
|
||||
r0);
|
||||
__ beq(&use_receiver);
|
||||
} else {
|
||||
__ b(&use_receiver);
|
||||
}
|
||||
__ bind(&do_throw);
|
||||
{
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
|
||||
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
|
||||
}
|
||||
__ bind(&dont_throw);
|
||||
|
||||
// Throw away the result of the constructor invocation and use the
|
||||
// on-stack receiver as the result.
|
||||
__ bind(&use_receiver);
|
||||
__ LoadP(r2, MemOperand(sp));
|
||||
__ JumpIfRoot(r2, Heap::kTheHoleValueRootIndex, &do_throw);
|
||||
|
||||
__ bind(&leave_frame);
|
||||
// Restore smi-tagged arguments count from the frame.
|
||||
__ LoadP(r3, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
// Leave construct frame.
|
||||
}
|
||||
|
||||
// Remove caller arguments from the stack and return.
|
||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
|
||||
__ SmiToPtrArrayOffset(r3, r3);
|
||||
__ AddP(sp, sp, r3);
|
||||
__ AddP(sp, sp, Operand(kPointerSize));
|
||||
if (create_implicit_receiver) {
|
||||
__ IncrementCounter(isolate->counters()->constructed_objects(), 1, r3, r4);
|
||||
}
|
||||
__ Ret();
|
||||
|
||||
// Store offset of trampoline address for deoptimizer. This is the bailout
|
||||
// point after the receiver instantiation but before the function invocation.
|
||||
// We need to restore some registers in order to continue the above code.
|
||||
if (create_implicit_receiver && !disallow_non_object_return &&
|
||||
!is_api_function) {
|
||||
masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset(
|
||||
masm->pc_offset());
|
||||
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2 : newly allocated object
|
||||
// -- sp[0] : constructor function
|
||||
// -----------------------------------
|
||||
|
||||
__ pop(r3);
|
||||
__ Push(r2, r2);
|
||||
|
||||
// Retrieve smi-tagged arguments count from the stack.
|
||||
__ LoadP(r2, MemOperand(fp, ConstructFrameConstants::kLengthOffset));
|
||||
__ SmiUntag(r2);
|
||||
|
||||
// Retrieve the new target value from the stack. This was placed into the
|
||||
// frame description in place of the receiver by the optimizing compiler.
|
||||
__ la(r5, MemOperand(fp, StandardFrameConstants::kCallerSPOffset));
|
||||
__ ShiftLeftP(ip, r2, Operand(kPointerSizeLog2));
|
||||
__ LoadP(r5, MemOperand(r5, ip));
|
||||
|
||||
// Continue with constructor function invocation.
|
||||
__ b(&post_instantiation_deopt_entry);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, true, false);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, true, false, false);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, false, false);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSBuiltinsConstructStubForBase(MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, true, true);
|
||||
}
|
||||
|
||||
void Builtins::Generate_JSBuiltinsConstructStubForDerived(
|
||||
void Builtins::Generate_JSConstructStubGenericRestrictedReturn(
|
||||
MacroAssembler* masm) {
|
||||
Generate_JSConstructStubHelper(masm, false, false, true);
|
||||
Generate_JSConstructStubGeneric(masm, true);
|
||||
}
|
||||
void Builtins::Generate_JSConstructStubGenericUnrestrictedReturn(
|
||||
MacroAssembler* masm) {
|
||||
Generate_JSConstructStubGeneric(masm, false);
|
||||
}
|
||||
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
|
||||
Generate_JSBuiltinsConstructStubHelper(masm);
|
||||
}
|
||||
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
|
||||
Generate_JSBuiltinsConstructStubHelper(masm);
|
||||
}
|
||||
|
||||
// static
|
||||
|
Loading…
Reference in New Issue
Block a user