[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:
parent
c097315679
commit
52ef2a1c27
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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: [
|
||||
|
@ -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: [
|
||||
|
86
test/mjsunit/compiler/nary-binary-ops.js
Normal file
86
test/mjsunit/compiler/nary-binary-ops.js
Normal 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);
|
||||
}
|
@ -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 =
|
||||
|
@ -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()));
|
||||
|
Loading…
Reference in New Issue
Block a user