[parser] Add an n-ary node for large binop chains

Expressions of the form

    a_0 + a_1 + a_2 + a_3 + ... + a_n

seem to be reasonably common for cases such as building templates.
However, parsing these expressions results in a n-deep expression tree:

           ...
          /
         +
        / \
       +  a_2
      / \
    a_0 a_1

Traversing this tree during compilation can cause a stack overflow when n is
large.

Instead, for left-associate operations such as add, we now build up an
n-ary node in the parse tree, of the form

         n-ary +
       /  |      \
      /   |  ...  \
    a_0  a_1      a_n

The bytecode compiler can now iterate through the child expressions
rather than recursing.

This patch only supports arithmetic operations -- subsequent patches
will enable the same optimization for logical tests and comma
expressions.

Bug: v8:6964
Bug: chromium:724961
Bug: chromium:731861
Bug: chromium:752081
Bug: chromium:771653
Bug: chromium:777302
Change-Id: Ie97e4ce42506fe62a7bc4ffbdaa90a9f698352cb
Reviewed-on: https://chromium-review.googlesource.com/733120
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48920}
This commit is contained in:
Leszek Swirski 2017-10-25 11:47:24 +01:00 committed by Commit Bot
parent c097315679
commit 52ef2a1c27
18 changed files with 365 additions and 46 deletions

View File

@ -346,6 +346,14 @@ void AstExpressionRewriter::VisitBinaryOperation(BinaryOperation* node) {
AST_REWRITE_PROPERTY(Expression, node, right);
}
void AstExpressionRewriter::VisitNaryOperation(NaryOperation* node) {
REWRITE_THIS(node);
AST_REWRITE_PROPERTY(Expression, node, first);
for (size_t i = 0; i < node->subsequent_length(); ++i) {
AST_REWRITE(Expression, node->subsequent(i),
node->set_subsequent(i, replacement));
}
}
void AstExpressionRewriter::VisitCompareOperation(CompareOperation* node) {
REWRITE_THIS(node);

View File

@ -221,6 +221,13 @@ void AstNumberingVisitor::VisitBinaryOperation(BinaryOperation* node) {
Visit(node->right());
}
void AstNumberingVisitor::VisitNaryOperation(NaryOperation* node) {
Visit(node->first());
for (size_t i = 0; i < node->subsequent_length(); ++i) {
Visit(node->subsequent(i));
}
}
void AstNumberingVisitor::VisitCompareOperation(CompareOperation* node) {
Visit(node->left());
Visit(node->right());

View File

@ -443,6 +443,15 @@ void AstTraversalVisitor<Subclass>::VisitBinaryOperation(
RECURSE_EXPRESSION(Visit(expr->right()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitNaryOperation(NaryOperation* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->first()));
for (size_t i = 0; i < expr->subsequent_length(); ++i) {
RECURSE_EXPRESSION(Visit(expr->subsequent(i)));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCompareOperation(
CompareOperation* expr) {

View File

@ -74,6 +74,7 @@ namespace internal {
V(Assignment) \
V(Await) \
V(BinaryOperation) \
V(NaryOperation) \
V(Call) \
V(CallNew) \
V(CallRuntime) \
@ -1751,6 +1752,73 @@ class BinaryOperation final : public Expression {
: public BitField<Token::Value, Expression::kNextBitFieldIndex, 7> {};
};
class NaryOperation final : public Expression {
public:
Token::Value op() const { return OperatorField::decode(bit_field_); }
Expression* first() const { return first_; }
void set_first(Expression* e) { first_ = e; }
Expression* subsequent(size_t index) const {
if (index == 0) return second_;
return subsequent_[index - 1].expression;
}
Expression* set_subsequent(size_t index, Expression* e) {
if (index == 0) return second_ = e;
return subsequent_[index - 1].expression = e;
}
size_t subsequent_length() const { return 1 + subsequent_.size(); }
int subsequent_op_position(size_t index) const {
if (index == 0) return position();
return subsequent_[index - 1].op_position;
}
void AddSubsequent(Expression* expr, int pos) {
subsequent_.emplace_back(expr, pos);
}
private:
friend class AstNodeFactory;
NaryOperation(Zone* zone, Token::Value op, Expression* first,
Expression* second, int pos)
: Expression(pos, kNaryOperation),
first_(first),
second_(second),
subsequent_(zone) {
bit_field_ |= OperatorField::encode(op);
DCHECK(Token::IsBinaryOp(op));
DCHECK_NE(op, Token::EXP);
}
// Nary operations store the first operation (and so first two child
// expressions) inline, where the position of the first operation is the
// position of this expression. Subsequent child expressions are stored
// out-of-line, along with with their operation's position and feedback slot.
//
// So an nary add:
//
// expr + expr + expr + expr + ...
//
// is stored as:
//
// (expr + expr) [(+ expr), (+ expr), ...]
// '-----.-----' '-----------.-----------'
// this subsequent entry list
Expression* first_;
Expression* second_;
struct NaryOperationEntry {
Expression* expression;
int op_position;
NaryOperationEntry(Expression* e, int pos)
: expression(e), op_position(pos) {}
};
ZoneVector<NaryOperationEntry> subsequent_;
class OperatorField
: public BitField<Token::Value, Expression::kNextBitFieldIndex, 7> {};
};
class CountOperation final : public Expression {
public:
@ -3003,6 +3071,11 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) BinaryOperation(op, left, right, pos);
}
NaryOperation* NewNaryOperation(Token::Value op, Expression* first,
Expression* second, int pos) {
return new (zone_) NaryOperation(zone_, op, first, second, pos);
}
CountOperation* NewCountOperation(Token::Value op,
bool is_prefix,
Expression* expr,

View File

@ -383,6 +383,17 @@ void CallPrinter::VisitBinaryOperation(BinaryOperation* node) {
Print(")");
}
void CallPrinter::VisitNaryOperation(NaryOperation* node) {
Print("(");
Find(node->first(), true);
for (size_t i = 0; i < node->subsequent_length(); ++i) {
Print(" ");
Print(Token::String(node->op()));
Print(" ");
Find(node->subsequent(i), true);
}
Print(")");
}
void CallPrinter::VisitCompareOperation(CompareOperation* node) {
Print("(");
@ -1221,6 +1232,13 @@ void AstPrinter::VisitBinaryOperation(BinaryOperation* node) {
Visit(node->right());
}
void AstPrinter::VisitNaryOperation(NaryOperation* node) {
IndentedScope indent(this, Token::Name(node->op()), node->position());
Visit(node->first());
for (size_t i = 0; i < node->subsequent_length(); ++i) {
Visit(node->subsequent(i));
}
}
void AstPrinter::VisitCompareOperation(CompareOperation* node) {
IndentedScope indent(this, Token::Name(node->op()), node->position());

View File

@ -459,11 +459,15 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
}
void SetExpressionPosition(Expression* expr) {
if (expr->position() == kNoSourcePosition) return;
SetExpressionPosition(expr->position());
}
void SetExpressionPosition(int position) {
if (position == kNoSourcePosition) return;
if (!latest_source_info_.is_statement()) {
// Ensure the current expression position is overwritten with the
// latest value.
latest_source_info_.MakeExpressionPosition(expr->position());
latest_source_info_.MakeExpressionPosition(position);
}
}

View File

@ -3754,6 +3754,23 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
}
}
void BytecodeGenerator::VisitNaryOperation(NaryOperation* expr) {
switch (expr->op()) {
case Token::COMMA:
VisitNaryCommaExpression(expr);
break;
case Token::OR:
VisitNaryLogicalOrExpression(expr);
break;
case Token::AND:
VisitNaryLogicalAndExpression(expr);
break;
default:
VisitNaryArithmeticExpression(expr);
break;
}
}
void BytecodeGenerator::BuildLiteralCompareNil(Token::Value op, NilValue nil) {
if (execution_result()->IsTest()) {
TestResultScope* test_result = execution_result()->AsTest();
@ -3833,6 +3850,29 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
}
}
void BytecodeGenerator::VisitNaryArithmeticExpression(NaryOperation* expr) {
// TODO(leszeks): Add support for lhs smi in commutative ops.
VisitForAccumulatorValue(expr->first());
for (size_t i = 0; i < expr->subsequent_length(); ++i) {
RegisterAllocationScope register_scope(this);
if (expr->subsequent(i)->IsSmiLiteral()) {
builder()->SetExpressionPosition(expr->subsequent_op_position(i));
builder()->BinaryOperationSmiLiteral(
expr->op(), expr->subsequent(i)->AsLiteral()->AsSmiLiteral(),
feedback_index(feedback_spec()->AddBinaryOpICSlot()));
} else {
Register lhs = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(lhs);
VisitForAccumulatorValue(expr->subsequent(i));
builder()->SetExpressionPosition(expr->subsequent_op_position(i));
builder()->BinaryOperation(
expr->op(), lhs,
feedback_index(feedback_spec()->AddBinaryOpICSlot()));
}
}
}
void BytecodeGenerator::VisitSpread(Spread* expr) { Visit(expr->expression()); }
void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) {
@ -3944,6 +3984,11 @@ void BytecodeGenerator::VisitCommaExpression(BinaryOperation* binop) {
Visit(binop->right());
}
void BytecodeGenerator::VisitNaryCommaExpression(NaryOperation* expr) {
// TODO(leszeks): Implement
UNREACHABLE();
}
void BytecodeGenerator::BuildLogicalTest(Token::Value token, Expression* left,
Expression* right) {
DCHECK(token == Token::OR || token == Token::AND);
@ -3997,6 +4042,10 @@ void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) {
}
}
}
void BytecodeGenerator::VisitNaryLogicalOrExpression(NaryOperation* expr) {
// TODO(leszeks): Implement.
UNREACHABLE();
}
void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
Expression* left = binop->left();
@ -4026,6 +4075,10 @@ void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
}
}
}
void BytecodeGenerator::VisitNaryLogicalAndExpression(NaryOperation* expr) {
// TODO(leszeks): Implement.
UNREACHABLE();
}
void BytecodeGenerator::VisitRewritableExpression(RewritableExpression* expr) {
Visit(expr->expression());

View File

@ -75,6 +75,12 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitLogicalOrExpression(BinaryOperation* binop);
void VisitLogicalAndExpression(BinaryOperation* binop);
// Dispatched from VisitNaryOperation.
void VisitNaryArithmeticExpression(NaryOperation* expr);
void VisitNaryCommaExpression(NaryOperation* expr);
void VisitNaryLogicalOrExpression(NaryOperation* expr);
void VisitNaryLogicalAndExpression(NaryOperation* expr);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
void VisitTypeOf(UnaryOperation* expr);

View File

@ -3124,6 +3124,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression(
}
} else if (op == Token::EXP) {
x = impl()->RewriteExponentiation(x, y, pos);
} else if (impl()->CollapseNaryExpression(&x, y, op, pos)) {
continue;
} else {
// We have a "normal" binary operation.
x = factory()->NewBinaryOperation(op, x, y, pos);

View File

@ -307,6 +307,38 @@ bool Parser::ShortcutNumericLiteralBinaryExpression(Expression** x,
return false;
}
bool Parser::CollapseNaryExpression(Expression** x, Expression* y,
Token::Value op, int pos) {
// Filter out unsupported ops.
// TODO(leszeks): Support AND, OR and COMMA in bytecode generator.
if (!Token::IsBinaryOp(op) || op == Token::AND || op == Token::OR ||
op == Token::EXP || op == Token::COMMA)
return false;
// Convert *x into an nary operation with the given op, returning false if
// this is not possible.
NaryOperation* nary = nullptr;
if ((*x)->IsBinaryOperation()) {
BinaryOperation* binop = (*x)->AsBinaryOperation();
if (binop->op() != op) return false;
nary = factory()->NewNaryOperation(op, binop->left(), binop->right(),
binop->position());
*x = nary;
} else if ((*x)->IsNaryOperation()) {
nary = (*x)->AsNaryOperation();
if (nary->op() != op) return false;
} else {
return false;
}
// Append our current expression to the nary operation.
// TODO(leszeks): Do some literal collapsing here if we're appending Smi or
// String literals.
nary->AddSubsequent(y, pos);
return true;
}
Expression* Parser::BuildUnaryExpression(Expression* expression,
Token::Value op, int pos) {
DCHECK_NOT_NULL(expression);

View File

@ -757,6 +757,13 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
bool ShortcutNumericLiteralBinaryExpression(Expression** x, Expression* y,
Token::Value op, int pos);
// Returns true if we have a binary operation between a binary/n-ary
// expression (with the same operation) and a value, which can be collapsed
// into a single n-ary expression. In that case, *x will be changed to an
// n-ary expression.
bool CollapseNaryExpression(Expression** x, Expression* y, Token::Value op,
int pos);
// Rewrites the following types of unary expressions:
// not <literal> -> true / false
// + <numeric literal> -> <numeric literal>

View File

@ -726,6 +726,7 @@ void PatternRewriter::VisitProperty(v8::internal::Property* node) {
void PatternRewriter::Visit##Node(v8::internal::Node*) { UNREACHABLE(); }
NOT_A_PATTERN(BinaryOperation)
NOT_A_PATTERN(NaryOperation)
NOT_A_PATTERN(Block)
NOT_A_PATTERN(BreakStatement)
NOT_A_PATTERN(Call)

View File

@ -1295,6 +1295,12 @@ class PreParser : public ParserBase<PreParser> {
return false;
}
V8_INLINE bool CollapseNaryExpression(PreParserExpression* x,
PreParserExpression y, Token::Value op,
int pos) {
return false;
}
V8_INLINE PreParserExpression BuildUnaryExpression(
const PreParserExpression& expression, Token::Value op, int pos) {
return PreParserExpression::Default();

View File

@ -73,11 +73,11 @@ bytecodes: [
/* 46 S> */ B(LdaSmi), I8(100),
B(Mov), R(0), R(1),
B(Star), R(0),
/* 52 E> */ B(Add), R(1), U8(1),
/* 52 E> */ B(Add), R(1), U8(0),
B(Star), R(1),
B(LdaSmi), I8(101),
B(Star), R(0),
/* 64 E> */ B(Add), R(1), U8(0),
/* 64 E> */ B(Add), R(1), U8(1),
B(Star), R(0),
/* 86 S> */ B(Return),
]
@ -133,7 +133,7 @@ bytecodes: [
/* 54 S> */ B(LdaSmi), I8(1),
B(Mov), R(0), R(2),
B(Star), R(0),
/* 56 E> */ B(Add), R(2), U8(2),
/* 56 E> */ B(Add), R(2), U8(0),
B(Star), R(2),
B(LdaSmi), I8(2),
B(Star), R(0),
@ -141,7 +141,7 @@ bytecodes: [
B(Star), R(2),
B(LdaSmi), I8(3),
B(Star), R(0),
/* 76 E> */ B(Add), R(2), U8(0),
/* 76 E> */ B(Add), R(2), U8(2),
B(Star), R(1),
/* 96 S> */ B(Return),
]
@ -166,7 +166,7 @@ bytecodes: [
/* 54 S> */ B(LdaSmi), I8(1),
B(Mov), R(0), R(1),
B(Star), R(0),
/* 56 E> */ B(Add), R(1), U8(2),
/* 56 E> */ B(Add), R(1), U8(0),
B(Star), R(1),
B(LdaSmi), I8(2),
B(Star), R(0),
@ -174,7 +174,7 @@ bytecodes: [
B(Star), R(1),
B(LdaSmi), I8(3),
B(Star), R(0),
/* 76 E> */ B(Add), R(1), U8(0),
/* 76 E> */ B(Add), R(1), U8(2),
B(Star), R(0),
/* 96 S> */ B(Return),
]
@ -200,30 +200,30 @@ bytecodes: [
/* 54 S> */ B(LdaSmi), I8(1),
B(Mov), R(0), R(2),
B(Star), R(0),
/* 63 E> */ B(Add), R(2), U8(5),
/* 63 E> */ B(Add), R(2), U8(0),
B(Star), R(2),
B(Ldar), R(0),
/* 78 E> */ B(AddSmi), I8(1), U8(7),
/* 78 E> */ B(AddSmi), I8(1), U8(2),
B(Star), R(3),
B(LdaSmi), I8(2),
B(Star), R(1),
/* 83 E> */ B(Mul), R(3), U8(6),
/* 73 E> */ B(Add), R(2), U8(4),
/* 83 E> */ B(Mul), R(3), U8(1),
/* 73 E> */ B(Add), R(2), U8(3),
B(Star), R(2),
B(LdaSmi), I8(3),
B(Star), R(1),
/* 93 E> */ B(Add), R(2), U8(3),
/* 93 E> */ B(Add), R(2), U8(4),
B(Star), R(2),
B(LdaSmi), I8(4),
B(Star), R(0),
/* 103 E> */ B(Add), R(2), U8(2),
/* 103 E> */ B(Add), R(2), U8(5),
B(Star), R(2),
B(LdaSmi), I8(5),
B(Star), R(1),
/* 113 E> */ B(Add), R(2), U8(1),
/* 113 E> */ B(Add), R(2), U8(6),
B(Star), R(2),
B(Ldar), R(1),
/* 123 E> */ B(Add), R(2), U8(0),
/* 123 E> */ B(Add), R(2), U8(7),
/* 127 S> */ B(Return),
]
constant pool: [
@ -246,20 +246,20 @@ bytecodes: [
/* 46 S> */ B(LdaSmi), I8(1),
B(Star), R(1),
B(Ldar), R(0),
/* 55 E> */ B(Add), R(1), U8(2),
/* 55 E> */ B(Add), R(1), U8(0),
B(Star), R(1),
B(Ldar), R(0),
B(ToNumeric), U8(3),
B(ToNumeric), U8(1),
B(Star), R(2),
B(Inc), U8(3),
B(Inc), U8(1),
B(Star), R(0),
B(Ldar), R(2),
/* 59 E> */ B(Add), R(1), U8(1),
/* 59 E> */ B(Add), R(1), U8(2),
B(Star), R(1),
B(Ldar), R(0),
B(Inc), U8(4),
B(Inc), U8(3),
B(Star), R(0),
/* 67 E> */ B(Add), R(1), U8(0),
/* 67 E> */ B(Add), R(1), U8(4),
/* 75 S> */ B(Return),
]
constant pool: [

View File

@ -21,10 +21,10 @@ bytecodes: [
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(Ldar), R(1),
/* 65 E> */ B(Add), R(0), U8(1),
/* 65 E> */ B(Add), R(0), U8(0),
B(Star), R(2),
B(LdaConstant), U8(0),
/* 69 E> */ B(Add), R(2), U8(0),
/* 69 E> */ B(Add), R(2), U8(1),
/* 80 S> */ B(Return),
]
constant pool: [
@ -51,10 +51,10 @@ bytecodes: [
/* 56 S> */ B(LdaConstant), U8(0),
B(Star), R(2),
B(Ldar), R(0),
/* 72 E> */ B(Add), R(2), U8(1),
/* 72 E> */ B(Add), R(2), U8(0),
B(Star), R(2),
B(Ldar), R(1),
/* 76 E> */ B(Add), R(2), U8(0),
/* 76 E> */ B(Add), R(2), U8(1),
/* 80 S> */ B(Return),
]
constant pool: [
@ -79,10 +79,10 @@ bytecodes: [
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(LdaConstant), U8(0),
/* 65 E> */ B(Add), R(0), U8(1),
/* 65 E> */ B(Add), R(0), U8(0),
B(Star), R(2),
B(Ldar), R(1),
/* 76 E> */ B(Add), R(2), U8(0),
/* 76 E> */ B(Add), R(2), U8(1),
/* 80 S> */ B(Return),
]
constant pool: [
@ -109,17 +109,17 @@ bytecodes: [
/* 56 S> */ B(LdaConstant), U8(0),
B(Star), R(2),
B(Ldar), R(0),
/* 69 E> */ B(Add), R(2), U8(4),
/* 69 E> */ B(Add), R(2), U8(0),
B(Star), R(2),
B(LdaConstant), U8(1),
/* 73 E> */ B(Add), R(2), U8(3),
/* 73 E> */ B(Add), R(2), U8(1),
B(Star), R(2),
B(Ldar), R(1),
/* 81 E> */ B(Add), R(2), U8(2),
B(Star), R(2),
B(LdaConstant), U8(2),
/* 85 E> */ B(Add), R(2), U8(1),
/* 93 E> */ B(AddSmi), I8(1), U8(0),
/* 85 E> */ B(Add), R(2), U8(3),
/* 93 E> */ B(AddSmi), I8(1), U8(4),
/* 97 S> */ B(Return),
]
constant pool: [
@ -146,13 +146,13 @@ bytecodes: [
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(LdaConstant), U8(0),
/* 66 E> */ B(Add), R(0), U8(1),
/* 66 E> */ B(Add), R(0), U8(0),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
B(Ldar), R(1),
/* 90 E> */ B(Add), R(3), U8(2),
/* 78 E> */ B(Add), R(2), U8(0),
/* 90 E> */ B(Add), R(3), U8(1),
/* 78 E> */ B(Add), R(2), U8(2),
/* 95 S> */ B(Return),
]
constant pool: [
@ -181,14 +181,14 @@ bytecodes: [
B(Star), R(1),
/* 80 S> */ B(LdaConstant), U8(1),
B(Star), R(3),
/* 98 E> */ B(CallUndefinedReceiver2), R(2), R(0), R(1), U8(4),
/* 98 E> */ B(CallUndefinedReceiver2), R(2), R(0), R(1), U8(1),
/* 96 E> */ B(Add), R(3), U8(3),
B(Star), R(3),
B(Ldar), R(0),
/* 108 E> */ B(Add), R(3), U8(2),
/* 108 E> */ B(Add), R(3), U8(4),
B(Star), R(3),
B(Ldar), R(1),
/* 112 E> */ B(Add), R(3), U8(1),
/* 112 E> */ B(Add), R(3), U8(5),
/* 116 S> */ B(Return),
]
constant pool: [

View File

@ -0,0 +1,86 @@
// Copyright 2017 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.
// Test that n-ary chains of binary ops give an equal result to individual
// binary op calls.
// Generate a function of the form
//
// function(init,a0,...,aN) {
// return init + a0 + ... + aN;
// }
//
// where + can be any binary operation.
function generate_chained_op(op, num_ops) {
let str = "(function(init";
for (let i = 0; i < num_ops; i++) {
str += ",a"+i;
}
str += "){return (init";
for (let i = 0; i < num_ops; i++) {
str += op+"a"+i;
}
str += ");})";
return eval(str);
}
// Generate a function of the form
//
// function(init,a0,...,aN) {
// var tmp = init;
// tmp = tmp + a0;
// ...
// tmp = tmp + aN;
// return tmp;
// }
//
// where + can be any binary operation.
function generate_nonchained_op(op, num_ops) {
let str = "(function(init";
for (let i = 0; i < num_ops; i++) {
str += ",a"+i;
}
str += "){ var tmp=init; ";
for (let i = 0; i < num_ops; i++) {
str += "tmp=(tmp"+op+"a"+i+");";
}
str += "return tmp;})";
return eval(str);
}
const BINOPS = [
",",
"||",
"&&",
"|",
"^",
"&",
"<<",
">>",
">>>",
"+",
"-",
"*",
"/",
"%",
];
// Test each binop to see if the chained version is equivalent to the non-
// chained one.
for (let op of BINOPS) {
let chained = generate_chained_op(op, 5);
let nonchained = generate_nonchained_op(op, 5);
// With numbers.
assertEquals(
nonchained(1,2,3,4,5),
chained(1,2,3,4,5),
"numeric " + op);
// With numbers and strings.
assertEquals(
nonchained(1,"2",3,"4",5),
chained(1,"2",3,"4",5),
"numeric and string " + op);
}

View File

@ -439,8 +439,9 @@ TEST_F(CompilerDispatcherTest, IdleTaskException) {
std::string func_name("f" STR(__LINE__));
std::string script("function g() { function " + func_name + "(x) { var a = ");
for (int i = 0; i < 1000; i++) {
script += "'x' + ";
for (int i = 0; i < 500; i++) {
// Alternate + and - to avoid n-ary operation nodes.
script += "'x' + 'x' - ";
}
script += " 'x'; }; return " + func_name + "; } g();";
Handle<JSFunction> f =
@ -579,8 +580,9 @@ TEST_F(CompilerDispatcherTest, FinishNowException) {
std::string func_name("f" STR(__LINE__));
std::string script("function g() { function " + func_name + "(x) { var a = ");
for (int i = 0; i < 1000; i++) {
script += "'x' + ";
for (int i = 0; i < 500; i++) {
// Alternate + and - to avoid n-ary operation nodes.
script += "'x' + 'x' - ";
}
script += " 'x'; }; return " + func_name + "; } g();";
Handle<JSFunction> f =

View File

@ -202,8 +202,12 @@ TEST_F(UnoptimizedCompileJobTest, CompileAndRun) {
TEST_F(UnoptimizedCompileJobTest, CompileFailureToAnalyse) {
std::string raw_script("() { var a = ");
for (int i = 0; i < 100000; i++) {
raw_script += "'x' + ";
for (int i = 0; i < 500000; i++) {
// TODO(leszeks): Figure out a more "unit-test-y" way of forcing an analysis
// failure than a binop stack overflow.
// Alternate + and - to avoid n-ary operation nodes.
raw_script += "'x' + 'x' - ";
}
raw_script += " 'x'; }";
test::ScriptResource script(raw_script.c_str(), strlen(raw_script.c_str()));
@ -229,8 +233,9 @@ TEST_F(UnoptimizedCompileJobTest, CompileFailureToAnalyse) {
TEST_F(UnoptimizedCompileJobTest, CompileFailureToFinalize) {
std::string raw_script("() { var a = ");
for (int i = 0; i < 1000; i++) {
raw_script += "'x' + ";
for (int i = 0; i < 500; i++) {
// Alternate + and - to avoid n-ary operation nodes.
raw_script += "'x' + 'x' - ";
}
raw_script += " 'x'; }";
test::ScriptResource script(raw_script.c_str(), strlen(raw_script.c_str()));