From 8df7b4f6b502e2c318b61ce604332d51544081c6 Mon Sep 17 00:00:00 2001 From: oth Date: Thu, 10 Sep 2015 09:21:34 -0700 Subject: [PATCH] [Interpreter] Skeleton bytecode graph builder Add skeleton version bytecode-graph-builder.{h,cc} for existing bytecodes. BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1291693004 Cr-Commit-Position: refs/heads/master@{#30687} --- BUILD.gn | 4 + src/DEPS | 1 + src/compiler/bytecode-graph-builder.cc | 433 ++++++++++++++++++ src/compiler/bytecode-graph-builder.h | 174 +++++++ src/compiler/pipeline.cc | 19 +- src/interpreter/bytecode-array-iterator.cc | 72 +++ src/interpreter/bytecode-array-iterator.h | 45 ++ src/objects-inl.h | 5 + src/objects.h | 3 + test/cctest/cctest.gyp | 1 + .../test-run-bytecode-graph-builder.cc | 259 +++++++++++ .../interpreter/test-bytecode-generator.cc | 3 +- .../bytecode-graph-builder-unittest.cc | 248 ++++++++++ test/unittests/compiler/node-test-utils.cc | 1 + test/unittests/compiler/node-test-utils.h | 2 + .../bytecode-array-iterator-unittest.cc | 100 ++++ test/unittests/unittests.gyp | 2 + tools/gyp/v8.gyp | 4 + 18 files changed, 1371 insertions(+), 5 deletions(-) create mode 100644 src/compiler/bytecode-graph-builder.cc create mode 100644 src/compiler/bytecode-graph-builder.h create mode 100644 src/interpreter/bytecode-array-iterator.cc create mode 100644 src/interpreter/bytecode-array-iterator.h create mode 100644 test/cctest/compiler/test-run-bytecode-graph-builder.cc create mode 100644 test/unittests/compiler/bytecode-graph-builder-unittest.cc create mode 100644 test/unittests/interpreter/bytecode-array-iterator-unittest.cc diff --git a/BUILD.gn b/BUILD.gn index 6253f5487..d6341f744 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -713,6 +713,8 @@ source_set("v8_base") { "src/compiler/ast-loop-assignment-analyzer.h", "src/compiler/basic-block-instrumentor.cc", "src/compiler/basic-block-instrumentor.h", + "src/compiler/bytecode-graph-builder.cc", + "src/compiler/bytecode-graph-builder.h", "src/compiler/change-lowering.cc", "src/compiler/change-lowering.h", "src/compiler/c-linkage.cc", @@ -1067,6 +1069,8 @@ source_set("v8_base") { "src/interpreter/bytecodes.h", "src/interpreter/bytecode-array-builder.cc", "src/interpreter/bytecode-array-builder.h", + "src/interpreter/bytecode-array-iterator.cc", + "src/interpreter/bytecode-array-iterator.h", "src/interpreter/bytecode-generator.cc", "src/interpreter/bytecode-generator.h", "src/interpreter/interpreter.cc", diff --git a/src/DEPS b/src/DEPS index 7350f61f9..26b080874 100644 --- a/src/DEPS +++ b/src/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+src/heap/heap.h", "+src/heap/heap-inl.h", "-src/interpreter", + "+src/interpreter/bytecode-array-iterator.h", "+src/interpreter/bytecodes.h", "+src/interpreter/interpreter.h", "-src/libplatform", diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc new file mode 100644 index 000000000..29530f91c --- /dev/null +++ b/src/compiler/bytecode-graph-builder.cc @@ -0,0 +1,433 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/compiler/bytecode-graph-builder.h" + +#include "src/compiler/linkage.h" +#include "src/compiler/operator-properties.h" +#include "src/interpreter/bytecode-array-iterator.h" + +namespace v8 { +namespace internal { +namespace compiler { + +// Issues: +// - Need to deal with FrameState / FrameStateBeforeAndAfter / StateValue. +// - Scopes - intimately tied to AST. Need to eval what is needed. +// - Need to resolve closure parameter treatment. +BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder, + int register_count, + int parameter_count, + Node* control_dependency, + Node* context) + : builder_(builder), + register_count_(register_count), + parameter_count_(parameter_count), + context_(context), + control_dependency_(control_dependency), + effect_dependency_(control_dependency), + values_(builder->local_zone()) { + // The layout of values_ is: + // + // [receiver] [parameters] [registers] + // + // parameter[0] is the receiver (this), parameters 1..N are the + // parameters supplied to the method (arg0..argN-1). The accumulator + // is stored separately. + + // Parameters including the receiver + for (int i = 0; i < parameter_count; i++) { + const char* debug_name = (i == 0) ? "%this" : nullptr; + const Operator* op = common()->Parameter(i, debug_name); + Node* parameter = builder->graph()->NewNode(op, graph()->start()); + values()->push_back(parameter); + } + + // Registers + register_base_ = static_cast(values()->size()); + Node* undefined_constant = builder->jsgraph()->UndefinedConstant(); + values()->insert(values()->end(), register_count, undefined_constant); + + // Accumulator + accumulator_ = undefined_constant; +} + + +int BytecodeGraphBuilder::Environment::RegisterToValuesIndex( + interpreter::Register the_register) const { + if (the_register.is_parameter()) { + return the_register.ToParameterIndex(parameter_count()); + } else { + return the_register.index() + register_base(); + } +} + + +void BytecodeGraphBuilder::Environment::BindRegister( + interpreter::Register the_register, Node* node) { + int values_index = RegisterToValuesIndex(the_register); + values()->at(values_index) = node; +} + + +Node* BytecodeGraphBuilder::Environment::LookupRegister( + interpreter::Register the_register) const { + int values_index = RegisterToValuesIndex(the_register); + return values()->at(values_index); +} + + +void BytecodeGraphBuilder::Environment::BindAccumulator(Node* node) { + accumulator_ = node; +} + + +Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const { + return accumulator_; +} + + +bool BytecodeGraphBuilder::Environment::IsMarkedAsUnreachable() const { + return GetControlDependency()->opcode() == IrOpcode::kDead; +} + + +void BytecodeGraphBuilder::Environment::MarkAsUnreachable() { + UpdateControlDependency(builder()->jsgraph()->Dead()); +} + + +BytecodeGraphBuilder::BytecodeGraphBuilder(Zone* local_zone, + CompilationInfo* compilation_info, + JSGraph* jsgraph) + : local_zone_(local_zone), + info_(compilation_info), + jsgraph_(jsgraph), + input_buffer_size_(0), + input_buffer_(nullptr), + exit_controls_(local_zone) { + bytecode_array_ = handle(info()->shared_info()->bytecode_array()); +} + + +Node* BytecodeGraphBuilder::GetFunctionContext() { + if (!function_context_.is_set()) { + // Parameter (arity + 1) is special for the outer context of the function + const Operator* op = + common()->Parameter(bytecode_array()->parameter_count(), "%context"); + Node* node = NewNode(op, graph()->start()); + function_context_.set(node); + } + return function_context_.get(); +} + + +bool BytecodeGraphBuilder::CreateGraph(bool stack_check) { + // Set up the basic structure of the graph. Outputs for {Start} are + // the formal parameters (including the receiver) plus context and + // closure. + + // The additional count items are for the context and closure. + int actual_parameter_count = bytecode_array()->parameter_count() + 2; + graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count))); + + Environment env(this, bytecode_array()->register_count(), + bytecode_array()->parameter_count(), graph()->start(), + GetFunctionContext()); + set_environment(&env); + + // Build function context only if there are context allocated variables. + if (info()->num_heap_slots() > 0) { + UNIMPLEMENTED(); // TODO(oth): Write ast-graph-builder equivalent. + } else { + // Simply use the outer function context in building the graph. + CreateGraphBody(stack_check); + } + + // Finish the basic structure of the graph. + DCHECK_NE(0u, exit_controls_.size()); + int const input_count = static_cast(exit_controls_.size()); + Node** const inputs = &exit_controls_.front(); + Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs); + graph()->SetEnd(end); + + return true; +} + + +void BytecodeGraphBuilder::CreateGraphBody(bool stack_check) { + // TODO(oth): Review ast-graph-builder equivalent, i.e. arguments + // object setup, this function variable if used, tracing hooks. + VisitBytecodes(); +} + + +void BytecodeGraphBuilder::VisitBytecodes() { + interpreter::BytecodeArrayIterator iterator(bytecode_array()); + while (!iterator.done()) { + switch (iterator.current_bytecode()) { +#define BYTECODE_CASE(name, ...) \ + case interpreter::Bytecode::k##name: \ + Visit##name(iterator); \ + break; + BYTECODE_LIST(BYTECODE_CASE) +#undef BYTECODE_CODE + } + iterator.Advance(); + } +} + + +void BytecodeGraphBuilder::VisitLdaZero( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->ZeroConstant(); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdaSmi8( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->Constant(iterator.GetSmi8Operand(0)); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdaConstant( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->Constant(iterator.GetConstantForIndexOperand(0)); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdaUndefined( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->UndefinedConstant(); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdaNull( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->NullConstant(); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdaTheHole( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->TheHoleConstant(); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdaTrue( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->TrueConstant(); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdaFalse( + const interpreter::BytecodeArrayIterator& iterator) { + Node* node = jsgraph()->FalseConstant(); + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitLdar( + const interpreter::BytecodeArrayIterator& iterator) { + Node* value = environment()->LookupRegister(iterator.GetRegisterOperand(0)); + environment()->BindAccumulator(value); +} + + +void BytecodeGraphBuilder::VisitStar( + const interpreter::BytecodeArrayIterator& iterator) { + Node* value = environment()->LookupAccumulator(); + environment()->BindRegister(iterator.GetRegisterOperand(0), value); +} + + +void BytecodeGraphBuilder::VisitLoadIC( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitKeyedLoadIC( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitStoreIC( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitKeyedStoreIC( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::BuildBinaryOp( + const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) { + Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0)); + Node* right = environment()->LookupAccumulator(); + Node* node = NewNode(js_op, left, right); + + // TODO(oth): Real frame state and environment check pointing. + int frame_state_count = + OperatorProperties::GetFrameStateInputCount(node->op()); + for (int i = 0; i < frame_state_count; i++) { + NodeProperties::ReplaceFrameStateInput(node, i, + jsgraph()->EmptyFrameState()); + } + environment()->BindAccumulator(node); +} + + +void BytecodeGraphBuilder::VisitAdd( + const interpreter::BytecodeArrayIterator& iterator) { + BuildBinaryOp(javascript()->Add(language_mode()), iterator); +} + + +void BytecodeGraphBuilder::VisitSub( + const interpreter::BytecodeArrayIterator& iterator) { + BuildBinaryOp(javascript()->Subtract(language_mode()), iterator); +} + + +void BytecodeGraphBuilder::VisitMul( + const interpreter::BytecodeArrayIterator& iterator) { + BuildBinaryOp(javascript()->Multiply(language_mode()), iterator); +} + + +void BytecodeGraphBuilder::VisitDiv( + const interpreter::BytecodeArrayIterator& iterator) { + BuildBinaryOp(javascript()->Divide(language_mode()), iterator); +} + + +void BytecodeGraphBuilder::VisitMod( + const interpreter::BytecodeArrayIterator& iterator) { + BuildBinaryOp(javascript()->Modulus(language_mode()), iterator); +} + + +void BytecodeGraphBuilder::VisitReturn( + const interpreter::BytecodeArrayIterator& iterator) { + Node* control = + NewNode(common()->Return(), environment()->LookupAccumulator()); + UpdateControlDependencyToLeaveFunction(control); +} + + +Node** BytecodeGraphBuilder::EnsureInputBufferSize(int size) { + if (size > input_buffer_size_) { + size = size + kInputBufferSizeIncrement + input_buffer_size_; + input_buffer_ = local_zone()->NewArray(size); + input_buffer_size_ = size; + } + return input_buffer_; +} + + +Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count, + Node** value_inputs, bool incomplete) { + DCHECK_EQ(op->ValueInputCount(), value_input_count); + + bool has_context = OperatorProperties::HasContextInput(op); + int frame_state_count = OperatorProperties::GetFrameStateInputCount(op); + bool has_control = op->ControlInputCount() == 1; + bool has_effect = op->EffectInputCount() == 1; + + DCHECK_LT(op->ControlInputCount(), 2); + DCHECK_LT(op->EffectInputCount(), 2); + + Node* result = NULL; + if (!has_context && frame_state_count == 0 && !has_control && !has_effect) { + result = graph()->NewNode(op, value_input_count, value_inputs, incomplete); + } else { + int input_count_with_deps = value_input_count; + if (has_context) ++input_count_with_deps; + input_count_with_deps += frame_state_count; + if (has_control) ++input_count_with_deps; + if (has_effect) ++input_count_with_deps; + Node** buffer = EnsureInputBufferSize(input_count_with_deps); + memcpy(buffer, value_inputs, kPointerSize * value_input_count); + Node** current_input = buffer + value_input_count; + if (has_context) { + *current_input++ = environment()->Context(); + } + for (int i = 0; i < frame_state_count; i++) { + // The frame state will be inserted later. Here we misuse + // the {Dead} node as a sentinel to be later overwritten + // with the real frame state. + *current_input++ = jsgraph()->Dead(); + } + if (has_effect) { + *current_input++ = environment()->GetEffectDependency(); + } + if (has_control) { + *current_input++ = environment()->GetControlDependency(); + } + result = graph()->NewNode(op, input_count_with_deps, buffer, incomplete); + if (!environment()->IsMarkedAsUnreachable()) { + // Update the current control dependency for control-producing nodes. + if (NodeProperties::IsControl(result)) { + environment()->UpdateControlDependency(result); + } + // Update the current effect dependency for effect-producing nodes. + if (result->op()->EffectOutputCount() > 0) { + environment()->UpdateEffectDependency(result); + } + // Add implicit success continuation for throwing nodes. + if (!result->op()->HasProperty(Operator::kNoThrow)) { + const Operator* op = common()->IfSuccess(); + Node* on_success = graph()->NewNode(op, result); + environment_->UpdateControlDependency(on_success); + } + } + } + + return result; +} + + +Node* BytecodeGraphBuilder::MergeControl(Node* control, Node* other) { + int inputs = control->op()->ControlInputCount() + 1; + if (control->opcode() == IrOpcode::kLoop) { + // Control node for loop exists, add input. + const Operator* op = common()->Loop(inputs); + control->AppendInput(graph_zone(), other); + control->set_op(op); + } else if (control->opcode() == IrOpcode::kMerge) { + // Control node for merge exists, add input. + const Operator* op = common()->Merge(inputs); + control->AppendInput(graph_zone(), other); + control->set_op(op); + } else { + // Control node is a singleton, introduce a merge. + const Operator* op = common()->Merge(inputs); + Node* inputs[] = {control, other}; + control = graph()->NewNode(op, arraysize(inputs), inputs, true); + } + return control; +} + + +void BytecodeGraphBuilder::UpdateControlDependencyToLeaveFunction(Node* exit) { + if (environment()->IsMarkedAsUnreachable()) return; + environment()->MarkAsUnreachable(); + exit_controls_.push_back(exit); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/src/compiler/bytecode-graph-builder.h b/src/compiler/bytecode-graph-builder.h new file mode 100644 index 000000000..4e479ba3e --- /dev/null +++ b/src/compiler/bytecode-graph-builder.h @@ -0,0 +1,174 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_ +#define V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_ + +#include "src/compiler.h" +#include "src/compiler/js-graph.h" +#include "src/interpreter/bytecode-array-iterator.h" +#include "src/interpreter/bytecodes.h" + +namespace v8 { +namespace internal { +namespace compiler { + +// The BytecodeGraphBuilder produces a high-level IR graph based on +// interpreter bytecodes. +class BytecodeGraphBuilder { + public: + BytecodeGraphBuilder(Zone* local_zone, CompilationInfo* info, + JSGraph* jsgraph); + + // Creates a graph by visiting bytecodes. + bool CreateGraph(bool stack_check = true); + + Graph* graph() const { return jsgraph_->graph(); } + + private: + class Environment; + + void CreateGraphBody(bool stack_check); + void VisitBytecodes(); + + Node* LoadAccumulator(Node* value); + + Node* GetFunctionContext(); + + void set_environment(Environment* env) { environment_ = env; } + const Environment* environment() const { return environment_; } + Environment* environment() { return environment_; } + + // Node creation helpers + Node* NewNode(const Operator* op, bool incomplete = false) { + return MakeNode(op, 0, static_cast(NULL), incomplete); + } + + Node* NewNode(const Operator* op, Node* n1) { + Node* buffer[] = {n1}; + return MakeNode(op, arraysize(buffer), buffer, false); + } + + Node* NewNode(const Operator* op, Node* n1, Node* n2) { + Node* buffer[] = {n1, n2}; + return MakeNode(op, arraysize(buffer), buffer, false); + } + + Node* MakeNode(const Operator* op, int value_input_count, Node** value_inputs, + bool incomplete); + + Node* MergeControl(Node* control, Node* other); + + Node** EnsureInputBufferSize(int size); + + void UpdateControlDependencyToLeaveFunction(Node* exit); + + void BuildBinaryOp(const Operator* op, + const interpreter::BytecodeArrayIterator& iterator); + + // Growth increment for the temporary buffer used to construct input lists to + // new nodes. + static const int kInputBufferSizeIncrement = 64; + + // Field accessors + CommonOperatorBuilder* common() const { return jsgraph_->common(); } + Zone* graph_zone() const { return graph()->zone(); } + CompilationInfo* info() const { return info_; } + JSGraph* jsgraph() const { return jsgraph_; } + JSOperatorBuilder* javascript() const { return jsgraph_->javascript(); } + Zone* local_zone() const { return local_zone_; } + const Handle& bytecode_array() const { + return bytecode_array_; + } + + LanguageMode language_mode() const { + // TODO(oth): need to propagate language mode through + return LanguageMode::SLOPPY; + } + +#define DECLARE_VISIT_BYTECODE(name, ...) \ + void Visit##name(const interpreter::BytecodeArrayIterator& iterator); + BYTECODE_LIST(DECLARE_VISIT_BYTECODE) +#undef DECLARE_VISIT_BYTECODE + + Zone* local_zone_; + CompilationInfo* info_; + JSGraph* jsgraph_; + Handle bytecode_array_; + Environment* environment_; + + // Temporary storage for building node input lists. + int input_buffer_size_; + Node** input_buffer_; + + // Nodes representing values in the activation record. + SetOncePointer function_context_; + + // Control nodes that exit the function body. + ZoneVector exit_controls_; + + DISALLOW_COPY_AND_ASSIGN(BytecodeGraphBuilder); +}; + + +class BytecodeGraphBuilder::Environment : public ZoneObject { + public: + Environment(BytecodeGraphBuilder* builder, int register_count, + int parameter_count, Node* control_dependency, Node* context); + + int parameter_count() const { return parameter_count_; } + int register_count() const { return register_count_; } + + void BindRegister(interpreter::Register the_register, Node* node); + Node* LookupRegister(interpreter::Register the_register) const; + + void BindAccumulator(Node* node); + Node* LookupAccumulator() const; + + bool IsMarkedAsUnreachable() const; + void MarkAsUnreachable(); + + // Effect dependency tracked by this environment. + Node* GetEffectDependency() { return effect_dependency_; } + void UpdateEffectDependency(Node* dependency) { + effect_dependency_ = dependency; + } + + // Control dependency tracked by this environment. + Node* GetControlDependency() const { return control_dependency_; } + void UpdateControlDependency(Node* dependency) { + control_dependency_ = dependency; + } + + Node* Context() const { return context_; } + + private: + int RegisterToValuesIndex(interpreter::Register the_register) const; + + Zone* zone() const { return builder_->local_zone(); } + Graph* graph() const { return builder_->graph(); } + CommonOperatorBuilder* common() const { return builder_->common(); } + BytecodeGraphBuilder* builder() const { return builder_; } + const NodeVector* values() const { return &values_; } + NodeVector* values() { return &values_; } + Node* accumulator() { return accumulator_; } + int register_base() const { return register_base_; } + + BytecodeGraphBuilder* builder_; + int register_count_; + int parameter_count_; + Node* accumulator_; + Node* context_; + Node* control_dependency_; + Node* effect_dependency_; + NodeVector values_; + int register_base_; +}; + + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_ diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index bd0302096..fd79a865e 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -12,6 +12,7 @@ #include "src/compiler/ast-graph-builder.h" #include "src/compiler/ast-loop-assignment-analyzer.h" #include "src/compiler/basic-block-instrumentor.h" +#include "src/compiler/bytecode-graph-builder.h" #include "src/compiler/change-lowering.h" #include "src/compiler/code-generator.h" #include "src/compiler/common-operator-reducer.h" @@ -472,11 +473,21 @@ struct GraphBuilderPhase { static const char* phase_name() { return "graph builder"; } void Run(PipelineData* data, Zone* temp_zone) { - AstGraphBuilderWithPositions graph_builder( - temp_zone, data->info(), data->jsgraph(), data->loop_assignment(), - data->js_type_feedback(), data->source_positions()); bool stack_check = !data->info()->IsStub(); - if (!graph_builder.CreateGraph(stack_check)) { + bool succeeded = false; + + if (data->info()->shared_info()->HasBytecodeArray()) { + BytecodeGraphBuilder graph_builder(temp_zone, data->info(), + data->jsgraph()); + succeeded = graph_builder.CreateGraph(stack_check); + } else { + AstGraphBuilderWithPositions graph_builder( + temp_zone, data->info(), data->jsgraph(), data->loop_assignment(), + data->js_type_feedback(), data->source_positions()); + succeeded = graph_builder.CreateGraph(stack_check); + } + + if (!succeeded) { data->set_compilation_failed(); } } diff --git a/src/interpreter/bytecode-array-iterator.cc b/src/interpreter/bytecode-array-iterator.cc new file mode 100644 index 000000000..84be6cc67 --- /dev/null +++ b/src/interpreter/bytecode-array-iterator.cc @@ -0,0 +1,72 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/interpreter/bytecode-array-iterator.h" + +#include "src/objects-inl.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +BytecodeArrayIterator::BytecodeArrayIterator( + Handle bytecode_array) + : bytecode_array_(bytecode_array), bytecode_offset_(0) {} + + +void BytecodeArrayIterator::Advance() { + bytecode_offset_ += Bytecodes::Size(current_bytecode()); +} + + +bool BytecodeArrayIterator::done() const { + return bytecode_offset_ >= bytecode_array()->length(); +} + + +Bytecode BytecodeArrayIterator::current_bytecode() const { + DCHECK(!done()); + uint8_t current_byte = bytecode_array()->get(bytecode_offset_); + return interpreter::Bytecodes::FromByte(current_byte); +} + + +uint8_t BytecodeArrayIterator::GetOperand(int operand_index, + OperandType operand_type) const { + DCHECK_GE(operand_index, 0); + DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode())); + DCHECK_EQ(operand_type, + Bytecodes::GetOperandType(current_bytecode(), operand_index)); + int operands_start = bytecode_offset_ + 1; + return bytecode_array()->get(operands_start + operand_index); +} + + +int8_t BytecodeArrayIterator::GetSmi8Operand(int operand_index) const { + uint8_t operand = GetOperand(operand_index, OperandType::kImm8); + return static_cast(operand); +} + + +int BytecodeArrayIterator::GetIndexOperand(int operand_index) const { + uint8_t operand = GetOperand(operand_index, OperandType::kIdx); + return static_cast(operand); +} + + +Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const { + uint8_t operand = GetOperand(operand_index, OperandType::kReg); + return Register::FromOperand(operand); +} + + +Handle BytecodeArrayIterator::GetConstantForIndexOperand( + int operand_index) const { + Handle constants = handle(bytecode_array()->constant_pool()); + return FixedArray::get(constants, GetIndexOperand(operand_index)); +} + +} // namespace interpreter +} // namespace internal +} // namespace v8 diff --git a/src/interpreter/bytecode-array-iterator.h b/src/interpreter/bytecode-array-iterator.h new file mode 100644 index 000000000..a0de3f0e7 --- /dev/null +++ b/src/interpreter/bytecode-array-iterator.h @@ -0,0 +1,45 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_INTERPRETER_BYTECODE_ARRAY_ITERATOR_H_ +#define V8_INTERPRETER_BYTECODE_ARRAY_ITERATOR_H_ + +#include "src/handles.h" +#include "src/interpreter/bytecodes.h" +#include "src/objects.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +class BytecodeArrayIterator { + public: + explicit BytecodeArrayIterator(Handle bytecode_array); + + void Advance(); + bool done() const; + Bytecode current_bytecode() const; + const Handle& bytecode_array() const { + return bytecode_array_; + } + + int8_t GetSmi8Operand(int operand_index) const; + int GetIndexOperand(int operand_index) const; + Register GetRegisterOperand(int operand_index) const; + Handle GetConstantForIndexOperand(int operand_index) const; + + private: + uint8_t GetOperand(int operand_index, OperandType operand_type) const; + + Handle bytecode_array_; + int bytecode_offset_; + + DISALLOW_COPY_AND_ASSIGN(BytecodeArrayIterator); +}; + +} // namespace interpreter +} // namespace internal +} // namespace v8 + +#endif // V8_INTERPRETER_BYTECODE_GRAPH_ITERATOR_H_ diff --git a/src/objects-inl.h b/src/objects-inl.h index 42e8354fb..9ddaa81af 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -4091,6 +4091,11 @@ int BytecodeArray::frame_size() const { } +int BytecodeArray::register_count() const { + return frame_size() / kPointerSize; +} + + void BytecodeArray::set_parameter_count(int number_of_parameters) { DCHECK_GE(number_of_parameters, 0); // Parameter count is stored as the size on stack of the parameters to allow diff --git a/src/objects.h b/src/objects.h index 751e3cf34..f547c8388 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4211,6 +4211,9 @@ class BytecodeArray : public FixedArrayBase { inline int frame_size() const; inline void set_frame_size(int frame_size); + // Accessor for register count (derived from frame_size). + inline int register_count() const; + // Accessors for parameter count (including implicit 'this' receiver). inline int parameter_count() const; inline void set_parameter_count(int number_of_parameters); diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index d15820a0d..0a9d5354b 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -68,6 +68,7 @@ 'compiler/test-osr.cc', 'compiler/test-pipeline.cc', 'compiler/test-representation-change.cc', + 'compiler/test-run-bytecode-graph-builder.cc', 'compiler/test-run-deopt.cc', 'compiler/test-run-inlining.cc', 'compiler/test-run-intrinsics.cc', diff --git a/test/cctest/compiler/test-run-bytecode-graph-builder.cc b/test/cctest/compiler/test-run-bytecode-graph-builder.cc new file mode 100644 index 000000000..c64a9346f --- /dev/null +++ b/test/cctest/compiler/test-run-bytecode-graph-builder.cc @@ -0,0 +1,259 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "src/v8.h" + +#include "src/compiler/pipeline.h" +#include "src/execution.h" +#include "src/handles.h" +#include "src/interpreter/bytecode-array-builder.h" +#include "src/interpreter/interpreter.h" +#include "src/parser.h" +#include "test/cctest/cctest.h" + +namespace v8 { +namespace internal { +namespace compiler { + + +static const char kFunctionName[] = "f"; + + +static MaybeHandle CallFunction(Isolate* isolate, + Handle function) { + return Execution::Call(isolate, function, + isolate->factory()->undefined_value(), 0, nullptr, + false); +} + + +template +static MaybeHandle CallFunction(Isolate* isolate, + Handle function, + A... args) { + Handle argv[] = {args...}; + return Execution::Call(isolate, function, + isolate->factory()->undefined_value(), sizeof...(args), + argv, false); +} + + +template +class BytecodeGraphCallable { + public: + BytecodeGraphCallable(Isolate* isolate, Handle function) + : isolate_(isolate), function_(function) {} + virtual ~BytecodeGraphCallable() {} + + MaybeHandle operator()(A... args) { + return CallFunction(isolate_, function_, args...); + } + + private: + Isolate* isolate_; + Handle function_; +}; + + +class BytecodeGraphTester { + public: + BytecodeGraphTester(Isolate* isolate, Zone* zone, const char* script) + : isolate_(isolate), zone_(zone), script_(script) { + i::FLAG_ignition = true; + i::FLAG_always_opt = false; + i::FLAG_vector_stores = true; + // Set ignition filter flag via SetFlagsFromString to avoid double-free + // (or potential leak with StrDup() based on ownership confusion). + ScopedVector ignition_filter(64); + SNPrintF(ignition_filter, "--ignition-filter=%s", kFunctionName); + FlagList::SetFlagsFromString(ignition_filter.start(), + ignition_filter.length()); + // Ensure handler table is generated. + isolate->interpreter()->Initialize(); + } + virtual ~BytecodeGraphTester() {} + + template + BytecodeGraphCallable GetCallable() { + return BytecodeGraphCallable(isolate_, GetFunction()); + } + + private: + Isolate* isolate_; + Zone* zone_; + const char* script_; + + Handle GetFunction() { + CompileRun(script_); + Local api_function = + Local::Cast(CcTest::global()->Get(v8_str(kFunctionName))); + Handle function = v8::Utils::OpenHandle(*api_function); + CHECK(function->shared()->HasBytecodeArray()); + + ParseInfo parse_info(zone_, function); + + CompilationInfo compilation_info(&parse_info); + compilation_info.SetOptimizing(BailoutId::None(), Handle()); + Parser parser(&parse_info); + CHECK(parser.Parse(&parse_info)); + compiler::Pipeline pipeline(&compilation_info); + Handle code = pipeline.GenerateCode(); + function->ReplaceCode(*code); + + return function; + } + + DISALLOW_COPY_AND_ASSIGN(BytecodeGraphTester); +}; + +} // namespace compiler +} // namespace internal +} // namespace v8 + + +using namespace v8::internal; +using namespace v8::internal::compiler; + +template +struct ExpectedSnippet { + const char* code_snippet; + Handle return_value_and_parameters[N + 1]; + + inline Handle return_value() const { + return return_value_and_parameters[0]; + } + + inline Handle parameter(int i) const { + return return_value_and_parameters[1 + i]; + } +}; + + +TEST(BytecodeGraphBuilderReturnStatements) { + HandleAndZoneScope scope; + Isolate* isolate = scope.main_isolate(); + Zone* zone = scope.main_zone(); + Factory* factory = isolate->factory(); + + ExpectedSnippet<0> snippets[] = { + {"return;", {factory->undefined_value()}}, + {"return null;", {factory->null_value()}}, + {"return true;", {factory->true_value()}}, + {"return false;", {factory->false_value()}}, + {"return 0;", {factory->NewNumberFromInt(0)}}, + {"return +1;", {factory->NewNumberFromInt(1)}}, + {"return -1;", {factory->NewNumberFromInt(-1)}}, + {"return +127;", {factory->NewNumberFromInt(127)}}, + {"return -128;", {factory->NewNumberFromInt(-128)}}, + {"return 0.001;", {factory->NewNumber(0.001)}}, + {"return 3.7e-60;", {factory->NewNumber(3.7e-60)}}, + {"return -3.7e60;", {factory->NewNumber(-3.7e60)}}, + {"return '';", {factory->NewStringFromStaticChars("")}}, + {"return 'catfood';", {factory->NewStringFromStaticChars("catfood")}} + // TODO(oth): {"return NaN;", {factory->NewNumber(NAN)}} + }; + + size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); + for (size_t i = 0; i < num_snippets; i++) { + ScopedVector script(1024); + SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName, + snippets[i].code_snippet, kFunctionName); + + BytecodeGraphTester tester(isolate, zone, script.start()); + auto callable = tester.GetCallable<>(); + Handle return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*snippets[i].return_value())); + } +} + + +TEST(BytecodeGraphBuilderPrimitiveExpressions) { + HandleAndZoneScope scope; + Isolate* isolate = scope.main_isolate(); + Zone* zone = scope.main_zone(); + Factory* factory = isolate->factory(); + + ExpectedSnippet<0> snippets[] = { + {"return 1 + 1;", {factory->NewNumberFromInt(2)}}, + {"return 20 - 30;", {factory->NewNumberFromInt(-10)}}, + {"return 4 * 100;", {factory->NewNumberFromInt(400)}}, + {"return 100 / 5;", {factory->NewNumberFromInt(20)}}, + {"return 25 % 7;", {factory->NewNumberFromInt(4)}}, + }; + + size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); + for (size_t i = 0; i < num_snippets; i++) { + ScopedVector script(1024); + SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName, + snippets[i].code_snippet, kFunctionName); + + BytecodeGraphTester tester(isolate, zone, script.start()); + auto callable = tester.GetCallable<>(); + Handle return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*snippets[i].return_value())); + } +} + + +TEST(BytecodeGraphBuilderTwoParameterTests) { + HandleAndZoneScope scope; + Isolate* isolate = scope.main_isolate(); + Zone* zone = scope.main_zone(); + Factory* factory = isolate->factory(); + + ExpectedSnippet<2> snippets[] = { + // Integers + {"return p1 + p2;", + {factory->NewNumberFromInt(-70), factory->NewNumberFromInt(3), + factory->NewNumberFromInt(-73)}}, + {"return p1 + p2 + 3;", + {factory->NewNumberFromInt(1139044), factory->NewNumberFromInt(300), + factory->NewNumberFromInt(1138741)}}, + {"return p1 - p2;", + {factory->NewNumberFromInt(1100), factory->NewNumberFromInt(1000), + factory->NewNumberFromInt(-100)}}, + {"return p1 * p2;", + {factory->NewNumberFromInt(-100000), factory->NewNumberFromInt(1000), + factory->NewNumberFromInt(-100)}}, + {"return p1 / p2;", + {factory->NewNumberFromInt(-10), factory->NewNumberFromInt(1000), + factory->NewNumberFromInt(-100)}}, + {"return p1 % p2;", + {factory->NewNumberFromInt(5), factory->NewNumberFromInt(373), + factory->NewNumberFromInt(16)}}, + // Doubles + {"return p1 + p2;", + {factory->NewHeapNumber(9.999), factory->NewHeapNumber(3.333), + factory->NewHeapNumber(6.666)}}, + {"return p1 - p2;", + {factory->NewHeapNumber(-3.333), factory->NewHeapNumber(3.333), + factory->NewHeapNumber(6.666)}}, + {"return p1 * p2;", + {factory->NewHeapNumber(3.333 * 6.666), factory->NewHeapNumber(3.333), + factory->NewHeapNumber(6.666)}}, + {"return p1 / p2;", + {factory->NewHeapNumber(2.25), factory->NewHeapNumber(9), + factory->NewHeapNumber(4)}}, + // Strings + {"return p1 + p2;", + {factory->NewStringFromStaticChars("abcdef"), + factory->NewStringFromStaticChars("abc"), + factory->NewStringFromStaticChars("def")}}}; + + size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); + for (size_t i = 0; i < num_snippets; i++) { + ScopedVector script(1024); + SNPrintF(script, "function %s(p1, p2) { %s }\n%s(0, 0);", kFunctionName, + snippets[i].code_snippet, kFunctionName); + + BytecodeGraphTester tester(isolate, zone, script.start()); + auto callable = tester.GetCallable, Handle>(); + Handle return_value = + callable(snippets[i].parameter(0), snippets[i].parameter(1)) + .ToHandleChecked(); + CHECK(return_value->SameValue(*snippets[i].return_value())); + } +} diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index 1faaa707a..42125f07f 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -23,7 +23,8 @@ class BytecodeGeneratorHelper { BytecodeGeneratorHelper() { i::FLAG_vector_stores = true; i::FLAG_ignition = true; - i::FLAG_ignition_filter = kFunctionName; + i::FLAG_ignition_filter = StrDup(kFunctionName); + i::FLAG_always_opt = false; CcTest::i_isolate()->interpreter()->Initialize(); } diff --git a/test/unittests/compiler/bytecode-graph-builder-unittest.cc b/test/unittests/compiler/bytecode-graph-builder-unittest.cc new file mode 100644 index 000000000..22b9b893e --- /dev/null +++ b/test/unittests/compiler/bytecode-graph-builder-unittest.cc @@ -0,0 +1,248 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "src/compiler/bytecode-graph-builder.h" +#include "src/compiler/common-operator.h" +#include "src/compiler/graph-visualizer.h" +#include "src/compiler/instruction.h" +#include "src/compiler/instruction-selector.h" +#include "src/compiler/js-graph.h" +#include "src/compiler/js-operator.h" +#include "src/interpreter/bytecode-array-builder.h" +#include "src/parser.h" +#include "test/unittests/compiler/compiler-test-utils.h" +#include "test/unittests/compiler/graph-unittest.h" +#include "test/unittests/compiler/node-test-utils.h" +#include "test/unittests/test-utils.h" + +using ::testing::_; + +namespace v8 { +namespace internal { +namespace compiler { + +class BytecodeGraphBuilderTest : public TestWithIsolateAndZone { + public: + BytecodeGraphBuilderTest() : array_builder_(isolate(), zone()) {} + + Graph* GetCompletedGraph(); + + Matcher IsUndefinedConstant(); + Matcher IsNullConstant(); + Matcher IsTheHoleConstant(); + Matcher IsFalseConstant(); + Matcher IsTrueConstant(); + + interpreter::BytecodeArrayBuilder* array_builder() { return &array_builder_; } + + private: + interpreter::BytecodeArrayBuilder array_builder_; + + DISALLOW_COPY_AND_ASSIGN(BytecodeGraphBuilderTest); +}; + + +Graph* BytecodeGraphBuilderTest::GetCompletedGraph() { + MachineOperatorBuilder* machine = new (zone()) MachineOperatorBuilder( + zone(), kMachPtr, InstructionSelector::SupportedMachineOperatorFlags()); + CommonOperatorBuilder* common = new (zone()) CommonOperatorBuilder(zone()); + JSOperatorBuilder* javascript = new (zone()) JSOperatorBuilder(zone()); + Graph* graph = new (zone()) Graph(zone()); + JSGraph* jsgraph = + new (zone()) JSGraph(isolate(), graph, common, javascript, machine); + + Handle name = factory()->NewStringFromStaticChars("test"); + Handle script = factory()->NewStringFromStaticChars("test() {}"); + Handle shared_info = + factory()->NewSharedFunctionInfo(name, MaybeHandle()); + shared_info->set_script(*factory()->NewScript(script)); + + ParseInfo parse_info(zone(), shared_info); + CompilationInfo info(&parse_info); + Handle bytecode_array = array_builder()->ToBytecodeArray(); + info.shared_info()->set_function_data(*bytecode_array); + + BytecodeGraphBuilder graph_builder(zone(), &info, jsgraph); + graph_builder.CreateGraph(); + return graph; +} + + +Matcher BytecodeGraphBuilderTest::IsUndefinedConstant() { + return IsHeapConstant(factory()->undefined_value()); +} + + +Matcher BytecodeGraphBuilderTest::IsNullConstant() { + return IsHeapConstant(factory()->null_value()); +} + + +Matcher BytecodeGraphBuilderTest::IsTheHoleConstant() { + return IsHeapConstant(factory()->the_hole_value()); +} + + +Matcher BytecodeGraphBuilderTest::IsFalseConstant() { + return IsHeapConstant(factory()->false_value()); +} + + +Matcher BytecodeGraphBuilderTest::IsTrueConstant() { + return IsHeapConstant(factory()->true_value()); +} + + +TEST_F(BytecodeGraphBuilderTest, ReturnUndefined) { + array_builder()->set_locals_count(0); + array_builder()->set_parameter_count(1); + array_builder()->LoadUndefined().Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + Node* effect = graph->start(); + Node* control = graph->start(); + EXPECT_THAT(ret, IsReturn(IsUndefinedConstant(), effect, control)); +} + + +TEST_F(BytecodeGraphBuilderTest, ReturnNull) { + array_builder()->set_locals_count(0); + array_builder()->set_parameter_count(1); + array_builder()->LoadNull().Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + EXPECT_THAT(ret, IsReturn(IsNullConstant(), graph->start(), graph->start())); +} + + +TEST_F(BytecodeGraphBuilderTest, ReturnTheHole) { + array_builder()->set_locals_count(0); + array_builder()->set_parameter_count(1); + array_builder()->LoadTheHole().Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + Node* effect = graph->start(); + Node* control = graph->start(); + EXPECT_THAT(ret, IsReturn(IsTheHoleConstant(), effect, control)); +} + + +TEST_F(BytecodeGraphBuilderTest, ReturnTrue) { + array_builder()->set_locals_count(0); + array_builder()->set_parameter_count(1); + array_builder()->LoadTrue().Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + Node* effect = graph->start(); + Node* control = graph->start(); + EXPECT_THAT(ret, IsReturn(IsTrueConstant(), effect, control)); +} + + +TEST_F(BytecodeGraphBuilderTest, ReturnFalse) { + array_builder()->set_locals_count(0); + array_builder()->set_parameter_count(1); + array_builder()->LoadFalse().Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + Node* effect = graph->start(); + Node* control = graph->start(); + EXPECT_THAT(ret, IsReturn(IsFalseConstant(), effect, control)); +} + + +TEST_F(BytecodeGraphBuilderTest, ReturnInt8) { + static const int kValue = 3; + array_builder()->set_locals_count(0); + array_builder()->set_parameter_count(1); + array_builder()->LoadLiteral(Smi::FromInt(kValue)).Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + Node* effect = graph->start(); + Node* control = graph->start(); + EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), effect, control)); +} + + +TEST_F(BytecodeGraphBuilderTest, ReturnDouble) { + const double kValue = 0.123456789; + array_builder()->set_locals_count(0); + array_builder()->set_parameter_count(1); + array_builder()->LoadLiteral(factory()->NewHeapNumber(kValue)); + array_builder()->Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + Node* effect = graph->start(); + Node* control = graph->start(); + EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), effect, control)); +} + + +TEST_F(BytecodeGraphBuilderTest, SimpleExpressionWithParameters) { + array_builder()->set_locals_count(1); + array_builder()->set_parameter_count(3); + array_builder() + ->LoadAccumulatorWithRegister(array_builder()->Parameter(1)) + .BinaryOperation(Token::Value::ADD, array_builder()->Parameter(2)) + .StoreAccumulatorInRegister(interpreter::Register(0)) + .Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + // NB binary operation is . The register represents + // the left-hand side, which is why parameters appear in opposite + // order to construction via the builder. + EXPECT_THAT(ret, IsReturn(IsJSAdd(IsParameter(2), IsParameter(1)), _, _)); +} + + +TEST_F(BytecodeGraphBuilderTest, SimpleExpressionWithRegister) { + static const int kLeft = -655371; + static const int kRight = +2000000; + array_builder()->set_locals_count(1); + array_builder()->set_parameter_count(1); + array_builder() + ->LoadLiteral(Smi::FromInt(kLeft)) + .StoreAccumulatorInRegister(interpreter::Register(0)) + .LoadLiteral(Smi::FromInt(kRight)) + .BinaryOperation(Token::Value::ADD, interpreter::Register(0)) + .Return(); + + Graph* graph = GetCompletedGraph(); + Node* end = graph->end(); + EXPECT_EQ(1, end->InputCount()); + Node* ret = end->InputAt(0); + EXPECT_THAT( + ret, IsReturn(IsJSAdd(IsNumberConstant(kLeft), IsNumberConstant(kRight)), + _, _)); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/test/unittests/compiler/node-test-utils.cc b/test/unittests/compiler/node-test-utils.cc index 4cd7293a1..2ca1b78d0 100644 --- a/test/unittests/compiler/node-test-utils.cc +++ b/test/unittests/compiler/node-test-utils.cc @@ -2008,6 +2008,7 @@ IS_BINOP_MATCHER(Uint32LessThan) IS_BINOP_MATCHER(Uint32LessThanOrEqual) IS_BINOP_MATCHER(Int64Add) IS_BINOP_MATCHER(Int64Sub) +IS_BINOP_MATCHER(JSAdd) IS_BINOP_MATCHER(Float32Max) IS_BINOP_MATCHER(Float32Min) IS_BINOP_MATCHER(Float32Equal) diff --git a/test/unittests/compiler/node-test-utils.h b/test/unittests/compiler/node-test-utils.h index 2b68fdbfe..7042d9943 100644 --- a/test/unittests/compiler/node-test-utils.h +++ b/test/unittests/compiler/node-test-utils.h @@ -295,6 +295,8 @@ Matcher IsInt64Add(const Matcher& lhs_matcher, const Matcher& rhs_matcher); Matcher IsInt64Sub(const Matcher& lhs_matcher, const Matcher& rhs_matcher); +Matcher IsJSAdd(const Matcher& lhs_matcher, + const Matcher& rhs_matcher); Matcher IsChangeFloat64ToInt32(const Matcher& input_matcher); Matcher IsChangeFloat64ToUint32(const Matcher& input_matcher); Matcher IsChangeInt32ToFloat64(const Matcher& input_matcher); diff --git a/test/unittests/interpreter/bytecode-array-iterator-unittest.cc b/test/unittests/interpreter/bytecode-array-iterator-unittest.cc new file mode 100644 index 000000000..b27085626 --- /dev/null +++ b/test/unittests/interpreter/bytecode-array-iterator-unittest.cc @@ -0,0 +1,100 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/v8.h" + +#include "src/interpreter/bytecode-array-builder.h" +#include "src/interpreter/bytecode-array-iterator.h" +#include "test/unittests/test-utils.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +class BytecodeArrayIteratorTest : public TestWithIsolateAndZone { + public: + BytecodeArrayIteratorTest() {} + ~BytecodeArrayIteratorTest() override {} +}; + + +TEST_F(BytecodeArrayIteratorTest, IteratesBytecodeArray) { + // Use a builder to create an array with containing multiple bytecodes + // with 0, 1 and 2 operands. + BytecodeArrayBuilder builder(isolate(), zone()); + builder.set_parameter_count(3); + builder.set_locals_count(2); + + Factory* factory = isolate()->factory(); + Handle heap_num_0 = factory->NewHeapNumber(2.718); + Handle heap_num_1 = factory->NewHeapNumber(2147483647); + Smi* zero = Smi::FromInt(0); + Smi* smi_0 = Smi::FromInt(64); + Smi* smi_1 = Smi::FromInt(-65536); + Register reg_0(0); + Register reg_1(1); + Register reg_2 = Register::FromParameterIndex(2, builder.parameter_count()); + int feedback_slot = 97; + + builder.LoadLiteral(heap_num_0) + .LoadLiteral(heap_num_1) + .LoadLiteral(zero) + .LoadLiteral(smi_0) + .LoadLiteral(smi_1) + .LoadAccumulatorWithRegister(reg_0) + .LoadNamedProperty(reg_1, feedback_slot, LanguageMode::SLOPPY) + .StoreAccumulatorInRegister(reg_2) + .Return(); + + // Test iterator sees the expected output from the builder. + BytecodeArrayIterator iterator(builder.ToBytecodeArray()); + CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaConstant); + CHECK(iterator.GetConstantForIndexOperand(0).is_identical_to(heap_num_0)); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaConstant); + CHECK(iterator.GetConstantForIndexOperand(0).is_identical_to(heap_num_1)); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaZero); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaSmi8); + CHECK_EQ(Smi::FromInt(iterator.GetSmi8Operand(0)), smi_0); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaConstant); + CHECK_EQ(*iterator.GetConstantForIndexOperand(0), smi_1); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdar); + CHECK_EQ(iterator.GetRegisterOperand(0).index(), reg_0.index()); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kLoadIC); + CHECK_EQ(iterator.GetRegisterOperand(0).index(), reg_1.index()); + CHECK_EQ(iterator.GetIndexOperand(1), feedback_slot); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kStar); + CHECK_EQ(iterator.GetRegisterOperand(0).index(), reg_2.index()); + CHECK(!iterator.done()); + iterator.Advance(); + + CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn); + CHECK(!iterator.done()); + iterator.Advance(); + CHECK(iterator.done()); +} + +} // namespace interpreter +} // namespace internal +} // namespace v8 diff --git a/test/unittests/unittests.gyp b/test/unittests/unittests.gyp index 51f2edceb..39502c7bf 100644 --- a/test/unittests/unittests.gyp +++ b/test/unittests/unittests.gyp @@ -43,6 +43,7 @@ 'base/sys-info-unittest.cc', 'base/utils/random-number-generator-unittest.cc', 'char-predicates-unittest.cc', + 'compiler/bytecode-graph-builder-unittest.cc', 'compiler/change-lowering-unittest.cc', 'compiler/coalesced-live-ranges-unittest.cc', 'compiler/common-operator-reducer-unittest.cc', @@ -98,6 +99,7 @@ 'counters-unittest.cc', 'interpreter/bytecodes-unittest.cc', 'interpreter/bytecode-array-builder-unittest.cc', + 'interpreter/bytecode-array-iterator-unittest.cc', 'libplatform/default-platform-unittest.cc', 'libplatform/task-queue-unittest.cc', 'libplatform/worker-thread-unittest.cc', diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index ef56b91ea..a64851a3e 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -454,6 +454,8 @@ '../../src/compiler/ast-loop-assignment-analyzer.h', '../../src/compiler/basic-block-instrumentor.cc', '../../src/compiler/basic-block-instrumentor.h', + '../../src/compiler/bytecode-graph-builder.cc', + '../../src/compiler/bytecode-graph-builder.h', '../../src/compiler/change-lowering.cc', '../../src/compiler/change-lowering.h', '../../src/compiler/c-linkage.cc', @@ -810,6 +812,8 @@ '../../src/interpreter/bytecode-generator.h', '../../src/interpreter/bytecode-array-builder.cc', '../../src/interpreter/bytecode-array-builder.h', + '../../src/interpreter/bytecode-array-iterator.cc', + '../../src/interpreter/bytecode-array-iterator.h', '../../src/interpreter/interpreter.cc', '../../src/interpreter/interpreter.h', '../../src/isolate-inl.h', -- 2.34.1