[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:
parent
cf82eea6d7
commit
a7e16e5132
2
BUILD.gn
2
BUILD.gn
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
45
src/interpreter/control-flow-builders.cc
Normal file
45
src/interpreter/control-flow-builders.cc
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.
|
||||
|
||||
#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
|
68
src/interpreter/control-flow-builders.h
Normal file
68
src/interpreter/control-flow-builders.h
Normal 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_
|
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user