// 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_