Initial support for marking live code.

As part of aggressive dead code elimination, we initially assume all
flow-graph instructions are not live.  We mark those that are critical
and recursively all their children.  The children of variable
references (VariableProxies occurring as rvalues) include all their
reaching definitions.

Review URL: http://codereview.chromium.org/1159005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4246 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 7a861287
......@@ -28,6 +28,7 @@
#include "v8.h"
#include "ast.h"
#include "data-flow.h"
#include "parser.h"
#include "scopes.h"
#include "string-stream.h"
......@@ -600,6 +601,199 @@ bool BinaryOperation::IsPrimitive() {
bool CompareOperation::IsPrimitive() { return true; }
// Overridden IsCritical member functions. IsCritical is true for AST nodes
// whose evaluation is absolutely required (they are never dead) because
// they are externally visible.
// References to global variables or lookup slots are critical because they
// may have getters. All others, including parameters rewritten to explicit
// property references, are not critical.
bool VariableProxy::IsCritical() {
Variable* var = AsVariable();
return var != NULL &&
(var->slot() == NULL || var->slot()->type() == Slot::LOOKUP);
}
// Literals are never critical.
bool Literal::IsCritical() { return false; }
// Property assignments and throwing of reference errors are always
// critical. Assignments to escaping variables are also critical. In
// addition the operation of compound assignments is critical if either of
// its operands is non-primitive (the arithmetic operations all use one of
// ToPrimitive, ToNumber, ToInt32, or ToUint32 on each of their operands).
// In this case, we mark the entire AST node as critical because there is
// no binary operation node to mark.
bool Assignment::IsCritical() {
Variable* var = AssignedVariable();
return var == NULL ||
!var->IsStackAllocated() ||
(is_compound() && (!target()->IsPrimitive() || !value()->IsPrimitive()));
}
// Property references are always critical, because they may have getters.
bool Property::IsCritical() { return true; }
// Calls are always critical.
bool Call::IsCritical() { return true; }
// +,- use ToNumber on the value of their operand.
bool UnaryOperation::IsCritical() {
ASSERT(op() == Token::ADD || op() == Token::SUB);
return !expression()->IsPrimitive();
}
// Count operations targeting properties and reference errors are always
// critical. Count operations on escaping variables are critical. Count
// operations targeting non-primitives are also critical because they use
// ToNumber.
bool CountOperation::IsCritical() {
Variable* var = AssignedVariable();
return var == NULL ||
!var->IsStackAllocated() ||
!expression()->IsPrimitive();
}
// Arithmetic operations all use one of ToPrimitive, ToNumber, ToInt32, or
// ToUint32 on each of their operands.
bool BinaryOperation::IsCritical() {
ASSERT(op() != Token::COMMA);
ASSERT(op() != Token::OR);
ASSERT(op() != Token::AND);
return !left()->IsPrimitive() || !right()->IsPrimitive();
}
// <, >, <=, and >= all use ToPrimitive on both their operands.
bool CompareOperation::IsCritical() {
ASSERT(op() != Token::EQ);
ASSERT(op() != Token::NE);
ASSERT(op() != Token::EQ_STRICT);
ASSERT(op() != Token::NE_STRICT);
ASSERT(op() != Token::INSTANCEOF);
ASSERT(op() != Token::IN);
return !left()->IsPrimitive() || !right()->IsPrimitive();
}
static inline void MarkIfNotLive(Expression* expr, List<AstNode*>* stack) {
if (!expr->is_live()) {
expr->mark_as_live();
stack->Add(expr);
}
}
// Overloaded functions for marking children of live code as live.
void VariableProxy::ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
// A reference to a stack-allocated variable depends on all the
// definitions reaching it.
BitVector* defs = reaching_definitions();
if (defs != NULL) {
ASSERT(var()->IsStackAllocated());
// The first variable_count definitions are the initial parameter and
// local declarations.
for (int i = variable_count; i < defs->length(); i++) {
if (defs->Contains(i)) {
MarkIfNotLive(body_definitions->at(i - variable_count), stack);
}
}
}
}
void Literal::ProcessNonLiveChildren(List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
// Leaf node, no children.
}
void Assignment::ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
Property* prop = target()->AsProperty();
VariableProxy* proxy = target()->AsVariableProxy();
if (prop != NULL) {
if (!prop->key()->IsPropertyName()) MarkIfNotLive(prop->key(), stack);
MarkIfNotLive(prop->obj(), stack);
} else if (proxy == NULL) {
// Must be a reference error.
ASSERT(!target()->IsValidLeftHandSide());
MarkIfNotLive(target(), stack);
} else if (is_compound()) {
// A variable assignment so lhs is an operand to the operation.
MarkIfNotLive(target(), stack);
}
MarkIfNotLive(value(), stack);
}
void Property::ProcessNonLiveChildren(List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
if (!key()->IsPropertyName()) MarkIfNotLive(key(), stack);
MarkIfNotLive(obj(), stack);
}
void Call::ProcessNonLiveChildren(List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
ZoneList<Expression*>* args = arguments();
for (int i = args->length() - 1; i >= 0; i--) {
MarkIfNotLive(args->at(i), stack);
}
MarkIfNotLive(expression(), stack);
}
void UnaryOperation::ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
MarkIfNotLive(expression(), stack);
}
void CountOperation::ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
MarkIfNotLive(expression(), stack);
}
void BinaryOperation::ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
MarkIfNotLive(right(), stack);
MarkIfNotLive(left(), stack);
}
void CompareOperation::ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
MarkIfNotLive(right(), stack);
MarkIfNotLive(left(), stack);
}
// Implementation of a copy visitor. The visitor create a deep copy
// of ast nodes. Nodes that do not require a deep copy are copied
// with the default copy constructor.
......
......@@ -152,6 +152,10 @@ class AstNode: public ZoneObject {
virtual ArrayLiteral* AsArrayLiteral() { return NULL; }
virtual CompareOperation* AsCompareOperation() { return NULL; }
// True if the AST node is critical (its execution is needed or externally
// visible in some way).
virtual bool IsCritical() { UNREACHABLE(); return true; }
int num() { return num_; }
void set_num(int n) { num_ = n; }
......@@ -211,7 +215,7 @@ class Expression: public AstNode {
virtual bool IsValidLeftHandSide() { return false; }
virtual Variable* AssignedVar() { return NULL; }
virtual Variable* AssignedVariable() { return NULL; }
// Symbols that cannot be parsed as array indices are considered property
// names. We do not treat symbols that can be array indexes as property
......@@ -283,6 +287,19 @@ class Expression: public AstNode {
bitfields_ |= NumBitOpsField::encode(num_bit_ops);
}
// Functions used for dead-code elimination. Predicate is true if the
// expression is not dead code.
int is_live() const { return LiveField::decode(bitfields_); }
void mark_as_live() { bitfields_ |= LiveField::encode(true); }
// Mark non-live children as live and push them on a stack for further
// processing.
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
}
private:
static const int kMaxNumBitOps = (1 << 5) - 1;
......@@ -295,6 +312,7 @@ class Expression: public AstNode {
class ToInt32Field : public BitField<bool, 2, 1> {};
class NumBitOpsField : public BitField<int, 3, 5> {};
class LoopConditionField: public BitField<bool, 8, 1> {};
class LiveField: public BitField<bool, 9, 1> {};
};
......@@ -881,6 +899,11 @@ class Literal: public Expression {
virtual bool IsLeaf() { return true; }
virtual bool IsTrivial() { return true; }
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
// Identity testers.
bool IsNull() const { return handle_.is_identical_to(Factory::null_value()); }
......@@ -1087,6 +1110,11 @@ class VariableProxy: public Expression {
virtual bool IsTrivial() { return is_trivial_; }
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
void SetIsPrimitive(bool value) { is_primitive_ = value; }
......@@ -1224,6 +1252,11 @@ class Property: public Expression {
virtual bool IsValidLeftHandSide() { return true; }
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
Expression* obj() const { return obj_; }
Expression* key() const { return key_; }
......@@ -1258,6 +1291,11 @@ class Call: public Expression {
virtual Call* AsCall() { return this; }
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
Expression* expression() const { return expression_; }
ZoneList<Expression*>* arguments() const { return arguments_; }
......@@ -1336,6 +1374,11 @@ class UnaryOperation: public Expression {
virtual UnaryOperation* AsUnaryOperation() { return this; }
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
Token::Value op() const { return op_; }
Expression* expression() const { return expression_; }
......@@ -1361,6 +1404,11 @@ class BinaryOperation: public Expression {
virtual BinaryOperation* AsBinaryOperation() { return this; }
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
// True iff the result can be safely overwritten (to avoid allocation).
// False for operations that can return one of their operands.
......@@ -1412,11 +1460,16 @@ class CountOperation: public Expression {
virtual CountOperation* AsCountOperation() { return this; }
virtual Variable* AssignedVar() {
virtual Variable* AssignedVariable() {
return expression()->AsVariableProxy()->AsVariable();
}
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
bool is_prefix() const { return is_prefix_; }
bool is_postfix() const { return !is_prefix_; }
......@@ -1449,6 +1502,11 @@ class CompareOperation: public Expression {
virtual void Accept(AstVisitor* v);
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
Token::Value op() const { return op_; }
Expression* left() const { return left_; }
......@@ -1502,10 +1560,15 @@ class Assignment: public Expression {
virtual Assignment* AsAssignment() { return this; }
virtual bool IsPrimitive();
virtual bool IsCritical();
virtual void ProcessNonLiveChildren(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
Assignment* AsSimpleAssignment() { return !is_compound() ? this : NULL; }
virtual Variable* AssignedVar() {
virtual Variable* AssignedVariable() {
return target()->AsVariableProxy()->AsVariable();
}
......
// Copyright 2009 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -106,6 +106,10 @@ static Handle<Code> MakeCode(Handle<Context> context, CompilationInfo* info) {
variable_count,
function->num_parameters());
ta.Compute();
MarkLiveCode(builder.preorder(),
builder.body_definitions(),
variable_count);
}
}
......@@ -521,6 +525,10 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
variable_count,
literal->num_parameters());
ta.Compute();
MarkLiveCode(builder.preorder(),
builder.body_definitions(),
variable_count);
}
}
......
......@@ -1663,9 +1663,16 @@ void BlockNode::PrintText() {
PrintF("L%d: Block\n", number());
TextInstructionPrinter printer;
for (int i = 0, len = instructions_.length(); i < len; i++) {
AstNode* instr = instructions_[i];
// Print a star next to dead instructions.
if (instr->AsExpression() != NULL && instr->AsExpression()->is_live()) {
PrintF(" ");
} else {
PrintF("* ");
}
PrintF("%d ", printer.NextNumber());
printer.Visit(instructions_[i]);
printer.AssignNumber(instructions_[i]);
printer.Visit(instr);
printer.AssignNumber(instr);
PrintF("\n");
}
PrintF("goto L%d\n\n", successor_->number());
......@@ -1753,7 +1760,7 @@ void BlockNode::InitializeReachingDefinitions(int definition_count,
for (int i = 0; i < instruction_count; i++) {
Expression* expr = instructions_[i]->AsExpression();
if (expr == NULL) continue;
Variable* var = expr->AssignedVar();
Variable* var = expr->AssignedVariable();
if (var == NULL || !var->IsStackAllocated()) continue;
// All definitions of this variable are killed.
......@@ -1930,7 +1937,7 @@ void BlockNode::PropagateReachingDefinitions(List<BitVector*>* variables) {
// It may instead (or also) be a definition. If so update the running
// value of reaching definitions for the block.
Variable* var = expr->AssignedVar();
Variable* var = expr->AssignedVariable();
if (var == NULL || !var->IsStackAllocated()) continue;
// All definitions of this variable are killed.
......@@ -1961,7 +1968,7 @@ void ReachingDefinitions::Compute() {
for (int i = 0, len = body_definitions_->length(); i < len; i++) {
// Account for each definition in the body as a definition of the
// defined variable.
Variable* var = body_definitions_->at(i)->AssignedVar();
Variable* var = body_definitions_->at(i)->AssignedVariable();
variables[IndexFor(var, variable_count_)]->Add(i + variable_count_);
}
......@@ -2046,4 +2053,50 @@ void TypeAnalyzer::Compute() {
}
void Node::MarkCriticalInstructions(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
}
void BlockNode::MarkCriticalInstructions(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count) {
for (int i = instructions_.length() - 1; i >= 0; i--) {
// Only expressions can appear in the flow graph for now.
Expression* expr = instructions_[i]->AsExpression();
if (expr != NULL && !expr->is_live() &&
(expr->is_loop_condition() || expr->IsCritical())) {
expr->mark_as_live();
expr->ProcessNonLiveChildren(stack, body_definitions, variable_count);
}
}
}
void MarkLiveCode(ZoneList<Node*>* nodes,
ZoneList<Expression*>* body_definitions,
int variable_count) {
List<AstNode*> stack(20);
// Mark the critical AST nodes as live; mark their dependencies and
// add them to the marking stack.
for (int i = nodes->length() - 1; i >= 0; i--) {
nodes->at(i)->MarkCriticalInstructions(&stack, body_definitions,
variable_count);
}
// Continue marking dependencies until no more.
while (!stack.is_empty()) {
// Only expressions can appear in the flow graph for now.
Expression* expr = stack.RemoveLast()->AsExpression();
if (expr != NULL) {
expr->ProcessNonLiveChildren(&stack, body_definitions, variable_count);
}
}
}
} } // namespace v8::internal
......@@ -241,6 +241,12 @@ class Node: public ZoneObject {
virtual void UpdateRDIn(WorkList<Node>* worklist, bool mark) = 0;
virtual void PropagateReachingDefinitions(List<BitVector*>* variables);
// Functions used by dead-code elimination.
virtual void MarkCriticalInstructions(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
#ifdef DEBUG
void AssignNodeNumber();
void PrintReachingDefinitions();
......@@ -263,24 +269,24 @@ class ExitNode: public Node {
public:
ExitNode() : predecessors_(4) {}
bool IsExitNode() { return true; }
virtual bool IsExitNode() { return true; }
void AddPredecessor(Node* predecessor) {
virtual void AddPredecessor(Node* predecessor) {
ASSERT(predecessor != NULL);
predecessors_.Add(predecessor);
}
void AddSuccessor(Node* successor) { UNREACHABLE(); }
virtual void AddSuccessor(Node* successor) { UNREACHABLE(); }
void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
virtual void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
void ComputeRDOut(BitVector* result);
void UpdateRDIn(WorkList<Node>* worklist, bool mark);
virtual void ComputeRDOut(BitVector* result);
virtual void UpdateRDIn(WorkList<Node>* worklist, bool mark);
#ifdef DEBUG
void PrintText();
virtual void PrintText();
#endif
private:
......@@ -301,18 +307,18 @@ class BlockNode: public Node {
return reinterpret_cast<BlockNode*>(node);
}
bool IsBlockNode() { return true; }
virtual bool IsBlockNode() { return true; }
bool is_empty() { return instructions_.is_empty(); }
ZoneList<AstNode*>* instructions() { return &instructions_; }
void AddPredecessor(Node* predecessor) {
virtual void AddPredecessor(Node* predecessor) {
ASSERT(predecessor_ == NULL && predecessor != NULL);
predecessor_ = predecessor;
}
void AddSuccessor(Node* successor) {
virtual void AddSuccessor(Node* successor) {
ASSERT(successor_ == NULL && successor != NULL);
successor_ = successor;
}
......@@ -321,20 +327,25 @@ class BlockNode: public Node {
instructions_.Add(instruction);
}
void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
virtual void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
virtual void InitializeReachingDefinitions(int definition_count,
List<BitVector*>* variables,
WorkList<Node>* worklist,
bool mark);
virtual void ComputeRDOut(BitVector* result);
virtual void UpdateRDIn(WorkList<Node>* worklist, bool mark);
virtual void PropagateReachingDefinitions(List<BitVector*>* variables);
void InitializeReachingDefinitions(int definition_count,
List<BitVector*>* variables,
WorkList<Node>* worklist,
bool mark);
void ComputeRDOut(BitVector* result);
void UpdateRDIn(WorkList<Node>* worklist, bool mark);
void PropagateReachingDefinitions(List<BitVector*>* variables);
virtual void MarkCriticalInstructions(
List<AstNode*>* stack,
ZoneList<Expression*>* body_definitions,
int variable_count);
#ifdef DEBUG
void PrintText();
virtual void PrintText();
#endif
private:
......@@ -351,14 +362,14 @@ class BranchNode: public Node {
public:
BranchNode() : predecessor_(NULL), successor0_(NULL), successor1_(NULL) {}
bool IsBranchNode() { return true; }
virtual bool IsBranchNode() { return true; }
void AddPredecessor(Node* predecessor) {
virtual void AddPredecessor(Node* predecessor) {
ASSERT(predecessor_ == NULL && predecessor != NULL);
predecessor_ = predecessor;
}
void AddSuccessor(Node* successor) {
virtual void AddSuccessor(Node* successor) {
ASSERT(successor1_ == NULL && successor != NULL);
if (successor0_ == NULL) {
successor0_ = successor;
......@@ -367,15 +378,15 @@ class BranchNode: public Node {
}
}
void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
virtual void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
void ComputeRDOut(BitVector* result);
void UpdateRDIn(WorkList<Node>* worklist, bool mark);
virtual void ComputeRDOut(BitVector* result);
virtual void UpdateRDIn(WorkList<Node>* worklist, bool mark);
#ifdef DEBUG
void PrintText();
virtual void PrintText();
#endif
private:
......@@ -397,27 +408,27 @@ class JoinNode: public Node {
return reinterpret_cast<JoinNode*>(node);
}
bool IsJoinNode() { return true; }
virtual bool IsJoinNode() { return true; }
void AddPredecessor(Node* predecessor) {
virtual void AddPredecessor(Node* predecessor) {
ASSERT(predecessor != NULL);
predecessors_.Add(predecessor);
}
void AddSuccessor(Node* successor) {
virtual void AddSuccessor(Node* successor) {
ASSERT(successor_ == NULL && successor != NULL);
successor_ = successor;
}
void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
virtual void Traverse(bool mark,
ZoneList<Node*>* preorder,
ZoneList<Node*>* postorder);
void ComputeRDOut(BitVector* result);
void UpdateRDIn(WorkList<Node>* worklist, bool mark);
virtual void ComputeRDOut(BitVector* result);
virtual void UpdateRDIn(WorkList<Node>* worklist, bool mark);
#ifdef DEBUG
void PrintText();
virtual void PrintText();
#endif
private:
......@@ -499,6 +510,7 @@ class FlowGraphBuilder: public AstVisitor {
void Build(FunctionLiteral* lit);
FlowGraph* graph() { return &graph_; }
ZoneList<Node*>* preorder() { return &preorder_; }
ZoneList<Node*>* postorder() { return &postorder_; }
ZoneList<Expression*>* body_definitions() { return &body_definitions_; }
......@@ -622,7 +634,6 @@ class ReachingDefinitions BASE_EMBEDDED {
};
class TypeAnalyzer BASE_EMBEDDED {
public:
TypeAnalyzer(ZoneList<Node*>* postorder,
......@@ -645,8 +656,16 @@ class TypeAnalyzer BASE_EMBEDDED {
ZoneList<Expression*>* body_definitions_;
int variable_count_;
int param_count_;
DISALLOW_COPY_AND_ASSIGN(TypeAnalyzer);
};
void MarkLiveCode(ZoneList<Node*>* nodes,
ZoneList<Expression*>* body_definitions,
int variable_count);
} } // namespace v8::internal
......
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