[Interpreter] Skeleton bytecode graph builder
authoroth <oth@chromium.org>
Thu, 10 Sep 2015 16:21:34 +0000 (09:21 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 10 Sep 2015 16:21:40 +0000 (16:21 +0000)
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}

18 files changed:
BUILD.gn
src/DEPS
src/compiler/bytecode-graph-builder.cc [new file with mode: 0644]
src/compiler/bytecode-graph-builder.h [new file with mode: 0644]
src/compiler/pipeline.cc
src/interpreter/bytecode-array-iterator.cc [new file with mode: 0644]
src/interpreter/bytecode-array-iterator.h [new file with mode: 0644]
src/objects-inl.h
src/objects.h
test/cctest/cctest.gyp
test/cctest/compiler/test-run-bytecode-graph-builder.cc [new file with mode: 0644]
test/cctest/interpreter/test-bytecode-generator.cc
test/unittests/compiler/bytecode-graph-builder-unittest.cc [new file with mode: 0644]
test/unittests/compiler/node-test-utils.cc
test/unittests/compiler/node-test-utils.h
test/unittests/interpreter/bytecode-array-iterator-unittest.cc [new file with mode: 0644]
test/unittests/unittests.gyp
tools/gyp/v8.gyp

index 6253f548779a5220574b748af40f35f4aec0734c..d6341f7449d6674354e462261e0ab250101694c9 100644 (file)
--- 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",
index 7350f61f9f2b755481502b2e201e535055da25e2..26b080874019f71e226fc04cc77502644bb4a452 100644 (file)
--- 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 (file)
index 0000000..29530f9
--- /dev/null
@@ -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<int>(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<int>(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<Node*>(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 (file)
index 0000000..4e479ba
--- /dev/null
@@ -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<Node**>(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<BytecodeArray>& 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<BytecodeArray> 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<Node> function_context_;
+
+  // Control nodes that exit the function body.
+  ZoneVector<Node*> 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_
index bd03020965aafd5f6d0c4c96e66c4ef0610a0672..fd79a865e2403b423ab819043bdceb7443dfda8c 100644 (file)
@@ -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 (file)
index 0000000..84be6cc
--- /dev/null
@@ -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<BytecodeArray> 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<int8_t>(operand);
+}
+
+
+int BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
+  uint8_t operand = GetOperand(operand_index, OperandType::kIdx);
+  return static_cast<int>(operand);
+}
+
+
+Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
+  uint8_t operand = GetOperand(operand_index, OperandType::kReg);
+  return Register::FromOperand(operand);
+}
+
+
+Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
+    int operand_index) const {
+  Handle<FixedArray> 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 (file)
index 0000000..a0de3f0
--- /dev/null
@@ -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<BytecodeArray> bytecode_array);
+
+  void Advance();
+  bool done() const;
+  Bytecode current_bytecode() const;
+  const Handle<BytecodeArray>& 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<Object> GetConstantForIndexOperand(int operand_index) const;
+
+ private:
+  uint8_t GetOperand(int operand_index, OperandType operand_type) const;
+
+  Handle<BytecodeArray> bytecode_array_;
+  int bytecode_offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(BytecodeArrayIterator);
+};
+
+}  // namespace interpreter
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_INTERPRETER_BYTECODE_GRAPH_ITERATOR_H_
index 42e8354fb51483b2e9af5805a0e7f21d8b157a6d..9ddaa81af44ba1df971ae679a90d5c3a50d60f89 100644 (file)
@@ -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
index 751e3cf3453e76a3f4409d061e6e841d6c37e027..f547c8388cf9df637a2436ba64c4bd3342cbef7a 100644 (file)
@@ -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);
index d15820a0d27717f37ab65bd9ed4fc723c9441f84..0a9d5354bd99faf24a5131359e908c71a2639849 100644 (file)
@@ -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 (file)
index 0000000..c64a934
--- /dev/null
@@ -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 <utility>
+
+#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<Object> CallFunction(Isolate* isolate,
+                                        Handle<JSFunction> function) {
+  return Execution::Call(isolate, function,
+                         isolate->factory()->undefined_value(), 0, nullptr,
+                         false);
+}
+
+
+template <class... A>
+static MaybeHandle<Object> CallFunction(Isolate* isolate,
+                                        Handle<JSFunction> function,
+                                        A... args) {
+  Handle<Object> argv[] = {args...};
+  return Execution::Call(isolate, function,
+                         isolate->factory()->undefined_value(), sizeof...(args),
+                         argv, false);
+}
+
+
+template <class... A>
+class BytecodeGraphCallable {
+ public:
+  BytecodeGraphCallable(Isolate* isolate, Handle<JSFunction> function)
+      : isolate_(isolate), function_(function) {}
+  virtual ~BytecodeGraphCallable() {}
+
+  MaybeHandle<Object> operator()(A... args) {
+    return CallFunction(isolate_, function_, args...);
+  }
+
+ private:
+  Isolate* isolate_;
+  Handle<JSFunction> 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<char> 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 <class... A>
+  BytecodeGraphCallable<A...> GetCallable() {
+    return BytecodeGraphCallable<A...>(isolate_, GetFunction());
+  }
+
+ private:
+  Isolate* isolate_;
+  Zone* zone_;
+  const char* script_;
+
+  Handle<JSFunction> GetFunction() {
+    CompileRun(script_);
+    Local<Function> api_function =
+        Local<Function>::Cast(CcTest::global()->Get(v8_str(kFunctionName)));
+    Handle<JSFunction> 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<Code>());
+    Parser parser(&parse_info);
+    CHECK(parser.Parse(&parse_info));
+    compiler::Pipeline pipeline(&compilation_info);
+    Handle<Code> 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 <int N>
+struct ExpectedSnippet {
+  const char* code_snippet;
+  Handle<Object> return_value_and_parameters[N + 1];
+
+  inline Handle<Object> return_value() const {
+    return return_value_and_parameters[0];
+  }
+
+  inline Handle<Object> 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<char> 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<Object> 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<char> 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<Object> 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<char> 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<Object>, Handle<Object>>();
+    Handle<Object> return_value =
+        callable(snippets[i].parameter(0), snippets[i].parameter(1))
+            .ToHandleChecked();
+    CHECK(return_value->SameValue(*snippets[i].return_value()));
+  }
+}
index 1faaa707af2173bbc873e9c8f55ae039a1027965..42125f07f63924a4f64aac16916cb9eedee030f4 100644 (file)
@@ -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 (file)
index 0000000..22b9b89
--- /dev/null
@@ -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 <iostream>
+
+#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<Node*> IsUndefinedConstant();
+  Matcher<Node*> IsNullConstant();
+  Matcher<Node*> IsTheHoleConstant();
+  Matcher<Node*> IsFalseConstant();
+  Matcher<Node*> 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<String> name = factory()->NewStringFromStaticChars("test");
+  Handle<String> script = factory()->NewStringFromStaticChars("test() {}");
+  Handle<SharedFunctionInfo> shared_info =
+      factory()->NewSharedFunctionInfo(name, MaybeHandle<Code>());
+  shared_info->set_script(*factory()->NewScript(script));
+
+  ParseInfo parse_info(zone(), shared_info);
+  CompilationInfo info(&parse_info);
+  Handle<BytecodeArray> 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<Node*> BytecodeGraphBuilderTest::IsUndefinedConstant() {
+  return IsHeapConstant(factory()->undefined_value());
+}
+
+
+Matcher<Node*> BytecodeGraphBuilderTest::IsNullConstant() {
+  return IsHeapConstant(factory()->null_value());
+}
+
+
+Matcher<Node*> BytecodeGraphBuilderTest::IsTheHoleConstant() {
+  return IsHeapConstant(factory()->the_hole_value());
+}
+
+
+Matcher<Node*> BytecodeGraphBuilderTest::IsFalseConstant() {
+  return IsHeapConstant(factory()->false_value());
+}
+
+
+Matcher<Node*> 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 <reg> <op> <acc>. 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
index 4cd7293a1a55bc686a12c196cc9ab8a8c6fd369f..2ca1b78d09f6c54299c4729649b2927bdde9583e 100644 (file)
@@ -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)
index 2b68fdbfebff42d001c73e8bddd423a9cc39e54e..7042d9943ba638bd804d66d74a9fd8f68bcb7bc2 100644 (file)
@@ -295,6 +295,8 @@ Matcher<Node*> IsInt64Add(const Matcher<Node*>& lhs_matcher,
                           const Matcher<Node*>& rhs_matcher);
 Matcher<Node*> IsInt64Sub(const Matcher<Node*>& lhs_matcher,
                           const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsJSAdd(const Matcher<Node*>& lhs_matcher,
+                       const Matcher<Node*>& rhs_matcher);
 Matcher<Node*> IsChangeFloat64ToInt32(const Matcher<Node*>& input_matcher);
 Matcher<Node*> IsChangeFloat64ToUint32(const Matcher<Node*>& input_matcher);
 Matcher<Node*> IsChangeInt32ToFloat64(const Matcher<Node*>& 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 (file)
index 0000000..b270856
--- /dev/null
@@ -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<HeapObject> heap_num_0 = factory->NewHeapNumber(2.718);
+  Handle<HeapObject> 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
index 51f2edceb576b29bd7882c3f187f6ff55831e5ff..39502c7bf13135de9ccbbd8816ee2923719905f7 100644 (file)
@@ -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',
index ef56b91ea595d63e634beb5b57bdbbe1972a4c86..a64851a3ed9bcfb1051d7e4e947e65c85bb9ae36 100644 (file)
         '../../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',
         '../../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',