summaryrefslogblamecommitdiff
path: root/sources/scala/tools/nsc/backend/icode/Opcodes.scala
blob: 1efa4422cf9e136a7524abf135499f8b605f1249 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                      








                                     
                             



















                                              







                                                                       
                                
                                                         














































                                                                                               
                                                                    
                                                                
                                                                         























                                                                                                       

                                                                                   




                                










                                                                



                                                         
                                                                     
                                                                
                                                                          


































                                                                                                        
                                                                              




















                                                       






                                                                                    

                                                                          













                                                  

                                                                          

                     
                                                      
                                                                
                                                                   









                                                        
                                                                    










                                                                          
                                                               










                                                                     
                                                              









                                                                    



                                                                                       
       
                                                                                            

























                                                                            


                                                        
 
                                                                

                                                                          
 


                                





                                                                                    



                                                           
                                                                

                                                                                          

































                                                                
                                                         










                                                                
                                                        


































                                                                
















                                              
                                  




                                                           






                                                        


                                            

                                                                







                                                               
/* NSC -- new scala compiler
 * Copyright 2005 LAMP/EPFL
 * @author  Martin Odersky
 */

// $Id$


package scala.tools.nsc.backend.icode;

import scala.tools.nsc.ast._;

/*
  A pattern match

  case THIS(clasz) =>
  case CONSTANT(const) =>
  case LOAD_ARRAY_ITEM(kind) =>
  case LOAD_LOCAL(local, isArg) =>
  case LOAD_FIELD(field, isStatic) =>
  case LOAD_MODULE(module) =>
  case STORE_ARRAY_ITEM(kind) =>
  case STORE_LOCAL(local, isArg) =>
  case STORE_FIELD(field, isStatic) =>
  case CALL_PRIMITIVE(primitive) =>
  case CALL_METHOD(method, style) =>
  case NEW(clasz) =>
  case CREATE_ARRAY(elem) =>
  case IS_INSTANCE(tpe) =>
  case CHECK_CAST(tpe) =>
  case SWITCH(tags, labels) =>
  case JUMP(where) =>
  case CJUMP(success, failure, cond) =>
  case CZJUMP(success, failure, cond, kind) =>
  case RETURN() =>
  case THROW() =>
  case DROP(kind) =>
  case DUP(kind) =>
  case MONITOR_ENTER() =>
  case MONITOR_EXIT() =>
*/


/**
 * The ICode intermediate representation. It is a stack-based
 * representation, very close to the JVM and .NET. It uses the
 * erased types of Scala and references Symbols to refer named entities
 * in the source files.
 */
abstract class Opcodes: ICodes {
  import global.{Symbol, NoSymbol, Type, Name, Constant};

  /** This class represents an instruction of the intermediate code.
   *  Each case subclass will represent a specific operation.
   */
  abstract class Instruction {

    /** This abstract method returns the number of used elements on the stack */
    def consumed : Int = 0;

    /** This abstract method returns the number of produced elements on the stack */
    def produced : Int = 0;

    /** This method returns the difference of size of the stack when the instruction is used */
    def difference = produced-consumed;
  }

  object opcodes {

    /** Loads the "this" references on top of the stack.
     * Stack: ...
     *    ->: ...:ref
     */
    case class THIS(clasz: Symbol) extends Instruction {
      /** Returns a string representation of this constant */
      override def toString(): String = "THIS";

      override def consumed = 0;
      override def produced = 1;
    }

    /** Loads a constant on the stack.
     * Stack: ...
     *    ->: ...:constant
     */
    case class CONSTANT(constant: Constant) extends Instruction{
      /** Returns a string representation of this constant */
      override def toString(): String = "CONSTANT ("+constant.toString()+")";

      override def consumed = 0;
      override def produced = 1;
    }

    /** Loads an element of an array. The array and the index should
     * be on top of the stack.
     * Stack: ...:array[a](Ref):index(Int)
     *    ->: ...:element(a)
     */
    case class LOAD_ARRAY_ITEM(kind: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String = "LOAD_ARRAY_ITEM (" + kind + ")";

      override def consumed = 2;
      override def produced = 1;
    }

    /** Load a local variable on the stack. It can be a method argument.
     * Stack: ...
     *    ->: ...:value
     */
    case class LOAD_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String = "LOAD_LOCAL "+local.toString(); //+isArgument?" (argument)":"";

      override def consumed = 0;
      override def produced = 1;
    }

    /** Load a field on the stack. The object to which it refers should be
     * on the stack.
     * Stack: ...:ref
     *    ->: ...:value
     */
    case class LOAD_FIELD(field: Symbol, isStatic: boolean) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String =
        "LOAD_FIELD " + (if (isStatic) field.fullNameString else field.toString());

      override def consumed = 1;
      override def produced = 1;
    }

    case class LOAD_MODULE(module: Symbol) extends Instruction {
      assert(module != NoSymbol,
             "Invalid module symbol");
      /** Returns a string representation of this instruction */
      override def toString(): String =
        "LOAD_MODULE " + module.toString();

      override def consumed = 0;
      override def produced = 1;
    }

    /** Store a value into an array at a specified index.
     * Stack: ...:array[a](Ref):index(Int):value(a)
     *    ->: ...
     */
    case class STORE_ARRAY_ITEM(kind: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String = "STORE_ARRAY_ITEM (" + kind + ")";

      override def consumed = 3;
      override def produced = 0;
    }

    /** Store a value into a local variable. It can be an argument.
     * Stack: ...:value
     *    ->: ...
     */
    case class STORE_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String = "STORE_LOCAL "+local.toString(); //+isArgument?" (argument)":"";

      override def consumed = 1;
      override def produced = 0;
    }

    /** Store a value into a field.
     * Stack: ...:ref:value
     *    ->: ...
     */
    case class STORE_FIELD(field: Symbol, isStatic: boolean) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String = "STORE_FIELD "+field.toString(); //+isStatic?" (static)":"";

      override def consumed = 2;
      override def produced = 0;
    }

    /** Call a primitive function.
     * Stack: ...:arg1:arg2:...:argn
     *    ->: ...:result
     */
    case class CALL_PRIMITIVE(primitive: Primitive) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="CALL_PRIMITIVE "+primitive.toString();

      override def consumed = primitive match {
        case (Negation(_)) => 1;
        case (Test(_,_,true)) => 1;
        case (Test(_,_,false)) => 2;
        case (Comparison(_,_)) => 2;
        case (Arithmetic(_,_)) => 2;
        case (Logical(_,_)) => 2;
        case (Shift(_,_)) => 2;
        case (Conversion(_,_)) => 1;
        case (ArrayLength(_)) => 1;
        case (StringConcat(_,_)) => 2;
      }
      override def produced = 1;
    }

    /** This class represents a CALL_METHOD instruction
     * STYLE: dynamic / static(StaticInstance)
     * Stack: ...:ref:arg1:arg2:...:argn
     *    ->: ...:result
     *
     * STYLE: static(StaticClass)
     * Stack: ...:arg1:arg2:...:argn
     *    ->: ...:result
     *
     */
    case class CALL_METHOD(method: Symbol, style: InvokeStyle) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String =
        "CALL_METHOD " + method.fullNameString +" ("+style.toString()+")";

      override def consumed = {
        var result = method.tpe.paramTypes.length;
        result = result + (style match {
          case Dynamic => 1
          case Static(true) => 1
          case _ => 0
        });

        result;
      }
      override def produced = 1;
    }

    /** Create a new instance of a class through the specified constructor
     * Stack: ...:arg1:arg2:...:argn
     *    ->: ...:ref
     */
    case class NEW(ctor: Symbol) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String = "NEW "+ctor.fullNameString;

      override def consumed = 0;
      override def produced = 1;
    }


    /** This class represents a CREATE_ARRAY instruction
     * Stack: ...:size(int)
     *    ->: ...:arrayref
     */
    case class CREATE_ARRAY(element: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="CREATE_ARRAY "+element.toString();

      override def consumed = 1;
      override def produced = 1;
    }

    /** This class represents a IS_INSTANCE instruction
     * Stack: ...:ref
     *    ->: ...:result(boolean)
     */
    case class IS_INSTANCE(typ: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="IS_INSTANCE "+typ.toString();

      override def consumed = 1;
      override def produced = 1;
    }

    /** This class represents a CHECK_CAST instruction
     * Stack: ...:ref(oldtype)
     *    ->: ...:ref(typ <=: oldtype)
     */
    case class CHECK_CAST(typ: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="CHECK_CAST "+typ.toString();

      override def consumed = 1;
      override def produced = 1;
    }

    /** This class represents a SWITCH instruction
     * Stack: ...:index(int)
     *    ->: ...:
     *
     * The tags array contains one entry per label, each entry consisting of
     * an array of ints, any of which will trigger the jump to the corresponding label.
     * labels should contain an extra label, which is the 'default' jump.
     */
    case class SWITCH(tags: List[List[Int]], labels: List[BasicBlock]) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="SWITCH ...";

      override def consumed = 1;
      override def produced = 0;
    }

    /** This class represents a JUMP instruction
     * Stack: ...
     *    ->: ...
     */
    case class JUMP(where: BasicBlock) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="JUMP "+where.label;

      override def consumed = 0;
      override def produced = 0;
    }

    /** This class represents a CJUMP instruction
     * It compares the two values on the stack with the 'cond' test operator
     * Stack: ...:value1:value2
     *    ->: ...
     */
    case class CJUMP(successBlock: BasicBlock,
		     failureBlock: BasicBlock,
		     cond: TestOp,
                     kind: TypeKind) extends Instruction
    {

      /** Returns a string representation of this instruction */
      override def toString(): String ="CJUMP (" + kind + ")" +
        cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label;

      override def consumed = 2;
      override def produced = 0;
    }

    /** This class represents a CZJUMP instruction
     * It compares the one value on the stack and zero with the 'cond' test operator
     * Stack: ...:value:
     *    ->: ...
     */
    case class CZJUMP(successBlock: BasicBlock,
                      failureBlock: BasicBlock,
                      cond: TestOp,
                      kind: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="CZJUMP )" + kind + ")" +
                        cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label;

      override def consumed = 1;
      override def produced = 0;
    }


    /** This class represents a RETURN instruction
     * Stack: ...
     *    ->: ...
     */
    case class RETURN() extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="RETURN";

      override def consumed = 0;
      override def produced = 0;
    }

    /** This class represents a THROW instruction
     * Stack: ...:Throwable(Ref)
     *    ->: ...:
     */
    case class THROW() extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="THROW";

      override def consumed = 1;
      override def produced = 0;
    }

    /** This class represents a DROP instruction
     * Stack: ...:something
     *    ->: ...
     */
    case class DROP (typ: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="DROP "+typ.toString();

      override def consumed = 1;
      override def produced = 0;
    }

    /** This class represents a DUP instruction
     * Stack: ...:something
     *    ->: ...:something:something
     */
    case class DUP (typ: TypeKind) extends Instruction {
      /** Returns a string representation of this instruction */
      override def toString(): String ="DUP";

      override def consumed = 1;
      override def produced = 2;
    }

    /** This class represents a MONITOR_ENTER instruction
     * Stack: ...:object(ref)
     *    ->: ...:
     */
    case class MONITOR_ENTER() extends Instruction {

      /** Returns a string representation of this instruction */
      override def toString(): String ="MONITOR_ENTER";

      override def consumed = 1;
      override def produced = 0;
    }

    /** This class represents a MONITOR_EXIT instruction
     * Stack: ...:object(ref)
     *    ->: ...:
     */
    case class MONITOR_EXIT() extends Instruction {

      /** Returns a string representation of this instruction */
      override def toString(): String ="MONITOR_EXIT";

      override def consumed = 1;
      override def produced = 0;
    }

    /** This class represents a method invocation style. */
    trait InvokeStyle {

      /** Is this a dynamic method call? */
      def isDynamic: Boolean = this match {
        case Dynamic =>  true;
        case _       => false;
      }

      /** Is this a static method call? */
      def isStatic: Boolean = this match {
        case Static(_) => true;
        case _ =>  false;
      }

      /** Is this an instance method call? */
      def hasInstance: Boolean = this match {
        case Dynamic => true;
        case Static(onInstance) => onInstance;
        case SuperCall(_) => true;
        case _ => false;
      }

      /** Returns a string representation of this style. */
      override def toString(): String = this match {
        case Dynamic =>  "dynamic";
        case Static(false) => "static-class";
        case Static(true) =>  "static-instance";
        case SuperCall(mixin) => "super(" + mixin + ")";
      }
    }

    case object Dynamic extends InvokeStyle;

    /**
     * Special invoke. Static(true) is used for calls to private
     * members.
     */
    case class Static(onInstance: Boolean) extends InvokeStyle;

    /** Call through super[mixin]. */
    case class SuperCall(mixin: Name) extends InvokeStyle;

  }
}