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:
jyan 2016-05-17 10:36:49 -07:00 committed by Commit bot
parent bbb8ff1907
commit e9aad72f39
14 changed files with 0 additions and 375 deletions

View File

@ -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());

View File

@ -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());

View File

@ -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) {

View File

@ -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());

View File

@ -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());

View File

@ -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) {

View File

@ -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 =

View File

@ -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 =

View File

@ -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 -------------

View File

@ -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();

View File

@ -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; }

View File

@ -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 -------------

View File

@ -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();

View File

@ -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; }