PPC/S390: [es6] Reintroduce the instanceof operator in the backends.
port 551e0aa11b
Original Commit Messag:
This adds back the instanceof operator support in the backends and
introduces a @@hasInstance protector cell on the isolate that guards the
fast path for the InstanceOfStub. This way we recover the ~10%
regression on Octane EarleyBoyer in Crankshaft and greatly improve
TurboFan and Ignition performance of instanceof.
R=bmeurer@chromium.org, ishell@chromium.org, joransiu@ca.ibm.com, michael_dawson@ca.ibm.com, mbrandy@us.ibm.com
TBR=hpayer@chromium.org,rossberg@chromium.org
BUG=chromium:597249, v8:4447
LOG=n
Review-Url: https://codereview.chromium.org/1989523002
Cr-Commit-Position: refs/heads/master@{#36290}
This commit is contained in:
parent
bbb8ff1907
commit
e9aad72f39
@ -2551,16 +2551,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
|
|
||||||
DCHECK(ToRegister(instr->context()).is(cp));
|
|
||||||
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
|
|
||||||
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
|
|
||||||
DCHECK(ToRegister(instr->result()).is(r3));
|
|
||||||
InstanceOfStub stub(isolate());
|
|
||||||
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void LCodeGen::DoHasInPrototypeChainAndBranch(
|
void LCodeGen::DoHasInPrototypeChainAndBranch(
|
||||||
LHasInPrototypeChainAndBranch* instr) {
|
LHasInPrototypeChainAndBranch* instr) {
|
||||||
Register const object = ToRegister(instr->object());
|
Register const object = ToRegister(instr->object());
|
||||||
|
@ -949,17 +949,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
|
|
||||||
LOperand* left =
|
|
||||||
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
|
|
||||||
LOperand* right =
|
|
||||||
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
|
|
||||||
LOperand* context = UseFixed(instr->context(), cp);
|
|
||||||
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
|
|
||||||
return MarkAsCall(DefineFixed(result, r3), instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
|
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
|
||||||
HHasInPrototypeChainAndBranch* instr) {
|
HHasInPrototypeChainAndBranch* instr) {
|
||||||
LOperand* object = UseRegister(instr->object());
|
LOperand* object = UseRegister(instr->object());
|
||||||
|
@ -79,7 +79,6 @@ class LCodeGen;
|
|||||||
V(HasInPrototypeChainAndBranch) \
|
V(HasInPrototypeChainAndBranch) \
|
||||||
V(HasInstanceTypeAndBranch) \
|
V(HasInstanceTypeAndBranch) \
|
||||||
V(InnerAllocatedObject) \
|
V(InnerAllocatedObject) \
|
||||||
V(InstanceOf) \
|
|
||||||
V(InstructionGap) \
|
V(InstructionGap) \
|
||||||
V(Integer32ToDouble) \
|
V(Integer32ToDouble) \
|
||||||
V(InvokeFunction) \
|
V(InvokeFunction) \
|
||||||
@ -1116,22 +1115,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
|
|
||||||
public:
|
|
||||||
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
|
|
||||||
inputs_[0] = context;
|
|
||||||
inputs_[1] = left;
|
|
||||||
inputs_[2] = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOperand* context() const { return inputs_[0]; }
|
|
||||||
LOperand* left() const { return inputs_[1]; }
|
|
||||||
LOperand* right() const { return inputs_[2]; }
|
|
||||||
|
|
||||||
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
|
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
|
||||||
public:
|
public:
|
||||||
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
|
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
|
||||||
|
@ -2537,15 +2537,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
|
|||||||
EmitBranch(instr, eq);
|
EmitBranch(instr, eq);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
|
|
||||||
DCHECK(ToRegister(instr->context()).is(cp));
|
|
||||||
DCHECK(ToRegister(instr->left()).is(InstanceOfDescriptor::LeftRegister()));
|
|
||||||
DCHECK(ToRegister(instr->right()).is(InstanceOfDescriptor::RightRegister()));
|
|
||||||
DCHECK(ToRegister(instr->result()).is(r2));
|
|
||||||
InstanceOfStub stub(isolate());
|
|
||||||
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCodeGen::DoHasInPrototypeChainAndBranch(
|
void LCodeGen::DoHasInPrototypeChainAndBranch(
|
||||||
LHasInPrototypeChainAndBranch* instr) {
|
LHasInPrototypeChainAndBranch* instr) {
|
||||||
Register const object = ToRegister(instr->object());
|
Register const object = ToRegister(instr->object());
|
||||||
|
@ -865,16 +865,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
|
|||||||
return DefineAsRegister(new (zone()) LArgumentsElements);
|
return DefineAsRegister(new (zone()) LArgumentsElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
|
|
||||||
LOperand* left =
|
|
||||||
UseFixed(instr->left(), InstanceOfDescriptor::LeftRegister());
|
|
||||||
LOperand* right =
|
|
||||||
UseFixed(instr->right(), InstanceOfDescriptor::RightRegister());
|
|
||||||
LOperand* context = UseFixed(instr->context(), cp);
|
|
||||||
LInstanceOf* result = new (zone()) LInstanceOf(context, left, right);
|
|
||||||
return MarkAsCall(DefineFixed(result, r2), instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
|
LInstruction* LChunkBuilder::DoHasInPrototypeChainAndBranch(
|
||||||
HHasInPrototypeChainAndBranch* instr) {
|
HHasInPrototypeChainAndBranch* instr) {
|
||||||
LOperand* object = UseRegister(instr->object());
|
LOperand* object = UseRegister(instr->object());
|
||||||
|
@ -79,7 +79,6 @@ class LCodeGen;
|
|||||||
V(HasInPrototypeChainAndBranch) \
|
V(HasInPrototypeChainAndBranch) \
|
||||||
V(HasInstanceTypeAndBranch) \
|
V(HasInstanceTypeAndBranch) \
|
||||||
V(InnerAllocatedObject) \
|
V(InnerAllocatedObject) \
|
||||||
V(InstanceOf) \
|
|
||||||
V(InstructionGap) \
|
V(InstructionGap) \
|
||||||
V(Integer32ToDouble) \
|
V(Integer32ToDouble) \
|
||||||
V(InvokeFunction) \
|
V(InvokeFunction) \
|
||||||
@ -1036,21 +1035,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
|
|||||||
Token::Value op() const { return hydrogen()->token(); }
|
Token::Value op() const { return hydrogen()->token(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class LInstanceOf final : public LTemplateInstruction<1, 3, 0> {
|
|
||||||
public:
|
|
||||||
LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
|
|
||||||
inputs_[0] = context;
|
|
||||||
inputs_[1] = left;
|
|
||||||
inputs_[2] = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOperand* context() const { return inputs_[0]; }
|
|
||||||
LOperand* left() const { return inputs_[1]; }
|
|
||||||
LOperand* right() const { return inputs_[2]; }
|
|
||||||
|
|
||||||
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
|
|
||||||
};
|
|
||||||
|
|
||||||
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
|
class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 0> {
|
||||||
public:
|
public:
|
||||||
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
|
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
|
||||||
|
@ -3117,12 +3117,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
|
|||||||
context()->Plug(r3);
|
context()->Plug(r3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
|
|
||||||
DCHECK_EQ(0, expr->arguments()->length());
|
|
||||||
__ LoadNativeContextSlot(Context::ORDINARY_HAS_INSTANCE_INDEX, r3);
|
|
||||||
context()->Plug(r3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
|
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
|
||||||
DCHECK(expr->arguments()->length() == 0);
|
DCHECK(expr->arguments()->length() == 0);
|
||||||
ExternalReference debug_is_active =
|
ExternalReference debug_is_active =
|
||||||
|
@ -3037,12 +3037,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
|
|||||||
context()->Plug(r2);
|
context()->Plug(r2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullCodeGenerator::EmitGetOrdinaryHasInstance(CallRuntime* expr) {
|
|
||||||
DCHECK_EQ(0, expr->arguments()->length());
|
|
||||||
__ LoadNativeContextSlot(Context::ORDINARY_HAS_INSTANCE_INDEX, r2);
|
|
||||||
context()->Plug(r2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
|
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
|
||||||
DCHECK(expr->arguments()->length() == 0);
|
DCHECK(expr->arguments()->length() == 0);
|
||||||
ExternalReference debug_is_active =
|
ExternalReference debug_is_active =
|
||||||
|
@ -1770,28 +1770,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
|
|||||||
__ TailCallRuntime(Runtime::kThrowNotDateError);
|
__ TailCallRuntime(Runtime::kThrowNotDateError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
|
|
||||||
// ----------- S t a t e -------------
|
|
||||||
// -- r3 : argc
|
|
||||||
// -- sp[0] : first argument (left-hand side)
|
|
||||||
// -- sp[4] : receiver (right-hand side)
|
|
||||||
// -----------------------------------
|
|
||||||
|
|
||||||
{
|
|
||||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
|
||||||
__ LoadP(InstanceOfDescriptor::LeftRegister(),
|
|
||||||
MemOperand(fp, 2 * kPointerSize)); // Load left-hand side.
|
|
||||||
__ LoadP(InstanceOfDescriptor::RightRegister(),
|
|
||||||
MemOperand(fp, 3 * kPointerSize)); // Load right-hand side.
|
|
||||||
InstanceOfStub stub(masm->isolate(), true);
|
|
||||||
__ CallStub(&stub);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop the argument and the receiver.
|
|
||||||
__ Ret(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
|
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
|
@ -1372,126 +1372,6 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InstanceOfStub::Generate(MacroAssembler* masm) {
|
|
||||||
Register const object = r4; // Object (lhs).
|
|
||||||
Register const function = r3; // Function (rhs).
|
|
||||||
Register const object_map = r5; // Map of {object}.
|
|
||||||
Register const function_map = r6; // Map of {function}.
|
|
||||||
Register const function_prototype = r7; // Prototype of {function}.
|
|
||||||
Register const scratch = r8;
|
|
||||||
|
|
||||||
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
|
|
||||||
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
|
|
||||||
|
|
||||||
// Check if {object} is a smi.
|
|
||||||
Label object_is_smi;
|
|
||||||
__ JumpIfSmi(object, &object_is_smi);
|
|
||||||
|
|
||||||
// Lookup the {function} and the {object} map in the global instanceof cache.
|
|
||||||
// Note: This is safe because we clear the global instanceof cache whenever
|
|
||||||
// we change the prototype of any object.
|
|
||||||
Label fast_case, slow_case;
|
|
||||||
__ LoadP(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
|
|
||||||
__ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
|
|
||||||
__ bne(&fast_case);
|
|
||||||
__ CompareRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
|
|
||||||
__ bne(&fast_case);
|
|
||||||
__ LoadRoot(r3, Heap::kInstanceofCacheAnswerRootIndex);
|
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// If {object} is a smi we can safely return false if {function} is a JS
|
|
||||||
// function, otherwise we have to miss to the runtime and throw an exception.
|
|
||||||
__ bind(&object_is_smi);
|
|
||||||
__ JumpIfSmi(function, &slow_case);
|
|
||||||
__ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
|
|
||||||
__ bne(&slow_case);
|
|
||||||
__ LoadRoot(r3, Heap::kFalseValueRootIndex);
|
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// Fast-case: The {function} must be a valid JSFunction.
|
|
||||||
__ bind(&fast_case);
|
|
||||||
__ JumpIfSmi(function, &slow_case);
|
|
||||||
__ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
|
|
||||||
__ bne(&slow_case);
|
|
||||||
|
|
||||||
// Go to the runtime if the function is not a constructor.
|
|
||||||
__ lbz(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
|
|
||||||
__ TestBit(scratch, Map::kIsConstructor, r0);
|
|
||||||
__ beq(&slow_case, cr0);
|
|
||||||
|
|
||||||
// Ensure that {function} has an instance prototype.
|
|
||||||
__ TestBit(scratch, Map::kHasNonInstancePrototype, r0);
|
|
||||||
__ bne(&slow_case, cr0);
|
|
||||||
|
|
||||||
// Get the "prototype" (or initial map) of the {function}.
|
|
||||||
__ LoadP(function_prototype,
|
|
||||||
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
|
||||||
__ AssertNotSmi(function_prototype);
|
|
||||||
|
|
||||||
// Resolve the prototype if the {function} has an initial map. Afterwards the
|
|
||||||
// {function_prototype} will be either the JSReceiver prototype object or the
|
|
||||||
// hole value, which means that no instances of the {function} were created so
|
|
||||||
// far and hence we should return false.
|
|
||||||
Label function_prototype_valid;
|
|
||||||
__ CompareObjectType(function_prototype, scratch, scratch, MAP_TYPE);
|
|
||||||
__ bne(&function_prototype_valid);
|
|
||||||
__ LoadP(function_prototype,
|
|
||||||
FieldMemOperand(function_prototype, Map::kPrototypeOffset));
|
|
||||||
__ bind(&function_prototype_valid);
|
|
||||||
__ AssertNotSmi(function_prototype);
|
|
||||||
|
|
||||||
// Update the global instanceof cache with the current {object} map and
|
|
||||||
// {function}. The cached answer will be set when it is known below.
|
|
||||||
__ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
|
|
||||||
__ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
|
|
||||||
|
|
||||||
// Loop through the prototype chain looking for the {function} prototype.
|
|
||||||
// Assume true, and change to false if not found.
|
|
||||||
Register const object_instance_type = function_map;
|
|
||||||
Register const map_bit_field = function_map;
|
|
||||||
Register const null = scratch;
|
|
||||||
Register const result = r3;
|
|
||||||
|
|
||||||
Label done, loop, fast_runtime_fallback;
|
|
||||||
__ LoadRoot(result, Heap::kTrueValueRootIndex);
|
|
||||||
__ LoadRoot(null, Heap::kNullValueRootIndex);
|
|
||||||
__ bind(&loop);
|
|
||||||
|
|
||||||
// Check if the object needs to be access checked.
|
|
||||||
__ lbz(map_bit_field, FieldMemOperand(object_map, Map::kBitFieldOffset));
|
|
||||||
__ TestBit(map_bit_field, Map::kIsAccessCheckNeeded, r0);
|
|
||||||
__ bne(&fast_runtime_fallback, cr0);
|
|
||||||
// Check if the current object is a Proxy.
|
|
||||||
__ CompareInstanceType(object_map, object_instance_type, JS_PROXY_TYPE);
|
|
||||||
__ beq(&fast_runtime_fallback);
|
|
||||||
|
|
||||||
__ LoadP(object, FieldMemOperand(object_map, Map::kPrototypeOffset));
|
|
||||||
__ cmp(object, function_prototype);
|
|
||||||
__ beq(&done);
|
|
||||||
__ cmp(object, null);
|
|
||||||
__ LoadP(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
|
|
||||||
__ bne(&loop);
|
|
||||||
__ LoadRoot(result, Heap::kFalseValueRootIndex);
|
|
||||||
__ bind(&done);
|
|
||||||
__ StoreRoot(result, Heap::kInstanceofCacheAnswerRootIndex);
|
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// Found Proxy or access check needed: Call the runtime
|
|
||||||
__ bind(&fast_runtime_fallback);
|
|
||||||
__ Push(object, function_prototype);
|
|
||||||
// Invalidate the instanceof cache.
|
|
||||||
__ LoadSmiLiteral(scratch, Smi::FromInt(0));
|
|
||||||
__ StoreRoot(scratch, Heap::kInstanceofCacheFunctionRootIndex);
|
|
||||||
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
|
|
||||||
|
|
||||||
// Slow-case: Call the %InstanceOf runtime function.
|
|
||||||
__ bind(&slow_case);
|
|
||||||
__ Push(object, function);
|
|
||||||
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
|
|
||||||
: Runtime::kInstanceOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
||||||
Label miss;
|
Label miss;
|
||||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||||
|
@ -46,10 +46,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return r5; }
|
|||||||
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return r3; }
|
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return r3; }
|
||||||
|
|
||||||
|
|
||||||
const Register InstanceOfDescriptor::LeftRegister() { return r4; }
|
|
||||||
const Register InstanceOfDescriptor::RightRegister() { return r3; }
|
|
||||||
|
|
||||||
|
|
||||||
const Register StringCompareDescriptor::LeftRegister() { return r4; }
|
const Register StringCompareDescriptor::LeftRegister() { return r4; }
|
||||||
const Register StringCompareDescriptor::RightRegister() { return r3; }
|
const Register StringCompareDescriptor::RightRegister() { return r3; }
|
||||||
|
|
||||||
|
@ -1731,28 +1731,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
|
|||||||
__ TailCallRuntime(Runtime::kThrowNotDateError);
|
__ TailCallRuntime(Runtime::kThrowNotDateError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
void Builtins::Generate_FunctionHasInstance(MacroAssembler* masm) {
|
|
||||||
// ----------- S t a t e -------------
|
|
||||||
// -- r2 : argc
|
|
||||||
// -- sp[0] : first argument (left-hand side)
|
|
||||||
// -- sp[4] : receiver (right-hand side)
|
|
||||||
// -----------------------------------
|
|
||||||
|
|
||||||
{
|
|
||||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
|
||||||
__ LoadP(InstanceOfDescriptor::LeftRegister(),
|
|
||||||
MemOperand(fp, 2 * kPointerSize)); // Load left-hand side.
|
|
||||||
__ LoadP(InstanceOfDescriptor::RightRegister(),
|
|
||||||
MemOperand(fp, 3 * kPointerSize)); // Load right-hand side.
|
|
||||||
InstanceOfStub stub(masm->isolate(), true);
|
|
||||||
__ CallStub(&stub);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop the argument and the receiver.
|
|
||||||
__ Ret(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
|
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
|
@ -1367,125 +1367,6 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
|||||||
__ b(r14);
|
__ b(r14);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceOfStub::Generate(MacroAssembler* masm) {
|
|
||||||
Register const object = r3; // Object (lhs).
|
|
||||||
Register const function = r2; // Function (rhs).
|
|
||||||
Register const object_map = r4; // Map of {object}.
|
|
||||||
Register const function_map = r5; // Map of {function}.
|
|
||||||
Register const function_prototype = r6; // Prototype of {function}.
|
|
||||||
Register const scratch = r7;
|
|
||||||
|
|
||||||
DCHECK(object.is(InstanceOfDescriptor::LeftRegister()));
|
|
||||||
DCHECK(function.is(InstanceOfDescriptor::RightRegister()));
|
|
||||||
|
|
||||||
// Check if {object} is a smi.
|
|
||||||
Label object_is_smi;
|
|
||||||
__ JumpIfSmi(object, &object_is_smi);
|
|
||||||
|
|
||||||
// Lookup the {function} and the {object} map in the global instanceof cache.
|
|
||||||
// Note: This is safe because we clear the global instanceof cache whenever
|
|
||||||
// we change the prototype of any object.
|
|
||||||
Label fast_case, slow_case;
|
|
||||||
__ LoadP(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
|
|
||||||
__ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
|
|
||||||
__ bne(&fast_case);
|
|
||||||
__ CompareRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
|
|
||||||
__ bne(&fast_case);
|
|
||||||
__ LoadRoot(r2, Heap::kInstanceofCacheAnswerRootIndex);
|
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// If {object} is a smi we can safely return false if {function} is a JS
|
|
||||||
// function, otherwise we have to miss to the runtime and throw an exception.
|
|
||||||
__ bind(&object_is_smi);
|
|
||||||
__ JumpIfSmi(function, &slow_case);
|
|
||||||
__ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
|
|
||||||
__ bne(&slow_case);
|
|
||||||
__ LoadRoot(r2, Heap::kFalseValueRootIndex);
|
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// Fast-case: The {function} must be a valid JSFunction.
|
|
||||||
__ bind(&fast_case);
|
|
||||||
__ JumpIfSmi(function, &slow_case);
|
|
||||||
__ CompareObjectType(function, function_map, scratch, JS_FUNCTION_TYPE);
|
|
||||||
__ bne(&slow_case);
|
|
||||||
|
|
||||||
// Go to the runtime if the function is not a constructor.
|
|
||||||
__ LoadlB(scratch, FieldMemOperand(function_map, Map::kBitFieldOffset));
|
|
||||||
__ TestBit(scratch, Map::kIsConstructor, r0);
|
|
||||||
__ beq(&slow_case);
|
|
||||||
|
|
||||||
// Ensure that {function} has an instance prototype.
|
|
||||||
__ TestBit(scratch, Map::kHasNonInstancePrototype, r0);
|
|
||||||
__ bne(&slow_case);
|
|
||||||
|
|
||||||
// Get the "prototype" (or initial map) of the {function}.
|
|
||||||
__ LoadP(function_prototype,
|
|
||||||
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
|
||||||
__ AssertNotSmi(function_prototype);
|
|
||||||
|
|
||||||
// Resolve the prototype if the {function} has an initial map. Afterwards the
|
|
||||||
// {function_prototype} will be either the JSReceiver prototype object or the
|
|
||||||
// hole value, which means that no instances of the {function} were created so
|
|
||||||
// far and hence we should return false.
|
|
||||||
Label function_prototype_valid;
|
|
||||||
__ CompareObjectType(function_prototype, scratch, scratch, MAP_TYPE);
|
|
||||||
__ bne(&function_prototype_valid);
|
|
||||||
__ LoadP(function_prototype,
|
|
||||||
FieldMemOperand(function_prototype, Map::kPrototypeOffset));
|
|
||||||
__ bind(&function_prototype_valid);
|
|
||||||
__ AssertNotSmi(function_prototype);
|
|
||||||
|
|
||||||
// Update the global instanceof cache with the current {object} map and
|
|
||||||
// {function}. The cached answer will be set when it is known below.
|
|
||||||
__ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
|
|
||||||
__ StoreRoot(object_map, Heap::kInstanceofCacheMapRootIndex);
|
|
||||||
|
|
||||||
// Loop through the prototype chain looking for the {function} prototype.
|
|
||||||
// Assume true, and change to false if not found.
|
|
||||||
Register const object_instance_type = function_map;
|
|
||||||
Register const map_bit_field = function_map;
|
|
||||||
Register const null = scratch;
|
|
||||||
Register const result = r2;
|
|
||||||
|
|
||||||
Label done, loop, fast_runtime_fallback;
|
|
||||||
__ LoadRoot(result, Heap::kTrueValueRootIndex);
|
|
||||||
__ LoadRoot(null, Heap::kNullValueRootIndex);
|
|
||||||
__ bind(&loop);
|
|
||||||
|
|
||||||
// Check if the object needs to be access checked.
|
|
||||||
__ LoadlB(map_bit_field, FieldMemOperand(object_map, Map::kBitFieldOffset));
|
|
||||||
__ TestBit(map_bit_field, Map::kIsAccessCheckNeeded, r0);
|
|
||||||
__ bne(&fast_runtime_fallback);
|
|
||||||
// Check if the current object is a Proxy.
|
|
||||||
__ CompareInstanceType(object_map, object_instance_type, JS_PROXY_TYPE);
|
|
||||||
__ beq(&fast_runtime_fallback);
|
|
||||||
|
|
||||||
__ LoadP(object, FieldMemOperand(object_map, Map::kPrototypeOffset));
|
|
||||||
__ CmpP(object, function_prototype);
|
|
||||||
__ beq(&done);
|
|
||||||
__ CmpP(object, null);
|
|
||||||
__ LoadP(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
|
|
||||||
__ bne(&loop);
|
|
||||||
__ LoadRoot(result, Heap::kFalseValueRootIndex);
|
|
||||||
__ bind(&done);
|
|
||||||
__ StoreRoot(result, Heap::kInstanceofCacheAnswerRootIndex);
|
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// Found Proxy or access check needed: Call the runtime
|
|
||||||
__ bind(&fast_runtime_fallback);
|
|
||||||
__ Push(object, function_prototype);
|
|
||||||
// Invalidate the instanceof cache.
|
|
||||||
__ LoadSmiLiteral(scratch, Smi::FromInt(0));
|
|
||||||
__ StoreRoot(scratch, Heap::kInstanceofCacheFunctionRootIndex);
|
|
||||||
__ TailCallRuntime(Runtime::kHasInPrototypeChain);
|
|
||||||
|
|
||||||
// Slow-case: Call the %InstanceOf runtime function.
|
|
||||||
__ bind(&slow_case);
|
|
||||||
__ Push(object, function);
|
|
||||||
__ TailCallRuntime(is_es6_instanceof() ? Runtime::kOrdinaryHasInstance
|
|
||||||
: Runtime::kInstanceOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
||||||
Label miss;
|
Label miss;
|
||||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||||
|
@ -36,9 +36,6 @@ const Register LoadGlobalViaContextDescriptor::SlotRegister() { return r4; }
|
|||||||
const Register StoreGlobalViaContextDescriptor::SlotRegister() { return r4; }
|
const Register StoreGlobalViaContextDescriptor::SlotRegister() { return r4; }
|
||||||
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return r2; }
|
const Register StoreGlobalViaContextDescriptor::ValueRegister() { return r2; }
|
||||||
|
|
||||||
const Register InstanceOfDescriptor::LeftRegister() { return r3; }
|
|
||||||
const Register InstanceOfDescriptor::RightRegister() { return r2; }
|
|
||||||
|
|
||||||
const Register StringCompareDescriptor::LeftRegister() { return r3; }
|
const Register StringCompareDescriptor::LeftRegister() { return r3; }
|
||||||
const Register StringCompareDescriptor::RightRegister() { return r2; }
|
const Register StringCompareDescriptor::RightRegister() { return r2; }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user