blob: 0eb0f929dfcfe554fcce6c88150a209152ad34f9 [file] [log] [blame]
// Copyright 2009 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:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_CFG_H_
#define V8_CFG_H_
#include "ast.h"
namespace v8 {
namespace internal {
class ExitNode;
class Location;
// Translate a source AST into a control-flow graph (CFG). The CFG contains
// single-entry, single-exit blocks of straight-line instructions and
// administrative nodes.
//
// Instructions are described by the following grammar.
//
// <Instruction> ::=
// Move <Location> <Value>
// | PropLoad <Location> <Value> <Value>
// | BinaryOp <Location> Token::Value <Value> <Value>
// | Return Nowhere <Value>
// | Position <Int>
//
// Values are trivial expressions:
//
// <Value> ::= Constant | <Location>
//
// Locations are storable values ('lvalues'). They can be slots,
// compiler-generated temporaries, or the special location 'Nowhere'
// indicating that no value is needed.
//
// <Location> ::=
// SlotLocation Slot::Type <Index>
// | TempLocation
// | Nowhere
// Administrative nodes: There are several types of 'administrative' nodes
// that do not contain instructions and do not necessarily have a single
// predecessor and a single successor.
//
// EntryNode: there is a distinguished entry node that has no predecessors
// and a single successor.
//
// ExitNode: there is a distinguished exit node that has arbitrarily many
// predecessors and no successor.
//
// JoinNode: join nodes have multiple predecessors and a single successor.
//
// BranchNode: branch nodes have a single predecessor and multiple
// successors.
// A convenient class to keep 'global' values when building a CFG. Since
// CFG construction can be invoked recursively, CFG globals are stacked.
class CfgGlobals BASE_EMBEDDED {
public:
explicit CfgGlobals(FunctionLiteral* fun);
~CfgGlobals() { top_ = previous_; }
static CfgGlobals* current() {
ASSERT(top_ != NULL);
return top_;
}
// The function currently being compiled.
FunctionLiteral* fun() { return global_fun_; }
// The shared global exit node for all exits from the function.
ExitNode* exit() { return global_exit_; }
// A singleton.
Location* nowhere() { return nowhere_; }
#ifdef DEBUG
int next_node_number() { return node_counter_++; }
int next_temp_number() { return temp_counter_++; }
#endif
private:
static CfgGlobals* top_;
FunctionLiteral* global_fun_;
ExitNode* global_exit_;
Location* nowhere_;
#ifdef DEBUG
// Used to number nodes and temporaries when printing.
int node_counter_;
int temp_counter_;
#endif
CfgGlobals* previous_;
};
class SlotLocation;
// Values represent trivial source expressions: ones with no side effects
// and that do not require code to be generated.
class Value : public ZoneObject {
public:
virtual ~Value() {}
// Predicates:
virtual bool is_temporary() { return false; }
virtual bool is_slot() { return false; }
virtual bool is_constant() { return false; }
// True if the value is a temporary allocated to the stack in
// fast-compilation mode.
virtual bool is_on_stack() { return false; }
// Support for fast-compilation mode:
// Move the value into a register.
virtual void Get(MacroAssembler* masm, Register reg) = 0;
// Push the value on the stack.
virtual void Push(MacroAssembler* masm) = 0;
// Move the value into a slot location.
virtual void MoveToSlot(MacroAssembler* masm, SlotLocation* loc) = 0;
#ifdef DEBUG
virtual void Print() = 0;
#endif
};
// A compile-time constant that appeared as a literal in the source AST.
class Constant : public Value {
public:
explicit Constant(Handle<Object> handle) : handle_(handle) {}
// Cast accessor.
static Constant* cast(Value* value) {
ASSERT(value->is_constant());
return reinterpret_cast<Constant*>(value);
}
// Accessors.
Handle<Object> handle() { return handle_; }
// Predicates.
bool is_constant() { return true; }
// Support for fast-compilation mode.
void Get(MacroAssembler* masm, Register reg);
void Push(MacroAssembler* masm);
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc);
#ifdef DEBUG
void Print();
#endif
private:
Handle<Object> handle_;
};
// Locations are values that can be stored into ('lvalues').
class Location : public Value {
public:
virtual ~Location() {}
// Static factory function returning the singleton nowhere location.
static Location* Nowhere() {
return CfgGlobals::current()->nowhere();
}
// Support for fast-compilation mode:
// Assumes temporaries have been allocated.
virtual void Get(MacroAssembler* masm, Register reg) = 0;
// Store the value in a register to the location. Assumes temporaries
// have been allocated.
virtual void Set(MacroAssembler* masm, Register reg) = 0;
// Assumes temporaries have been allocated, and if the value is a
// temporary it was not allocated to the stack.
virtual void Push(MacroAssembler* masm) = 0;
// Emit code to move a value into this location.
virtual void Move(MacroAssembler* masm, Value* value) = 0;
#ifdef DEBUG
virtual void Print() = 0;
#endif
};
// Nowhere is a special (singleton) location that indicates the value of a
// computation is not needed (though its side effects are).
class Nowhere : public Location {
public:
// We should not try to emit code to read Nowhere.
void Get(MacroAssembler* masm, Register reg) { UNREACHABLE(); }
void Push(MacroAssembler* masm) { UNREACHABLE(); }
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc) { UNREACHABLE(); }
// Setting Nowhere is ignored.
void Set(MacroAssembler* masm, Register reg) {}
void Move(MacroAssembler* masm, Value* value) {}
#ifdef DEBUG
void Print();
#endif
private:
Nowhere() {}
friend class CfgGlobals;
};
// SlotLocations represent parameters and stack-allocated (i.e.,
// non-context) local variables.
class SlotLocation : public Location {
public:
SlotLocation(Slot::Type type, int index) : type_(type), index_(index) {}
// Cast accessor.
static SlotLocation* cast(Value* value) {
ASSERT(value->is_slot());
return reinterpret_cast<SlotLocation*>(value);
}
// Accessors.
Slot::Type type() { return type_; }
int index() { return index_; }
// Predicates.
bool is_slot() { return true; }
// Support for fast-compilation mode.
void Get(MacroAssembler* masm, Register reg);
void Set(MacroAssembler* masm, Register reg);
void Push(MacroAssembler* masm);
void Move(MacroAssembler* masm, Value* value);
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc);
#ifdef DEBUG
void Print();
#endif
private:
Slot::Type type_;
int index_;
};
// TempLocations represent compiler generated temporaries. They are
// allocated to registers or memory either before code generation (in the
// optimized-for-speed compiler) or on the fly during code generation (in
// the optimized-for-space compiler).
class TempLocation : public Location {
public:
// Fast-compilation mode allocation decisions.
enum Where {
NOT_ALLOCATED, // Not yet allocated.
ACCUMULATOR, // Allocated to the dedicated accumulator register.
STACK // " " " " stack.
};
TempLocation() : where_(NOT_ALLOCATED) {
#ifdef DEBUG
number_ = -1;
#endif
}
// Cast accessor.
static TempLocation* cast(Value* value) {
ASSERT(value->is_temporary());
return reinterpret_cast<TempLocation*>(value);
}
// Accessors.
Where where() { return where_; }
void set_where(Where where) {
ASSERT(where_ == TempLocation::NOT_ALLOCATED);
where_ = where;
}
// Predicates.
bool is_on_stack() { return where_ == STACK; }
bool is_temporary() { return true; }
// Support for fast-compilation mode. Assume the temp has been allocated.
void Get(MacroAssembler* masm, Register reg);
void Set(MacroAssembler* masm, Register reg);
void Push(MacroAssembler* masm);
void Move(MacroAssembler* masm, Value* value);
void MoveToSlot(MacroAssembler* masm, SlotLocation* loc);
#ifdef DEBUG
int number() {
if (number_ == -1) number_ = CfgGlobals::current()->next_temp_number();
return number_;
}
void Print();
#endif
private:
Where where_;
#ifdef DEBUG
int number_;
#endif
};
// Instructions are computations. The represent non-trivial source
// expressions: typically ones that have side effects and require code to
// be generated.
class Instruction : public ZoneObject {
public:
// Accessors.
Location* location() { return location_; }
void set_location(Location* location) { location_ = location; }
// Support for fast-compilation mode:
// Emit code to perform the instruction.
virtual void Compile(MacroAssembler* masm) = 0;
// Allocate a temporary which is the result of the immediate predecessor
// instruction. It is allocated to the accumulator register if it is used
// as an operand to this instruction, otherwise to the stack.
virtual void FastAllocate(TempLocation* temp) = 0;
#ifdef DEBUG
virtual void Print() = 0;
#endif
protected:
// Every instruction has a location where its result is stored (which may
// be Nowhere).
explicit Instruction(Location* location) : location_(location) {}
virtual ~Instruction() {}
Location* location_;
};
// Base class of instructions that have no input operands.
class ZeroOperandInstruction : public Instruction {
public:
// Support for fast-compilation mode:
virtual void Compile(MacroAssembler* masm) = 0;
void FastAllocate(TempLocation* temp);
#ifdef DEBUG
// Printing support: print the operands (nothing).
virtual void Print() {}
#endif
protected:
explicit ZeroOperandInstruction(Location* loc) : Instruction(loc) {}
};
// Base class of instructions that have a single input operand.
class OneOperandInstruction : public Instruction {
public:
// Support for fast-compilation mode:
virtual void Compile(MacroAssembler* masm) = 0;
void FastAllocate(TempLocation* temp);
#ifdef DEBUG
// Printing support: print the operands.
virtual void Print();
#endif
protected:
OneOperandInstruction(Location* loc, Value* value)
: Instruction(loc), value_(value) {
}
Value* value_;
};
// Base class of instructions that have two input operands.
class TwoOperandInstruction : public Instruction {
public:
// Support for fast-compilation mode:
virtual void Compile(MacroAssembler* masm) = 0;
void FastAllocate(TempLocation* temp);
#ifdef DEBUG
// Printing support: print the operands.
virtual void Print();
#endif
protected:
TwoOperandInstruction(Location* loc, Value* value0, Value* value1)
: Instruction(loc), value0_(value0), value1_(value1) {
}
Value* value0_;
Value* value1_;
};
// A phantom instruction that indicates the start of a statement. It
// causes the statement position to be recorded in the relocation
// information but generates no code.
class PositionInstr : public ZeroOperandInstruction {
public:
explicit PositionInstr(int pos)
: ZeroOperandInstruction(CfgGlobals::current()->nowhere()), pos_(pos) {
}
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
// This should not be called. The last instruction of the previous
// statement should not have a temporary as its location.
void FastAllocate(TempLocation* temp) { UNREACHABLE(); }
#ifdef DEBUG
// Printing support. Print nothing.
void Print() {}
#endif
private:
int pos_;
};
// Move a value to a location.
class MoveInstr : public OneOperandInstruction {
public:
MoveInstr(Location* loc, Value* value)
: OneOperandInstruction(loc, value) {
}
// Accessors.
Value* value() { return value_; }
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
#ifdef DEBUG
// Printing support.
void Print();
#endif
};
// Load a property from a receiver, leaving the result in a location.
class PropLoadInstr : public TwoOperandInstruction {
public:
PropLoadInstr(Location* loc, Value* object, Value* key)
: TwoOperandInstruction(loc, object, key) {
}
// Accessors.
Value* object() { return value0_; }
Value* key() { return value1_; }
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
#ifdef DEBUG
void Print();
#endif
};
// Perform a (non-short-circuited) binary operation on a pair of values,
// leaving the result in a location.
class BinaryOpInstr : public TwoOperandInstruction {
public:
BinaryOpInstr(Location* loc, Token::Value op, Value* left, Value* right)
: TwoOperandInstruction(loc, left, right), op_(op) {
}
// Accessors.
Value* left() { return value0_; }
Value* right() { return value1_; }
Token::Value op() { return op_; }
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
#ifdef DEBUG
void Print();
#endif
private:
Token::Value op_;
};
// Return a value. Has the side effect of moving its value into the return
// value register. Can only occur as the last instruction in an instruction
// block, and implies that the block is closed (cannot have instructions
// appended or graph fragments concatenated to the end) and that the block's
// successor is the global exit node for the current function.
class ReturnInstr : public OneOperandInstruction {
public:
explicit ReturnInstr(Value* value)
: OneOperandInstruction(CfgGlobals::current()->nowhere(), value) {
}
virtual ~ReturnInstr() {}
// Accessors.
Value* value() { return value_; }
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
#ifdef DEBUG
void Print();
#endif
};
// Nodes make up control-flow graphs.
class CfgNode : public ZoneObject {
public:
CfgNode() : is_marked_(false) {
#ifdef DEBUG
number_ = -1;
#endif
}
virtual ~CfgNode() {}
// Because CFGs contain cycles, nodes support marking during traversal
// (e.g., for printing or compilation). The traversal functions will mark
// unmarked nodes and backtrack if they encounter a marked one. After a
// traversal, the graph should be explicitly unmarked by calling Unmark on
// the entry node.
bool is_marked() { return is_marked_; }
virtual void Unmark() = 0;
// Predicates:
// True if the node is an instruction block.
virtual bool is_block() { return false; }
// Support for fast-compilation mode. Emit the instructions or control
// flow represented by the node.
virtual void Compile(MacroAssembler* masm) = 0;
#ifdef DEBUG
int number() {
if (number_ == -1) number_ = CfgGlobals::current()->next_node_number();
return number_;
}
virtual void Print() = 0;
#endif
protected:
bool is_marked_;
#ifdef DEBUG
int number_;
#endif
};
// A block is a single-entry, single-exit block of instructions.
class InstructionBlock : public CfgNode {
public:
InstructionBlock() : successor_(NULL), instructions_(4) {}
virtual ~InstructionBlock() {}
void Unmark();
// Cast accessor.
static InstructionBlock* cast(CfgNode* node) {
ASSERT(node->is_block());
return reinterpret_cast<InstructionBlock*>(node);
}
bool is_block() { return true; }
// Accessors.
CfgNode* successor() { return successor_; }
void set_successor(CfgNode* succ) {
ASSERT(successor_ == NULL);
successor_ = succ;
}
ZoneList<Instruction*>* instructions() { return &instructions_; }
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
// Add an instruction to the end of the block.
void Append(Instruction* instr) { instructions_.Add(instr); }
#ifdef DEBUG
void Print();
#endif
private:
CfgNode* successor_;
ZoneList<Instruction*> instructions_;
};
// An entry node (one per function).
class EntryNode : public CfgNode {
public:
explicit EntryNode(InstructionBlock* succ) : successor_(succ) {}
virtual ~EntryNode() {}
void Unmark();
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
#ifdef DEBUG
void Print();
#endif
private:
InstructionBlock* successor_;
};
// An exit node (one per function).
class ExitNode : public CfgNode {
public:
ExitNode() {}
virtual ~ExitNode() {}
void Unmark();
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
#ifdef DEBUG
void Print();
#endif
};
// A CFG consists of a linked structure of nodes. Nodes are linked by
// pointing to their successors, always beginning with a (single) entry node
// (not necessarily of type EntryNode). If it is still possible to add
// nodes to the end of the graph (i.e., there is a (single) path that does
// not end with the global exit node), then the CFG has an exit node as
// well.
//
// The empty CFG is represented by a NULL entry and a NULL exit.
//
// We use the term 'open fragment' to mean a CFG whose entry and exits are
// both instruction blocks. It is always possible to add instructions and
// nodes to the beginning or end of an open fragment.
//
// We use the term 'closed fragment' to mean a CFG whose entry is an
// instruction block and whose exit is NULL (all paths go to the global
// exit).
//
// We use the term 'fragment' to refer to a CFG that is known to be an open
// or closed fragment.
class Cfg : public ZoneObject {
public:
// Create an empty CFG fragment.
Cfg() : entry_(NULL), exit_(NULL) {}
// Build the CFG for a function. The returned CFG begins with an
// EntryNode and all paths end with the ExitNode.
static Cfg* Build();
// The entry and exit nodes of the CFG (not necessarily EntryNode and
// ExitNode).
CfgNode* entry() { return entry_; }
CfgNode* exit() { return exit_; }
// True if the CFG has no nodes.
bool is_empty() { return entry_ == NULL; }
// True if the CFG has an available exit node (i.e., it can be appended or
// concatenated to).
bool has_exit() { return exit_ != NULL; }
// Add an EntryNode to a CFG fragment. It is no longer a fragment
// (instructions can no longer be prepended).
void PrependEntryNode();
// Append an instruction to the end of an open fragment.
void Append(Instruction* instr);
// Appends a return instruction to the end of an open fragment and make
// it a closed fragment (the exit's successor becomes global exit node).
void AppendReturnInstruction(Value* value);
// Glue an other CFG fragment to the end of this (open) fragment.
void Concatenate(Cfg* other);
// Support for compilation. Compile the entire CFG.
Handle<Code> Compile(Handle<Script> script);
#ifdef DEBUG
// Support for printing.
void Print();
#endif
private:
// Entry and exit nodes.
CfgNode* entry_;
CfgNode* exit_;
};
// An implementation of a set of locations (currently slot locations), most
// of the operations are destructive.
class LocationSet BASE_EMBEDDED {
public:
// Construct an empty location set.
LocationSet() : parameters_(0), locals_(0) {}
// Raw accessors.
uintptr_t parameters() { return parameters_; }
uintptr_t locals() { return locals_; }
// Make this the empty set.
void Empty() {
parameters_ = locals_ = 0;
}
// Insert an element.
void AddElement(SlotLocation* location) {
if (location->type() == Slot::PARAMETER) {
// Parameter indexes begin with -1 ('this').
ASSERT(location->index() < kBitsPerPointer - 1);
parameters_ |= (1 << (location->index() + 1));
} else {
ASSERT(location->type() == Slot::LOCAL);
ASSERT(location->index() < kBitsPerPointer);
locals_ |= (1 << location->index());
}
}
// (Destructively) compute the union with another set.
void Union(LocationSet* other) {
parameters_ |= other->parameters();
locals_ |= other->locals();
}
bool Contains(SlotLocation* location) {
if (location->type() == Slot::PARAMETER) {
ASSERT(location->index() < kBitsPerPointer - 1);
return (parameters_ & (1 << (location->index() + 1)));
} else {
ASSERT(location->type() == Slot::LOCAL);
ASSERT(location->index() < kBitsPerPointer);
return (locals_ & (1 << location->index()));
}
}
private:
uintptr_t parameters_;
uintptr_t locals_;
};
// An ExpressionCfgBuilder traverses an expression and returns an open CFG
// fragment (currently a possibly empty list of instructions represented by
// a singleton instruction block) and the expression's value.
//
// Failure to build the CFG is indicated by a NULL CFG.
class ExpressionCfgBuilder : public AstVisitor {
public:
ExpressionCfgBuilder() : destination_(NULL), value_(NULL), graph_(NULL) {}
// Result accessors.
Value* value() { return value_; }
Cfg* graph() { return graph_; }
LocationSet* assigned_vars() { return &assigned_vars_; }
// Build the cfg for an expression and remember its value. The
// destination is a 'hint' where the value should go which may be ignored.
// NULL is used to indicate no preference.
//
// Concretely, if the expression needs to generate a temporary for its
// value, it should use the passed destination or generate one if NULL.
void Build(Expression* expr, Location* destination) {
value_ = NULL;
graph_ = new Cfg();
assigned_vars_.Empty();
destination_ = destination;
Visit(expr);
}
// AST node visitors.
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
private:
// State for the visitor. Input parameter:
Location* destination_;
// Output parameters:
Value* value_;
Cfg* graph_;
LocationSet assigned_vars_;
};
// A StatementCfgBuilder maintains a CFG fragment accumulator. When it
// visits a statement, it concatenates the CFG for the statement to the end
// of the accumulator.
class StatementCfgBuilder : public AstVisitor {
public:
StatementCfgBuilder() : graph_(new Cfg()) {}
Cfg* graph() { return graph_; }
void VisitStatements(ZoneList<Statement*>* stmts);
// AST node visitors.
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
private:
// State for the visitor. Input/output parameter:
Cfg* graph_;
};
} } // namespace v8::internal
#endif // V8_CFG_H_