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(
|
||||
LHasInPrototypeChainAndBranch* instr) {
|
||||
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(
|
||||
HHasInPrototypeChainAndBranch* instr) {
|
||||
LOperand* object = UseRegister(instr->object());
|
||||
|
@ -79,7 +79,6 @@ class LCodeGen;
|
||||
V(HasInPrototypeChainAndBranch) \
|
||||
V(HasInstanceTypeAndBranch) \
|
||||
V(InnerAllocatedObject) \
|
||||
V(InstanceOf) \
|
||||
V(InstructionGap) \
|
||||
V(Integer32ToDouble) \
|
||||
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> {
|
||||
public:
|
||||
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
|
||||
|
@ -2537,15 +2537,6 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
|
||||
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(
|
||||
LHasInPrototypeChainAndBranch* instr) {
|
||||
Register const object = ToRegister(instr->object());
|
||||
|
@ -865,16 +865,6 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
|
||||
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(
|
||||
HHasInPrototypeChainAndBranch* instr) {
|
||||
LOperand* object = UseRegister(instr->object());
|
||||
|
@ -79,7 +79,6 @@ class LCodeGen;
|
||||
V(HasInPrototypeChainAndBranch) \
|
||||
V(HasInstanceTypeAndBranch) \
|
||||
V(InnerAllocatedObject) \
|
||||
V(InstanceOf) \
|
||||
V(InstructionGap) \
|
||||
V(Integer32ToDouble) \
|
||||
V(InvokeFunction) \
|
||||
@ -1036,21 +1035,6 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
|
||||
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> {
|
||||
public:
|
||||
LHasInPrototypeChainAndBranch(LOperand* object, LOperand* prototype) {
|
||||
|
@ -3117,12 +3117,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
|
||||
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) {
|
||||
DCHECK(expr->arguments()->length() == 0);
|
||||
ExternalReference debug_is_active =
|
||||
|
@ -3037,12 +3037,6 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
|
||||
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) {
|
||||
DCHECK(expr->arguments()->length() == 0);
|
||||
ExternalReference debug_is_active =
|
||||
|
@ -1770,28 +1770,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
|
||||
__ 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
|
||||
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
|
||||
// ----------- 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) {
|
||||
Label miss;
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
|
@ -46,10 +46,6 @@ const Register StoreGlobalViaContextDescriptor::SlotRegister() { return r5; }
|
||||
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::RightRegister() { return r3; }
|
||||
|
||||
|
@ -1731,28 +1731,6 @@ void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm,
|
||||
__ 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
|
||||
void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
|
@ -1367,125 +1367,6 @@ void JSEntryStub::Generate(MacroAssembler* masm) {
|
||||
__ 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) {
|
||||
Label miss;
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
|
@ -36,9 +36,6 @@ const Register LoadGlobalViaContextDescriptor::SlotRegister() { return r4; }
|
||||
const Register StoreGlobalViaContextDescriptor::SlotRegister() { return r4; }
|
||||
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::RightRegister() { return r2; }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user