// $Id: MethodDescriptor.java,v 1.8 1999/12/10 10:32:49 mpp Exp $

package IR2;

import java.util.*;

public class MethodDescriptor extends Descriptor {
  private ParamScalarVarDescriptor parameters[];
  private Block method;

  private CFG cfg;
  private int stack_size;


  public MethodDescriptor(String name, int return_type,
                          ParamScalarVarDescriptor p[], int line) {
    super(name, return_type, line); parameters = p; method = null;
  }

  /* This is necessary for the parser to implement recursion. */
  public void set_code_block(Block b) throws SemanticTypeException {
    method = b;
    typecheck_returns(method);
  }


  /* Get rid of this if possible. */
  public ParamScalarVarDescriptor[] get_parameters() { return parameters; }


  /* Semantic rules 7 and 8. */
  private void typecheck_returns(Block b) throws SemanticTypeException {
    HighInstruction insns[] = b.get_instructions();
    for (int i = 0; i < insns.length; i++) {
      if (insns[i] instanceof ReturnInstruction) {
        RValue expr = ((ReturnInstruction)insns[i]).get_value();
        if (expr != null && expr.get_type() != get_type())
          throw new SemanticTypeException("invalid type of return " +
                                          "from method " + get_name());
      } else {
        for (Enumeration e=insns[i].subblocks(); e.hasMoreElements(); )
          typecheck_returns((Block) e.nextElement());
      }
    }
  }  

  /* Semantic rule 5. */
  public void typecheck_args(RValue args[]) throws SemanticTypeException {
    if (parameters.length != args.length)
      throw new SemanticTypeException("method " + name + " expects " + 
                                      parameters.length + " arguments");
    for (int i = 0; i < args.length; i++) {
      if (parameters[i] != null && args[i] != null && 
          parameters[i].get_type() != args[i].get_type())
        throw new SemanticTypeException
          ("incorrect type passed to argument "
           + (i+1) + " of method " + name
           + ": expected " + Typed.typenames[parameters[i].get_type()]
           + ", but got " + Typed.typenames[args[i].get_type()]);
    }
  }

  /* Accessor routine get_symbol_table, used in TempVarDescriptor constructor.
   * Maybe this can be made to go away. */
  SymbolTable get_symbol_table() { return method.get_symbol_table(); }


  /* Procedure to place locals in the stack frame. */
  void allocate_locations(Codegen c) {
    for (int i = 0; i < parameters.length; i++) {
      parameters[i].set_parameter_num(i);
    }

    //New allocator
    ReachingDefinitions rd = new ReachingDefinitions(cfg.get_enter_block(), 
                                                     cfg.get_exit_block());
    rd.computeFixedPoint();
    InterferenceGraph graph = new InterferenceGraph(rd.get_chains(), this);
    graph.build_graph();
    graph.color(c);

    /*
    // Allocate the first few locals to registers.
    int regnum = 0;
    DelocalizedRegister[] registers = c.allocatable_registers();
    Enumeration e1 = get_symbol_table().elements();
    while (regnum < registers.length && e1.hasMoreElements()) {
      Descriptor d = (Descriptor) e1.nextElement();
      if (d instanceof LocalScalarVarDescriptor) {
        ((LocalScalarVarDescriptor) d).set_proxy(registers[regnum++]);
      }
    }
    */

    // Allocate the remaining locals to stack slots.
    stack_size = 0;
    Enumeration e = get_symbol_table().elements();
    while (e.hasMoreElements()) {
      Descriptor d = (Descriptor) e.nextElement();
      /* I think instanceof is probably OK here because I'm using it
       * to sift out the param descriptors.  Better approaches welcome. */
      if (d instanceof LocalScalarVarDescriptor) {
        LocalScalarVarDescriptor ld = (LocalScalarVarDescriptor) d;
        //if (ld.rproxy() != ld) {
          stack_size = ld.set_offset(stack_size);
        //}
      } else if (d instanceof ParamScalarVarDescriptor) {
        /* Nothing --- it already knows where it is. */
      }

      if (stack_size < 0) {
        /* Data area overflow: there's over 2 gigawords!  This can't
         * happen in a valid Decaf program.  Ignore.  */
      }
    }
  }

  // Returns stack size in bytes
  public int stack_size() {
    return stack_size;
  }

  // destructure a Method to its low IR equivalent
  public LowInstruction destructure(LowInstruction next) {
    final LowInstruction exit_node = new ExitInstruction(next);
    LowInstruction last_node;
    if (get_type() == Typed.VOID)
      last_node = exit_node;
    else
      last_node = new ExceptionInstruction("fell_off_end_runtime_exception");

    LowInstruction enter_node
      = new EnterInstruction(method.destructure(last_node));

    // Now fixup return instructions' next field
    exit_node.add_label();
    Traversal return_target_traversal = new Traversal() {
      public boolean visit(Traversable t) {
        if (t instanceof ReturnInstruction)
          ((ReturnInstruction) t).next = exit_node;
        return true;
      }
    };
    return_target_traversal.traverse(enter_node);

    return enter_node;
  }

  // Delocalized code generation
  public void generate_delocalized_code(final Codegen c) {
    // Destructure, output a label, and then generate_code for the lowIR
    // sequence that corresponds to the body of the method

    cfg = new CFG();
    LowInstruction enter_node = this.destructure(null);
    enter_node.set_label("_" + name);

    Traversal algebraic_optimization_traversal = new Traversal() {
      public boolean visit(Traversable t) {
        ((LowInstruction) t).algebraic_simplify();
        return true;
      }
    };
    algebraic_optimization_traversal.traverse(enter_node);

    // Traverse entire IR graph producing the Delocalized CFG
    Traversal generate_asm_traversal = new Traversal() {
      DelocalizedInstruction prev = null;
      Stack saves = new Stack();

      // Need for cbrs so we can remember what "prev" should be
      public void save() { saves.push(prev); }
      public void restore() { 
	  prev = (DelocalizedInstruction)saves.pop(); 
	  cfg.set_last(prev); 
      }

      // Already seen? Link to the DelocalizedInstruction produced last time
      // node was visited
      public void already_seen(Traversable t) { 
	prev = cfg.addElement(((LowInstruction)t).get_deloc_instr(), prev);
      }
	
      public boolean visit(Traversable t) {
        LowInstruction li = (LowInstruction)t;

        // If this node has a label, toss that in there as well.
        if (li.get_label() != null) 
	   cfg.set_next_label_source(li);
	
        prev = li.do_asm(c, MethodDescriptor.this, cfg, prev);
        return true;
      }
    };
    generate_asm_traversal.traverse(enter_node);

    // Generate Basicblocks, so FixedPointAlgorithms will work
    cfg.generate_basicblocks();
  }

  // Optimize 
  public void optimize(boolean cp, boolean dce) {
    if (cp)
      (new CopyPropagation(cfg.get_enter_block(), cfg.get_exit_block()))
        .computeFixedPoint();

    if (dce)
      (new DeadCodeElimination(cfg.get_enter_block(), cfg.get_exit_block()))
        .computeFixedPoint();
  }

  /* This method outputs the final code stream. */
  public void generate_cooked_code(Codegen c) {
    Enumeration e = cfg.linearize().elements();
    while (e.hasMoreElements()) {
      DelocalizedInstruction insn = (DelocalizedInstruction) e.nextElement();
      insn.generate_code(c);
    }
  }
 
  /* Walkable implementation. */
  public String node_name() {
    return "method_descriptor";
  }
  public Enumeration neighbors() {
    return new ShortEnumeration(method);
  }
}
