Fix assigned variables analysis.
This change fixes a bug with the arguments object that occurred with r4087 and r4088. The fix is not marking the arguments variable as trivial since it can have side effects. Review URL: http://codereview.chromium.org/851002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4099 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
8f76082701
commit
7d933936d3
@ -364,6 +364,8 @@ class VirtualFrame : public ZoneObject {
|
||||
// the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
|
||||
inline void Nip(int num_dropped);
|
||||
|
||||
inline void SetTypeForLocalAt(int index, NumberInfo info);
|
||||
|
||||
private:
|
||||
static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
|
||||
static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
|
||||
|
16
src/ast.cc
16
src/ast.cc
@ -58,13 +58,27 @@ AST_NODE_LIST(DECL_ACCEPT)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Implementation of other node functionality.
|
||||
|
||||
Assignment* ExpressionStatement::StatementAsSimpleAssignment() {
|
||||
return (expression()->AsAssignment() != NULL &&
|
||||
!expression()->AsAssignment()->is_compound())
|
||||
? expression()->AsAssignment()
|
||||
: NULL;
|
||||
}
|
||||
|
||||
|
||||
CountOperation* ExpressionStatement::StatementAsCountOperation() {
|
||||
return expression()->AsCountOperation();
|
||||
}
|
||||
|
||||
|
||||
VariableProxy::VariableProxy(Handle<String> name,
|
||||
bool is_this,
|
||||
bool inside_with)
|
||||
: name_(name),
|
||||
var_(NULL),
|
||||
is_this_(is_this),
|
||||
inside_with_(inside_with) {
|
||||
inside_with_(inside_with),
|
||||
is_trivial_(false) {
|
||||
// names must be canonicalized for fast equality checks
|
||||
ASSERT(name->IsSymbol());
|
||||
}
|
||||
|
35
src/ast.h
35
src/ast.h
@ -137,6 +137,7 @@ class AstNode: public ZoneObject {
|
||||
virtual BreakableStatement* AsBreakableStatement() { return NULL; }
|
||||
virtual IterationStatement* AsIterationStatement() { return NULL; }
|
||||
virtual UnaryOperation* AsUnaryOperation() { return NULL; }
|
||||
virtual CountOperation* AsCountOperation() { return NULL; }
|
||||
virtual BinaryOperation* AsBinaryOperation() { return NULL; }
|
||||
virtual Assignment* AsAssignment() { return NULL; }
|
||||
virtual FunctionLiteral* AsFunctionLiteral() { return NULL; }
|
||||
@ -161,6 +162,9 @@ class Statement: public AstNode {
|
||||
virtual Statement* AsStatement() { return this; }
|
||||
virtual ReturnStatement* AsReturnStatement() { return NULL; }
|
||||
|
||||
virtual Assignment* StatementAsSimpleAssignment() { return NULL; }
|
||||
virtual CountOperation* StatementAsCountOperation() { return NULL; }
|
||||
|
||||
bool IsEmpty() { return AsEmptyStatement() != NULL; }
|
||||
|
||||
void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; }
|
||||
@ -321,6 +325,16 @@ class Block: public BreakableStatement {
|
||||
|
||||
virtual void Accept(AstVisitor* v);
|
||||
|
||||
virtual Assignment* StatementAsSimpleAssignment() {
|
||||
if (statements_.length() != 1) return NULL;
|
||||
return statements_[0]->StatementAsSimpleAssignment();
|
||||
}
|
||||
|
||||
virtual CountOperation* StatementAsCountOperation() {
|
||||
if (statements_.length() != 1) return NULL;
|
||||
return statements_[0]->StatementAsCountOperation();
|
||||
}
|
||||
|
||||
void AddStatement(Statement* statement) { statements_.Add(statement); }
|
||||
|
||||
ZoneList<Statement*>* statements() { return &statements_; }
|
||||
@ -442,8 +456,8 @@ class ForStatement: public IterationStatement {
|
||||
init_(NULL),
|
||||
cond_(NULL),
|
||||
next_(NULL),
|
||||
may_have_function_literal_(true) {
|
||||
}
|
||||
may_have_function_literal_(true),
|
||||
loop_variable_(NULL) {}
|
||||
|
||||
void Initialize(Statement* init,
|
||||
Expression* cond,
|
||||
@ -464,12 +478,17 @@ class ForStatement: public IterationStatement {
|
||||
return may_have_function_literal_;
|
||||
}
|
||||
|
||||
bool is_fast_smi_loop() { return loop_variable_ != NULL; }
|
||||
Variable* loop_variable() { return loop_variable_; }
|
||||
void set_loop_variable(Variable* var) { loop_variable_ = var; }
|
||||
|
||||
private:
|
||||
Statement* init_;
|
||||
Expression* cond_;
|
||||
Statement* next_;
|
||||
// True if there is a function literal subexpression in the condition.
|
||||
bool may_have_function_literal_;
|
||||
Variable* loop_variable_;
|
||||
|
||||
friend class AstOptimizer;
|
||||
};
|
||||
@ -507,6 +526,9 @@ class ExpressionStatement: public Statement {
|
||||
// Type testing & conversion.
|
||||
virtual ExpressionStatement* AsExpressionStatement() { return this; }
|
||||
|
||||
virtual Assignment* StatementAsSimpleAssignment();
|
||||
virtual CountOperation* StatementAsCountOperation();
|
||||
|
||||
void set_expression(Expression* e) { expression_ = e; }
|
||||
Expression* expression() { return expression_; }
|
||||
|
||||
@ -964,7 +986,7 @@ class VariableProxy: public Expression {
|
||||
|
||||
// Reading from a mutable variable is a side effect, but 'this' is
|
||||
// immutable.
|
||||
virtual bool IsTrivial() { return is_this(); }
|
||||
virtual bool IsTrivial() { return is_trivial_; }
|
||||
|
||||
bool IsVariable(Handle<String> n) {
|
||||
return !is_this() && name().is_identical_to(n);
|
||||
@ -979,6 +1001,8 @@ class VariableProxy: public Expression {
|
||||
Variable* var() const { return var_; }
|
||||
bool is_this() const { return is_this_; }
|
||||
bool inside_with() const { return inside_with_; }
|
||||
bool is_trivial() { return is_trivial_; }
|
||||
void set_is_trivial(bool b) { is_trivial_ = b; }
|
||||
|
||||
// Bind this proxy to the variable var.
|
||||
void BindTo(Variable* var);
|
||||
@ -988,6 +1012,7 @@ class VariableProxy: public Expression {
|
||||
Variable* var_; // resolved variable, or NULL
|
||||
bool is_this_;
|
||||
bool inside_with_;
|
||||
bool is_trivial_;
|
||||
|
||||
VariableProxy(Handle<String> name, bool is_this, bool inside_with);
|
||||
explicit VariableProxy(bool is_this);
|
||||
@ -1246,6 +1271,8 @@ class CountOperation: public Expression {
|
||||
|
||||
virtual void Accept(AstVisitor* v);
|
||||
|
||||
virtual CountOperation* AsCountOperation() { return this; }
|
||||
|
||||
bool is_prefix() const { return is_prefix_; }
|
||||
bool is_postfix() const { return !is_prefix_; }
|
||||
Token::Value op() const { return op_; }
|
||||
@ -1324,6 +1351,8 @@ class Assignment: public Expression {
|
||||
virtual void Accept(AstVisitor* v);
|
||||
virtual Assignment* AsAssignment() { return this; }
|
||||
|
||||
Assignment* AsSimpleAssignment() { return !is_compound() ? this : NULL; }
|
||||
|
||||
Token::Value binary_op() const;
|
||||
|
||||
Token::Value op() const { return op_; }
|
||||
|
@ -79,6 +79,15 @@ static Handle<Code> MakeCode(Handle<Context> context, CompilationInfo* info) {
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
if (function->scope()->num_parameters() > 0 ||
|
||||
function->scope()->num_stack_slots()) {
|
||||
AssignedVariablesAnalyzer ava(function);
|
||||
ava.Analyze();
|
||||
if (ava.HasStackOverflow()) {
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAG_use_flow_graph) {
|
||||
FlowGraphBuilder builder;
|
||||
builder.Build(function);
|
||||
@ -463,6 +472,15 @@ Handle<JSFunction> Compiler::BuildBoilerplate(FunctionLiteral* literal,
|
||||
return Handle<JSFunction>::null();
|
||||
}
|
||||
|
||||
if (literal->scope()->num_parameters() > 0 ||
|
||||
literal->scope()->num_stack_slots()) {
|
||||
AssignedVariablesAnalyzer ava(literal);
|
||||
ava.Analyze();
|
||||
if (ava.HasStackOverflow()) {
|
||||
return Handle<JSFunction>::null();
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAG_use_flow_graph) {
|
||||
FlowGraphBuilder builder;
|
||||
builder.Build(literal);
|
||||
|
441
src/data-flow.cc
441
src/data-flow.cc
@ -28,6 +28,7 @@
|
||||
#include "v8.h"
|
||||
|
||||
#include "data-flow.h"
|
||||
#include "scopes.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -1035,6 +1036,446 @@ void LivenessAnalyzer::VisitThisFunction(ThisFunction* expr) {
|
||||
}
|
||||
|
||||
|
||||
AssignedVariablesAnalyzer::AssignedVariablesAnalyzer(FunctionLiteral* fun)
|
||||
: fun_(fun),
|
||||
av_(fun->scope()->num_parameters() + fun->scope()->num_stack_slots()) {}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::Analyze() {
|
||||
ASSERT(av_.length() > 0);
|
||||
VisitStatements(fun_->body());
|
||||
}
|
||||
|
||||
|
||||
Variable* AssignedVariablesAnalyzer::FindSmiLoopVariable(ForStatement* stmt) {
|
||||
// The loop must have all necessary parts.
|
||||
if (stmt->init() == NULL || stmt->cond() == NULL || stmt->next() == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// The initialization statement has to be a simple assignment.
|
||||
Assignment* init = stmt->init()->StatementAsSimpleAssignment();
|
||||
if (init == NULL) return NULL;
|
||||
|
||||
// We only deal with local variables.
|
||||
Variable* loop_var = init->target()->AsVariableProxy()->AsVariable();
|
||||
if (!loop_var->IsStackAllocated()) return NULL;
|
||||
|
||||
// The initial value has to be a smi.
|
||||
Literal* init_lit = init->value()->AsLiteral();
|
||||
if (init_lit == NULL || !init_lit->handle()->IsSmi()) return NULL;
|
||||
int init_value = Smi::cast(*init_lit->handle())->value();
|
||||
|
||||
// The condition must be a compare of variable with <, <=, >, or >=.
|
||||
CompareOperation* cond = stmt->cond()->AsCompareOperation();
|
||||
if (cond == NULL) return NULL;
|
||||
if (cond->op() != Token::LT
|
||||
&& cond->op() != Token::LTE
|
||||
&& cond->op() != Token::GT
|
||||
&& cond->op() != Token::GTE) return NULL;
|
||||
|
||||
// The lhs must be the same variable as in the init expression.
|
||||
if (cond->left()->AsVariableProxy()->AsVariable() != loop_var) return NULL;
|
||||
|
||||
// The rhs must be a smi.
|
||||
Literal* term_lit = cond->right()->AsLiteral();
|
||||
if (term_lit == NULL || !term_lit->handle()->IsSmi()) return NULL;
|
||||
int term_value = Smi::cast(*term_lit->handle())->value();
|
||||
|
||||
// The count operation updates the same variable as in the init expression.
|
||||
CountOperation* update = stmt->next()->StatementAsCountOperation();
|
||||
if (update == NULL) return NULL;
|
||||
if (update->expression()->AsVariableProxy()->AsVariable() != loop_var) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The direction of the count operation must agree with the start and the end
|
||||
// value. We currently do not allow the initial value to be the same as the
|
||||
// terminal value. This _would_ be ok as long as the loop body never executes
|
||||
// or executes exactly one time.
|
||||
if (init_value == term_value) return NULL;
|
||||
if (init_value < term_value && update->op() != Token::INC) return NULL;
|
||||
if (init_value > term_value && update->op() != Token::DEC) return NULL;
|
||||
|
||||
// Found a smi loop variable.
|
||||
return loop_var;
|
||||
}
|
||||
|
||||
int AssignedVariablesAnalyzer::BitIndex(Variable* var) {
|
||||
ASSERT(var != NULL);
|
||||
ASSERT(var->IsStackAllocated());
|
||||
Slot* slot = var->slot();
|
||||
if (slot->type() == Slot::PARAMETER) {
|
||||
return slot->index();
|
||||
} else {
|
||||
return fun_->scope()->num_parameters() + slot->index();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::RecordAssignedVar(Variable* var) {
|
||||
ASSERT(var != NULL);
|
||||
if (var->IsStackAllocated()) {
|
||||
av_.Add(BitIndex(var));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::MarkIfTrivial(Expression* expr) {
|
||||
Variable* var = expr->AsVariableProxy()->AsVariable();
|
||||
if (var != NULL &&
|
||||
var->IsStackAllocated() &&
|
||||
!var->is_arguments() &&
|
||||
(var->is_this() || !av_.Contains(BitIndex(var)))) {
|
||||
expr->AsVariableProxy()->set_is_trivial(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::ProcessExpression(Expression* expr) {
|
||||
BitVector saved_av(av_);
|
||||
av_.Clear();
|
||||
Visit(expr);
|
||||
av_.Union(saved_av);
|
||||
}
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitBlock(Block* stmt) {
|
||||
VisitStatements(stmt->statements());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitExpressionStatement(
|
||||
ExpressionStatement* stmt) {
|
||||
ProcessExpression(stmt->expression());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitIfStatement(IfStatement* stmt) {
|
||||
ProcessExpression(stmt->condition());
|
||||
Visit(stmt->then_statement());
|
||||
Visit(stmt->else_statement());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitContinueStatement(
|
||||
ContinueStatement* stmt) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitBreakStatement(BreakStatement* stmt) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitReturnStatement(ReturnStatement* stmt) {
|
||||
ProcessExpression(stmt->expression());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitWithEnterStatement(
|
||||
WithEnterStatement* stmt) {
|
||||
ProcessExpression(stmt->expression());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitWithExitStatement(
|
||||
WithExitStatement* stmt) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) {
|
||||
BitVector result(av_);
|
||||
av_.Clear();
|
||||
Visit(stmt->tag());
|
||||
result.Union(av_);
|
||||
for (int i = 0; i < stmt->cases()->length(); i++) {
|
||||
CaseClause* clause = stmt->cases()->at(i);
|
||||
if (!clause->is_default()) {
|
||||
av_.Clear();
|
||||
Visit(clause->label());
|
||||
result.Union(av_);
|
||||
}
|
||||
VisitStatements(clause->statements());
|
||||
}
|
||||
av_.Union(result);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) {
|
||||
ProcessExpression(stmt->cond());
|
||||
Visit(stmt->body());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitWhileStatement(WhileStatement* stmt) {
|
||||
ProcessExpression(stmt->cond());
|
||||
Visit(stmt->body());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitForStatement(ForStatement* stmt) {
|
||||
if (stmt->init() != NULL) Visit(stmt->init());
|
||||
|
||||
if (stmt->cond() != NULL) ProcessExpression(stmt->cond());
|
||||
|
||||
if (stmt->next() != NULL) Visit(stmt->next());
|
||||
|
||||
// Process loop body. After visiting the loop body av_ contains
|
||||
// the assigned variables of the loop body.
|
||||
BitVector saved_av(av_);
|
||||
av_.Clear();
|
||||
Visit(stmt->body());
|
||||
|
||||
Variable* var = FindSmiLoopVariable(stmt);
|
||||
if (var != NULL && !av_.Contains(BitIndex(var))) {
|
||||
stmt->set_loop_variable(var);
|
||||
}
|
||||
|
||||
av_.Union(saved_av);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitForInStatement(ForInStatement* stmt) {
|
||||
ProcessExpression(stmt->each());
|
||||
ProcessExpression(stmt->enumerable());
|
||||
Visit(stmt->body());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitTryCatchStatement(
|
||||
TryCatchStatement* stmt) {
|
||||
Visit(stmt->try_block());
|
||||
Visit(stmt->catch_block());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitTryFinallyStatement(
|
||||
TryFinallyStatement* stmt) {
|
||||
Visit(stmt->try_block());
|
||||
Visit(stmt->finally_block());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitDebuggerStatement(
|
||||
DebuggerStatement* stmt) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) {
|
||||
// Nothing to do.
|
||||
ASSERT(av_.IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitFunctionBoilerplateLiteral(
|
||||
FunctionBoilerplateLiteral* expr) {
|
||||
// Nothing to do.
|
||||
ASSERT(av_.IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitConditional(Conditional* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
|
||||
Visit(expr->condition());
|
||||
|
||||
BitVector result(av_);
|
||||
av_.Clear();
|
||||
Visit(expr->then_expression());
|
||||
result.Union(av_);
|
||||
|
||||
av_.Clear();
|
||||
Visit(expr->else_expression());
|
||||
av_.Union(result);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitSlot(Slot* expr) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitVariableProxy(VariableProxy* expr) {
|
||||
// Nothing to do.
|
||||
ASSERT(av_.IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitLiteral(Literal* expr) {
|
||||
// Nothing to do.
|
||||
ASSERT(av_.IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) {
|
||||
// Nothing to do.
|
||||
ASSERT(av_.IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
BitVector result(av_.length());
|
||||
for (int i = 0; i < expr->properties()->length(); i++) {
|
||||
Visit(expr->properties()->at(i)->value());
|
||||
result.Union(av_);
|
||||
av_.Clear();
|
||||
}
|
||||
av_.CopyFrom(result);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
BitVector result(av_.length());
|
||||
for (int i = 0; i < expr->values()->length(); i++) {
|
||||
Visit(expr->values()->at(i));
|
||||
result.Union(av_);
|
||||
av_.Clear();
|
||||
}
|
||||
av_.CopyFrom(result);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitCatchExtensionObject(
|
||||
CatchExtensionObject* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->key());
|
||||
ProcessExpression(expr->value());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitAssignment(Assignment* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
|
||||
Visit(expr->target());
|
||||
|
||||
ProcessExpression(expr->value());
|
||||
|
||||
Variable* var = expr->target()->AsVariableProxy()->AsVariable();
|
||||
if (var != NULL) RecordAssignedVar(var);
|
||||
|
||||
// If we have a variable as a receiver in a property store, check if
|
||||
// we can mark it as trivial.
|
||||
if (expr->target()->AsProperty() != NULL) {
|
||||
MarkIfTrivial(expr->target()->AsProperty()->obj());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitThrow(Throw* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->exception());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitProperty(Property* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->obj());
|
||||
ProcessExpression(expr->key());
|
||||
|
||||
// In case we have a variable as a receiver, check if we can mark
|
||||
// it as trivial.
|
||||
MarkIfTrivial(expr->obj());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitCall(Call* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->expression());
|
||||
BitVector result(av_);
|
||||
for (int i = 0; i < expr->arguments()->length(); i++) {
|
||||
av_.Clear();
|
||||
Visit(expr->arguments()->at(i));
|
||||
result.Union(av_);
|
||||
}
|
||||
av_.CopyFrom(result);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitCallNew(CallNew* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->expression());
|
||||
BitVector result(av_);
|
||||
for (int i = 0; i < expr->arguments()->length(); i++) {
|
||||
av_.Clear();
|
||||
Visit(expr->arguments()->at(i));
|
||||
result.Union(av_);
|
||||
}
|
||||
av_.CopyFrom(result);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitCallRuntime(CallRuntime* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
BitVector result(av_);
|
||||
for (int i = 0; i < expr->arguments()->length(); i++) {
|
||||
av_.Clear();
|
||||
Visit(expr->arguments()->at(i));
|
||||
result.Union(av_);
|
||||
}
|
||||
av_.CopyFrom(result);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitUnaryOperation(UnaryOperation* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->expression());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitCountOperation(CountOperation* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
|
||||
Visit(expr->expression());
|
||||
|
||||
Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
|
||||
if (var != NULL) RecordAssignedVar(var);
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitBinaryOperation(BinaryOperation* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->left());
|
||||
|
||||
ProcessExpression(expr->right());
|
||||
|
||||
// In case we have a variable on the left side, check if we can mark
|
||||
// it as trivial.
|
||||
MarkIfTrivial(expr->left());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitCompareOperation(CompareOperation* expr) {
|
||||
ASSERT(av_.IsEmpty());
|
||||
Visit(expr->left());
|
||||
|
||||
ProcessExpression(expr->right());
|
||||
|
||||
// In case we have a variable on the left side, check if we can mark
|
||||
// it as trivial.
|
||||
MarkIfTrivial(expr->left());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitThisFunction(ThisFunction* expr) {
|
||||
// Nothing to do.
|
||||
ASSERT(av_.IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
void AssignedVariablesAnalyzer::VisitDeclaration(Declaration* decl) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
// Print a textual representation of an instruction in a flow graph. Using
|
||||
|
@ -486,6 +486,41 @@ class LivenessAnalyzer : public AstVisitor {
|
||||
};
|
||||
|
||||
|
||||
// Computes the set of assigned variables and annotates variables proxies
|
||||
// that are trivial sub-expressions and for-loops where the loop variable
|
||||
// is guaranteed to be a smi.
|
||||
class AssignedVariablesAnalyzer : public AstVisitor {
|
||||
public:
|
||||
explicit AssignedVariablesAnalyzer(FunctionLiteral* fun);
|
||||
|
||||
void Analyze();
|
||||
|
||||
private:
|
||||
Variable* FindSmiLoopVariable(ForStatement* stmt);
|
||||
|
||||
int BitIndex(Variable* var);
|
||||
|
||||
void RecordAssignedVar(Variable* var);
|
||||
|
||||
void MarkIfTrivial(Expression* expr);
|
||||
|
||||
// Visits an expression saving the accumulator before, clearing
|
||||
// it before visting and restoring it after visiting.
|
||||
void ProcessExpression(Expression* expr);
|
||||
|
||||
// AST node visit functions.
|
||||
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
|
||||
AST_NODE_LIST(DECLARE_VISIT)
|
||||
#undef DECLARE_VISIT
|
||||
|
||||
FunctionLiteral* fun_;
|
||||
|
||||
// Accumulator for assigned variables set.
|
||||
BitVector av_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AssignedVariablesAnalyzer);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
|
||||
|
@ -2286,61 +2286,69 @@ void CodeGenerator::Comparison(AstNode* node,
|
||||
// a jump target and branching to duplicate the virtual frame at
|
||||
// the first split. We manually handle the off-frame references
|
||||
// by reconstituting them on the non-fall-through path.
|
||||
JumpTarget is_smi;
|
||||
__ test(left_side.reg(), Immediate(kSmiTagMask));
|
||||
is_smi.Branch(zero, taken);
|
||||
|
||||
bool is_for_loop_compare = (node->AsCompareOperation() != NULL)
|
||||
&& node->AsCompareOperation()->is_for_loop_condition();
|
||||
if (!is_for_loop_compare
|
||||
&& CpuFeatures::IsSupported(SSE2)
|
||||
&& right_val->IsSmi()) {
|
||||
// Right side is a constant smi and left side has been checked
|
||||
// not to be a smi.
|
||||
CpuFeatures::Scope use_sse2(SSE2);
|
||||
JumpTarget not_number;
|
||||
__ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
|
||||
Immediate(Factory::heap_number_map()));
|
||||
not_number.Branch(not_equal, &left_side);
|
||||
__ movdbl(xmm1,
|
||||
FieldOperand(left_reg, HeapNumber::kValueOffset));
|
||||
int value = Smi::cast(*right_val)->value();
|
||||
if (value == 0) {
|
||||
__ xorpd(xmm0, xmm0);
|
||||
} else {
|
||||
Result temp = allocator()->Allocate();
|
||||
__ mov(temp.reg(), Immediate(value));
|
||||
__ cvtsi2sd(xmm0, Operand(temp.reg()));
|
||||
temp.Unuse();
|
||||
if (left_side.is_smi()) {
|
||||
if (FLAG_debug_code) {
|
||||
__ AbortIfNotSmi(left_side.reg(), "Argument not a smi");
|
||||
}
|
||||
__ comisd(xmm1, xmm0);
|
||||
// Jump to builtin for NaN.
|
||||
not_number.Branch(parity_even, &left_side);
|
||||
left_side.Unuse();
|
||||
Condition double_cc = cc;
|
||||
switch (cc) {
|
||||
case less: double_cc = below; break;
|
||||
case equal: double_cc = equal; break;
|
||||
case less_equal: double_cc = below_equal; break;
|
||||
case greater: double_cc = above; break;
|
||||
case greater_equal: double_cc = above_equal; break;
|
||||
default: UNREACHABLE();
|
||||
} else {
|
||||
JumpTarget is_smi;
|
||||
__ test(left_side.reg(), Immediate(kSmiTagMask));
|
||||
is_smi.Branch(zero, taken);
|
||||
|
||||
bool is_for_loop_compare = (node->AsCompareOperation() != NULL)
|
||||
&& node->AsCompareOperation()->is_for_loop_condition();
|
||||
if (!is_for_loop_compare
|
||||
&& CpuFeatures::IsSupported(SSE2)
|
||||
&& right_val->IsSmi()) {
|
||||
// Right side is a constant smi and left side has been checked
|
||||
// not to be a smi.
|
||||
CpuFeatures::Scope use_sse2(SSE2);
|
||||
JumpTarget not_number;
|
||||
__ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
|
||||
Immediate(Factory::heap_number_map()));
|
||||
not_number.Branch(not_equal, &left_side);
|
||||
__ movdbl(xmm1,
|
||||
FieldOperand(left_reg, HeapNumber::kValueOffset));
|
||||
int value = Smi::cast(*right_val)->value();
|
||||
if (value == 0) {
|
||||
__ xorpd(xmm0, xmm0);
|
||||
} else {
|
||||
Result temp = allocator()->Allocate();
|
||||
__ mov(temp.reg(), Immediate(value));
|
||||
__ cvtsi2sd(xmm0, Operand(temp.reg()));
|
||||
temp.Unuse();
|
||||
}
|
||||
__ comisd(xmm1, xmm0);
|
||||
// Jump to builtin for NaN.
|
||||
not_number.Branch(parity_even, &left_side);
|
||||
left_side.Unuse();
|
||||
Condition double_cc = cc;
|
||||
switch (cc) {
|
||||
case less: double_cc = below; break;
|
||||
case equal: double_cc = equal; break;
|
||||
case less_equal: double_cc = below_equal; break;
|
||||
case greater: double_cc = above; break;
|
||||
case greater_equal: double_cc = above_equal; break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
dest->true_target()->Branch(double_cc);
|
||||
dest->false_target()->Jump();
|
||||
not_number.Bind(&left_side);
|
||||
}
|
||||
dest->true_target()->Branch(double_cc);
|
||||
|
||||
// Setup and call the compare stub.
|
||||
CompareStub stub(cc, strict, kCantBothBeNaN);
|
||||
Result result = frame_->CallStub(&stub, &left_side, &right_side);
|
||||
result.ToRegister();
|
||||
__ cmp(result.reg(), 0);
|
||||
result.Unuse();
|
||||
dest->true_target()->Branch(cc);
|
||||
dest->false_target()->Jump();
|
||||
not_number.Bind(&left_side);
|
||||
|
||||
is_smi.Bind();
|
||||
}
|
||||
|
||||
// Setup and call the compare stub.
|
||||
CompareStub stub(cc, strict, kCantBothBeNaN);
|
||||
Result result = frame_->CallStub(&stub, &left_side, &right_side);
|
||||
result.ToRegister();
|
||||
__ cmp(result.reg(), 0);
|
||||
result.Unuse();
|
||||
dest->true_target()->Branch(cc);
|
||||
dest->false_target()->Jump();
|
||||
|
||||
is_smi.Bind();
|
||||
left_side = Result(left_reg);
|
||||
right_side = Result(right_val);
|
||||
// Test smi equality and comparison by signed int comparison.
|
||||
@ -3579,6 +3587,24 @@ void CodeGenerator::VisitForStatement(ForStatement* node) {
|
||||
}
|
||||
|
||||
CheckStack(); // TODO(1222600): ignore if body contains calls.
|
||||
|
||||
// If we have (a) a loop with a compile-time constant trip count
|
||||
// and (b) the loop induction variable is not assignend inside the
|
||||
// loop we update the number type of the induction variable to be smi.
|
||||
|
||||
if (node->is_fast_smi_loop()) {
|
||||
// Set number type of the loop variable to smi.
|
||||
Slot* slot = node->loop_variable()->slot();
|
||||
ASSERT(slot->type() == Slot::LOCAL);
|
||||
frame_->SetTypeForLocalAt(slot->index(), NumberInfo::Smi());
|
||||
if (FLAG_debug_code) {
|
||||
frame_->PushLocalAt(slot->index());
|
||||
Result var = frame_->Pop();
|
||||
var.ToRegister();
|
||||
__ AbortIfNotSmi(var.reg(), "Loop variable not a smi.");
|
||||
}
|
||||
}
|
||||
|
||||
Visit(node->body());
|
||||
|
||||
// If there is an update expression, compile it if necessary.
|
||||
@ -6624,15 +6650,6 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
|
||||
__ Set(tmp.reg(), Immediate(0));
|
||||
}
|
||||
|
||||
DeferredCode* deferred = NULL;
|
||||
if (is_postfix) {
|
||||
deferred = new DeferredPostfixCountOperation(new_value.reg(),
|
||||
old_value.reg(),
|
||||
is_increment);
|
||||
} else {
|
||||
deferred = new DeferredPrefixCountOperation(new_value.reg(),
|
||||
is_increment);
|
||||
}
|
||||
|
||||
if (is_increment) {
|
||||
__ add(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
|
||||
@ -6640,24 +6657,41 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
|
||||
__ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
|
||||
}
|
||||
|
||||
// If the count operation didn't overflow and the result is a valid
|
||||
// smi, we're done. Otherwise, we jump to the deferred slow-case
|
||||
// code.
|
||||
if (tmp.is_valid()) {
|
||||
// We combine the overflow and the smi tag check if we could
|
||||
// successfully allocate a temporary byte register.
|
||||
__ setcc(overflow, tmp.reg());
|
||||
__ or_(Operand(tmp.reg()), new_value.reg());
|
||||
__ test(tmp.reg(), Immediate(kSmiTagMask));
|
||||
tmp.Unuse();
|
||||
deferred->Branch(not_zero);
|
||||
if (new_value.is_smi()) {
|
||||
if (FLAG_debug_code) {
|
||||
__ AbortIfNotSmi(new_value.reg(), "Argument not a smi");
|
||||
}
|
||||
if (tmp.is_valid()) tmp.Unuse();
|
||||
} else {
|
||||
// Otherwise we test separately for overflow and smi tag.
|
||||
deferred->Branch(overflow);
|
||||
__ test(new_value.reg(), Immediate(kSmiTagMask));
|
||||
deferred->Branch(not_zero);
|
||||
DeferredCode* deferred = NULL;
|
||||
if (is_postfix) {
|
||||
deferred = new DeferredPostfixCountOperation(new_value.reg(),
|
||||
old_value.reg(),
|
||||
is_increment);
|
||||
} else {
|
||||
deferred = new DeferredPrefixCountOperation(new_value.reg(),
|
||||
is_increment);
|
||||
}
|
||||
|
||||
// If the count operation didn't overflow and the result is a valid
|
||||
// smi, we're done. Otherwise, we jump to the deferred slow-case
|
||||
// code.
|
||||
if (tmp.is_valid()) {
|
||||
// We combine the overflow and the smi tag check if we could
|
||||
// successfully allocate a temporary byte register.
|
||||
__ setcc(overflow, tmp.reg());
|
||||
__ or_(Operand(tmp.reg()), new_value.reg());
|
||||
__ test(tmp.reg(), Immediate(kSmiTagMask));
|
||||
tmp.Unuse();
|
||||
deferred->Branch(not_zero);
|
||||
} else {
|
||||
// Otherwise we test separately for overflow and smi tag.
|
||||
deferred->Branch(overflow);
|
||||
__ test(new_value.reg(), Immediate(kSmiTagMask));
|
||||
deferred->Branch(not_zero);
|
||||
}
|
||||
deferred->BindExit();
|
||||
}
|
||||
deferred->BindExit();
|
||||
|
||||
// Postfix: store the old value in the allocated slot under the
|
||||
// reference.
|
||||
@ -6823,8 +6857,15 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
|
||||
overwrite_mode = OVERWRITE_RIGHT;
|
||||
}
|
||||
|
||||
Load(node->left());
|
||||
Load(node->right());
|
||||
if (node->left()->IsTrivial()) {
|
||||
Load(node->right());
|
||||
Result right = frame_->Pop();
|
||||
frame_->Push(node->left());
|
||||
frame_->Push(&right);
|
||||
} else {
|
||||
Load(node->left());
|
||||
Load(node->right());
|
||||
}
|
||||
GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
|
||||
}
|
||||
}
|
||||
@ -7024,8 +7065,20 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (!left_already_loaded) Load(left);
|
||||
Load(right);
|
||||
|
||||
if (left->IsTrivial()) {
|
||||
if (!left_already_loaded) {
|
||||
Load(right);
|
||||
Result right_result = frame_->Pop();
|
||||
frame_->Push(left);
|
||||
frame_->Push(&right_result);
|
||||
} else {
|
||||
Load(right);
|
||||
}
|
||||
} else {
|
||||
if (!left_already_loaded) Load(left);
|
||||
Load(right);
|
||||
}
|
||||
Comparison(node, cc, strict, destination());
|
||||
}
|
||||
|
||||
|
@ -397,6 +397,12 @@ void MacroAssembler::AbortIfNotNumber(Register object, const char* msg) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::AbortIfNotSmi(Register object, const char* msg) {
|
||||
test(object, Immediate(kSmiTagMask));
|
||||
Assert(equal, msg);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::EnterFrame(StackFrame::Type type) {
|
||||
push(ebp);
|
||||
mov(ebp, Operand(esp));
|
||||
|
@ -179,6 +179,9 @@ class MacroAssembler: public Assembler {
|
||||
// Abort execution if argument is not a number. Used in debug code.
|
||||
void AbortIfNotNumber(Register object, const char* msg);
|
||||
|
||||
// Abort execution if argument is not a smi. Used in debug code.
|
||||
void AbortIfNotSmi(Register object, const char* msg);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exception handling
|
||||
|
||||
|
@ -1171,11 +1171,17 @@ void VirtualFrame::Push(Expression* expr) {
|
||||
}
|
||||
|
||||
VariableProxy* proxy = expr->AsVariableProxy();
|
||||
if (proxy != NULL && proxy->is_this()) {
|
||||
PushParameterAt(-1);
|
||||
return;
|
||||
if (proxy != NULL) {
|
||||
Slot* slot = proxy->var()->slot();
|
||||
if (slot->type() == Slot::LOCAL) {
|
||||
PushLocalAt(slot->index());
|
||||
return;
|
||||
}
|
||||
if (slot->type() == Slot::PARAMETER) {
|
||||
PushParameterAt(slot->index());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
@ -422,6 +422,9 @@ class VirtualFrame: public ZoneObject {
|
||||
// the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
|
||||
inline void Nip(int num_dropped);
|
||||
|
||||
// Update the type information of a local variable frame element directly.
|
||||
inline void SetTypeForLocalAt(int index, NumberInfo info);
|
||||
|
||||
private:
|
||||
static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
|
||||
static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
|
||||
|
@ -277,7 +277,6 @@ class Scope: public ZoneObject {
|
||||
// The number of contexts between this and scope; zero if this == scope.
|
||||
int ContextChainLength(Scope* scope);
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Debugging.
|
||||
|
||||
|
@ -119,6 +119,12 @@ bool VirtualFrame::Equals(VirtualFrame* other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void VirtualFrame::SetTypeForLocalAt(int index, NumberInfo info) {
|
||||
elements_[local0_index() + index].set_number_info(info);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_VIRTUAL_FRAME_INL_H_
|
||||
|
@ -416,6 +416,8 @@ class VirtualFrame : public ZoneObject {
|
||||
// the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
|
||||
inline void Nip(int num_dropped);
|
||||
|
||||
inline void SetTypeForLocalAt(int index, NumberInfo info);
|
||||
|
||||
private:
|
||||
static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
|
||||
static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
|
||||
|
Loading…
Reference in New Issue
Block a user