Commit 2d5edc66 authored by Victor Gomes's avatar Victor Gomes Committed by V8 LUCI CQ

[maglev] Support ForIn

It introduces GetSecondReturnedValue node, which must be added
immediately after a node that calls a builtin that expects 2
returned values.

It simply binds kReturnRegister1 to a value node. Since the previous
node must have been a builtin call, kReturnRegister1 is free in
the register allocator. No gap moves will be emitted between these
two nodes.

Bug: v8:7700
Change-Id: Iddd81ef534a6397bad5682fa1430a94d2075b746
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3810183
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82204}
parent a52e5527
......@@ -2393,11 +2393,65 @@ void MaglevGraphBuilder::VisitJumpIfJSReceiver() {
}
MAGLEV_UNIMPLEMENTED_BYTECODE(SwitchOnSmiNoFeedback)
MAGLEV_UNIMPLEMENTED_BYTECODE(ForInEnumerate)
MAGLEV_UNIMPLEMENTED_BYTECODE(ForInPrepare)
MAGLEV_UNIMPLEMENTED_BYTECODE(ForInContinue)
MAGLEV_UNIMPLEMENTED_BYTECODE(ForInNext)
MAGLEV_UNIMPLEMENTED_BYTECODE(ForInStep)
void MaglevGraphBuilder::VisitForInEnumerate() {
// ForInEnumerate <receiver>
ValueNode* receiver = LoadRegisterTagged(0);
SetAccumulator(BuildCallBuiltin<Builtin::kForInEnumerate>({receiver}));
}
void MaglevGraphBuilder::VisitForInPrepare() {
// ForInPrepare <cache_info_triple>
ValueNode* enumerator = GetAccumulatorTagged();
FeedbackSlot slot = GetSlotOperand(1);
compiler::FeedbackSource feedback_source{feedback(), slot};
// TODO(v8:7700): Use feedback and create fast path.
ValueNode* context = GetContext();
ForInPrepare* result =
AddNewNode<ForInPrepare>({context, enumerator}, feedback_source);
// No need to set the accumulator.
DCHECK(!GetOutLiveness()->AccumulatorIsLive());
// The result is output in registers |cache_info_triple| to
// |cache_info_triple + 2|, with the registers holding cache_type,
// cache_array, and cache_length respectively.
interpreter::Register first = iterator_.GetRegisterOperand(0);
interpreter::Register second(first.index() + 1);
interpreter::Register third(first.index() + 2);
StoreRegister(second, result);
StoreRegister(third, GetSecondValue(result));
}
void MaglevGraphBuilder::VisitForInContinue() {
// ForInContinue <index> <cache_length>
ValueNode* index = LoadRegisterTagged(0);
ValueNode* cache_length = LoadRegisterTagged(1);
SetAccumulator(AddNewNode<TaggedNotEqual>({index, cache_length}));
}
void MaglevGraphBuilder::VisitForInNext() {
// ForInNext <receiver> <index> <cache_info_pair>
ValueNode* receiver = LoadRegisterTagged(0);
ValueNode* index = LoadRegisterTagged(1);
interpreter::Register cache_type_reg, cache_array_reg;
std::tie(cache_type_reg, cache_array_reg) =
iterator_.GetRegisterPairOperand(2);
ValueNode* cache_type = GetTaggedValue(cache_type_reg);
ValueNode* cache_array = GetTaggedValue(cache_array_reg);
FeedbackSlot slot = GetSlotOperand(3);
compiler::FeedbackSource feedback_source{feedback(), slot};
ValueNode* context = GetContext();
SetAccumulator(AddNewNode<ForInNext>(
{context, receiver, cache_array, cache_type, index}, feedback_source));
}
void MaglevGraphBuilder::VisitForInStep() {
// TODO(victorgomes): We should be able to assert that Register(0)
// contains an Smi.
ValueNode* index = LoadRegisterInt32(0);
ValueNode* one = GetInt32Constant(1);
SetAccumulator(AddNewInt32BinaryOperationNode<Operation::kAdd>({index, one}));
}
MAGLEV_UNIMPLEMENTED_BYTECODE(SetPendingMessage)
void MaglevGraphBuilder::VisitThrow() {
......
......@@ -611,6 +611,19 @@ class MaglevGraphBuilder {
StoreRegister(interpreter::Register::virtual_accumulator(), node);
}
ValueNode* GetSecondValue(ValueNode* result) {
// GetSecondReturnedValue must be added just after a node that calls a
// builtin that expects 2 returned values. It simply binds kReturnRegister1
// to a value node. Since the previous node must have been a builtin
// call, the register is available in the register allocator. No gap moves
// would be emitted between these two nodes.
DCHECK_EQ(result->opcode(), Opcode::kForInPrepare);
// {result} must be the last node in the current block.
DCHECK(current_block_->nodes().Contains(result));
DCHECK_EQ(result->NextNode(), nullptr);
return AddNewNode<GetSecondReturnedValue>({});
}
template <typename NodeT>
void StoreRegister(interpreter::Register target, NodeT* value) {
// We should only set register values to nodes that were newly created in
......
......@@ -88,6 +88,7 @@ class MaglevGraphVerifier {
case Opcode::kDeopt:
case Opcode::kFloat64Constant:
case Opcode::kGapMove:
case Opcode::kGetSecondReturnedValue:
case Opcode::kInitialValue:
case Opcode::kInt32Constant:
case Opcode::kJump:
......@@ -147,6 +148,7 @@ class MaglevGraphVerifier {
DCHECK_EQ(node->input_count(), 1);
CheckValueInputIs(node, 0, ValueRepresentation::kFloat64);
break;
case Opcode::kForInPrepare:
case Opcode::kGenericAdd:
case Opcode::kGenericBitwiseAnd:
case Opcode::kGenericBitwiseOr:
......@@ -168,6 +170,7 @@ class MaglevGraphVerifier {
case Opcode::kGenericLessThanOrEqual:
case Opcode::kGenericStrictEqual:
case Opcode::kTaggedEqual:
case Opcode::kTaggedNotEqual:
case Opcode::kStoreGlobal:
// TODO(victorgomes): Can we check that first input is an Object?
case Opcode::kStoreTaggedFieldNoWriteBarrier:
......@@ -251,6 +254,7 @@ class MaglevGraphVerifier {
case Opcode::kCallWithSpread:
case Opcode::kConstruct:
case Opcode::kConstructWithSpread:
case Opcode::kForInNext:
case Opcode::kPhi:
// All inputs should be tagged.
for (int i = 0; i < node->input_count(); i++) {
......
......@@ -778,6 +778,72 @@ void DeleteProperty::PrintParams(std::ostream& os,
os << "(" << LanguageMode2String(mode()) << ")";
}
void ForInPrepare::AllocateVreg(MaglevVregAllocationState* vreg_state) {
using D = CallInterfaceDescriptorFor<Builtin::kForInPrepare>::type;
UseFixed(context(), kContextRegister);
UseFixed(enumerator(), D::GetRegisterParameter(D::kEnumerator));
DefineAsFixed(vreg_state, this, kReturnRegister0);
}
void ForInPrepare::GenerateCode(MaglevCodeGenState* code_gen_state,
const ProcessingState& state) {
using D = CallInterfaceDescriptorFor<Builtin::kForInPrepare>::type;
DCHECK_EQ(ToRegister(context()), kContextRegister);
DCHECK_EQ(ToRegister(enumerator()), D::GetRegisterParameter(D::kEnumerator));
__ Move(D::GetRegisterParameter(D::kVectorIndex),
TaggedIndex::FromIntptr(feedback().index()));
__ Move(D::GetRegisterParameter(D::kFeedbackVector), feedback().vector);
__ CallBuiltin(Builtin::kForInPrepare);
}
void ForInNext::AllocateVreg(MaglevVregAllocationState* vreg_state) {
using D = CallInterfaceDescriptorFor<Builtin::kForInNext>::type;
UseFixed(context(), kContextRegister);
UseFixed(receiver(), D::GetRegisterParameter(D::kReceiver));
UseFixed(cache_array(), D::GetRegisterParameter(D::kCacheArray));
UseFixed(cache_type(), D::GetRegisterParameter(D::kCacheType));
UseFixed(cache_index(), D::GetRegisterParameter(D::kCacheIndex));
DefineAsFixed(vreg_state, this, kReturnRegister0);
}
void ForInNext::GenerateCode(MaglevCodeGenState* code_gen_state,
const ProcessingState& state) {
using D = CallInterfaceDescriptorFor<Builtin::kForInNext>::type;
DCHECK_EQ(ToRegister(context()), kContextRegister);
DCHECK_EQ(ToRegister(receiver()), D::GetRegisterParameter(D::kReceiver));
DCHECK_EQ(ToRegister(cache_array()), D::GetRegisterParameter(D::kCacheArray));
DCHECK_EQ(ToRegister(cache_type()), D::GetRegisterParameter(D::kCacheType));
DCHECK_EQ(ToRegister(cache_index()), D::GetRegisterParameter(D::kCacheIndex));
__ Move(D::GetRegisterParameter(D::kSlot), Immediate(feedback().index()));
// Feedback vector is pushed into the stack.
DCHECK_EQ(D::GetRegisterParameterCount(), D::kFeedbackVector);
DCHECK_EQ(D::GetStackParameterCount(), 1);
__ Push(feedback().vector);
__ CallBuiltin(Builtin::kForInNext);
code_gen_state->DefineLazyDeoptPoint(lazy_deopt_info());
}
void GetSecondReturnedValue::AllocateVreg(
MaglevVregAllocationState* vreg_state) {
DefineAsFixed(vreg_state, this, kReturnRegister1);
}
void GetSecondReturnedValue::GenerateCode(MaglevCodeGenState* code_gen_state,
const ProcessingState& state) {
// No-op. This is just a hack that binds kReturnRegister1 to a value node.
// kReturnRegister1 is guaranteed to be free in the register allocator, since
// previous node in the basic block is a call.
#ifdef DEBUG
// Check if the previous node is call.
Node* previous = nullptr;
for (Node* node : state.block()->nodes()) {
if (node == this) {
break;
}
previous = node;
}
DCHECK_NE(previous, nullptr);
DCHECK(previous->properties().is_call());
#endif // DEBUG
}
void InitialValue::AllocateVreg(MaglevVregAllocationState* vreg_state) {
// TODO(leszeks): Make this nicer.
result().SetUnallocated(compiler::UnallocatedOperand::FIXED_SLOT,
......@@ -2226,6 +2292,23 @@ void TaggedEqual::GenerateCode(MaglevCodeGenState* code_gen_state,
__ bind(&done);
}
void TaggedNotEqual::AllocateVreg(MaglevVregAllocationState* vreg_state) {
UseRegister(lhs());
UseRegister(rhs());
DefineAsRegister(vreg_state, this);
}
void TaggedNotEqual::GenerateCode(MaglevCodeGenState* code_gen_state,
const ProcessingState& state) {
Label done, if_equal;
__ cmp_tagged(ToRegister(lhs()), ToRegister(rhs()));
__ j(equal, &if_equal, Label::kNear);
__ LoadRoot(ToRegister(result()), RootIndex::kTrueValue);
__ jmp(&done, Label::kNear);
__ bind(&if_equal);
__ LoadRoot(ToRegister(result()), RootIndex::kFalseValue);
__ bind(&done);
}
void TestInstanceOf::AllocateVreg(MaglevVregAllocationState* vreg_state) {
using D = CallInterfaceDescriptorFor<Builtin::kInstanceOf>::type;
UseFixed(context(), kContextRegister);
......
......@@ -134,6 +134,9 @@ class CompactInterpreterFrameState;
V(FastCreateClosure) \
V(CreateRegExpLiteral) \
V(DeleteProperty) \
V(ForInPrepare) \
V(ForInNext) \
V(GetSecondReturnedValue) \
V(InitialValue) \
V(LoadTaggedField) \
V(LoadDoubleField) \
......@@ -158,6 +161,7 @@ class CompactInterpreterFrameState;
V(LogicalNot) \
V(ToBooleanLogicalNot) \
V(TaggedEqual) \
V(TaggedNotEqual) \
V(TestInstanceOf) \
V(TestUndetectable) \
V(TestTypeOf) \
......@@ -1623,6 +1627,20 @@ class TaggedEqual : public FixedInputValueNodeT<2, TaggedEqual> {
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class TaggedNotEqual : public FixedInputValueNodeT<2, TaggedNotEqual> {
using Base = FixedInputValueNodeT<2, TaggedNotEqual>;
public:
explicit TaggedNotEqual(uint64_t bitfield) : Base(bitfield) {}
Input& lhs() { return Node::input(0); }
Input& rhs() { return Node::input(1); }
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class TestInstanceOf : public FixedInputValueNodeT<3, TestInstanceOf> {
using Base = FixedInputValueNodeT<3, TestInstanceOf>;
......@@ -1735,6 +1753,65 @@ class DeleteProperty : public FixedInputValueNodeT<3, DeleteProperty> {
const LanguageMode mode_;
};
class ForInPrepare : public FixedInputValueNodeT<2, ForInPrepare> {
using Base = FixedInputValueNodeT<2, ForInPrepare>;
public:
explicit ForInPrepare(uint64_t bitfield, compiler::FeedbackSource& feedback)
: Base(bitfield), feedback_(feedback) {}
static constexpr OpProperties kProperties = OpProperties::Call();
compiler::FeedbackSource feedback() const { return feedback_; }
Input& context() { return Node::input(0); }
Input& enumerator() { return Node::input(1); }
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
private:
const compiler::FeedbackSource feedback_;
};
class ForInNext : public FixedInputValueNodeT<5, ForInNext> {
using Base = FixedInputValueNodeT<5, ForInNext>;
public:
explicit ForInNext(uint64_t bitfield, compiler::FeedbackSource& feedback)
: Base(bitfield), feedback_(feedback) {}
static constexpr OpProperties kProperties = OpProperties::JSCall();
compiler::FeedbackSource feedback() const { return feedback_; }
Input& context() { return Node::input(0); }
Input& receiver() { return Node::input(1); }
Input& cache_array() { return Node::input(2); }
Input& cache_type() { return Node::input(3); }
Input& cache_index() { return Node::input(4); }
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
private:
const compiler::FeedbackSource feedback_;
};
class GetSecondReturnedValue
: public FixedInputValueNodeT<0, GetSecondReturnedValue> {
using Base = FixedInputValueNodeT<0, GetSecondReturnedValue>;
public:
explicit GetSecondReturnedValue(uint64_t bitfield) : Base(bitfield) {}
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class ToObject : public FixedInputValueNodeT<2, ToObject> {
using Base = FixedInputValueNodeT<2, ToObject>;
......
......@@ -858,6 +858,8 @@ void StraightForwardRegisterAllocator::AddMoveBeforeCurrentNode(
node_it_ = (*block_it_)->nodes().end();
} else {
DCHECK_NE(node_it_, (*block_it_)->nodes().end());
// We should not add any gap move before a GetSecondReturnedValue.
DCHECK_NE(node_it_->opcode(), Opcode::kGetSecondReturnedValue);
node_it_.InsertBefore(gap_move);
}
}
......
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