[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}
This commit is contained in:
parent
752b0308df
commit
8df7b4f6b5
4
BUILD.gn
4
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",
|
||||
|
1
src/DEPS
1
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",
|
||||
|
433
src/compiler/bytecode-graph-builder.cc
Normal file
433
src/compiler/bytecode-graph-builder.cc
Normal file
@ -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
|
174
src/compiler/bytecode-graph-builder.h
Normal file
174
src/compiler/bytecode-graph-builder.h
Normal file
@ -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_
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
72
src/interpreter/bytecode-array-iterator.cc
Normal file
72
src/interpreter/bytecode-array-iterator.cc
Normal file
@ -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
|
45
src/interpreter/bytecode-array-iterator.h
Normal file
45
src/interpreter/bytecode-array-iterator.h
Normal file
@ -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_
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
259
test/cctest/compiler/test-run-bytecode-graph-builder.cc
Normal file
259
test/cctest/compiler/test-run-bytecode-graph-builder.cc
Normal file
@ -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()));
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
248
test/unittests/compiler/bytecode-graph-builder-unittest.cc
Normal file
248
test/unittests/compiler/bytecode-graph-builder-unittest.cc
Normal file
@ -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
|
@ -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)
|
||||
|
@ -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);
|
||||
|
100
test/unittests/interpreter/bytecode-array-iterator-unittest.cc
Normal file
100
test/unittests/interpreter/bytecode-array-iterator-unittest.cc
Normal file
@ -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
|
@ -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',
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user