| Index: src/cfg.h
|
| ===================================================================
|
| --- src/cfg.h (revision 2620)
|
| +++ src/cfg.h (working copy)
|
| @@ -34,7 +34,48 @@
|
| 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> ::=
|
| +// BinaryOpInstr <Location> Token::Value <Value> <Value>
|
| +// | ReturnInstr Effect <Value>
|
| +//
|
| +// Values are trivial expressions:
|
| +//
|
| +// <Value> ::= Constant | <Location>
|
| +//
|
| +// Locations are storable values ('lvalues'). They can be slots,
|
| +// compiler-generated temporaries, or the special location 'Effect'
|
| +// indicating that no value is needed.
|
| +//
|
| +// <Location> ::=
|
| +// SlotLocation Slot::Type <Index>
|
| +// | TempLocation
|
| +// | Effect
|
| +
|
| +
|
| +// 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 {
|
| @@ -48,41 +89,59 @@
|
| 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 effect location.
|
| + Location* effect_location() { return effect_; }
|
| +
|
| #ifdef DEBUG
|
| - int next_number() { return node_counter_++; }
|
| + int next_node_number() { return node_counter_++; }
|
| + int next_temp_number() { return temp_counter_++; }
|
| #endif
|
|
|
| private:
|
| static CfgGlobals* top_;
|
| -
|
| - // Function literal currently compiling.
|
| FunctionLiteral* global_fun_;
|
| -
|
| - // Shared global exit node for all returns from the same function.
|
| ExitNode* global_exit_;
|
| + Location* effect_;
|
|
|
| #ifdef DEBUG
|
| - // Used to number nodes when printing.
|
| + // Used to number nodes and temporaries when printing.
|
| int node_counter_;
|
| + int temp_counter_;
|
| #endif
|
|
|
| CfgGlobals* previous_;
|
| };
|
|
|
|
|
| -// Values appear in instructions. They represent trivial source
|
| -// expressions: ones with no side effects and that do not require code to be
|
| -// generated.
|
| +// 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() {}
|
|
|
| - virtual void ToRegister(MacroAssembler* masm, Register reg) = 0;
|
| + // Predicates:
|
|
|
| + // True if the value is a temporary allocated to the stack in
|
| + // fast-compilation mode.
|
| + virtual bool is_on_stack() { return false; }
|
| +
|
| + // True if the value is a compiler-generated temporary location.
|
| + virtual bool is_temporary() { 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;
|
| +
|
| #ifdef DEBUG
|
| virtual void Print() = 0;
|
| #endif
|
| @@ -96,7 +155,9 @@
|
|
|
| virtual ~Constant() {}
|
|
|
| - void ToRegister(MacroAssembler* masm, Register reg);
|
| + // Support for fast-compilation mode.
|
| + void Get(MacroAssembler* masm, Register reg);
|
| + void Push(MacroAssembler* masm);
|
|
|
| #ifdef DEBUG
|
| void Print();
|
| @@ -112,22 +173,65 @@
|
| public:
|
| virtual ~Location() {}
|
|
|
| - virtual void ToRegister(MacroAssembler* masm, Register reg) = 0;
|
| + // Static factory function returning the singleton effect location.
|
| + static Location* Effect() {
|
| + return CfgGlobals::current()->effect_location();
|
| + }
|
|
|
| + // 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;
|
| +
|
| #ifdef DEBUG
|
| virtual void Print() = 0;
|
| #endif
|
| };
|
|
|
|
|
| +// Effect is a special (singleton) location that indicates the value of a
|
| +// computation is not needed (though its side effects are).
|
| +class Effect : public Location {
|
| + public:
|
| + // We should not try to emit code to read or write to Effect.
|
| + void Get(MacroAssembler* masm, Register reg) { UNREACHABLE(); }
|
| + void Set(MacroAssembler* masm, Register reg) { UNREACHABLE(); }
|
| + void Push(MacroAssembler* masm) { UNREACHABLE(); }
|
| +
|
| +#ifdef DEBUG
|
| + void Print();
|
| +#endif
|
| +
|
| + private:
|
| + Effect() {}
|
| +
|
| + 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) {}
|
|
|
| - void ToRegister(MacroAssembler* masm, Register reg);
|
| + // Accessors.
|
| + Slot::Type type() { return type_; }
|
| + int index() { return index_; }
|
|
|
| + // Support for fast-compilation mode.
|
| + void Get(MacroAssembler* masm, Register reg);
|
| + void Set(MacroAssembler* masm, Register reg);
|
| + void Push(MacroAssembler* masm);
|
| +
|
| #ifdef DEBUG
|
| void Print();
|
| #endif
|
| @@ -138,29 +242,136 @@
|
| };
|
|
|
|
|
| +// 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 {
|
| + NOWHERE, // Not yet allocated.
|
| + ACCUMULATOR, // Allocated to the dedicated accumulator register.
|
| + STACK // " " " " stack.
|
| + };
|
| +
|
| + TempLocation() : where_(NOWHERE) {
|
| +#ifdef DEBUG
|
| + number_ = -1;
|
| +#endif
|
| + }
|
| +
|
| + // Cast accessor.
|
| + static TempLocation* cast(Location* loc) {
|
| + ASSERT(loc->is_temporary());
|
| + return reinterpret_cast<TempLocation*>(loc);
|
| + }
|
| +
|
| + // Accessors.
|
| + Where where() { return where_; }
|
| + void set_where(Where where) { 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);
|
| +
|
| +#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:
|
| + // Every instruction has a location where its result is stored (which may
|
| + // be Effect).
|
| + Instruction(Location* loc) : loc_(loc) {}
|
| +
|
| virtual ~Instruction() {}
|
|
|
| + // Accessors.
|
| + Location* location() { return loc_; }
|
| +
|
| + // 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:
|
| + Location* loc_;
|
| };
|
|
|
|
|
| -// Return a value.
|
| +// Perform a (non-short-circuited) binary operation on a pair of values,
|
| +// leaving the result in a location.
|
| +class BinaryOpInstr : public Instruction {
|
| + public:
|
| + BinaryOpInstr(Location* loc, Token::Value op, Value* val0, Value* val1)
|
| + : Instruction(loc), op_(op), val0_(val0), val1_(val1) {
|
| + }
|
| +
|
| + // Support for fast-compilation mode.
|
| + void Compile(MacroAssembler* masm);
|
| + void FastAllocate(TempLocation* temp);
|
| +
|
| +#ifdef DEBUG
|
| + void Print();
|
| +#endif
|
| +
|
| + private:
|
| + Token::Value op_;
|
| + Value* val0_;
|
| + Value* val1_;
|
| +};
|
| +
|
| +
|
| +// 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 Instruction {
|
| public:
|
| - explicit ReturnInstr(Value* value) : value_(value) {}
|
| + // Location is always Effect.
|
| + explicit ReturnInstr(Value* value)
|
| + : Instruction(CfgGlobals::current()->effect_location()),
|
| + value_(value) {
|
| + }
|
|
|
| virtual ~ReturnInstr() {}
|
|
|
| + // Support for fast-compilation mode.
|
| void Compile(MacroAssembler* masm);
|
| + void FastAllocate(TempLocation* temp);
|
|
|
| #ifdef DEBUG
|
| void Print();
|
| @@ -171,9 +382,7 @@
|
| };
|
|
|
|
|
| -// Nodes make up control-flow graphs. They contain single-entry,
|
| -// single-exit blocks of instructions and administrative nodes making up the
|
| -// graph structure.
|
| +// Nodes make up control-flow graphs.
|
| class CfgNode : public ZoneObject {
|
| public:
|
| CfgNode() : is_marked_(false) {
|
| @@ -184,17 +393,26 @@
|
|
|
| virtual ~CfgNode() {}
|
|
|
| + // Because CFGs contain cycles, nodes support marking during traversal
|
| + // (e.g., for printing or compilation). The traversal functions mark
|
| + // unmarked nodes and bailout 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; }
|
|
|
| - virtual void Unmark() = 0;
|
| -
|
| + // 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_number();
|
| + if (number_ == -1) number_ = CfgGlobals::current()->next_node_number();
|
| return number_;
|
| }
|
|
|
| @@ -217,22 +435,30 @@
|
|
|
| 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;
|
| }
|
|
|
| - bool is_block() { return true; }
|
| + ZoneList<Instruction*>* instructions() { return &instructions_; }
|
|
|
| - void Unmark();
|
| -
|
| + // 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
|
| @@ -245,9 +471,7 @@
|
| };
|
|
|
|
|
| -// The CFG for a function has a distinguished entry node. It has no
|
| -// predecessors and a single successor. The successor is the block
|
| -// containing the function's first instruction.
|
| +// An entry node (one per function).
|
| class EntryNode : public CfgNode {
|
| public:
|
| explicit EntryNode(InstructionBlock* succ) : successor_(succ) {}
|
| @@ -256,6 +480,7 @@
|
|
|
| void Unmark();
|
|
|
| + // Support for fast-compilation mode.
|
| void Compile(MacroAssembler* masm);
|
|
|
| #ifdef DEBUG
|
| @@ -267,9 +492,7 @@
|
| };
|
|
|
|
|
| -// The CFG for a function has a distinguished exit node. It has no
|
| -// successor and arbitrarily many predecessors. The predecessors are all
|
| -// the blocks returning from the function.
|
| +// An exit node (one per function).
|
| class ExitNode : public CfgNode {
|
| public:
|
| ExitNode() {}
|
| @@ -278,6 +501,7 @@
|
|
|
| void Unmark();
|
|
|
| + // Support for fast-compilation mode.
|
| void Compile(MacroAssembler* masm);
|
|
|
| #ifdef DEBUG
|
| @@ -286,28 +510,36 @@
|
| };
|
|
|
|
|
| -// A CFG consists of a linked structure of nodes. It has a single entry
|
| -// node and optionally an exit node. There is a distinguished global exit
|
| -// node that is used as the successor of all blocks that return from the
|
| -// function.
|
| +// 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.
|
| //
|
| -// Fragments of control-flow graphs, produced when traversing the statements
|
| -// and expressions in the source AST, are represented by the same class.
|
| -// They have instruction blocks as both their entry and exit (if there is
|
| -// one). Instructions can always be prepended or appended to fragments, and
|
| -// fragments can always be concatenated.
|
| +// The empty CFG is represented by a NULL entry and a NULL exit.
|
| //
|
| -// A singleton CFG fragment (i.e., with only one node) has the same node as
|
| -// both entry and exit (if the exit is available).
|
| +// 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 a singleton CFG fragment.
|
| - explicit Cfg(InstructionBlock* block) : entry_(block), exit_(block) {}
|
| + // Create an empty CFG fragment.
|
| + Cfg() : entry_(NULL), exit_(NULL) {}
|
|
|
| - // Build the CFG for a function.
|
| + // 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.
|
| + // The entry and exit nodes of the CFG (not necessarily EntryNode and
|
| + // ExitNode).
|
| CfgNode* entry() { return entry_; }
|
| CfgNode* exit() { return exit_; }
|
|
|
| @@ -318,18 +550,21 @@
|
| // concatenated to).
|
| bool has_exit() { return exit_ != NULL; }
|
|
|
| - // Add an entry node to a CFG fragment. It is no longer a fragment
|
| - // (instructions cannot be prepended).
|
| + // 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 a CFG fragment. Assumes it has an
|
| - // available exit.
|
| + // Append an instruction to the end of an open fragment.
|
| void Append(Instruction* instr);
|
|
|
| - // Appends a return instruction to the end of a CFG fragment. It no
|
| - // longer has an available exit node.
|
| + // 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
|
| @@ -344,13 +579,25 @@
|
| };
|
|
|
|
|
| -// An Expression Builder traverses a trivial expression and returns a value.
|
| +// An ExpressionBuilder 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 is to build the CFG is indicated by a NULL CFG.
|
| class ExpressionBuilder : public AstVisitor {
|
| public:
|
| - ExpressionBuilder() : value_(new Constant(Handle<Object>::null())) {}
|
| + ExpressionBuilder() : value_(NULL), cfg_(NULL) {}
|
|
|
| + // Result accessors.
|
| Value* value() { return value_; }
|
| + Cfg* cfg() { return cfg_; }
|
|
|
| + void Build(Expression* expr) {
|
| + value_ = NULL;
|
| + cfg_ = new Cfg();
|
| + Visit(expr);
|
| + }
|
| +
|
| // AST node visitors.
|
| #define DECLARE_VISIT(type) void Visit##type(type* node);
|
| AST_NODE_LIST(DECLARE_VISIT)
|
| @@ -358,13 +605,16 @@
|
|
|
| private:
|
| Value* value_;
|
| + Cfg* cfg_;
|
| };
|
|
|
|
|
| -// A StatementBuilder traverses a statement and returns a CFG.
|
| +// A StatementBuilder maintains a CFG fragment accumulator. When it visits
|
| +// a statement, it concatenates the CFG for the statement to the end of the
|
| +// accumulator.
|
| class StatementBuilder : public AstVisitor {
|
| public:
|
| - StatementBuilder() : cfg_(new Cfg(new InstructionBlock())) {}
|
| + StatementBuilder() : cfg_(new Cfg()) {}
|
|
|
| Cfg* cfg() { return cfg_; }
|
|
|
|
|