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
parent 8f760827
......@@ -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;
......
......@@ -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());
}
......
......@@ -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);
......
This diff is collapsed.
......@@ -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);
}
// 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();
}
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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment