| Index: src/interpreter/bytecode-generator.cc
|
| diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
|
| index d5305fa84f9cfde746c437c3c0c86e96b7097163..d90603a25d81792c29df025efd45c4642b86b484 100644
|
| --- a/src/interpreter/bytecode-generator.cc
|
| +++ b/src/interpreter/bytecode-generator.cc
|
| @@ -8,6 +8,7 @@
|
| #include "src/code-stubs.h"
|
| #include "src/compiler.h"
|
| #include "src/interpreter/bytecode-flags.h"
|
| +#include "src/interpreter/bytecode-label.h"
|
| #include "src/interpreter/bytecode-register-allocator.h"
|
| #include "src/interpreter/control-flow-builders.h"
|
| #include "src/objects.h"
|
| @@ -445,6 +446,12 @@ class BytecodeGenerator::ExpressionResultScope {
|
|
|
| bool IsEffect() const { return kind_ == Expression::kEffect; }
|
| bool IsValue() const { return kind_ == Expression::kValue; }
|
| + bool IsTest() const { return kind_ == Expression::kTest; }
|
| +
|
| + TestResultScope* AsTest() {
|
| + DCHECK(IsTest());
|
| + return reinterpret_cast<TestResultScope*>(this);
|
| + }
|
|
|
| virtual void SetResultInAccumulator() = 0;
|
| virtual void SetResultInRegister(Register reg) = 0;
|
| @@ -535,6 +542,61 @@ class BytecodeGenerator::RegisterResultScope final
|
| Register result_register_;
|
| };
|
|
|
| +// Scoped class used when the result of the current expression to be
|
| +// evaluated is only tested with jumps to two branches.
|
| +class BytecodeGenerator::TestResultScope final : public ExpressionResultScope {
|
| + public:
|
| + TestResultScope(BytecodeGenerator* generator, BytecodeLabels* then_labels,
|
| + BytecodeLabels* else_labels, TestFallthrough fallthrough)
|
| + : ExpressionResultScope(generator, Expression::kTest),
|
| + then_labels_(then_labels),
|
| + else_labels_(else_labels),
|
| + fallthrough_(fallthrough),
|
| + result_consumed_by_test_(false) {}
|
| +
|
| + virtual void SetResultInAccumulator() { set_result_identified(); }
|
| +
|
| + virtual void SetResultInRegister(Register reg) {
|
| + builder()->LoadAccumulatorWithRegister(reg);
|
| + set_result_identified();
|
| + }
|
| +
|
| + // Used when code special cases for TestResultScope and consumes any
|
| + // possible value by testing and jumping to a then/else label.
|
| + void SetResultConsumedByTest() {
|
| + result_consumed_by_test_ = true;
|
| + set_result_identified();
|
| + }
|
| +
|
| + bool ResultConsumedByTest() { return result_consumed_by_test_; }
|
| +
|
| + BytecodeLabel* NewThenLabel() { return then_labels_->New(); }
|
| + BytecodeLabel* NewElseLabel() { return else_labels_->New(); }
|
| +
|
| + BytecodeLabels* then_labels() const { return then_labels_; }
|
| + BytecodeLabels* else_labels() const { return else_labels_; }
|
| +
|
| + TestFallthrough fallthrough() const { return fallthrough_; }
|
| + TestFallthrough inverted_fallthrough() const {
|
| + switch (fallthrough_) {
|
| + case TestFallthrough::kThen:
|
| + return TestFallthrough::kElse;
|
| + case TestFallthrough::kElse:
|
| + return TestFallthrough::kThen;
|
| + default:
|
| + return TestFallthrough::kNone;
|
| + }
|
| + }
|
| +
|
| + private:
|
| + BytecodeLabels* then_labels_;
|
| + BytecodeLabels* else_labels_;
|
| + TestFallthrough fallthrough_;
|
| + bool result_consumed_by_test_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestResultScope);
|
| +};
|
| +
|
| // Used to build a list of global declaration initial value pairs.
|
| class BytecodeGenerator::GlobalDeclarationsBuilder final : public ZoneObject {
|
| public:
|
| @@ -983,7 +1045,6 @@ void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
|
|
|
| void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
|
| builder()->SetStatementPosition(stmt);
|
| - BytecodeLabel else_label, end_label;
|
| if (stmt->condition()->ToBooleanIsTrue()) {
|
| // Generate then block unconditionally as always true.
|
| Visit(stmt->then_statement());
|
| @@ -996,15 +1057,20 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
|
| // TODO(oth): If then statement is BreakStatement or
|
| // ContinueStatement we can reduce number of generated
|
| // jump/jump_ifs here. See BasicLoops test.
|
| - VisitForAccumulatorValue(stmt->condition());
|
| - builder()->JumpIfFalse(&else_label);
|
| + BytecodeLabel end_label;
|
| + BytecodeLabels then_labels(zone()), else_labels(zone());
|
| + VisitForTest(stmt->condition(), &then_labels, &else_labels,
|
| + TestFallthrough::kThen);
|
| +
|
| + then_labels.Bind(builder());
|
| Visit(stmt->then_statement());
|
| +
|
| if (stmt->HasElseStatement()) {
|
| builder()->Jump(&end_label);
|
| - builder()->Bind(&else_label);
|
| + else_labels.Bind(builder());
|
| Visit(stmt->else_statement());
|
| } else {
|
| - builder()->Bind(&else_label);
|
| + else_labels.Bind(builder());
|
| }
|
| builder()->Bind(&end_label);
|
| }
|
| @@ -1113,6 +1179,7 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
|
| VisitIterationHeader(stmt, &loop_builder);
|
| VisitIterationBody(stmt, &loop_builder);
|
| builder()->SetExpressionAsStatementPosition(stmt->cond());
|
| + // TODO(klaasb) VisitForTest for loop conditions
|
| VisitForAccumulatorValue(stmt->cond());
|
| loop_builder.JumpToHeaderIfTrue();
|
| }
|
| @@ -1129,6 +1196,7 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
|
| VisitIterationHeader(stmt, &loop_builder);
|
| if (!stmt->cond()->ToBooleanIsTrue()) {
|
| builder()->SetExpressionAsStatementPosition(stmt->cond());
|
| + // TODO(klaasb) VisitForTest for loop conditions
|
| VisitForAccumulatorValue(stmt->cond());
|
| loop_builder.BreakIfFalse();
|
| }
|
| @@ -1151,6 +1219,7 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
|
| VisitIterationHeader(stmt, &loop_builder);
|
| if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
|
| builder()->SetExpressionAsStatementPosition(stmt->cond());
|
| + // TODO(klaasb) VisitForTest for loop conditions
|
| VisitForAccumulatorValue(stmt->cond());
|
| loop_builder.BreakIfFalse();
|
| }
|
| @@ -1569,21 +1638,27 @@ void BytecodeGenerator::VisitDoExpression(DoExpression* expr) {
|
| }
|
|
|
| void BytecodeGenerator::VisitConditional(Conditional* expr) {
|
| - // TODO(rmcilroy): 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;
|
| + if (expr->condition()->ToBooleanIsTrue()) {
|
| + // Generate then block unconditionally as always true.
|
| + VisitForAccumulatorValue(expr->then_expression());
|
| + } else if (expr->condition()->ToBooleanIsFalse()) {
|
| + // Generate else block unconditionally if it exists.
|
| + VisitForAccumulatorValue(expr->else_expression());
|
| + } else {
|
| + BytecodeLabel end_label;
|
| + BytecodeLabels then_labels(zone()), else_labels(zone());
|
|
|
| - VisitForAccumulatorValue(expr->condition());
|
| - builder()->JumpIfFalse(&else_label);
|
| + VisitForTest(expr->condition(), &then_labels, &else_labels,
|
| + TestFallthrough::kThen);
|
|
|
| - VisitForAccumulatorValue(expr->then_expression());
|
| - builder()->Jump(&end_label);
|
| + then_labels.Bind(builder());
|
| + VisitForAccumulatorValue(expr->then_expression());
|
| + builder()->Jump(&end_label);
|
|
|
| - builder()->Bind(&else_label);
|
| - VisitForAccumulatorValue(expr->else_expression());
|
| - builder()->Bind(&end_label);
|
| + else_labels.Bind(builder());
|
| + VisitForAccumulatorValue(expr->else_expression());
|
| + builder()->Bind(&end_label);
|
| + }
|
|
|
| execution_result()->SetResultInAccumulator();
|
| }
|
| @@ -1866,6 +1941,9 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
|
| switch (variable->location()) {
|
| case VariableLocation::LOCAL: {
|
| Register source(Register(variable->index()));
|
| + // We need to load the variable into the accumulator, even when in a
|
| + // VisitForRegisterScope, in order to avoid register aliasing if
|
| + // subsequent expressions assign to the same variable.
|
| builder()->LoadAccumulatorWithRegister(source);
|
| BuildHoleCheckForVariableLoad(variable);
|
| break;
|
| @@ -1874,6 +1952,9 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
|
| // 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);
|
| + // We need to load the variable into the accumulator, even when in a
|
| + // VisitForRegisterScope, in order to avoid register aliasing if
|
| + // subsequent expressions assign to the same variable.
|
| builder()->LoadAccumulatorWithRegister(source);
|
| BuildHoleCheckForVariableLoad(variable);
|
| break;
|
| @@ -2718,9 +2799,21 @@ void BytecodeGenerator::VisitTypeOf(UnaryOperation* expr) {
|
| }
|
|
|
| void BytecodeGenerator::VisitNot(UnaryOperation* expr) {
|
| - VisitForAccumulatorValue(expr->expression());
|
| - builder()->LogicalNot();
|
| - execution_result()->SetResultInAccumulator();
|
| + if (execution_result()->IsEffect()) {
|
| + VisitForEffect(expr->expression());
|
| + } else if (execution_result()->IsTest()) {
|
| + TestResultScope* test_result = execution_result()->AsTest();
|
| + // No actual logical negation happening, we just swap the control flow by
|
| + // swapping the target labels and the fallthrough branch.
|
| + VisitForTest(expr->expression(), test_result->else_labels(),
|
| + test_result->then_labels(),
|
| + test_result->inverted_fallthrough());
|
| + test_result->SetResultConsumedByTest();
|
| + } else {
|
| + VisitForAccumulatorValue(expr->expression());
|
| + builder()->LogicalNot();
|
| + execution_result()->SetResultInAccumulator();
|
| + }
|
| }
|
|
|
| void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
|
| @@ -2996,36 +3089,72 @@ void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) {
|
| Expression* left = binop->left();
|
| Expression* right = binop->right();
|
|
|
| - // Short-circuit evaluation- If it is known that left is always true,
|
| - // no need to visit right
|
| - if (left->ToBooleanIsTrue()) {
|
| - VisitForAccumulatorValue(left);
|
| + if (execution_result()->IsTest()) {
|
| + TestResultScope* test_result = execution_result()->AsTest();
|
| +
|
| + if (left->ToBooleanIsTrue() || right->ToBooleanIsTrue()) {
|
| + builder()->Jump(test_result->NewThenLabel());
|
| + } else if (left->ToBooleanIsFalse() && right->ToBooleanIsFalse()) {
|
| + builder()->Jump(test_result->NewElseLabel());
|
| + } else {
|
| + BytecodeLabels test_right(zone());
|
| + VisitForTest(left, test_result->then_labels(), &test_right,
|
| + TestFallthrough::kElse);
|
| + test_right.Bind(builder());
|
| + VisitForTest(right, test_result->then_labels(),
|
| + test_result->else_labels(), test_result->fallthrough());
|
| + }
|
| + test_result->SetResultConsumedByTest();
|
| } else {
|
| - BytecodeLabel end_label;
|
| - VisitForAccumulatorValue(left);
|
| - builder()->JumpIfTrue(&end_label);
|
| - VisitForAccumulatorValue(right);
|
| - builder()->Bind(&end_label);
|
| + if (left->ToBooleanIsTrue()) {
|
| + VisitForAccumulatorValue(left);
|
| + } else if (left->ToBooleanIsFalse()) {
|
| + VisitForAccumulatorValue(right);
|
| + } else {
|
| + BytecodeLabel end_label;
|
| + VisitForAccumulatorValue(left);
|
| + builder()->JumpIfTrue(&end_label);
|
| + VisitForAccumulatorValue(right);
|
| + builder()->Bind(&end_label);
|
| + }
|
| + execution_result()->SetResultInAccumulator();
|
| }
|
| - execution_result()->SetResultInAccumulator();
|
| }
|
|
|
| void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
|
| Expression* left = binop->left();
|
| Expression* right = binop->right();
|
|
|
| - // Short-circuit evaluation- If it is known that left is always false,
|
| - // no need to visit right
|
| - if (left->ToBooleanIsFalse()) {
|
| - VisitForAccumulatorValue(left);
|
| + if (execution_result()->IsTest()) {
|
| + TestResultScope* test_result = execution_result()->AsTest();
|
| +
|
| + if (left->ToBooleanIsFalse() || right->ToBooleanIsFalse()) {
|
| + builder()->Jump(test_result->NewElseLabel());
|
| + } else if (left->ToBooleanIsTrue() && right->ToBooleanIsTrue()) {
|
| + builder()->Jump(test_result->NewThenLabel());
|
| + } else {
|
| + BytecodeLabels test_right(zone());
|
| + VisitForTest(left, &test_right, test_result->else_labels(),
|
| + TestFallthrough::kThen);
|
| + test_right.Bind(builder());
|
| + VisitForTest(right, test_result->then_labels(),
|
| + test_result->else_labels(), test_result->fallthrough());
|
| + }
|
| + test_result->SetResultConsumedByTest();
|
| } else {
|
| - BytecodeLabel end_label;
|
| - VisitForAccumulatorValue(left);
|
| - builder()->JumpIfFalse(&end_label);
|
| - VisitForAccumulatorValue(right);
|
| - builder()->Bind(&end_label);
|
| + if (left->ToBooleanIsFalse()) {
|
| + VisitForAccumulatorValue(left);
|
| + } else if (left->ToBooleanIsTrue()) {
|
| + VisitForAccumulatorValue(right);
|
| + } else {
|
| + BytecodeLabel end_label;
|
| + VisitForAccumulatorValue(left);
|
| + builder()->JumpIfFalse(&end_label);
|
| + VisitForAccumulatorValue(right);
|
| + builder()->Bind(&end_label);
|
| + }
|
| + execution_result()->SetResultInAccumulator();
|
| }
|
| - execution_result()->SetResultInAccumulator();
|
| }
|
|
|
| void BytecodeGenerator::VisitRewritableExpression(RewritableExpression* expr) {
|
| @@ -3267,6 +3396,36 @@ void BytecodeGenerator::VisitForRegisterValue(Expression* expr,
|
| builder()->StoreAccumulatorInRegister(destination);
|
| }
|
|
|
| +// Visits the expression |expr| for testing its boolean value and jumping to the
|
| +// |then| or |other| label depending on value and short-circuit semantics
|
| +void BytecodeGenerator::VisitForTest(Expression* expr,
|
| + BytecodeLabels* then_labels,
|
| + BytecodeLabels* else_labels,
|
| + TestFallthrough fallthrough) {
|
| + bool result_consumed;
|
| + {
|
| + // To make sure that all temporary registers are returned before generating
|
| + // jumps below, we ensure that the result scope is deleted before doing so.
|
| + // Dead registers might be materialized otherwise.
|
| + TestResultScope test_result(this, then_labels, else_labels, fallthrough);
|
| + Visit(expr);
|
| + result_consumed = test_result.ResultConsumedByTest();
|
| + }
|
| + if (!result_consumed) {
|
| + switch (fallthrough) {
|
| + case TestFallthrough::kThen:
|
| + builder()->JumpIfFalse(else_labels->New());
|
| + break;
|
| + case TestFallthrough::kElse:
|
| + builder()->JumpIfTrue(then_labels->New());
|
| + break;
|
| + case TestFallthrough::kNone:
|
| + builder()->JumpIfTrue(then_labels->New());
|
| + builder()->Jump(else_labels->New());
|
| + }
|
| + }
|
| +}
|
| +
|
| void BytecodeGenerator::VisitInScope(Statement* stmt, Scope* scope) {
|
| ContextScope context_scope(this, scope);
|
| DCHECK(scope->declarations()->is_empty());
|
|
|