[Interpreter] Add for/while/do support to the bytecode generator.

Improve bytecode generation for if when there's no else clause.

Display target addresses for jump instructions in
Bytecode::Disassemble().

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31052}
This commit is contained in:
oth 2015-10-01 08:04:09 -07:00 committed by Commit bot
parent cf82eea6d7
commit a7e16e5132
12 changed files with 644 additions and 89 deletions

View File

@ -1051,6 +1051,8 @@ source_set("v8_base") {
"src/interpreter/bytecode-array-iterator.h",
"src/interpreter/bytecode-generator.cc",
"src/interpreter/bytecode-generator.h",
"src/interpreter/control-flow-builders.cc",
"src/interpreter/control-flow-builders.h",
"src/interpreter/interpreter.cc",
"src/interpreter/interpreter.h",
"src/isolate-inl.h",

View File

@ -10,6 +10,7 @@ namespace interpreter {
BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
: isolate_(isolate),
zone_(zone),
bytecodes_(zone),
bytecode_generated_(false),
last_block_end_(0),
@ -314,11 +315,14 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
}
// static
bool BytecodeArrayBuilder::IsJumpWithImm8Operand(Bytecode jump_bytecode) {
return jump_bytecode == Bytecode::kJump ||
jump_bytecode == Bytecode::kJumpIfTrue ||
jump_bytecode == Bytecode::kJumpIfFalse;
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
BytecodeLabel* label) {
DCHECK_EQ(label->is_bound(), false);
DCHECK_EQ(target.is_bound(), true);
PatchJump(bytecodes()->begin() + target.offset(),
bytecodes()->begin() + label->offset());
label->bind_to(target.offset());
return *this;
}
@ -345,9 +349,9 @@ void BytecodeArrayBuilder::PatchJump(
Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
int delta = static_cast<int>(jump_target - jump_location);
DCHECK(IsJumpWithImm8Operand(jump_bytecode));
DCHECK(Bytecodes::IsJump(jump_bytecode));
DCHECK_EQ(Bytecodes::Size(jump_bytecode), 2);
DCHECK_GE(delta, 0);
DCHECK_NE(delta, 0);
if (FitsInImm8Operand(delta)) {
// Just update the operand
@ -357,8 +361,8 @@ void BytecodeArrayBuilder::PatchJump(
// Update the jump type and operand
size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
if (FitsInIdxOperand(entry)) {
*jump_location++ =
Bytecodes::ToByte(GetJumpWithConstantOperand(jump_bytecode));
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
*jump_location++ = Bytecodes::ToByte(jump_bytecode);
*jump_location = static_cast<uint8_t>(entry);
} else {
// TODO(oth): OutputJump should reserve a constant pool entry

View File

@ -87,6 +87,8 @@ class BytecodeArrayBuilder {
// Flow Control.
BytecodeArrayBuilder& Bind(BytecodeLabel* label);
BytecodeArrayBuilder& Bind(const BytecodeLabel& target, BytecodeLabel* label);
BytecodeArrayBuilder& Jump(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfTrue(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label);
@ -95,6 +97,9 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& EnterBlock();
BytecodeArrayBuilder& LeaveBlock();
// Accessors
Zone* zone() const { return zone_; }
private:
ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; }
const ZoneVector<uint8_t>* bytecodes() const { return &bytecodes_; }
@ -105,7 +110,6 @@ class BytecodeArrayBuilder {
static bool FitsInIdxOperand(int value);
static bool FitsInIdxOperand(size_t value);
static bool FitsInImm8Operand(int value);
static bool IsJumpWithImm8Operand(Bytecode jump_bytecode);
static Bytecode GetJumpWithConstantOperand(Bytecode jump_with_smi8_operand);
template <size_t N>
@ -133,6 +137,7 @@ class BytecodeArrayBuilder {
void ReturnTemporaryRegister(int reg_index);
Isolate* isolate_;
Zone* zone_;
ZoneVector<uint8_t> bytecodes_;
bool bytecode_generated_;
size_t last_block_end_;
@ -159,7 +164,6 @@ class BytecodeArrayBuilder {
class BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
~BytecodeLabel() { DCHECK(bound_ && offset_ != kInvalidOffset); }
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
@ -188,7 +192,6 @@ class BytecodeLabel final {
size_t offset_;
friend class BytecodeArrayBuilder;
DISALLOW_COPY_AND_ASSIGN(BytecodeLabel);
};

View File

@ -7,6 +7,7 @@
#include <stack>
#include "src/compiler.h"
#include "src/interpreter/control-flow-builders.h"
#include "src/objects.h"
#include "src/scopes.h"
#include "src/token.h"
@ -15,8 +16,84 @@ namespace v8 {
namespace internal {
namespace interpreter {
// Scoped class for tracking control statements entered by the
// visitor. The pattern derives AstGraphBuilder::ControlScope.
class BytecodeGenerator::ControlScope BASE_EMBEDDED {
public:
explicit ControlScope(BytecodeGenerator* generator)
: generator_(generator), outer_(generator->control_scope()) {
generator_->set_control_scope(this);
}
virtual ~ControlScope() { generator_->set_control_scope(outer()); }
void Break(Statement* stmt) { PerformCommand(CMD_BREAK, stmt); }
void Continue(Statement* stmt) { PerformCommand(CMD_CONTINUE, stmt); }
protected:
enum Command { CMD_BREAK, CMD_CONTINUE };
void PerformCommand(Command command, Statement* statement);
virtual bool Execute(Command command, Statement* statement) = 0;
BytecodeGenerator* generator() const { return generator_; }
ControlScope* outer() const { return outer_; }
private:
BytecodeGenerator* generator_;
ControlScope* outer_;
DISALLOW_COPY_AND_ASSIGN(ControlScope);
};
// Scoped class for enabling 'break' and 'continue' in iteration
// constructs, e.g. do...while, while..., for...
class BytecodeGenerator::ControlScopeForIteration
: public BytecodeGenerator::ControlScope {
public:
ControlScopeForIteration(BytecodeGenerator* generator,
IterationStatement* statement,
LoopBuilder* loop_builder)
: ControlScope(generator),
statement_(statement),
loop_builder_(loop_builder) {}
protected:
virtual bool Execute(Command command, Statement* statement) {
if (statement != statement_) return false;
switch (command) {
case CMD_BREAK:
loop_builder_->Break();
return true;
case CMD_CONTINUE:
loop_builder_->Continue();
return true;
}
return false;
}
private:
Statement* statement_;
LoopBuilder* loop_builder_;
};
void BytecodeGenerator::ControlScope::PerformCommand(Command command,
Statement* statement) {
ControlScope* current = this;
do {
if (current->Execute(command, statement)) return;
current = current->outer();
} while (current != nullptr);
UNREACHABLE();
}
BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
: builder_(isolate, zone) {
: builder_(isolate, zone),
info_(nullptr),
scope_(nullptr),
control_scope_(nullptr) {
InitializeAstVisitor(isolate, zone);
}
@ -31,8 +108,8 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
// This a temporary guard (oth).
DCHECK(scope()->is_function_scope());
builder().set_parameter_count(info->num_parameters_including_this());
builder().set_locals_count(scope()->num_stack_slots());
builder()->set_parameter_count(info->num_parameters_including_this());
builder()->set_locals_count(scope()->num_stack_slots());
// Visit implicit declaration of the function name.
if (scope()->is_function_scope() && scope()->function() != NULL) {
@ -52,7 +129,7 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
void BytecodeGenerator::VisitBlock(Block* node) {
builder().EnterBlock();
builder()->EnterBlock();
if (node->scope() == NULL) {
// Visit statements in the same scope, no declarations.
VisitStatements(node->statements());
@ -65,7 +142,7 @@ void BytecodeGenerator::VisitBlock(Block* node) {
VisitStatements(node->statements());
}
}
builder().LeaveBlock();
builder()->LeaveBlock();
}
@ -114,20 +191,24 @@ void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
BytecodeLabel else_start, else_end;
// TODO(oth): Spot easy cases where there code would not need to
// emit the then block or the else block, e.g. condition is
// obviously true/1/false/0.
BytecodeLabel else_label, end_label;
Visit(stmt->condition());
builder().CastAccumulatorToBoolean();
builder().JumpIfFalse(&else_start);
builder()->CastAccumulatorToBoolean();
builder()->JumpIfFalse(&else_label);
Visit(stmt->then_statement());
builder().Jump(&else_end);
builder().Bind(&else_start);
Visit(stmt->else_statement());
builder().Bind(&else_end);
if (stmt->HasElseStatement()) {
builder()->Jump(&end_label);
builder()->Bind(&else_label);
Visit(stmt->else_statement());
} else {
builder()->Bind(&else_label);
}
builder()->Bind(&end_label);
}
@ -138,18 +219,18 @@ void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
UNIMPLEMENTED();
control_scope()->Continue(stmt->target());
}
void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
UNIMPLEMENTED();
control_scope()->Break(stmt->target());
}
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
Visit(stmt->expression());
builder().Return();
builder()->Return();
}
@ -167,17 +248,69 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
UNIMPLEMENTED();
LoopBuilder loop_builder(builder());
ControlScopeForIteration control_scope(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label;
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&condition_label);
Visit(stmt->cond());
builder()->JumpIfTrue(&body_label);
builder()->Bind(&done_label);
loop_builder.SetBreakTarget(done_label);
loop_builder.SetContinueTarget(condition_label);
}
void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
UNIMPLEMENTED();
LoopBuilder loop_builder(builder());
ControlScopeForIteration control_scope(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label;
builder()->Jump(&condition_label);
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&condition_label);
Visit(stmt->cond());
builder()->JumpIfTrue(&body_label);
builder()->Bind(&done_label);
loop_builder.SetBreakTarget(done_label);
loop_builder.SetContinueTarget(condition_label);
}
void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
UNIMPLEMENTED();
LoopBuilder loop_builder(builder());
ControlScopeForIteration control_scope(this, stmt, &loop_builder);
if (stmt->init() != nullptr) {
Visit(stmt->init());
}
BytecodeLabel body_label, condition_label, next_label, done_label;
if (stmt->cond() != nullptr) {
builder()->Jump(&condition_label);
}
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&next_label);
if (stmt->next() != nullptr) {
Visit(stmt->next());
}
if (stmt->cond()) {
builder()->Bind(&condition_label);
Visit(stmt->cond());
builder()->JumpIfTrue(&body_label);
} else {
builder()->Jump(&body_label);
}
builder()->Bind(&done_label);
loop_builder.SetBreakTarget(done_label);
loop_builder.SetContinueTarget(next_label);
}
@ -228,19 +361,19 @@ void BytecodeGenerator::VisitConditional(Conditional* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitLiteral(Literal* expr) {
Handle<Object> value = expr->value();
if (value->IsSmi()) {
builder().LoadLiteral(Smi::cast(*value));
builder()->LoadLiteral(Smi::cast(*value));
} else if (value->IsUndefined()) {
builder().LoadUndefined();
builder()->LoadUndefined();
} else if (value->IsTrue()) {
builder().LoadTrue();
builder()->LoadTrue();
} else if (value->IsFalse()) {
builder().LoadFalse();
builder()->LoadFalse();
} else if (value->IsNull()) {
builder().LoadNull();
builder()->LoadNull();
} else if (value->IsTheHole()) {
builder().LoadTheHole();
builder()->LoadTheHole();
} else {
builder().LoadLiteral(value);
builder()->LoadLiteral(value);
}
}
@ -269,14 +402,14 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable) {
switch (variable->location()) {
case VariableLocation::LOCAL: {
Register source(variable->index());
builder().LoadAccumulatorWithRegister(source);
builder()->LoadAccumulatorWithRegister(source);
break;
}
case VariableLocation::PARAMETER: {
// The parameter indices are shifted by 1 (receiver is variable
// index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register source(builder().Parameter(variable->index() + 1));
builder().LoadAccumulatorWithRegister(source);
Register source(builder()->Parameter(variable->index() + 1));
builder()->LoadAccumulatorWithRegister(source);
break;
}
case VariableLocation::GLOBAL: {
@ -285,7 +418,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable) {
// a generic version of LoadGlobalViaContextStub rather than calling the
// runtime.
DCHECK(variable->IsStaticGlobalObjectProperty());
builder().LoadGlobal(variable->index());
builder()->LoadGlobal(variable->index());
break;
}
case VariableLocation::UNALLOCATED:
@ -314,17 +447,17 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
object = temporary_register_scope.NewRegister();
key = temporary_register_scope.NewRegister();
Visit(property->obj());
builder().StoreAccumulatorInRegister(object);
builder().LoadLiteral(property->key()->AsLiteral()->AsPropertyName());
builder().StoreAccumulatorInRegister(key);
builder()->StoreAccumulatorInRegister(object);
builder()->LoadLiteral(property->key()->AsLiteral()->AsPropertyName());
builder()->StoreAccumulatorInRegister(key);
break;
case KEYED_PROPERTY:
object = temporary_register_scope.NewRegister();
key = temporary_register_scope.NewRegister();
Visit(property->obj());
builder().StoreAccumulatorInRegister(object);
builder()->StoreAccumulatorInRegister(object);
Visit(property->key());
builder().StoreAccumulatorInRegister(key);
builder()->StoreAccumulatorInRegister(key);
break;
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
@ -346,16 +479,16 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
Variable* variable = expr->target()->AsVariableProxy()->var();
DCHECK(variable->location() == VariableLocation::LOCAL);
Register destination(variable->index());
builder().StoreAccumulatorInRegister(destination);
builder()->StoreAccumulatorInRegister(destination);
break;
}
case NAMED_PROPERTY:
builder().StoreNamedProperty(object, key, feedback_index(slot),
language_mode());
builder()->StoreNamedProperty(object, key, feedback_index(slot),
language_mode());
break;
case KEYED_PROPERTY:
builder().StoreKeyedProperty(object, key, feedback_index(slot),
language_mode());
builder()->StoreKeyedProperty(object, key, feedback_index(slot),
language_mode());
break;
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
@ -377,13 +510,13 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) {
case VARIABLE:
UNREACHABLE();
case NAMED_PROPERTY: {
builder().LoadLiteral(expr->key()->AsLiteral()->AsPropertyName());
builder().LoadNamedProperty(obj, feedback_index(slot), language_mode());
builder()->LoadLiteral(expr->key()->AsLiteral()->AsPropertyName());
builder()->LoadNamedProperty(obj, feedback_index(slot), language_mode());
break;
}
case KEYED_PROPERTY: {
Visit(expr->key());
builder().LoadKeyedProperty(obj, feedback_index(slot), language_mode());
builder()->LoadKeyedProperty(obj, feedback_index(slot), language_mode());
break;
}
case NAMED_SUPER_PROPERTY:
@ -397,7 +530,7 @@ void BytecodeGenerator::VisitProperty(Property* expr) {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register obj = temporary_register_scope.NewRegister();
Visit(expr->obj());
builder().StoreAccumulatorInRegister(obj);
builder()->StoreAccumulatorInRegister(obj);
VisitPropertyLoad(obj, expr);
}
@ -419,19 +552,19 @@ void BytecodeGenerator::VisitCall(Call* expr) {
UNIMPLEMENTED();
}
Visit(property->obj());
builder().StoreAccumulatorInRegister(receiver);
builder()->StoreAccumulatorInRegister(receiver);
// Perform a property load of the callee.
VisitPropertyLoad(receiver, property);
builder().StoreAccumulatorInRegister(callee);
builder()->StoreAccumulatorInRegister(callee);
break;
}
case Call::GLOBAL_CALL: {
// Receiver is undefined for global calls.
builder().LoadUndefined().StoreAccumulatorInRegister(receiver);
builder()->LoadUndefined().StoreAccumulatorInRegister(receiver);
// Load callee as a global variable.
VariableProxy* proxy = callee_expr->AsVariableProxy();
VisitVariableLoad(proxy->var());
builder().StoreAccumulatorInRegister(callee);
builder()->StoreAccumulatorInRegister(callee);
break;
}
case Call::LOOKUP_SLOT_CALL:
@ -448,12 +581,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
Visit(args->at(i));
Register arg = temporary_register_scope.NewRegister();
DCHECK(arg.index() - i == receiver.index() + 1);
builder().StoreAccumulatorInRegister(arg);
builder()->StoreAccumulatorInRegister(arg);
}
// TODO(rmcilroy): Deal with possible direct eval here?
// TODO(rmcilroy): Use CallIC to allow call type feedback.
builder().Call(callee, receiver, args->length());
builder()->Call(callee, receiver, args->length());
}
@ -496,9 +629,9 @@ void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
Register temporary = temporary_register_scope.NewRegister();
Visit(left);
builder().StoreAccumulatorInRegister(temporary);
builder()->StoreAccumulatorInRegister(temporary);
Visit(right);
builder().CompareOperation(op, temporary, language_mode());
builder()->CompareOperation(op, temporary, language_mode());
}
@ -535,9 +668,9 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* binop) {
Register temporary = temporary_register_scope.NewRegister();
Visit(left);
builder().StoreAccumulatorInRegister(temporary);
builder()->StoreAccumulatorInRegister(temporary);
Visit(right);
builder().BinaryOperation(op, temporary);
builder()->BinaryOperation(op, temporary);
}

View File

@ -25,15 +25,20 @@ class BytecodeGenerator : public AstVisitor {
#undef DECLARE_VISIT
private:
class ControlScope;
class ControlScopeForIteration;
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitPropertyLoad(Register obj, Property* expr);
void VisitVariableLoad(Variable* variable);
inline BytecodeArrayBuilder& builder() { return builder_; }
inline BytecodeArrayBuilder* builder() { return &builder_; }
inline Scope* scope() const { return scope_; }
inline void set_scope(Scope* scope) { scope_ = scope; }
inline ControlScope* control_scope() const { return control_scope_; }
inline void set_control_scope(ControlScope* scope) { control_scope_ = scope; }
inline CompilationInfo* info() const { return info_; }
inline void set_info(CompilationInfo* info) { info_ = info; }
@ -43,6 +48,7 @@ class BytecodeGenerator : public AstVisitor {
BytecodeArrayBuilder builder_;
CompilationInfo* info_;
Scope* scope_;
ControlScope* control_scope_;
};
} // namespace interpreter

View File

@ -102,6 +102,21 @@ int Bytecodes::MaximumNumberOfOperands() { return kMaxOperands; }
int Bytecodes::MaximumSize() { return 1 + kMaxOperands; }
// static
bool Bytecodes::IsJump(Bytecode bytecode) {
return bytecode == Bytecode::kJump || bytecode == Bytecode::kJumpIfTrue ||
bytecode == Bytecode::kJumpIfFalse;
}
// static
bool Bytecodes::IsJumpConstant(Bytecode bytecode) {
return bytecode == Bytecode::kJumpConstant ||
bytecode == Bytecode::kJumpIfTrueConstant ||
bytecode == Bytecode::kJumpIfFalseConstant;
}
// static
std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
int parameter_count) {

View File

@ -178,6 +178,14 @@ class Bytecodes {
// Maximum size of a bytecode and its operands.
static int MaximumSize();
// Return true if the bytecode is a jump or a conditional jump taking
// an immediate byte operand (OperandType::kImm8).
static bool IsJump(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking a
// constant pool entry (OperandType::kIdx).
static bool IsJumpConstant(Bytecode bytecode);
// Decode a single bytecode and operands to |os|.
static std::ostream& Decode(std::ostream& os, const uint8_t* bytecode_start,
int number_of_parameters);

View 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.
#include "src/interpreter/control-flow-builders.h"
namespace v8 {
namespace internal {
namespace interpreter {
LoopBuilder::~LoopBuilder() {
DCHECK(continue_sites_.empty());
DCHECK(break_sites_.empty());
}
void LoopBuilder::SetContinueTarget(const BytecodeLabel& target) {
BindLabels(target, &continue_sites_);
}
void LoopBuilder::SetBreakTarget(const BytecodeLabel& target) {
BindLabels(target, &break_sites_);
}
void LoopBuilder::EmitJump(ZoneVector<BytecodeLabel>* sites) {
sites->push_back(BytecodeLabel());
builder()->Jump(&sites->back());
}
void LoopBuilder::BindLabels(const BytecodeLabel& target,
ZoneVector<BytecodeLabel>* sites) {
for (size_t i = 0; i < sites->size(); i++) {
BytecodeLabel& site = sites->at(i);
builder()->Bind(target, &site);
}
sites->clear();
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,68 @@
// 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_CONTROL_FLOW_BUILDERS_H_
#define V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_
#include "src/interpreter/bytecode-array-builder.h"
#include "src/zone-containers.h"
namespace v8 {
namespace internal {
namespace interpreter {
class ControlFlowBuilder BASE_EMBEDDED {
public:
explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
: builder_(builder) {}
virtual ~ControlFlowBuilder() {}
protected:
BytecodeArrayBuilder* builder() const { return builder_; }
private:
BytecodeArrayBuilder* builder_;
DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder);
};
// A class to help with co-ordinating break and continue statements with
// their loop.
// TODO(oth): add support for TF branch/merge info.
class LoopBuilder : public ControlFlowBuilder {
public:
explicit LoopBuilder(BytecodeArrayBuilder* builder)
: ControlFlowBuilder(builder),
continue_sites_(builder->zone()),
break_sites_(builder->zone()) {}
~LoopBuilder();
// These methods should be called by the LoopBuilder owner before
// destruction to update sites that emit jumps for break/continue.
void SetContinueTarget(const BytecodeLabel& continue_target);
void SetBreakTarget(const BytecodeLabel& break_target);
// These methods are called when visiting break and continue
// statements in the AST. Inserts a jump to a unbound label that is
// patched when the corresponding SetContinueTarget/SetBreakTarget
// is called.
void Break() { EmitJump(&break_sites_); }
void Continue() { EmitJump(&continue_sites_); }
private:
void BindLabels(const BytecodeLabel& target, ZoneVector<BytecodeLabel>* site);
void EmitJump(ZoneVector<BytecodeLabel>* labels);
// Unbound labels that identify jumps for continue/break statements
// in the code.
ZoneVector<BytecodeLabel> continue_sites_;
ZoneVector<BytecodeLabel> break_sites_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_

View File

@ -12557,6 +12557,16 @@ void BytecodeArray::Disassemble(std::ostream& os) {
SNPrintF(buf, "%p", bytecode_start);
os << buf.start() << " : ";
interpreter::Bytecodes::Decode(os, bytecode_start, parameter_count());
if (interpreter::Bytecodes::IsJump(bytecode)) {
int offset = static_cast<int8_t>(bytecode_start[1]);
SNPrintF(buf, " (%p)", bytecode_start + offset);
os << buf.start();
} else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
int index = static_cast<int>(bytecode_start[1]);
int offset = Smi::cast(constant_pool()->get(index))->value();
SNPrintF(buf, " (%p)", bytecode_start + offset);
os << buf.start();
}
os << "\n";
}

View File

@ -753,7 +753,7 @@ TEST(IfConditions) {
B(JumpIfFalse), U8(7), //
B(LdaSmi8), U8(1), //
B(Return), //
B(Jump), U8(5), // TODO(oth): Unreachable jump after return
B(Jump), U8(5), //
B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), //
@ -769,7 +769,7 @@ TEST(IfConditions) {
B(JumpIfFalse), U8(7), //
B(LdaSmi8), U8(1), //
B(Return), //
B(Jump), U8(5), // TODO(oth): Unreachable jump after return
B(Jump), U8(5), //
B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), //
@ -785,7 +785,7 @@ TEST(IfConditions) {
B(JumpIfFalse), U8(7), //
B(LdaSmi8), U8(1), //
B(Return), //
B(Jump), U8(5), // TODO(oth): Unreachable jump after return
B(Jump), U8(5), //
B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), //
@ -804,11 +804,11 @@ TEST(IfConditions) {
B(JumpIfFalse), U8(7), //
B(LdaConstant), U8(0), //
B(Return), //
B(Jump), U8(5), // TODO(oth): Unreachable jump after return
B(LdaConstant), U8(1), //
B(Return), //
B(LdaUndefined), //
B(Return)}, //
B(Jump), U8(5), //
B(LdaConstant), U8(1), //
B(Return), //
B(LdaUndefined), //
B(Return)}, //
2,
{helper.factory()->NewNumberFromInt(200),
helper.factory()->NewNumberFromInt(-200), unused, unused}},
@ -816,17 +816,16 @@ TEST(IfConditions) {
"f('prop', { prop: 'yes'});",
kPointerSize,
3,
17,
15,
{B(Ldar), R(helper.kLastParamIndex - 1), //
B(Star), R(0), //
B(Ldar), R(helper.kLastParamIndex), //
B(TestIn), R(0), //
B(JumpIfFalse), U8(7), //
B(JumpIfFalse), U8(5), //
B(LdaConstant), U8(0), //
B(Return), //
B(Jump), U8(2), // TODO(oth): Unreachable jump after return
B(LdaUndefined), //
B(Return)}, //
B(LdaUndefined), //
B(Return)}, //
1,
{helper.factory()->NewNumberFromInt(200), unused, unused, unused}},
{"function f(z) { var a = 0; var b = 0; if (a === 0.01) { "
@ -849,10 +848,9 @@ TEST(IfConditions) {
#define X B(Ldar), R(0), B(Star), R(1), B(Ldar), R(1), B(Star), R(0),
X X X X X X X X X X X X X X X X X X X X X X X X
#undef X
B(LdaConstant),
U8(1), //
B(LdaConstant), U8(1), //
B(Return), //
B(Jump), U8(5), // TODO(oth): Unreachable jump after return
B(Jump), U8(5), //
B(LdaConstant), U8(3), //
B(Return), //
B(LdaUndefined), //
@ -877,17 +875,16 @@ TEST(IfConditions) {
"} f(1, 1);",
kPointerSize,
3,
122,
106,
{
#define IF_CONDITION_RETURN(condition) \
#define IF_CONDITION_RETURN(condition) \
B(Ldar), R(helper.kLastParamIndex - 1), \
B(Star), R(0), \
B(Ldar), R(helper.kLastParamIndex), \
B(condition), R(0), \
B(JumpIfFalse), U8(7), \
B(JumpIfFalse), U8(5), \
B(LdaSmi8), U8(1), \
B(Return), \
B(Jump), U8(2),
B(Return),
IF_CONDITION_RETURN(TestEqual) //
IF_CONDITION_RETURN(TestEqualStrict) //
IF_CONDITION_RETURN(TestLessThan) //
@ -911,6 +908,268 @@ TEST(IfConditions) {
}
TEST(BasicLoops) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
{"var x = 0;"
"var y = 1;"
"while (x < 10) {"
" y = y * 10;"
" x = x + 1;"
"}"
"return y;",
3 * kPointerSize,
1,
42,
{
B(LdaZero), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Jump), U8(22), //
B(Ldar), R(1), //
B(Star), R(2), //
B(LdaSmi8), U8(10), //
B(Mul), R(2), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Add), R(2), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(10), //
B(TestLessThan), R(2), //
B(JumpIfTrue), U8(-28), //
B(Ldar), R(1), //
B(Return), //
},
0},
{"var i = 0;"
"while(true) {"
" if (i < 0) continue;"
" if (i == 3) break;"
" if (i == 4) break;"
" if (i == 10) continue;"
" if (i == 5) break;"
" i = i + 1;"
"}"
"return i;",
2 * kPointerSize,
1,
80,
{
B(LdaZero), //
B(Star), R(0), //
B(Jump), U8(71), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaZero), //
B(TestLessThan), R(1), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(60), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(3), //
B(TestEqual), R(1), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(51), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(4), //
B(TestEqual), R(1), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(39), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(10), //
B(TestEqual), R(1), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(24), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(5), //
B(TestEqual), R(1), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(15), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Add), R(1), //
B(Star), R(0), //
B(LdaTrue), //
B(JumpIfTrue), U8(-70), //
B(Ldar), R(0), //
B(Return) //
},
0},
{"var x = 0; var y = 1;"
"do {"
" y = y * 10;"
" if (x == 5) break;"
" if (x == 6) continue;"
" x = x + 1;"
"} while (x < 10);"
"return y;",
3 * kPointerSize,
1,
64,
{
B(LdaZero), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Star), R(2), //
B(LdaSmi8), U8(10), //
B(Mul), R(2), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(5), //
B(TestEqual), R(2), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(34), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(6), //
B(TestEqual), R(2), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(12), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Add), R(2), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(10), //
B(TestLessThan), R(2), //
B(JumpIfTrue), U8(-52), //
B(Ldar), R(1), //
B(Return) //
},
0},
{"var x = 0; "
"for(;;) {"
" if (x == 1) break;"
" x = x + 1;"
"}",
2 * kPointerSize,
1,
29,
{
B(LdaZero), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), //
U8(1), //
B(TestEqual), R(1), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(14), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Add), R(1), //
B(Star), R(0), //
B(Jump), U8(-22), //
B(LdaUndefined), //
B(Return), //
},
0},
{"var u = 0;"
"for(var i = 0; i < 100; i = i + 1) {"
" u = u + 1;"
" continue;"
"}",
3 * kPointerSize,
1,
42,
{
B(LdaZero), //
B(Star), R(0), //
B(LdaZero), //
B(Star), R(1), //
B(Jump), U8(24), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Add), R(2), //
B(Star), R(0), //
B(Jump), U8(2), //
B(Ldar), R(1), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Add), R(2), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Star), R(2), //
B(LdaSmi8), U8(100), //
B(TestLessThan), R(2), //
B(JumpIfTrue), U8(-30), //
B(LdaUndefined), //
B(Return), //
},
0},
{"var i = 0;"
"while(true) {"
" while (i < 3) {"
" if (i == 2) break;"
" i = i + 1;"
" }"
" i = i + 1;"
" break;"
"}"
"return i;",
2 * kPointerSize,
1,
57,
{
B(LdaZero), //
B(Star), R(0), //
B(Jump), U8(48), //
B(Jump), U8(24), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(2), //
B(TestEqual), R(1), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(22), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Add), R(1), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(3), //
B(TestLessThan), R(1), //
B(JumpIfTrue), U8(-30), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Add), R(1), //
B(Star), R(0), //
B(Jump), U8(5), //
B(LdaTrue), //
B(JumpIfTrue), U8(-47), //
B(Ldar), R(0), //
B(Return), //
},
0},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -816,6 +816,8 @@
'../../src/interpreter/bytecode-array-builder.h',
'../../src/interpreter/bytecode-array-iterator.cc',
'../../src/interpreter/bytecode-array-iterator.h',
'../../src/interpreter/control-flow-builders.cc',
'../../src/interpreter/control-flow-builders.h',
'../../src/interpreter/interpreter.cc',
'../../src/interpreter/interpreter.h',
'../../src/isolate-inl.h',