Commit e8ae8b34 authored by oth's avatar oth Committed by Commit bot

[Interpreter] Add support for Call bytecode to bytecode graph builder.

Adds support for visiting the Call bytecode to the bytecode graph builder.
This change also adds the call type feedback slot to the Call bytecode.
This is not currently used by the interpreter, but is used by the
graph builder.

Also adds a CallWide varient of the Call bytecode, and adds the kCount16
operand type.

Landed on behalf of rmcilroy.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1456453002

Cr-Commit-Position: refs/heads/master@{#32033}
parent 5b2ae9d9
......@@ -162,6 +162,8 @@ VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id) {
}
// TODO(mythria): Replace this function with one which adds real frame state.
// Also add before and after frame states and checkpointing if required.
void BytecodeGraphBuilder::AddEmptyFrameStateInputs(Node* node) {
int frame_state_count =
OperatorProperties::GetFrameStateInputCount(node->op());
......@@ -397,8 +399,6 @@ void BytecodeGraphBuilder::BuildNamedLoad(
const Operator* op = javascript()->LoadNamed(language_mode(), name, feedback);
Node* node = NewNode(op, object, BuildLoadFeedbackVector());
// TODO(mythria): Replace with real frame state. Also add before and after
// frame states if required.
AddEmptyFrameStateInputs(node);
environment()->BindAccumulator(node);
}
......@@ -552,9 +552,51 @@ void BytecodeGraphBuilder::VisitCreateObjectLiteral(
}
Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
interpreter::Register callee,
interpreter::Register receiver,
size_t arity) {
Node** all = info()->zone()->NewArray<Node*>(arity);
all[0] = environment()->LookupRegister(callee);
all[1] = environment()->LookupRegister(receiver);
int receiver_index = receiver.index();
for (int i = 2; i < static_cast<int>(arity); ++i) {
all[i] = environment()->LookupRegister(
interpreter::Register(receiver_index + i - 1));
}
Node* value = MakeNode(call_op, static_cast<int>(arity), all, false);
return value;
}
void BytecodeGraphBuilder::BuildCall(
const interpreter::BytecodeArrayIterator& iterator) {
// TODO(rmcilroy): Set receiver_hint correctly based on whether the receiver
// register has been loaded with null / undefined explicitly or we are sure it
// is not null / undefined.
ConvertReceiverMode receiver_hint = ConvertReceiverMode::kAny;
interpreter::Register callee = iterator.GetRegisterOperand(0);
interpreter::Register receiver = iterator.GetRegisterOperand(1);
size_t arg_count = iterator.GetCountOperand(2);
VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(3));
const Operator* call = javascript()->CallFunction(
arg_count + 2, language_mode(), feedback, receiver_hint);
Node* value = ProcessCallArguments(call, callee, receiver, arg_count + 2);
AddEmptyFrameStateInputs(value);
environment()->BindAccumulator(value);
}
void BytecodeGraphBuilder::VisitCall(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
BuildCall(iterator);
}
void BytecodeGraphBuilder::VisitCallWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildCall(iterator);
}
......@@ -588,7 +630,6 @@ void BytecodeGraphBuilder::BuildBinaryOp(
Node* right = environment()->LookupAccumulator();
Node* node = NewNode(js_op, left, right);
// TODO(oth): Real frame state and environment check pointing.
AddEmptyFrameStateInputs(node);
environment()->BindAccumulator(node);
}
......
......@@ -91,6 +91,12 @@ class BytecodeGraphBuilder {
void BuildNamedLoad(const interpreter::BytecodeArrayIterator& iterator);
Node* ProcessCallArguments(const Operator* call_op,
interpreter::Register callee,
interpreter::Register receiver, size_t arity);
void BuildCall(const interpreter::BytecodeArrayIterator& iterator);
// Growth increment for the temporary buffer used to construct input lists to
// new nodes.
static const int kInputBufferSizeIncrement = 64;
......
......@@ -193,9 +193,21 @@ Node* InterpreterAssembler::BytecodeOperandShort(int operand_index) {
Node* InterpreterAssembler::BytecodeOperandCount(int operand_index) {
DCHECK_EQ(interpreter::OperandType::kCount8,
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index));
return BytecodeOperand(operand_index);
switch (interpreter::Bytecodes::GetOperandSize(bytecode_, operand_index)) {
case interpreter::OperandSize::kByte:
DCHECK_EQ(
interpreter::OperandType::kCount8,
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index));
return BytecodeOperand(operand_index);
case interpreter::OperandSize::kShort:
DCHECK_EQ(
interpreter::OperandType::kCount16,
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index));
return BytecodeOperandShort(operand_index);
default:
UNREACHABLE();
return nullptr;
}
}
......
......@@ -136,6 +136,14 @@ void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t(&operands)[N]) {
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2,
uint32_t operand3) {
uint32_t operands[] = {operand0, operand1, operand2, operand3};
Output(bytecode, operands);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2) {
uint32_t operands[] = {operand0, operand1, operand2};
......@@ -768,10 +776,17 @@ void BytecodeArrayBuilder::EnsureReturn() {
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
Register receiver,
size_t arg_count) {
if (FitsInIdx8Operand(arg_count)) {
size_t arg_count,
int feedback_slot) {
if (FitsInIdx8Operand(arg_count) && FitsInIdx8Operand(feedback_slot)) {
Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(),
static_cast<uint8_t>(arg_count));
static_cast<uint8_t>(arg_count),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInIdx16Operand(arg_count) &&
FitsInIdx16Operand(feedback_slot)) {
Output(Bytecode::kCallWide, callable.ToOperand(), receiver.ToOperand(),
static_cast<uint16_t>(arg_count),
static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
......@@ -923,6 +938,7 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
switch (operand_type) {
case OperandType::kNone:
return false;
case OperandType::kCount16:
case OperandType::kIdx16:
return static_cast<uint16_t>(operand_value) == operand_value;
case OperandType::kCount8:
......
......@@ -136,7 +136,7 @@ class BytecodeArrayBuilder {
// arguments should be in registers <receiver + 1> to
// <receiver + 1 + arg_count>.
BytecodeArrayBuilder& Call(Register callable, Register receiver,
size_t arg_count);
size_t arg_count, int feedback_slot);
// Call the new operator. The |constructor| register is followed by
// |arg_count| consecutive registers containing arguments to be
......@@ -232,6 +232,8 @@ class BytecodeArrayBuilder {
template <size_t N>
INLINE(void Output(Bytecode bytecode, uint32_t(&oprands)[N]));
void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
uint32_t operand2, uint32_t operand3);
void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
uint32_t operand2);
void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1);
......
......@@ -60,7 +60,11 @@ int8_t BytecodeArrayIterator::GetImmediateOperand(int operand_index) const {
int BytecodeArrayIterator::GetCountOperand(int operand_index) const {
uint32_t operand = GetRawOperand(operand_index, OperandType::kCount8);
OperandSize size =
Bytecodes::GetOperandSize(current_bytecode(), operand_index);
OperandType type = (size == OperandSize::kByte) ? OperandType::kCount8
: OperandType::kCount16;
uint32_t operand = GetRawOperand(operand_index, type);
return static_cast<int>(operand);
}
......
......@@ -1563,7 +1563,8 @@ void BytecodeGenerator::VisitCall(Call* expr) {
// TODO(rmcilroy): Deal with possible direct eval here?
// TODO(rmcilroy): Use CallIC to allow call type feedback.
builder()->Call(callee, receiver, args->length());
builder()->Call(callee, receiver, args->length(),
feedback_index(expr->CallFeedbackICSlot()));
execution_result()->SetResultInAccumulator();
}
......
......@@ -209,13 +209,15 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
case interpreter::OperandType::kCount8:
os << "#" << static_cast<unsigned int>(*operand_start);
break;
case interpreter::OperandType::kCount16:
os << '#' << ReadUnalignedUInt16(operand_start);
break;
case interpreter::OperandType::kIdx8:
os << "[" << static_cast<unsigned int>(*operand_start) << "]";
break;
case interpreter::OperandType::kIdx16: {
case interpreter::OperandType::kIdx16:
os << "[" << ReadUnalignedUInt16(operand_start) << "]";
break;
}
case interpreter::OperandType::kImm8:
os << "#" << static_cast<int>(static_cast<int8_t>(*operand_start));
break;
......
......@@ -29,6 +29,7 @@ namespace interpreter {
V(MaybeReg8, OperandSize::kByte) \
\
/* Short operands. */ \
V(Count16, OperandSize::kShort) \
V(Idx16, OperandSize::kShort)
// The list of bytecodes which are interpreted by the interpreter.
......@@ -121,7 +122,10 @@ namespace interpreter {
V(DeletePropertySloppy, OperandType::kReg8) \
\
/* Call operations */ \
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8, \
OperandType::kIdx8) \
V(CallWide, OperandType::kReg8, OperandType::kReg8, OperandType::kCount16, \
OperandType::kIdx16) \
V(CallRuntime, OperandType::kIdx16, OperandType::kMaybeReg8, \
OperandType::kCount8) \
V(CallJSRuntime, OperandType::kIdx16, OperandType::kReg8, \
......
......@@ -882,22 +882,37 @@ void Interpreter::DoDeletePropertySloppy(
}
// Call <callable> <receiver> <arg_count>
//
// Call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
void Interpreter::DoJSCall(compiler::InterpreterAssembler* assembler) {
Node* function_reg = __ BytecodeOperandReg(0);
Node* function = __ LoadRegister(function_reg);
Node* receiver_reg = __ BytecodeOperandReg(1);
Node* first_arg = __ RegisterLocation(receiver_reg);
Node* args_count = __ BytecodeOperandCount(2);
// TODO(rmcilroy): Use the call type feedback slot to call via CallIC.
Node* result = __ CallJS(function, first_arg, args_count);
__ SetAccumulator(result);
__ Dispatch();
}
// Call <callable> <receiver> <arg_count>
//
// Call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
DoJSCall(assembler);
}
// CallWide <callable> <receiver> <arg_count>
//
// Call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoCallWide(compiler::InterpreterAssembler* assembler) {
DoJSCall(assembler);
}
// CallRuntime <function_id> <first_arg> <arg_count>
//
// Call the runtime function |function_id| with the first argument in
......
......@@ -84,6 +84,9 @@ class Interpreter {
// Generates code to perform a keyed property store via |ic|.
void DoKeyedStoreIC(Callable ic, compiler::InterpreterAssembler* assembler);
// Generates code to perform a JS call.
void DoJSCall(compiler::InterpreterAssembler* assembler);
// Generates code ro create a literal via |function_id|.
void DoCreateLiteral(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler);
......
......@@ -332,6 +332,41 @@ TEST(BytecodeGraphBuilderNamedLoad) {
}
}
TEST(BytecodeGraphBuilderPropertyCall) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"return p1.func();",
{factory->NewNumberFromInt(25),
BytecodeGraphTester::NewObject("({func() { return 25; }})")}},
{"return p1.func('abc');",
{factory->NewStringFromStaticChars("abc"),
BytecodeGraphTester::NewObject("({func(a) { return a; }})")}},
{"return p1.func(1, 2, 3, 4, 5, 6, 7, 8);",
{factory->NewNumberFromInt(36),
BytecodeGraphTester::NewObject(
"({func(a, b, c, d, e, f, g, h) {\n"
" return a + b + c + d + e + f + g + h;}})")}},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
ScopedVector<char> script(2048);
SNPrintF(script, "function %s(p1) { %s };\n%s({func() {}});", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_value =
callable(snippets[i].parameter(0)).ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -999,7 +999,7 @@ TEST(InterpreterCall) {
builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index,
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 0)
.Call(Register(0), builder.Parameter(0), 0, 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......@@ -1022,7 +1022,7 @@ TEST(InterpreterCall) {
builder.LoadNamedProperty(builder.Parameter(0), name_index, slot_index,
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 0)
.Call(Register(0), builder.Parameter(0), 0, 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......@@ -1054,7 +1054,7 @@ TEST(InterpreterCall) {
.StoreAccumulatorInRegister(Register(2))
.LoadLiteral(Smi::FromInt(11))
.StoreAccumulatorInRegister(Register(3))
.Call(Register(0), Register(1), 2)
.Call(Register(0), Register(1), 2, 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......@@ -1101,7 +1101,7 @@ TEST(InterpreterCall) {
.StoreAccumulatorInRegister(Register(10))
.LoadLiteral(factory->NewStringFromAsciiChecked("j"))
.StoreAccumulatorInRegister(Register(11))
.Call(Register(0), Register(1), 10)
.Call(Register(0), Register(1), 10, 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
......
......@@ -35,6 +35,7 @@ Handle<TypeFeedbackVector> NewTypeFeedbackVector(Isolate* isolate,
return TypeFeedbackVector::New(isolate, vector_metadata);
}
class BytecodeGraphBuilderTest : public TestWithIsolateAndZone {
public:
BytecodeGraphBuilderTest() {}
......@@ -347,6 +348,98 @@ TEST_F(BytecodeGraphBuilderTest, NamedLoad) {
}
}
TEST_F(BytecodeGraphBuilderTest, CallProperty0) {
FeedbackVectorSpec feedback_spec(zone());
FeedbackVectorSlot call_slot = feedback_spec.AddCallICSlot();
FeedbackVectorSlot load_slot = feedback_spec.AddLoadICSlot();
Handle<TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate(), &feedback_spec);
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(1);
array_builder.set_context_count(0);
array_builder.set_parameter_count(2);
Handle<Name> func_name = GetName(isolate(), "func");
size_t func_name_index = array_builder.GetConstantPoolEntry(func_name);
interpreter::Register reg0 = interpreter::Register(0);
array_builder.LoadNamedProperty(
array_builder.Parameter(1), func_name_index,
vector->GetIndex(load_slot), LanguageMode::SLOPPY)
.StoreAccumulatorInRegister(reg0)
.Call(reg0, array_builder.Parameter(1), 0, vector->GetIndex(call_slot))
.Return();
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector);
Node* ret = graph->end()->InputAt(0);
Node* start = graph->start();
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
Matcher<Node*> load_named_matcher = IsJSLoadNamed(
func_name, IsParameter(1), feedback_vector_matcher, start, start);
std::vector<Matcher<Node*>> call_inputs;
call_inputs.push_back(load_named_matcher);
call_inputs.push_back(IsParameter(1));
Matcher<Node*> call_matcher =
IsJSCallFunction(call_inputs, load_named_matcher, IsIfSuccess(_));
EXPECT_THAT(ret, IsReturn(call_matcher, _, _));
}
TEST_F(BytecodeGraphBuilderTest, CallProperty2) {
FeedbackVectorSpec feedback_spec(zone());
FeedbackVectorSlot call_slot = feedback_spec.AddCallICSlot();
FeedbackVectorSlot load_slot = feedback_spec.AddLoadICSlot();
Handle<TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate(), &feedback_spec);
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(4);
array_builder.set_context_count(0);
array_builder.set_parameter_count(4);
Handle<Name> func_name = GetName(isolate(), "func");
size_t func_name_index = array_builder.GetConstantPoolEntry(func_name);
interpreter::Register reg0 = interpreter::Register(0);
interpreter::Register reg1 = interpreter::Register(1);
interpreter::Register reg2 = interpreter::Register(2);
interpreter::Register reg3 = interpreter::Register(3);
array_builder.LoadNamedProperty(
array_builder.Parameter(1), func_name_index,
vector->GetIndex(load_slot), LanguageMode::SLOPPY)
.StoreAccumulatorInRegister(reg0)
.LoadAccumulatorWithRegister(array_builder.Parameter(1))
.StoreAccumulatorInRegister(reg1)
.LoadAccumulatorWithRegister(array_builder.Parameter(2))
.StoreAccumulatorInRegister(reg2)
.LoadAccumulatorWithRegister(array_builder.Parameter(3))
.StoreAccumulatorInRegister(reg3)
.Call(reg0, reg1, 2, vector->GetIndex(call_slot))
.Return();
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector);
Node* ret = graph->end()->InputAt(0);
Node* start = graph->start();
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
Matcher<Node*> load_named_matcher = IsJSLoadNamed(
func_name, IsParameter(1), feedback_vector_matcher, start, start);
std::vector<Matcher<Node*>> call_inputs;
call_inputs.push_back(load_named_matcher);
call_inputs.push_back(IsParameter(1));
call_inputs.push_back(IsParameter(2));
call_inputs.push_back(IsParameter(3));
Matcher<Node*> call_matcher =
IsJSCallFunction(call_inputs, load_named_matcher, IsIfSuccess(_));
EXPECT_THAT(ret, IsReturn(call_matcher, _, _));
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -322,6 +322,10 @@ TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) {
EXPECT_THAT(m.BytecodeOperandReg(i),
m.IsBytecodeOperandSignExtended(offset));
break;
case interpreter::OperandType::kCount16:
EXPECT_THAT(m.BytecodeOperandCount(i),
m.IsBytecodeOperandShort(offset));
break;
case interpreter::OperandType::kIdx16:
EXPECT_THAT(m.BytecodeOperandIdx(i),
m.IsBytecodeOperandShort(offset));
......
......@@ -1480,6 +1480,59 @@ class IsJSLoadNamedMatcher final : public NodeMatcher {
const Matcher<Node*> control_matcher_;
};
class IsJSCallFunctionMatcher final : public NodeMatcher {
public:
IsJSCallFunctionMatcher(const std::vector<Matcher<Node*>>& value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kJSCallFunction),
value_matchers_(value_matchers),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
for (size_t i = 0; i < value_matchers_.size(); ++i) {
if (i == 0) {
*os << " whose value0 (";
} else {
*os << "), value" << i << " (";
}
value_matchers_[i].DescribeTo(os);
}
*os << "), effect (";
effect_matcher_.DescribeTo(os);
*os << ") and control (";
control_matcher_.DescribeTo(os);
*os << ")";
}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
if (!NodeMatcher::MatchAndExplain(node, listener)) {
return false;
}
for (size_t i = 0; i < value_matchers_.size(); ++i) {
std::ostringstream ost;
ost << "value" << i;
if (!PrintMatchAndExplain(
NodeProperties::GetValueInput(node, static_cast<int>(i)),
ost.str(), value_matchers_[i], listener)) {
return false;
}
}
return (PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
}
private:
const std::vector<Matcher<Node*>> value_matchers_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
} // namespace
......@@ -2064,6 +2117,14 @@ Matcher<Node*> IsJSLoadNamed(const Handle<Name> name,
}
Matcher<Node*> IsJSCallFunction(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsJSCallFunctionMatcher(value_matchers, effect_matcher,
control_matcher));
}
#define IS_BINOP_MATCHER(Name) \
Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \
const Matcher<Node*>& rhs_matcher) { \
......
......@@ -99,6 +99,10 @@ Matcher<Node*> IsEffectSet(const Matcher<Node*>& effect0_matcher,
const Matcher<Node*>& effect1_matcher);
Matcher<Node*> IsProjection(const Matcher<size_t>& index_matcher,
const Matcher<Node*>& base_matcher);
Matcher<Node*> IsCall(const Matcher<const CallDescriptor*>& descriptor_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsCall(const Matcher<const CallDescriptor*>& descriptor_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,
......@@ -358,6 +362,9 @@ Matcher<Node*> IsJSLoadNamed(const Handle<Name> name,
const Matcher<Node*>& feedback_vector_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsJSCallFunction(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
} // namespace compiler
} // namespace internal
......
......@@ -102,7 +102,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CreateObjectLiteral(0, 0);
// Call operations.
builder.Call(reg, reg, 0)
builder.Call(reg, reg, 0, 0)
.Call(reg, reg, 0, 1024)
.CallRuntime(Runtime::kIsArray, reg, 1)
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg, 1);
......
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