Commit bcee1406 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Introduce InstanceOfIC to collect rhs feedback.

This adds a new InstanceOfIC where the TestInstanceOf bytecode collects
constant feedback about the right-hand side of instanceof operators,
including both JSFunction and JSBoundFunction instances. TurboFan then
uses the feedback to optimize instanceof in places where the right-hand
side is not a known constant (known to TurboFan).

This addresses the odd performance cliff that we see with instanceof in
functions with multiple closures. It was discovered as one of the main
bottlenecks on the uglify-es test in the web-tooling-benchmark. The
uglify-es test (run in separation) is ~18% faster with this change.

On the micro-benchmark in the tracking bug we go from

  instanceofSingleClosure_Const: 69 ms.
  instanceofSingleClosure_Class: 246 ms.
  instanceofMultiClosure: 246 ms.
  instanceofParameter: 246 ms.

to

  instanceofSingleClosure_Const: 70 ms.
  instanceofSingleClosure_Class: 75 ms.
  instanceofMultiClosure: 76 ms.
  instanceofParameter: 73 ms.

boosting performance by roughly 3.6x and thus effectively removing the
performance cliff around instanceof.

Bug: v8:6936, v8:6971
Change-Id: Ib88dbb9eaef9cafa4a0e260fbbde73427a54046e
Reviewed-on: https://chromium-review.googlesource.com/730686
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48820}
parent 319b7868
......@@ -2409,7 +2409,8 @@ void BytecodeGraphBuilder::VisitTestIn() {
}
void BytecodeGraphBuilder::VisitTestInstanceOf() {
BuildTestingOp(javascript()->InstanceOf());
int const slot_index = bytecode_iterator().GetIndexOperand(1);
BuildCompareOp(javascript()->InstanceOf(CreateVectorSlotPair(slot_index)));
}
void BytecodeGraphBuilder::VisitTestUndetectable() {
......
......@@ -152,6 +152,7 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
FeedbackParameter const& p = FeedbackParameterOf(node->op());
Node* object = NodeProperties::GetValueInput(node, 0);
Node* constructor = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
......@@ -159,10 +160,18 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Check if the right hand side is a known {receiver}.
// Check if the right hand side is a known {receiver}, or
// we have feedback from the InstanceOfIC.
Handle<JSObject> receiver;
HeapObjectMatcher m(constructor);
if (!m.HasValue() || !m.Value()->IsJSObject()) return NoChange();
Handle<JSObject> receiver = Handle<JSObject>::cast(m.Value());
if (m.HasValue() && m.Value()->IsJSObject()) {
receiver = Handle<JSObject>::cast(m.Value());
} else if (p.feedback().IsValid()) {
InstanceOfICNexus nexus(p.feedback().vector(), p.feedback().slot());
if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) return NoChange();
} else {
return NoChange();
}
Handle<Map> receiver_map(receiver->map(), isolate());
// Compute property access info for @@hasInstance on {receiver}.
......@@ -188,6 +197,10 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
native_context(), access_info.receiver_maps(), holder);
}
// Check that {constructor} is actually {receiver}.
constructor = access_builder.BuildCheckValue(constructor, &effect,
control, receiver);
// Monomorphic property access.
access_builder.BuildCheckMaps(constructor, &effect, control,
access_info.receiver_maps());
......@@ -226,6 +239,10 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
}
DCHECK(constant->IsCallable());
// Check that {constructor} is actually {receiver}.
constructor =
access_builder.BuildCheckValue(constructor, &effect, control, receiver);
// Monomorphic property access.
access_builder.BuildCheckMaps(constructor, &effect, control,
access_info.receiver_maps());
......@@ -360,7 +377,7 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
NodeProperties::ReplaceValueInput(node, object, 0);
NodeProperties::ReplaceValueInput(
node, jsgraph()->HeapConstant(bound_target_function), 1);
NodeProperties::ChangeOp(node, javascript()->InstanceOf());
NodeProperties::ChangeOp(node, javascript()->InstanceOf(VectorSlotPair()));
Reduction const reduction = ReduceJSInstanceOf(node);
return reduction.Changed() ? reduction : Changed(node);
}
......
......@@ -286,6 +286,7 @@ std::ostream& operator<<(std::ostream& os, FeedbackParameter const& p) {
FeedbackParameter const& FeedbackParameterOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kJSCreateEmptyLiteralArray ||
op->opcode() == IrOpcode::kJSInstanceOf ||
op->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral);
return OpParameter<FeedbackParameter>(op);
}
......@@ -593,7 +594,6 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(HasInPrototypeChain, Operator::kNoProperties, 2, 1) \
V(InstanceOf, Operator::kNoProperties, 2, 1) \
V(OrdinaryHasInstance, Operator::kNoProperties, 2, 1) \
V(ForInEnumerate, Operator::kNoProperties, 1, 1) \
V(LoadMessage, Operator::kNoThrow | Operator::kNoWrite, 0, 1) \
......@@ -885,6 +885,15 @@ const Operator* JSOperatorBuilder::LoadProperty(
access); // parameter
}
const Operator* JSOperatorBuilder::InstanceOf(VectorSlotPair const& feedback) {
FeedbackParameter parameter(feedback);
return new (zone()) Operator1<FeedbackParameter>( // --
IrOpcode::kJSInstanceOf, Operator::kNoProperties, // opcode
"JSInstanceOf", // name
2, 1, 1, 1, 1, 2, // counts
parameter); // parameter
}
const Operator* JSOperatorBuilder::ForInNext(ForInMode mode) {
return new (zone()) Operator1<ForInMode>( // --
IrOpcode::kJSForInNext, Operator::kNoProperties, // opcode
......
......@@ -745,7 +745,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* ClassOf();
const Operator* HasInPrototypeChain();
const Operator* InstanceOf();
const Operator* InstanceOf(const VectorSlotPair& feedback);
const Operator* OrdinaryHasInstance();
const Operator* ForInEnumerate();
......
......@@ -246,6 +246,18 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
}
break;
}
case IrOpcode::kJSInstanceOf: {
DCHECK(!slot.IsInvalid());
InstanceOfICNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
return LoweringResult::Exit(node);
}
// TODO(turbofan): Should we generally support early lowering of
// JSInstanceOf operators here?
break;
}
case IrOpcode::kJSBitwiseOr:
case IrOpcode::kJSBitwiseXor:
case IrOpcode::kJSBitwiseAnd:
......
......@@ -167,6 +167,19 @@ void PropertyAccessBuilder::BuildCheckMaps(
*effect, control);
}
Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
Node* control,
Handle<HeapObject> value) {
HeapObjectMatcher m(receiver);
if (m.Is(value)) return receiver;
Node* expected = jsgraph()->HeapConstant(value);
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
*effect = graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
check, *effect, control);
return expected;
}
void PropertyAccessBuilder::AssumePrototypesStable(
Handle<Context> native_context,
std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
......
......@@ -41,6 +41,8 @@ class PropertyAccessBuilder {
Node* BuildCheckHeapObject(Node* receiver, Node** effect, Node* control);
void BuildCheckMaps(Node* receiver, Node** effect, Node* control,
std::vector<Handle<Map>> const& receiver_maps);
Node* BuildCheckValue(Node* receiver, Node** effect, Node* control,
Handle<HeapObject> value);
// Adds stability dependencies on all prototypes of every class in
// {receiver_type} up to (and including) the {holder}.
......
......@@ -44,6 +44,7 @@ FeedbackVector* FeedbackVector::cast(Object* obj) {
int FeedbackMetadata::GetSlotSize(FeedbackSlotKind kind) {
switch (kind) {
case FeedbackSlotKind::kForIn:
case FeedbackSlotKind::kInstanceOf:
case FeedbackSlotKind::kCompareOp:
case FeedbackSlotKind::kBinaryOp:
case FeedbackSlotKind::kLiteral:
......@@ -293,6 +294,16 @@ void FeedbackVector::ComputeCounts(int* with_type_info, int* generic,
total++;
break;
}
case FeedbackSlotKind::kInstanceOf: {
if (obj->IsWeakCell()) {
with++;
} else if (obj == megamorphic_sentinel) {
gen++;
with++;
}
total++;
break;
}
case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kLiteral:
break;
......
......@@ -193,6 +193,8 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
return "TypeProfile";
case FeedbackSlotKind::kForIn:
return "ForIn";
case FeedbackSlotKind::kInstanceOf:
return "InstanceOf";
case FeedbackSlotKind::kKindsNumber:
break;
}
......@@ -282,6 +284,7 @@ Handle<FeedbackVector> FeedbackVector::New(Isolate* isolate,
case FeedbackSlotKind::kStoreKeyedStrict:
case FeedbackSlotKind::kStoreDataPropertyInLiteral:
case FeedbackSlotKind::kTypeProfile:
case FeedbackSlotKind::kInstanceOf:
vector->set(index, *uninitialized_sentinel, SKIP_WRITE_BARRIER);
break;
......@@ -448,9 +451,17 @@ bool FeedbackVector::ClearSlots(Isolate* isolate) {
// Set(slot, Smi::kZero);
break;
}
case FeedbackSlotKind::kCreateClosure: {
case FeedbackSlotKind::kTypeProfile:
break;
case FeedbackSlotKind::kInstanceOf: {
InstanceOfICNexus nexus(this, slot);
if (!nexus.IsCleared()) {
nexus.Clear();
feedback_updated = true;
}
break;
}
case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kTypeProfile: {
break;
}
case FeedbackSlotKind::kLiteral: {
Set(slot, Smi::kZero, SKIP_WRITE_BARRIER);
......@@ -955,6 +966,32 @@ ForInHint ForInICNexus::GetForInFeedback() const {
return ForInHintFromFeedback(feedback);
}
void InstanceOfICNexus::ConfigureUninitialized() {
SetFeedback(*FeedbackVector::UninitializedSentinel(GetIsolate()),
SKIP_WRITE_BARRIER);
}
InlineCacheState InstanceOfICNexus::StateFromFeedback() const {
Isolate* isolate = GetIsolate();
Object* feedback = GetFeedback();
if (feedback == *FeedbackVector::UninitializedSentinel(isolate)) {
return UNINITIALIZED;
} else if (feedback == *FeedbackVector::MegamorphicSentinel(isolate)) {
return MEGAMORPHIC;
}
return MONOMORPHIC;
}
MaybeHandle<JSObject> InstanceOfICNexus::GetConstructorFeedback() const {
Isolate* isolate = GetIsolate();
Object* feedback = GetFeedback();
if (feedback->IsWeakCell() && !WeakCell::cast(feedback)->cleared()) {
return handle(JSObject::cast(WeakCell::cast(feedback)->value()), isolate);
}
return MaybeHandle<JSObject>();
}
InlineCacheState StoreDataPropertyInLiteralICNexus::StateFromFeedback() const {
Isolate* isolate = GetIsolate();
Object* feedback = GetFeedback();
......
......@@ -48,6 +48,7 @@ enum class FeedbackSlotKind {
kCreateClosure,
kLiteral,
kForIn,
kInstanceOf,
kKindsNumber // Last value indicating number of kinds.
};
......@@ -342,6 +343,10 @@ class V8_EXPORT_PRIVATE FeedbackVectorSpecBase {
FeedbackSlot AddForInSlot() { return AddSlot(FeedbackSlotKind::kForIn); }
FeedbackSlot AddInstanceOfSlot() {
return AddSlot(FeedbackSlotKind::kInstanceOf);
}
FeedbackSlot AddLiteralSlot() { return AddSlot(FeedbackSlotKind::kLiteral); }
FeedbackSlot AddStoreDataPropertyInLiteralICSlot() {
......@@ -821,6 +826,31 @@ class ForInICNexus final : public FeedbackNexus {
}
};
class InstanceOfICNexus final : public FeedbackNexus {
public:
InstanceOfICNexus(Handle<FeedbackVector> vector, FeedbackSlot slot)
: FeedbackNexus(vector, slot) {
DCHECK_EQ(FeedbackSlotKind::kInstanceOf, vector->GetKind(slot));
}
InstanceOfICNexus(FeedbackVector* vector, FeedbackSlot slot)
: FeedbackNexus(vector, slot) {
DCHECK_EQ(FeedbackSlotKind::kInstanceOf, vector->GetKind(slot));
}
void ConfigureUninitialized() final;
InlineCacheState StateFromFeedback() const final;
MaybeHandle<JSObject> GetConstructorFeedback() const;
int ExtractMaps(MapHandles* maps) const final { return 0; }
MaybeHandle<Object> FindHandlerForMap(Handle<Map> map) const final {
return MaybeHandle<Code>();
}
bool FindHandlers(ObjectHandles* code_list, int length = -1) const final {
return length == 0;
}
};
class StoreDataPropertyInLiteralICNexus : public FeedbackNexus {
public:
StoreDataPropertyInLiteralICNexus(Handle<FeedbackVector> vector,
......
......@@ -493,6 +493,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
case Token::Value::GTE:
OutputTestGreaterThanOrEqual(reg, feedback_slot);
break;
case Token::Value::INSTANCEOF:
OutputTestInstanceOf(reg, feedback_slot);
break;
default:
UNREACHABLE();
}
......@@ -505,9 +508,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(Token::Value op,
case Token::Value::EQ_STRICT:
OutputTestEqualStrictNoFeedback(reg);
break;
case Token::Value::INSTANCEOF:
OutputTestInstanceOf(reg);
break;
case Token::Value::IN:
OutputTestIn(reg);
break;
......
......@@ -3754,8 +3754,11 @@ void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
Register lhs = VisitForRegisterValue(expr->left());
VisitForAccumulatorValue(expr->right());
builder()->SetExpressionPosition(expr);
if (expr->op() == Token::INSTANCEOF || expr->op() == Token::IN) {
if (expr->op() == Token::IN) {
builder()->CompareOperation(expr->op(), lhs);
} else if (expr->op() == Token::INSTANCEOF) {
FeedbackSlot slot = feedback_spec()->AddInstanceOfSlot();
builder()->CompareOperation(expr->op(), lhs, feedback_index(slot));
} else {
FeedbackSlot slot = feedback_spec()->AddCompareICSlot();
builder()->CompareOperation(expr->op(), lhs, feedback_index(slot));
......
......@@ -208,7 +208,8 @@ namespace interpreter {
V(TestGreaterThanOrEqual, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kIdx) \
V(TestEqualStrictNoFeedback, AccumulatorUse::kReadWrite, OperandType::kReg) \
V(TestInstanceOf, AccumulatorUse::kReadWrite, OperandType::kReg) \
V(TestInstanceOf, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kIdx) \
V(TestIn, AccumulatorUse::kReadWrite, OperandType::kReg) \
V(TestUndetectable, AccumulatorUse::kReadWrite) \
V(TestNull, AccumulatorUse::kReadWrite) \
......
......@@ -581,64 +581,55 @@ void InterpreterAssembler::CallEpilogue() {
}
}
Node* InterpreterAssembler::IncrementCallCount(Node* feedback_vector,
Node* slot_id) {
void InterpreterAssembler::IncrementCallCount(Node* feedback_vector,
Node* slot_id) {
Comment("increment call count");
Node* call_count =
LoadFeedbackVectorSlot(feedback_vector, slot_id, kPointerSize);
Node* new_count = SmiAdd(call_count, SmiConstant(1));
// Count is Smi, so we don't need a write barrier.
return StoreFeedbackVectorSlot(feedback_vector, slot_id, new_count,
SKIP_WRITE_BARRIER, kPointerSize);
StoreFeedbackVectorSlot(feedback_vector, slot_id, new_count,
SKIP_WRITE_BARRIER, kPointerSize);
}
void InterpreterAssembler::CollectCallFeedback(Node* target, Node* context,
Node* feedback_vector,
Node* slot_id) {
void InterpreterAssembler::CollectCallableFeedback(Node* target, Node* context,
Node* feedback_vector,
Node* slot_id) {
Label extra_checks(this, Label::kDeferred), done(this);
// Increment the call count.
IncrementCallCount(feedback_vector, slot_id);
// Check if we have monomorphic {target} feedback already.
Node* feedback_element = LoadFeedbackVectorSlot(feedback_vector, slot_id);
Node* feedback_value = LoadWeakCellValueUnchecked(feedback_element);
Branch(WordEqual(target, feedback_value), &done, &extra_checks);
Comment("check if monomorphic");
Node* is_monomorphic = WordEqual(target, feedback_value);
GotoIf(is_monomorphic, &done);
// Check if it is a megamorphic {target}.
Comment("check if megamorphic");
Node* is_megamorphic =
WordEqual(feedback_element,
HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())));
Branch(is_megamorphic, &done, &extra_checks);
BIND(&extra_checks);
{
Label check_initialized(this), initialize(this), mark_megamorphic(this);
// Check if it is a megamorphic {target}.
Comment("check if megamorphic");
Node* is_megamorphic =
WordEqual(feedback_element,
HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())));
GotoIf(is_megamorphic, &done);
Label initialize(this), mark_megamorphic(this);
Comment("check if weak cell");
Node* is_weak_cell = WordEqual(LoadMap(feedback_element),
LoadRoot(Heap::kWeakCellMapRootIndex));
GotoIfNot(is_weak_cell, &check_initialized);
Node* is_uninitialized = WordEqual(
feedback_element,
HeapConstant(FeedbackVector::UninitializedSentinel(isolate())));
GotoIf(is_uninitialized, &initialize);
CSA_ASSERT(this, IsWeakCell(feedback_element));
// If the weak cell is cleared, we have a new chance to become monomorphic.
Comment("check if weak cell is cleared");
Node* is_smi = TaggedIsSmi(feedback_value);
Branch(is_smi, &initialize, &mark_megamorphic);
BIND(&check_initialized);
{
// Check if it is uninitialized.
Comment("check if uninitialized");
Node* is_uninitialized = WordEqual(
feedback_element, LoadRoot(Heap::kuninitialized_symbolRootIndex));
Branch(is_uninitialized, &initialize, &mark_megamorphic);
}
BIND(&initialize);
{
// Check if {target} is a JSFunction in the current native
// context.
// Check if {target} is a JSFunction in the current native context.
Comment("check if function in same native context");
GotoIf(TaggedIsSmi(target), &mark_megamorphic);
// Check if the {target} is a JSFunction or JSBoundFunction
......@@ -706,6 +697,16 @@ void InterpreterAssembler::CollectCallFeedback(Node* target, Node* context,
BIND(&done);
}
void InterpreterAssembler::CollectCallFeedback(Node* target, Node* context,
Node* feedback_vector,
Node* slot_id) {
// Increment the call count.
IncrementCallCount(feedback_vector, slot_id);
// Collect the callable {target} feedback.
CollectCallableFeedback(target, context, feedback_vector, slot_id);
}
void InterpreterAssembler::CallJSAndDispatch(
Node* function, Node* context, Node* first_arg, Node* arg_count,
ConvertReceiverMode receiver_mode) {
......
......@@ -119,14 +119,21 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
// Increment the call count for a CALL_IC or construct call.
// The call count is located at feedback_vector[slot_id + 1].
compiler::Node* IncrementCallCount(compiler::Node* feedback_vector,
compiler::Node* slot_id);
void IncrementCallCount(compiler::Node* feedback_vector,
compiler::Node* slot_id);
// Collect the callable |target| feedback for either a CALL_IC or
// an INSTANCEOF_IC in the |feedback_vector| at |slot_id|.
void CollectCallableFeedback(compiler::Node* target, compiler::Node* context,
compiler::Node* feedback_vector,
compiler::Node* slot_id);
// Collect CALL_IC feedback for |target| function in the
// |feedback_vector| at |slot_id|.
// |feedback_vector| at |slot_id|, and the call counts in
// the |feedback_vector| at |slot_id+1|.
void CollectCallFeedback(compiler::Node* target, compiler::Node* context,
compiler::Node* slot_id,
compiler::Node* feedback_vector);
compiler::Node* feedback_vector,
compiler::Node* slot_id);
// Call JSFunction or Callable |function| with |arg_count| arguments (not
// including receiver) and the first argument located at |first_arg|, possibly
......
......@@ -1891,16 +1891,23 @@ IGNITION_HANDLER(TestIn, InterpreterAssembler) {
Dispatch();
}
// TestInstanceOf <src>
// TestInstanceOf <src> <feedback_slot>
//
// Test if the object referenced by the <src> register is an an instance of type
// referenced by the accumulator.
IGNITION_HANDLER(TestInstanceOf, InterpreterAssembler) {
Node* reg_index = BytecodeOperandReg(0);
Node* name = LoadRegister(reg_index);
Node* object = GetAccumulator();
Node* object_reg = BytecodeOperandReg(0);
Node* object = LoadRegister(object_reg);
Node* callable = GetAccumulator();
Node* slot_id = BytecodeOperandIdx(1);
Node* feedback_vector = LoadFeedbackVector();
Node* context = GetContext();
SetAccumulator(InstanceOf(name, object, context));
// Record feedback for the {callable} in the {feedback_vector}.
CollectCallableFeedback(callable, context, feedback_vector, slot_id);
// Perform the actual instanceof operation.
SetAccumulator(InstanceOf(object, callable, context));
Dispatch();
}
......
......@@ -798,6 +798,11 @@ void FeedbackVector::FeedbackVectorPrint(std::ostream& os) { // NOLINT
os << Code::ICState2String(nexus.StateFromFeedback());
break;
}
case FeedbackSlotKind::kInstanceOf: {
InstanceOfICNexus nexus(this, slot);
os << Code::ICState2String(nexus.StateFromFeedback());
break;
}
case FeedbackSlotKind::kStoreDataPropertyInLiteral: {
StoreDataPropertyInLiteralICNexus nexus(this, slot);
os << Code::ICState2String(nexus.StateFromFeedback());
......
......@@ -396,7 +396,7 @@ snippet: "
"
frame size: 0
parameter count: 3
bytecode array length: 81
bytecode array length: 82
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 21 S> */ B(Ldar), R(arg1),
......@@ -435,7 +435,7 @@ bytecodes: [
/* 202 S> */ B(LdaSmi), I8(1),
/* 211 S> */ B(Return),
/* 216 S> */ B(Ldar), R(arg1),
/* 222 E> */ B(TestInstanceOf), R(arg0),
/* 222 E> */ B(TestInstanceOf), R(arg0), U8(6),
B(JumpIfFalse), U8(5),
/* 238 S> */ B(LdaSmi), I8(1),
/* 247 S> */ B(Return),
......
......@@ -2101,21 +2101,26 @@ TEST(InterpreterInstanceOf) {
Handle<i::Object> cases[] = {Handle<i::Object>::cast(instance), other};
for (size_t i = 0; i < arraysize(cases); i++) {
bool expected_value = (i == 0);
BytecodeArrayBuilder builder(isolate, zone, 1, 1);
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(isolate, zone, 1, 1, &feedback_spec);
Register r0(0);
size_t case_entry = builder.AllocateDeferredConstantPoolEntry();
builder.SetDeferredConstantPoolEntry(case_entry, cases[i]);
builder.LoadConstantPoolEntry(case_entry).StoreAccumulatorInRegister(r0);
FeedbackSlot slot = feedback_spec.AddInstanceOfSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
size_t func_entry = builder.AllocateDeferredConstantPoolEntry();
builder.SetDeferredConstantPoolEntry(func_entry, func);
builder.LoadConstantPoolEntry(func_entry)
.CompareOperation(Token::Value::INSTANCEOF, r0)
.CompareOperation(Token::Value::INSTANCEOF, r0, GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
......
......@@ -143,3 +143,21 @@ F.__proto__ = null;
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
(function() {
class B extends A {};
function makeFoo() {
return function foo(b) {
return b instanceof B;
}
}
makeFoo();
const foo = makeFoo();
assertTrue(foo(new B));
assertFalse(foo(new A));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(new B));
assertFalse(foo(new A));
})();
......@@ -454,6 +454,10 @@ TEST_MONOTONICITY(GreaterThanOrEqual)
TEST_MONOTONICITY(Add)
#undef TEST_MONOTONICITY
TEST_F(TyperTest, Monotonicity_InstanceOf) {
TestBinaryMonotonicity(javascript_.InstanceOf(VectorSlotPair()));
}
// JS BINOPS without hint
#define TEST_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
......@@ -469,7 +473,6 @@ TEST_MONOTONICITY(Subtract)
TEST_MONOTONICITY(Multiply)
TEST_MONOTONICITY(Divide)
TEST_MONOTONICITY(Modulus)
TEST_MONOTONICITY(InstanceOf)
TEST_MONOTONICITY(OrdinaryHasInstance)
#undef TEST_MONOTONICITY
......
......@@ -250,7 +250,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CompareOperation(Token::Value::LTE, reg, 5)
.CompareOperation(Token::Value::GTE, reg, 6)
.CompareTypeOf(TestTypeOfFlags::LiteralFlag::kNumber)
.CompareOperation(Token::Value::INSTANCEOF, reg)
.CompareOperation(Token::Value::INSTANCEOF, reg, 7)
.CompareOperation(Token::Value::IN, reg)
.CompareUndetectable()
.CompareUndefined()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment