From bec9884b00d60a79671e51a5a65b1717f753f981 Mon Sep 17 00:00:00 2001 From: NAME Date: Fri, 30 Jan 2004 13:07:45 +0000 Subject: Added intermediate code and corresponding jvm b... Added intermediate code and corresponding jvm backend --- sources/scala/tools/scalac/CompilerPhases.scala | 4 + .../tools/scalac/backend/GenJVMFromICode.scala | 753 +++++++++++++++++++++ .../scalac/backend/GenJVMFromICodePhase.scala | 33 + sources/scala/tools/scalac/icode/IBasicBlock.scala | 111 +++ .../scala/tools/scalac/icode/ICInstruction.scala | 355 ++++++++++ sources/scala/tools/scalac/icode/ICTypeStack.scala | 145 ++++ sources/scala/tools/scalac/icode/ICode.scala | 486 +++++++++++++ sources/scala/tools/scalac/icode/ICodePhase.scala | 65 ++ .../scala/tools/scalac/icode/ICodePrinter.scala | 72 ++ sources/scalac/CompilerPhases.java | 16 +- sources/scalac/Global.java | 23 +- sources/scalac/atree/AMethod.java | 5 + sources/scalac/atree/AShiftOp.java | 14 +- sources/scalac/atree/ATreeTyper.java | 32 +- sources/scalac/transformer/ICodePhase.java | 59 ++ 15 files changed, 2161 insertions(+), 12 deletions(-) create mode 100644 sources/scala/tools/scalac/backend/GenJVMFromICode.scala create mode 100644 sources/scala/tools/scalac/backend/GenJVMFromICodePhase.scala create mode 100644 sources/scala/tools/scalac/icode/IBasicBlock.scala create mode 100644 sources/scala/tools/scalac/icode/ICInstruction.scala create mode 100644 sources/scala/tools/scalac/icode/ICTypeStack.scala create mode 100644 sources/scala/tools/scalac/icode/ICode.scala create mode 100644 sources/scala/tools/scalac/icode/ICodePhase.scala create mode 100644 sources/scala/tools/scalac/icode/ICodePrinter.scala create mode 100644 sources/scalac/transformer/ICodePhase.java (limited to 'sources') diff --git a/sources/scala/tools/scalac/CompilerPhases.scala b/sources/scala/tools/scalac/CompilerPhases.scala index 59ce0bf084..462b0d7327 100644 --- a/sources/scala/tools/scalac/CompilerPhases.scala +++ b/sources/scala/tools/scalac/CompilerPhases.scala @@ -22,6 +22,10 @@ class CompilerPhases extends scalac_CompilerPhases { Class.forName("scala.tools.scalac.typechecker.AnalyzerPhase$class"); protected override def TRANSMATCH_PHASE(): Class = Class.forName("scala.tools.scalac.transformer.TransMatchPhase$class"); + protected override def ICODE_PHASE(): Class = + Class.forName("scala.tools.scalac.icode.ICodePhase$class"); + protected override def GENJVMFROMICODE_PHASE(): Class = + Class.forName("scala.tools.scalac.backend.GenJVMFromICodePhase$class"); } } diff --git a/sources/scala/tools/scalac/backend/GenJVMFromICode.scala b/sources/scala/tools/scalac/backend/GenJVMFromICode.scala new file mode 100644 index 0000000000..2745c4b0ee --- /dev/null +++ b/sources/scala/tools/scalac/backend/GenJVMFromICode.scala @@ -0,0 +1,753 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +import scalac.symtab._; +import scalac.{Global => scalac_Global}; +import scalac.atree._; +import scalac.Unit; +import scalac.util.Debug; +import scala.tools.scalac.icode._; +import ch.epfl.lamp.fjbg._; +import scala.collection.mutable.HashMap; + +import java.util.StringTokenizer; +import java.io.File; + +package scala.tools.scalac.backend { + +/* This class implements the backend which create + * Java Virtual Machine's bytecode with + * The Intermediate Code of the compiler */ +class GenJVMFromICode(global: scalac_Global) { + + // ################################################## + // Private fields - Utils + + private val defs = global.definitions; + + private val fjbgContext = new FJBGContext(); + + private val typer = new ATreeTyper(global); + + // ################################################## + // Private fields - Data + private var currentSrcFileName: String = null; + + private val clasz = new HashMap[Symbol, JVMClass]; + + private val typeMap = new HashMap[Symbol, JType]; + + // ################################################## + // Constructor code + initTypeMap; + + // ################################################## + // Public methods + + /* This method generates byte code for a single unit */ + def translate(unit: Unit) = { + global.log("Jvm.translate() called"); + currentSrcFileName = unit.source.toString(); + // Generate the structure + val classes_it = new IterableArray(unit.repository.classes()).elements; + classes_it.foreach(genClass(null)); + dumpStructure; + // Generate the code & Save the classes + var pairs_it = clasz.elements; + pairs_it.foreach ((p: Pair[Symbol, JVMClass]) => { + val sym = p._1; + val jvmClass = p._2; + val methods_it = jvmClass.methods.values; + global.log("Jvm.translate: translating class "+sym); + methods_it.foreach ((m: JVMMethod) => if(!m.aMethod.isAbstract()) genCode(m)); + //jvmClass.jClass.writeTo("/tmp/"+javaName(sym)); // tmp + }); + pairs_it = clasz.elements; // Remettre joli quand ca marche !!! + pairs_it.foreach ((p: Pair[Symbol, JVMClass]) => { + val sym = p._1; + val jvmClass = p._2; + val fileName = javaFileName(javaName(sym)); + global.log("Jvm.translate: writing class "+sym); + jvmClass.jClass.writeTo(fileName); + }); + global.operation("Generation of "+currentSrcFileName+" succeded."); + + currentSrcFileName = null; + } + + // ################################################## + // Private methods - Generate code + + /* Generate a class */ + private def genClass (parent: JVMClass)(aClass: AClass) : unit = { + // 1. ##### Create JClass object + val sym : Symbol = aClass.symbol(); + val thisClassName : String = javaName(sym); + val flags : int = JAccessFlags.ACC_SUPER | // The way it has to be + (if (aClass.isPublic()) JAccessFlags.ACC_PUBLIC else 0) | + (if (aClass.isPrivate()) JAccessFlags.ACC_PRIVATE else 0) | + (if (aClass.isProtected()) JAccessFlags.ACC_PROTECTED else 0) | + (if (aClass.isFinal()) JAccessFlags.ACC_FINAL else 0) | + (if (aClass.isAbstract()) JAccessFlags.ACC_ABSTRACT else 0) | + (if (aClass.isInterface()) JAccessFlags.ACC_INTERFACE else 0); + + global.log("genClass: parents(): "); + val prnt_it = new IterableArray(aClass.parents()).elements; // debug + prnt_it.foreach((t: Type) => {global.log(" "+t.toString())}); // debug + + val baseTps = new IterableArray(aClass.parents()).elements; + assert (baseTps.hasNext, "Jvm::genClass: Invalid number of parents. "+Debug.show(sym)); + + var superClassName : String = null; + + val firstParent : Type = aClass.parents()(0); + //var aParentType : Type = baseTps.next; + global.log("first parent: "+firstParent); // Debug + if (aClass.isInterface()) { + if (firstParent.isSameAs(defs.ANY_TYPE())) { + global.log("test ANY succeded for interface. l112"); + baseTps.drop(1); + } + superClassName = JAVA_LANG_OBJECT; + } else { + superClassName = javaName(baseTps.next.symbol()); + } + + var interfaceNames_l : List[String] = Nil; + + baseTps.foreach((aParentType : Type) => + interfaceNames_l = javaName(aParentType.symbol())::interfaceNames_l); + val interfaceNames_a = new Array[String](interfaceNames_l.length); + interfaceNames_l.reverse.copyToArray(interfaceNames_a,0); + + val jclass : JClass = fjbgContext.JClass(flags, + thisClassName, + superClassName, + interfaceNames_a, + currentSrcFileName); + + // 2. ##### Modify context:: Enter class + //currentClass = aClass; + val jvmClass = new JVMClass(parent); + clasz += aClass.symbol() -> jvmClass; + jvmClass.jClass = jclass; + //classFields = new HashMap[Symbol, JField](); + + // 3. ##### Access the inner classes + // !!! Acces aux champs extérieurs + val classes_it = new IterableArray(aClass.classes()).elements; + classes_it.foreach(genClass(jvmClass)); + + // 4. ##### Add fields of the class + //genFields(aClass, jClass); + + val fields_it = new IterableArray(aClass.fields()).elements; + fields_it.foreach(genField(jvmClass)); + + // ICI -> Faut-il s'occuper du truc module ? (case ClassDef) + + // 5. ##### Enregistre les methodes + val methods_it = new IterableArray(aClass.methods()).elements; + global.log(" number of methods: "+aClass.methods().length); // Debug + methods_it.foreach(genMethod(jvmClass)); + + // ##### Modify context:: Leave class + //currentClass = null; + //classFields = null; + } + + /* Add a field to a class */ + private def genField(jvmClass : JVMClass)(aField : AField) : unit = { + //val fields_it = new IterableArray(aClass.fields()).elements; + + //fileds_it.foreach(aField: AField => { + //aField = fields_it.next; + val flags = (if (aField.isPublic()) JAccessFlags.ACC_PUBLIC else 0) | + (if (aField.isPrivate()) JAccessFlags.ACC_PRIVATE else 0) | + (if (aField.isProtected()) JAccessFlags.ACC_PROTECTED else 0) | + (if (aField.isStatic()) JAccessFlags.ACC_STATIC else 0) | + (if (aField.isFinal()) JAccessFlags.ACC_FINAL else 0) | + (if (aField.isVolatile()) JAccessFlags.ACC_VOLATILE else 0) | + (if (aField.isTransient()) JAccessFlags.ACC_TRANSIENT else 0); + jvmClass.fields += aField.symbol() -> + jvmClass.jClass.addNewField(flags, + aField.symbol().name.toString(), + typeStoJ(aField.symbol().info())); // Vérifier si name n'est pas plus simple + } + + /* Generate a method */ + private def genMethod(jvmClass: JVMClass)(aMethod: AMethod): unit = { + // 1. ##### Create JMethod object + + val flags = (if (aMethod.isPublic()) JAccessFlags.ACC_PUBLIC else 0) | + (if (aMethod.isPrivate()) JAccessFlags.ACC_PRIVATE else 0) | + (if (aMethod.isProtected()) JAccessFlags.ACC_PROTECTED else 0) | + (if (aMethod.isStatic()) JAccessFlags.ACC_STATIC else 0) | + (if (aMethod.isFinal()) JAccessFlags.ACC_FINAL else 0) | + (if (aMethod.isNative()) JAccessFlags.ACC_NATIVE else 0) | + (if (aMethod.isAbstract()) JAccessFlags.ACC_ABSTRACT else 0) | + (if (aMethod.isStrictFP()) JAccessFlags.ACC_STRICT else 0); + + var argTypes_l : List[JType] = Nil; + var argNames_l : List[String] = Nil; + + val vparams_it = new IterableArray(aMethod.vparams()).elements; + vparams_it.foreach((sym: Symbol) => { + argTypes_l = typeStoJ(sym.info())::argTypes_l; + argNames_l = sym.name.toString()::argNames_l; + }); + + val argTypes_a = new Array[JType](argTypes_l.length); + val argNames_a = new Array[String](argNames_l.length); + argTypes_l.reverse.copyToArray(argTypes_a,0); + argNames_l.reverse.copyToArray(argNames_a,0); + val jMethod : JMethod = jvmClass.jClass.addNewMethod(flags, + aMethod.symbol().name.toString(), + typeStoJ(aMethod.result()), + argTypes_a, + argNames_a); + + + // 2. ##### Modify context:: Enter method + //currentMethod = aMethod; + //methodLocals = new HashMap[Symbol, JLocalVariable]; + //methodArgs = new HashMap[Symbol, int]; + val jvmMethod = new JVMMethod(aMethod, jvmClass); + jvmClass.methods += aMethod.symbol() -> jvmMethod; + jvmMethod.jMethod = jMethod; + + var index : int = 1; + vparams_it.foreach((sym: Symbol) => { + jvmMethod.args += sym -> index; + index = index + 1; + }); + + if (! jvmMethod.aMethod.isAbstract()) { + jvmMethod.jCode = jMethod.getCode().asInstanceOf[JExtendedCode]; + + + + // 3. ##### Generate labels + + jvmMethod.aMethod.icode.asInstanceOf[ICode].icTraverse((bb : IBasicBlock) => { + val blockLabel : JCode$Label = jvmMethod.jCode.newLabel(); + jvmMethod.labels += bb -> blockLabel; + }); + } + //if (!aMethod.isAbstract()) { + // genCode(aMethod.icode, jMethod.getCode()); + //} + + // 3. ##### Modify context:: Leave method + //currentMethod = null; + //methodLocals = null; + + } + + /* Translate code */ + private def genCode(jvmMethod : JVMMethod) = { + val icode : ICode = jvmMethod.aMethod.icode.asInstanceOf[ICode]; + var stack : ICTypeStack = new ICTypeStack(); + val jCode = jvmMethod.jCode; + icode.icTraverse((bb: IBasicBlock) => { + val blockLabel = jvmMethod.labels.apply(bb); + blockLabel.anchorToNext(); + bb.bbTraverse((ic : ICInstruction) => stack = emitICInstruction(jvmMethod, stack)(ic)); + }); + } + + /* Translate an ICInstruction to a JVM instruction */ + private def emitICInstruction(jvmMethod: JVMMethod, stack: ICTypeStack)(instruction: ICInstruction) : ICTypeStack = { + val jcode = jvmMethod.jCode; + instruction match { + case THIS(_) => + jcode.emitALOAD_0(); + + + case CONSTANT(AConstant$BOOLEAN(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$BYTE(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$SHORT(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$CHAR(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$INT(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$LONG(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$FLOAT(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$DOUBLE(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant$STRING(v)) => + jcode.emitPUSH(v); + case CONSTANT(AConstant.NULL) => + jcode.emitACONST_NULL(); + case CONSTANT(AConstant.UNIT) => + ; // ?? + case CONSTANT(AConstant.ZERO) => + ; // ?? + + case LOAD_ARRAY_ITEM() => { + // depend the type of the elements of the array + val elementType = typer.getArrayElementType(stack.tail.head); + jcode.emitALOAD(typeStoJ(elementType)); + } + + case LOAD_LOCAL(local, false) => + jcode.emitLOAD(jvmMethod.locals.apply(local)); + + case LOAD_LOCAL(local, true) => + jcode.emitLOAD(jvmMethod.args.apply(local), typeStoJ(local.getType())); + + case LOAD_FIELD(field, static) => { + val className = javaName(field.owner()); + val fieldName = javaName(field); + if (static) + jcode.emitGETSTATIC(className, fieldName, typeStoJ(field.getType())); + else + jcode.emitGETFIELD(className, fieldName, typeStoJ(field.getType())); + } + + case STORE_ARRAY_ITEM() => + jcode.emitASTORE(typeStoJ(stack.head)); + + case STORE_LOCAL(local, false) => { + val jLocal : JLocalVariable = + if (jvmMethod.locals.contains(local)) + jvmMethod.locals.apply(local); + else { + val newLocal = jvmMethod.jMethod.addNewLocalVariable(typeStoJ(local.getType()), javaName(local)); + jvmMethod.locals += local -> newLocal; + newLocal; + } + jcode.emitSTORE(jLocal); + } + + case STORE_FIELD(field, static) => { + val className = javaName(field.owner()); + val fieldName = javaName(field); + if (static) + jcode.emitPUTSTATIC(className, fieldName, typeStoJ(field.getType())); + else + jcode.emitPUTFIELD(className, fieldName, typeStoJ(field.getType())); + } + + case CALL_PRIMITIVE(APrimitive$Negation(ATypeKind.I4)) => jcode.emitINEG(); + case CALL_PRIMITIVE(APrimitive$Negation(ATypeKind.I8)) => jcode.emitLNEG(); + case CALL_PRIMITIVE(APrimitive$Negation(ATypeKind.R4)) => jcode.emitFNEG(); + case CALL_PRIMITIVE(APrimitive$Negation(ATypeKind.R8)) => jcode.emitDNEG(); + + //case CALL_PRIMITIVE(APrimitive$Test(*)) + // !! Regarder les Test + + //case CALL_PRIMITIVE(AComparisonOp(*)) + // !! Regarder les comparison + + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.ADD, ATypeKind.I4)) => jcode.emitIADD(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.ADD, ATypeKind.I8)) => jcode.emitLADD(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.ADD, ATypeKind.R4)) => jcode.emitFADD(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.ADD, ATypeKind.R8)) => jcode.emitDADD(); + + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.SUB, ATypeKind.I4)) => jcode.emitISUB(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.SUB, ATypeKind.I8)) => jcode.emitLSUB(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.SUB, ATypeKind.R4)) => jcode.emitFSUB(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.SUB, ATypeKind.R8)) => jcode.emitDSUB(); + + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.MUL, ATypeKind.I4)) => jcode.emitIMUL(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.MUL, ATypeKind.I8)) => jcode.emitLMUL(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.MUL, ATypeKind.R4)) => jcode.emitFMUL(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.MUL, ATypeKind.R8)) => jcode.emitDMUL(); + + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.DIV, ATypeKind.I4)) => jcode.emitIDIV(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.DIV, ATypeKind.I8)) => jcode.emitLDIV(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.DIV, ATypeKind.R4)) => jcode.emitFDIV(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.DIV, ATypeKind.R8)) => jcode.emitDDIV(); + + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.REM, ATypeKind.I4)) => jcode.emitIREM(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.REM, ATypeKind.I8)) => jcode.emitLREM(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.REM, ATypeKind.R4)) => jcode.emitFREM(); + case CALL_PRIMITIVE(APrimitive$Arithmetic(AArithmeticOp.REM, ATypeKind.R8)) => jcode.emitDREM(); + + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.AND, ATypeKind.I4)) => jcode.emitIAND(); + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.AND, ATypeKind.BOOL)) => jcode.emitIAND(); // ??? is that true + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.AND, ATypeKind.I8)) => jcode.emitLAND(); + + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.OR, ATypeKind.I4)) => jcode.emitIOR(); + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.OR, ATypeKind.BOOL)) => jcode.emitIOR(); // ??? is that true + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.OR, ATypeKind.I8)) => jcode.emitLOR(); + + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.XOR, ATypeKind.I4)) => jcode.emitIXOR(); + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.XOR, ATypeKind.BOOL)) => jcode.emitIXOR(); // ??? is that true + case CALL_PRIMITIVE(APrimitive$Logical(ALogicalOp.XOR, ATypeKind.I8)) => jcode.emitLXOR(); + + case CALL_PRIMITIVE(APrimitive$Shift(AShiftOp.ASL, ATypeKind.I4)) => jcode.emitISHL(); + case CALL_PRIMITIVE(APrimitive$Shift(AShiftOp.ASL, ATypeKind.I8)) => jcode.emitLSHL(); + + case CALL_PRIMITIVE(APrimitive$Shift(AShiftOp.ASR, ATypeKind.I4)) => jcode.emitISHR(); + case CALL_PRIMITIVE(APrimitive$Shift(AShiftOp.ASR, ATypeKind.I8)) => jcode.emitLSHR(); + + case CALL_PRIMITIVE(APrimitive$Shift(AShiftOp.LSR, ATypeKind.I4)) => jcode.emitIUSHR(); + case CALL_PRIMITIVE(APrimitive$Shift(AShiftOp.LSR, ATypeKind.I8)) => jcode.emitLUSHR(); + + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I4, ATypeKind.I8)) => jcode.emitI2L(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I4, ATypeKind.R4)) => jcode.emitI2F(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I4, ATypeKind.R8)) => jcode.emitI2D(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I8, ATypeKind.I4)) => jcode.emitL2I(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I8, ATypeKind.R4)) => jcode.emitL2F(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I8, ATypeKind.R8)) => jcode.emitL2D(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.R4, ATypeKind.I4)) => jcode.emitF2I(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.R4, ATypeKind.I8)) => jcode.emitF2L(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.R4, ATypeKind.R8)) => jcode.emitF2D(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.R8, ATypeKind.I4)) => jcode.emitD2I(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.R8, ATypeKind.I8)) => jcode.emitD2L(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.R8, ATypeKind.R4)) => jcode.emitD2F(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I4, ATypeKind.I1)) => jcode.emitI2B(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I4, ATypeKind.U2)) => jcode.emitI2C(); + case CALL_PRIMITIVE(APrimitive$Conversion(ATypeKind.I4, ATypeKind.I2)) => jcode.emitI2S(); + + case CALL_PRIMITIVE(APrimitive$ArrayLength(_)) => jcode.emitARRAYLENGTH(); + + case CALL_PRIMITIVE(APrimitive$StringConcat(_,_)) => + ; // !!! + + case CALL_METHOD(method, style) => { + var calledMethod : JMethod = null; + /* + var owner : JVMClass = method.owner; + while (calledMethod == null && owner != null) do + if (owner.methods.contains(method)) + calledMethod = owner.methods.apply(method); + else + owner = owner.parent; + */ // Joli pour optimization ? + val clasz_it = clasz.values; + var aJvmClass : JVMClass = null; + while (clasz_it.hasNext && calledMethod == null) { + aJvmClass = clasz_it.next; + if (aJvmClass.methods.contains(method)) + calledMethod = aJvmClass.methods.apply(method).jMethod; + } + if (calledMethod != null) + jcode.emitINVOKE(calledMethod); // et le style !!! + else { + // cas normal + val methodName = method.name.toString(); + val className = javaName(method.owner()); + val methodType = typeStoJ(method.info()).asInstanceOf[JMethodType]; + style match { + case AInvokeStyle.Dynamic => + if (method.owner().isInterface()) + jcode.emitINVOKEINTERFACE(className, methodName, methodType); + else + jcode.emitINVOKEVIRTUAL(className, methodName, methodType); + case AInvokeStyle.StaticInstance => + jcode.emitINVOKESPECIAL(className, methodName, methodType); + case AInvokeStyle.StaticClass => + jcode.emitINVOKESTATIC(className, methodName, methodType); + } + } + } + + case NEW(clasz) => + jcode.emitNEW(javaName(clasz)); + + case CREATE_ARRAY(element) => + jcode.emitNEWARRAY(typeStoJ(element)); + + case IS_INSTANCE(typ) => + jcode.emitINSTANCEOF(typeStoJ(typ).asInstanceOf[JReferenceType]); + + case CHECK_CAST(typ) => + jcode.emitCHECKCAST(typeStoJ(typ).asInstanceOf[JReferenceType]); + + case SWITCH(tags,blocks) => { + val casesTags : List[Array[int]] = List.fromArray(tags,0,tags.length); + val casesLabels = blocks.take(blocks.length-1); + val defaultBranch = jvmMethod.labels(blocks.last); + + val tagsAndLabels = casesTags.zip(casesLabels); + var keys_l : List[int] = Nil; + var branches_l : List[JCode$Label] = Nil; + + tagsAndLabels.foreach ((p: Pair[Array[Int], IBasicBlock]) => { + val tags = p._1; + val label = jvmMethod.labels(p._2); + val tag_it = new IterableArray(tags).elements; + tag_it.foreach((tag: int) => { + keys_l = tag::keys_l; + branches_l = label::branches_l; + }); + }); + + val keys_a = new Array[int](keys_l.length); + val branches_a = new Array[JCode$Label](branches_l.length); + keys_l.copyToArray(keys_a,0); + branches_l.copyToArray(branches_a,0); + + jcode.emitSWITCH(keys_a, branches_a, defaultBranch, 15); // Min density = 15 ??? !!! + } + + case JUMP(basicBlock) => + jcode.emitGOTO(jvmMethod.labels(basicBlock)); + + case CJUMP(success, failure, cond) => { + val condTag : int = condAtoJ(cond); + val typ = typeStoJ(stack.head); + if (typ.getTag() == JType.T_REFERENCE) + jcode.emitIF_ACMP(condTag, jvmMethod.labels(success)); + else if (typ.getTag() == JType.T_INT) + jcode.emitIF_ICMP(condTag, jvmMethod.labels(success)); + // Tres tres bizarre !!! Et les autres cas ??? + jcode.emitGOTO(jvmMethod.labels.apply(failure)); // HA ha ha ! + } + + case CZJUMP(success, failure, cond) => { + val condTag = condAtoJ(cond); + jcode.emitIF(condTag, jvmMethod.labels.apply(success)); + jcode.emitGOTO(jvmMethod.labels.apply(failure)); // !!! Ha ha ha + } + + case RETURN() => { + if (stack.isEmpty) + //jcode.emitRETURN(); malheureusement ca ne marche pas + jcode.emitRETURN(JType.VOID); // mais ca oui + else + jcode.emitRETURN(typeStoJ(stack.head)); + } + + case THROW() => jcode.emitATHROW; + + case DROP(typ) => { // A voir mieux + // ??? On pourrait aussi regarder sur la pile + val jtyp = typeStoJ(typ); + val jtypTag : int = jtyp.getTag(); + if (jtyp.isObjectType() || + jtyp.isReferenceType() || + jtyp.isArrayType()) + jcode.emitPOP(); + else + jtypTag match { // cf. VM spec 3.11.1 + case JType.T_BOOLEAN => jcode.emitPOP(); + case JType.T_CHAR => jcode.emitPOP(); + case JType.T_BYTE => jcode.emitPOP(); + case JType.T_SHORT => jcode.emitPOP(); + case JType.T_INT => jcode.emitPOP(); + case JType.T_FLOAT => jcode.emitPOP(); + case JType.T_REFERENCE => jcode.emitPOP(); + case JType.T_ADDRESS => jcode.emitPOP(); + case JType.T_LONG => jcode.emitPOP2(); + case JType.T_DOUBLE => jcode.emitPOP2(); + } + } + + case DUP(typ) => { + val jtyp = typeStoJ(typ); + val jtypTag : int = jtyp.getTag(); + if (jtyp.isObjectType() || + jtyp.isReferenceType() || + jtyp.isArrayType()) + jcode.emitDUP(); + else + jtypTag match { // cf. VM spec 3.11.1 + case JType.T_BOOLEAN => jcode.emitDUP(); + case JType.T_CHAR => jcode.emitDUP(); + case JType.T_BYTE => jcode.emitDUP(); + case JType.T_SHORT => jcode.emitDUP(); + case JType.T_INT => jcode.emitDUP(); + case JType.T_FLOAT => jcode.emitDUP(); + case JType.T_REFERENCE => jcode.emitDUP(); + case JType.T_ADDRESS => jcode.emitDUP(); + case JType.T_LONG => jcode.emitDUP2(); + case JType.T_DOUBLE => jcode.emitDUP2(); + } + } + + case MONITOR_ENTER() => jcode.emitMONITORENTER(); + + case MONITOR_EXIT() => jcode.emitMONITOREXIT(); + + } + return stack.eval(instruction); + } + + /* Translate an ATree Test operation to the FJBG type */ + private def condAtoJ(cond : ATestOp) : int = cond match { + case ATestOp.EQ => JExtendedCode.COND_EQ; + case ATestOp.NE => JExtendedCode.COND_NE; + case ATestOp.GE => JExtendedCode.COND_GE; + case ATestOp.LT => JExtendedCode.COND_LT; + case ATestOp.LE => JExtendedCode.COND_LE; + case ATestOp.GT => JExtendedCode.COND_GT; + } + + // ################################################## + // Private methods - Debugging + + private def dumpStructure = { + global.log("### Dumping structure ###"); + val sym_it = clasz.keys; + sym_it.foreach((sym: Symbol) => { + val jvmClass = clasz.apply(sym); + global.log ("Classfile: "+javaName(sym)); + global.log ("/fileds:"); + val fields_it = jvmClass.fields.keys; + fields_it.foreach((f: Symbol) => { + global.log(" "+javaName(f)); + }); + global.log ("/methods:"); + val methods_it = jvmClass.methods.keys; + methods_it.foreach((m: Symbol) => { + global.log(" "+javaName(m)); + }); + }); + global.log("#########################"); + } + + //################################################## + // Private methods & fields + // translated from GenJVM - M. Schinz + + private val JAVA_LANG_OBJECT = "java.lang.Object"; + + private def initTypeMap = { + typeMap += defs.ANY_CLASS -> JObjectType.JAVA_LANG_OBJECT; + typeMap += defs.ANYREF_CLASS -> JObjectType.JAVA_LANG_OBJECT; + } + + /** + * Return a Java-compatible version of the name of the given + * symbol. The returned name is mangled and includes the names of + * the owners. + */ + private def javaName(theSym: Symbol) = { + var sym = theSym; + sym match { + case defs.ANY_CLASS => JAVA_LANG_OBJECT; + case defs.ANYREF_CLASS => JAVA_LANG_OBJECT; + case _ => { + val buf = new StringBuffer(sym.name.toString()); + if ((sym.isModule() || sym.isModuleClass()) && !sym.isJava()) + buf.append('$'); + sym = sym.owner(); + while (!sym.isPackage()) { + buf.insert(0,'$'); + buf.insert(0,sym.name); + sym = sym.owner(); + } + if (!sym.isRoot()) { + buf.insert(0, '.'); + buf.insert(0, sym.fullName()); + } + buf.toString(); + } + } + } + + /** + * Return the name of the file in which to store the given class. + */ + private def javaFileName(className : String) = { + val tokens = new StringTokenizer(className, " "); + var file = new File(global.outpath); + while (tokens.hasMoreElements()) { + file = new File(file, tokens.nextToken()); + } + file.getPath()+".class"; + } + + /** + * Return the Java type corresponding to the given Scala type. + */ + private def typeStoJ(tp: Type) : JType = tp match { + case Type$UnboxedType(TypeTags.BYTE) => JType.BYTE; + case Type$UnboxedType(TypeTags.CHAR) => JType.CHAR; + case Type$UnboxedType(TypeTags.SHORT) => JType.SHORT; + case Type$UnboxedType(TypeTags.INT) => JType.INT; + case Type$UnboxedType(TypeTags.LONG) => JType.LONG; + case Type$UnboxedType(TypeTags.FLOAT) => JType.FLOAT; + case Type$UnboxedType(TypeTags.DOUBLE) => JType.DOUBLE; + case Type$UnboxedType(TypeTags.BOOLEAN) => JType.BOOLEAN; + case Type$UnboxedType(TypeTags.UNIT) => JType.VOID; + case Type$UnboxedType(TypeTags.STRING) => JObjectType.JAVA_LANG_STRING; + case Type$UnboxedArrayType(elementType) => new JArrayType(typeStoJ(elementType)); + + case Type$MethodType(vparams: Array[Symbol], result: Type) => { + val argTypes_a = new Array[JType](vparams.length); + val vparams_it = new IterableArray(vparams).elements; + //val argTypes_it = vparams_it.map((s: Symbol) => typeStoJ(s.info())); + var argTypes_l : List[JType] = Nil; + vparams_it.foreach((s: Symbol) => argTypes_l = typeStoJ(s.info())::argTypes_l); + argTypes_l.reverse.copyToArray(argTypes_a,0); + new JMethodType(typeStoJ(result), argTypes_a); + } + + case _ => { + val sym = tp.symbol(); + if (sym == Symbol.NONE) + throw global.fail("invalid type ",tp); + else if (typeMap.contains(sym)) + typeMap.apply(sym).asInstanceOf[JType]; + else { + val jTp = new JObjectType(javaName(sym)); + typeMap += sym -> jTp; + jTp; + } + } + } +} + + +//################################################## +// Data structures + + /* This class represents a Class Context */ +class JVMClass(theParent: JVMClass) { + + /* Methods of the class */ + val methods = new HashMap[Symbol, JVMMethod]; + + /* Fields of the class */ + val fields = new HashMap[Symbol, JField]; + + /* The JClass object corresponding of this class */ + var jClass : JClass = null; + + /* The inner classes of the class */ + var innerClasses : HashMap[Symbol, JVMClass] = null; + + /* The parent class of the class */ + val parent : JVMClass = theParent +} + + /* This class represents a Method context */ +class JVMMethod(theAMethod : AMethod, theOwner : JVMClass){ + + /* The ATree object representing this method */ + val aMethod : AMethod = theAMethod; + + /* The FJBG's object representing this method */ + var jMethod : JMethod = null; + + /* The locals variables of the method */ + val locals = new HashMap[Symbol, JLocalVariable]; + + /* The arguments of the method */ + val args = new HashMap[Symbol, int]; + + /* The JVM code of the method */ + var jCode : JExtendedCode = null; + + /* The owner class of the method */ + val owner : JVMClass = theOwner; + + /* The labels of the IBasicBlock's composing the ICode */ + val labels = new HashMap[IBasicBlock, JCode$Label]; +} + } diff --git a/sources/scala/tools/scalac/backend/GenJVMFromICodePhase.scala b/sources/scala/tools/scalac/backend/GenJVMFromICodePhase.scala new file mode 100644 index 0000000000..d469c21f33 --- /dev/null +++ b/sources/scala/tools/scalac/backend/GenJVMFromICodePhase.scala @@ -0,0 +1,33 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +import scalac.{Global => scalac_Global} +import scalac.{Unit => scalac_Unit} +import scalac.PhaseDescriptor; +import scalac.Phase; + +package scala.tools.scalac.backend { + +/* This class represents the JVMFromICode backend production + * Phase. It uses the ICode to create class files using + * the JVM's bytecode */ +class GenJVMFromICodePhase(global: scalac_Global, descriptor: PhaseDescriptor) extends Phase(global, descriptor) { + + // ################################################## + // Public method + + /* Apply this phase to all units */ + def apply(units: Array[scalac_Unit]) = { + val generator = new GenJVMFromICode(global); // !!! super global + val units_it = new IterableArray(units).elements; + + units_it.foreach(generator.translate); + } + +} +} diff --git a/sources/scala/tools/scalac/icode/IBasicBlock.scala b/sources/scala/tools/scalac/icode/IBasicBlock.scala new file mode 100644 index 0000000000..588dad3709 --- /dev/null +++ b/sources/scala/tools/scalac/icode/IBasicBlock.scala @@ -0,0 +1,111 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +import scalac.symtab.Symbol; +import scalac.symtab.Type; + +import scalac.atree._; + +package scala.tools.scalac.icode { + +/** This class represents a basic block */ +class IBasicBlock (theLabel: int) { + + //################################################## + // Public fields + + /* The type stack at the begining of the block */ + var initialStack : ICTypeStack = null; + + /* The label of the block */ + val label = theLabel; + + /* The substitute variables of the block + * in the case of a recursive method */ + var substituteVars : List[Symbol] = null; + + /* The stack at the end of the block */ + var endStack : ICTypeStack = null; + + /* The successors of this block */ + var successors : List[IBasicBlock] = Nil; + + /* Is the block closed*/ + var isClosedBlock : boolean = false; + + //################################################## + // Private fields + + /* ICode instructions */ + private var instructions : List[ICInstruction] = Nil; + + //################################################## + // Public methods + + /* Compute an hashCode for the block */ + override def hashCode() = label; + + /* Apply a function to all the instructions of the block*/ + def bbTraverse(f: ICInstruction => unit) = instructions.reverse.foreach(f); + + /* Initialize the stack of the block, must be done before evaluing + * the type stack */ + def initStack(stack : ICTypeStack) = { + if (initialStack == null) { + initialStack = stack; + endStack = stack; + } + } + + /* Add a new instruction at the end of the block */ + def emit(instr: ICInstruction) = { + assert (!isClosedBlock, "IBasicBlock closed."); + instructions = instr::instructions; + } + + /* Compute the type stack of the block */ + def typeBlock = { + assert(initialStack != null, "Stack not initialized"); + bbTraverse((ic : ICInstruction) => endStack = endStack.eval(ic)); + } + + /* Add a successor to the block */ + def addSuccessor(s: IBasicBlock) = + if (!successors.contains(s)) + successors = s::successors; + + /* Add a list of successors to the block */ + def addSuccessors(ss: List[IBasicBlock]) = + ss.foreach(addSuccessor); + + /* Close the block */ + def close = isClosedBlock = true;; + + //################################################## + // Public methods - printing (used for debbuging) + // Instead of it, rather use a printer + def print() : unit = print(System.out); + + def print(out: java.io.PrintStream) : unit = { + out.println("block #"+label+" :"); + instructions.reverse.foreach( + (i: ICInstruction) => out.println(" "+i)); + out.print("Successors: "); + successors.foreach((x: IBasicBlock) => out.print(" "+x.label.toString())); + out.println(); + } + + // ################################################## + // Private methods + + // none. + +} + +} + diff --git a/sources/scala/tools/scalac/icode/ICInstruction.scala b/sources/scala/tools/scalac/icode/ICInstruction.scala new file mode 100644 index 0000000000..fc83269ead --- /dev/null +++ b/sources/scala/tools/scalac/icode/ICInstruction.scala @@ -0,0 +1,355 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + + +import scalac.symtab.Symbol; +import scalac.symtab.Type; + +import scalac.atree._; + +package scala.tools.scalac.icode { + +/** This class represents an instruction of intermediate code */ +abstract class ICInstruction { + + /** 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; +} + +//################################################## +// Instruction cases + +/** This class represents a THIS instruction + * Stack: ... + * ->: ...:ref + */ +case class THIS(clasz: Symbol) extends ICInstruction { + /** Returns a string representation of this constant */ + override def toString(): String = "THIS"; + + override def consumed = 0; + override def produced = 1; +} + +/** This class retpresents a CONSTANT instruction + * Stack: ... + * ->: ...:constant + */ +case class CONSTANT(constant: AConstant) extends ICInstruction{ + /** Returns a string representation of this constant */ + override def toString(): String = "CONSTANT ("+constant.toString()+")"; + + override def consumed = 0; + override def produced = 1; +} + +/** This class represents a LOAD_ARRAY_ITEM instruction + * Stack: ...:array[a](Ref):index(Int) + * ->: ...:element(a) + */ +case class LOAD_ARRAY_ITEM extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "LOAD_ARRAY_ITEM"; + + override def consumed = 2; + override def produced = 1; +} + +/** This class represents a LOAD_LOCAL instruction + * Stack: ... + * ->: ...:value + */ +case class LOAD_LOCAL(local: Symbol, isArgument: boolean) extends ICInstruction { + /** 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; +} + +/** This class represents a LOAD_FIELD instruction + * Stack: ...:ref + * ->: ...:value + */ +case class LOAD_FIELD(field: Symbol, isStatic: boolean) extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "LOAD_FIELD "+field.toString(); //+isStatic?" (static)":""; + + override def consumed = 1; + override def produced = 1; +} + +/** This class represents a STORE_ARRAY_ITEM instruction + * Stack: ...:array[a](Ref):index(Int):value(a) + * ->: ... + */ +case class STORE_ARRAY_ITEM extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "STORE_ARRAY_ITEM"; + + override def consumed = 3; + override def produced = 0; +} + +/** This class represents a STORE_LOCAL instruction + * Stack: ...:value + * ->: ... + */ +case class STORE_LOCAL(local: Symbol, isArgument: boolean) extends ICInstruction { + /** 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; +} + +/** This class represents a STORE_FIELD instruction + * Stack: ...:ref:value + * ->: ... + */ +case class STORE_FIELD(field: Symbol, isStatic: boolean) extends ICInstruction { + /** 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; +} + +/** This class represents a CALL_PRIMITIVE instruction + * Stack: ...:arg1:arg2:...:argn + * ->: ...:result + */ +case class CALL_PRIMITIVE(primitive: APrimitive) extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="CALL "+primitive.toString(); + + override def consumed = primitive match { + case (APrimitive$Negation(_)) => 1; + case (APrimitive$Test(_,_,true)) => 1; + case (APrimitive$Test(_,_,false)) => 2; + case (APrimitive$Comparison(_,_)) => 2; + case (APrimitive$Arithmetic(_,_)) => 2; + case (APrimitive$Logical(_,_)) => 2; + case (APrimitive$Shift(_,_)) => 2; + case (APrimitive$Conversion(_,_)) => 1; + case (APrimitive$ArrayLength(_)) => 1; + case (APrimitive$StringConcat(_,_)) => 2; + } + override def produced = 1; +} + +/** This class represents a CALL_METHOD instruction + * STYLE: dynamic / static(StaticInstance) + * Stack: ...:ref:arg1:arg2:...:argn + * ->: ...:result + * + * STYLE: new - unused by jvm + * Stack: ...:arg1:arg2:...:argn + * ->: ...:ref + * + * STYLE: static(StaticClass) + * Stack: ...:arg1:arg2:...:argn + * ->: ...:result + * + */ +case class CALL_METHOD(method: Symbol, style: AInvokeStyle) extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="CALL_METHOD "+method.toString()+" ("+style.toString()+")"; + + override def consumed = { + var result = method.getType().valueParams().length; + result = result + (style match + {case(AInvokeStyle.Dynamic) => 1 + case(AInvokeStyle.StaticInstance) => 1 + case(_) => 0 }); + result; + } + override def produced = 1; +} + +/** This class represents a NEW instruction + * Stack: ...: + * ->: ...:ref + */ + case class NEW(clasz: Symbol) extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "NEW "+clasz.toString(); + + override def consumed = 0; + override def produced = 1; + } + + + /** This class represents a CREATE_ARRAY instruction + * Stack: ...:size(int) + * ->: ...:arrayref + */ +case class CREATE_ARRAY(element: Type) extends ICInstruction { + /** 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: Type) extends ICInstruction { + /** 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: Type) extends ICInstruction { + /** 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) + * ->: ...: + */ +case class SWITCH(tags: Array[Array[int]], labels: List[IBasicBlock]) extends ICInstruction { + /** 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: IBasicBlock) extends ICInstruction { + /** 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: IBasicBlock, failureBlock: IBasicBlock, cond: ATestOp) extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="CJUMP "+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: IBasicBlock, failureBlock: IBasicBlock, cond: ATestOp) extends ICInstruction { + /** Returns a string representation of this instruction */ + override def toString(): String ="CZJUMP "+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 ICInstruction { + /** 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 ICInstruction { +/** 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: Type) extends ICInstruction { + /** 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: Type) extends ICInstruction { + /** 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 ICInstruction { + + /** 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 ICInstruction { + + /** Returns a string representation of this instruction */ + override def toString(): String ="MONITOR_EXIT"; + + override def consumed = 1; + override def produced = 0; + } + +} diff --git a/sources/scala/tools/scalac/icode/ICTypeStack.scala b/sources/scala/tools/scalac/icode/ICTypeStack.scala new file mode 100644 index 0000000000..9914fdf270 --- /dev/null +++ b/sources/scala/tools/scalac/icode/ICTypeStack.scala @@ -0,0 +1,145 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +import scalac.atree._; +import scalac.symtab._; +import scalac.Global; + +package scala.tools.scalac.icode { + +/* This class simulates the type of the opperand + * stack of the ICode. */ +class ICTypeStack() { + + // ################################################## + // Private fields + private var stack : List[Type] = Nil; + + private val global = Global.instance; + + private val typer = new ATreeTyper(global); // !!! Using atree typer ! + + private val definitions = global.definitions; + + // ################################################## + // Private constructor + + private def this(stack : List[Type]) = { + this(); + this.stack = stack; + } + + // ################################################## + // Public method + + /* This methods simulate the effect of an ICInstruction + * on the stack. It returns the new stack */ + def eval(instr: ICInstruction) : ICTypeStack = { + global.log(instr.toString()); + if (stack.length < instr.consumed) { + global.log("Invalid instruction :"+instr+" for stack "+toString()); + this; + } else { + val result = new ICTypeStack(erase(production(instr)):::stack.drop(instr.consumed)); /// !!! Opt + global.log("\t-> "+result.toString()); + result; + } + } + + // ################################################## + // Public methods - Operation on the stack + + /* Push a new type */ + def push(t: Type) = stack = t::stack; + + /* Returns the topmost type of the stack */ + def head = stack.head; + + /* Returns the stack without the topmost element */ + def tail = stack.tail; + + /* Is the stack empty ? */ + def isEmpty = stack.isEmpty; + + // ################################################## + // Private methods + + /* Compute what type(s) the instruction produce on the stack */ + private def production(instr: ICInstruction) : List[Type] = instr match { + case THIS(clasz) => clasz.thisType()::Nil; + + case CONSTANT(aConstant) => typer.computeType(aConstant)::Nil; + + case LOAD_ARRAY_ITEM() => typer.getArrayElementType(stack.tail.head)::Nil; + + case LOAD_LOCAL(local, _) => local.getType()::Nil; + + case LOAD_FIELD(field, _) => stack.head.memberStabilizedType(field)::Nil; // ?? + + case STORE_ARRAY_ITEM() => Nil; + + case STORE_LOCAL(_,_) => Nil; + + case STORE_FIELD(_,_) => Nil; + + case CALL_PRIMITIVE(p) => typer.computeType(p)::Nil; + + case CALL_METHOD(method, style) => style match { + //case AInvokeStyle.Dynamic => stack.head.memberStabilizedType(method)::Nil; // ??? + //case AInvokeStyle.Dynamic => stack.drop(method.getType().valueParams().length).head.resultType()::Nil; + case AInvokeStyle.New => method.owner().thisType()::Nil; + + case _ => method.resultType()::Nil; // Valable pour Dynamic + //case _ => method.owner().thisType().memberStabilizedType(method)::Nil; // ??? + } + case NEW(clasz) => clasz.getType()::Nil; + + case CREATE_ARRAY(element) => definitions.ARRAY_TYPE(element)::Nil; + + case IS_INSTANCE(_) => definitions.BOOLEAN_TYPE()::Nil; + + case CHECK_CAST(typ) => typ::Nil; + + case SWITCH(_,_) => Nil; + + case JUMP(_) => Nil; + + case CJUMP(_,_,_) => Nil; + + case CZJUMP(_,_,_) => Nil; + + case RETURN() => Nil; + + case THROW() => Nil; + + case DROP(_) => Nil; + + case DUP(_) => stack.head::stack.head::Nil; + + case MONITOR_ENTER() => Nil; + + case MONITOR_EXIT() => Nil; + } + + /* This method kill all the produced *Unit* elements */ + // !!! Hack + private def erase(production : List[Type]) = production.filter((t: Type) => t match { + case Type$UnboxedType(TypeTags.UNIT) => false; + case _ => true + }); + + /* This method returns a String representation of the stack */ + // !!! POUAH SALE ! + override def toString() = { + var ret : String = ""; + stack.foreach((t: Type) => ret=(t.toString()+"::")+ret); + ret; + + } +} +} diff --git a/sources/scala/tools/scalac/icode/ICode.scala b/sources/scala/tools/scalac/icode/ICode.scala new file mode 100644 index 0000000000..0fecf66109 --- /dev/null +++ b/sources/scala/tools/scalac/icode/ICode.scala @@ -0,0 +1,486 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +import scalac.{Global => scalac_Global} + +import scala.collection.mutable.HashMap; +import scala.collection.mutable.HashSet; + +import scalac.symtab._; +import scalac.atree._; +import scalac.util.Debug; + +package scala.tools.scalac.icode { + +/** This class represents the intermediate code of a method*/ +class ICode(label: String, global: scalac_Global) { + + //################################################## + // Public fields + + /* The set of all blocks */ + val blocks: HashSet[IBasicBlock] = new HashSet; + + /* The start block of the method */ + var startBlock : IBasicBlock = null; + + /* The stack produced by this method */ + var producedStack : ICTypeStack = null; + + //################################################## + // Private fields + private var currentLabel : int = 0; + + private var aTreeLabels : HashMap[Symbol, IBasicBlock] = new HashMap; + + //################################################## + // Constructor code + + startBlock = newBlock; + startBlock.initStack(new ICTypeStack); + + //################################################## + // Public methods + + /** This method produce the intermediate code for + * the given ATree */ + def generate(acode : ACode) = { + val ctx = gen(acode, new GenContext(startBlock, null)); + + ctx.emit(RETURN()); // ??? RETURN instruction rather come from ATree + genTypeStack; + + producedStack = ctx.currentBlock.endStack; + } + + /* This method applies the given function to all the block */ + def icTraverse(f: IBasicBlock => unit) = { + // ?? Define order (actually preorder) + val visited : HashMap[IBasicBlock, boolean] = new HashMap; + visited.incl(blocks.elements.map((x: IBasicBlock) => Pair(x, false))); + + var blockToVisit : List[IBasicBlock] = startBlock::Nil; + + while (blockToVisit != Nil) { + blockToVisit match { + case b::xs => { + if (!visited(b)) { + f(b); + blockToVisit = b.successors:::xs; + visited += b -> true; + } else + blockToVisit = xs; + } + } + } + } + + + /** This methods returns a string representation of the ICode */ + override def toString() : String = "ICode '"+label+"'"; // + + //################################################## + // Public method - printing (for debug use) + + /** This method print the code */ + def print() : unit = print(System.out); + + def print(out: java.io.PrintStream) : unit = { + icTraverse((bb: IBasicBlock) => { + out.println("Block #"+bb.label); + out.println("Substituable variables : "); + if (bb.substituteVars != null) + bb.substituteVars.foreach(out.print); + else + out.println(" {Empty} "); + out.println("Instructions:"); + bb.bbTraverse((ici: ICInstruction) => + out.println(" "+ici.toString())); + out.print ("Successors: "); + bb.successors.foreach((bb: IBasicBlock) => out.print(bb.label+", ")); + out.println (""); // ?? Del + out.println (); + }); + } + + + def logType = { + global.log ("// Typing "+toString()); + this.icTraverse((bb: IBasicBlock) => { + global.log ("Typing block #"+bb.label); + var typer = new ICTypeStack; + bb.bbTraverse((ic: ICInstruction) => { + typer = typer.eval(ic); + global.log(ic.toString()+" -> "+typer.toString()); + }); + + }); + } + + //################################################## + // Private methods + + /* Compute a unique new label */ + private def nextLabel = { + currentLabel = currentLabel + 1; + currentLabel; + } + + /* This method : + * 1/ create a new block + * 2/ add the new block to the set + * 3/ return the new block + */ + private def newBlock : IBasicBlock = { + val block = new IBasicBlock(nextLabel); + blocks += block; + block; + } + + /* Generate the code from a given ATree */ + private def gen(aCode: ACode, ctx: GenContext) : GenContext = + aCode match { + case ACode.Void => { + global.log ("ICodeGenerator::emit: Void node found"); + ctx; + } + + case ACode$This(clasz) => + ctx.emit(THIS(clasz)); + + case ACode$Constant(c) => + ctx.emit(CONSTANT(c)); + + case ACode$Load(ALocation$Module(_)) => { + global.log ("ICodeGenerator::emit: Load(Module) node found"); + ctx; + } + + case ACode$Load(ALocation$Field(obj, field ,isStatic)) => { + var ctx1 = gen(obj, ctx); + ctx.emit(LOAD_FIELD(field, isStatic)); + } + + case ACode$Load(ALocation$Local(local,isArgument)) => + ctx.emit(LOAD_LOCAL(local, isArgument)); + + case ACode$Load(ALocation$ArrayItem(array, index)) => { + var ctx1 = gen(array, ctx); + ctx1 = gen(index, ctx1); + ctx1.emit(LOAD_ARRAY_ITEM()); + } + + case ACode$Store(ALocation$Module(_),_) => { + global.log ("ICodeGenerator::emit: Store(Module(_)) node found"); + ctx; + } + + case ACode$Store(ALocation$Field(obj, field, isStatic), value) => { + var ctx1 = gen(obj, ctx); + ctx1 = gen(value, ctx1); + ctx1.emit(STORE_FIELD(field, isStatic)); + } + + case ACode$Store(ALocation$Local(local, isArgument), value) => { + val ctx1 = gen(value, ctx); + ctx1.emit(STORE_LOCAL(local, isArgument)); + } + + case ACode$Store(ALocation$ArrayItem(array, index), value) => { + var ctx1 = gen(array, ctx); + ctx1 = gen(index, ctx1); + ctx1 = gen(value, ctx1); + ctx1.emit(STORE_ARRAY_ITEM()); + } + + case ACode$Apply(AFunction$Method(_,sym,AInvokeStyle.New),_,vargs) => { + val vargs_it = new IterableArray(vargs).elements; + // !!! Depend the backend in use + ctx.emit(NEW(sym.owner())); + ctx.emit(DUP(sym.owner().getType())); + var ctx1 = ctx; + vargs_it.foreach((varg: ACode) => ctx1 = gen(varg, ctx1)); + ctx1.emit(CALL_METHOD(sym,AInvokeStyle.StaticInstance)); + } + + case ACode$Apply(AFunction$Method(obj,sym,style),_,vargs) => { + val vargs_it = new IterableArray(vargs).elements; + var ctx1 = ctx; + style match { + case AInvokeStyle.StaticClass => + ; // NOP + case _ => + ctx1 = gen(obj, ctx1); + } + vargs_it.foreach((varg: ACode) => ctx1 = gen(varg, ctx1)); + ctx1.emit(CALL_METHOD(sym,style)); + } + + case ACode$Apply(AFunction$Primitive(p),_,vargs) => { + val vargs_it = new IterableArray(vargs).elements; + var ctx1 = ctx; + + vargs_it.foreach((varg: ACode) => ctx1 = gen(varg, ctx1)); + ctx1.emit(CALL_PRIMITIVE(p)); + } + + case ACode$Apply(AFunction$NewArray(element),_,vargs) => { + var ctx1 = gen(vargs(0), ctx); // The size is given as first argument + ctx1.emit(CREATE_ARRAY(element)); + } + + case ACode$IsAs(value,typ,false) => { + var ctx1 = gen(value, ctx); + ctx1.emit(IS_INSTANCE(typ)); + } + + case ACode$IsAs(value,typ,true) => { + var ctx1 = gen(value, ctx); + ctx1.emit(CHECK_CAST(typ)); + } + + case ACode$If(test, success, failure) => { + genAlt(test, success, failure, ctx, newBlock); + } + + case ACode$Switch(test,tags,bodies) => { + + val switchBodies = List.fromArray(bodies, 0, bodies.length); + var switchBlocks: List[IBasicBlock] = Nil; + for (val i : ACode <- switchBodies) switchBlocks = newBlock::switchBlocks; + val switchPairs = switchBodies.zip(switchBlocks); + + val nextBlock = newBlock; + + var ctx1 = gen(test, ctx); + ctx1.emit(SWITCH(tags, switchBlocks)); + ctx1.currentBlock.addSuccessors(switchBlocks); + + switchPairs.foreach((p: Pair[ACode, IBasicBlock]) => { + val code = p._1; + val block = p._2; + + val ctx2 = gen(code, new GenContext(block, nextBlock)); + ctx2.closeBlock; + }); + + ctx1.changeBlock(nextBlock); + } + + case ACode$Synchronized(lock, value) => { + var ctx1 = gen(lock, ctx); + ctx1.emit(MONITOR_ENTER()); + ctx1 = gen(value,ctx); + ctx1 = gen(lock, ctx); + ctx1.emit(MONITOR_EXIT()); + } + + case ACode$Block(_,statements,value) => { + val statements_it = new IterableArray(statements).elements; + var ctx1 = ctx; + statements_it.foreach((st: ACode) => ctx1 = gen(st, ctx1)); + ctx1 = gen(value, ctx1); + ctx1; + } + + case ACode$Label(label, locals, value) => { + + val loopBlock = newBlock; + var ctx1 = ctx.changeBlock(loopBlock); + ctx.nextBlock = loopBlock; + ctx.closeBlock; + + aTreeLabels += label -> loopBlock; + + loopBlock.substituteVars = List.fromIterator((new IterableArray(locals)).elements); + gen(value, ctx1); + } + + case ACode$Goto(label,vargs) => { + + val vargs_it = new IterableArray(vargs).elements; + global.log("Current label mapping: "+aTreeLabels.keys.foreach((s: Symbol) => global.log(s.toString()))); + global.log("Looking for sym: "+label); + val gotoBlock = aTreeLabels(label); + var ctx1 = ctx; + + // Stack-> : + vargs_it.foreach((varg: ACode) => ctx1 = gen(varg, ctx1)); + + // Stack-> :varg0:varg1:...:vargn + // We should have the same number of varg that substitute vars + + gotoBlock.substituteVars.reverse.foreach( + (varg: Symbol) => ctx1.emit(STORE_LOCAL(varg, false)) // ?? false + ); + ctx1.nextBlock = gotoBlock; + ctx1.closeBlock; + + ctx1; + } + + case ACode$Return(_,value) => { + var ctx1 = gen(value, ctx); + ctx1.emit(RETURN()); + } + case ACode$Throw(value) => { + var ctx1 = gen(value, ctx); + ctx1.emit(THROW()); + } + + case ACode$Drop(value, typ) => { + var ctx1 = gen(value, ctx); + //global.log("Type de Drop: "+typ+" = unboxed:"+typ.unbox() ); + if (! typ.isSameAs(global.definitions.UNIT_TYPE())) + typ.unbox() match { // !!! Hack + case Type$UnboxedType(TypeTags.UNIT) => + global.log("it matches UNIT !"); // debug ; // NOP + case _ => + ctx1.emit(DROP(typ)); + } + else + global.log("it matches SCALAC_UNIT :-("); + ctx1; + } + } + + /* This method genrates an alternative. In the case of an if instruction + * It looks at the kind of alternatives and creates new basic blocks + * + * @param nextBlock : represents the block where alternatives meet again*/ + private def genAlt (cond : ACode, success: ACode, failure: ACode, ctx: GenContext, nextBlock: IBasicBlock) : GenContext = { + val successBlock = success match { + case ACode.Void => nextBlock; + + case ACode$If(test, innerSuccess, innerFailure) => { + val ctx1 = new GenContext(newBlock, null); + val ctx2 = genAlt(test, innerSuccess, innerFailure, ctx1, nextBlock); + //ctx2.closeBlock; // or close when CJUMP is emitted + ctx1.currentBlock; + } + + case _ => { + val ctx1 = new GenContext(newBlock, nextBlock); + val ctx2 = gen(success, ctx1); + ctx2.closeBlock; + ctx1.currentBlock; + } + } + + val failureBlock = failure match { + case ACode.Void => nextBlock; + + case ACode$If(test, innerSuccess, innerFailure) => { + val ctx1 = new GenContext(newBlock, null); + val ctx2 = genAlt(test, innerSuccess, innerFailure, ctx1, nextBlock); + //ctx2.closeBlock; // or close when CJUMP is emitted + ctx1.currentBlock; + } + + case _ => { + val ctx1 = new GenContext(newBlock, nextBlock); + val ctx2 = gen(failure, ctx1); + ctx2.closeBlock; + ctx1.currentBlock; + } + } + + var ctx1 = genCond(cond, successBlock, failureBlock, ctx); + ctx1.changeBlock(nextBlock); + } + + /* This methods generate the test and the jump instruction */ + private def genCond(cond: ACode, successBlock: IBasicBlock, failureBlock: IBasicBlock, ctx: GenContext) : GenContext= { + var ctx1 = ctx; + cond match { + case ACode$Apply(AFunction$Primitive(APrimitive$Test(op,_,zero)),_, vargs) => { + ctx1 = gen(vargs(0),ctx1); + if (zero) + ctx1.emit(CZJUMP(successBlock, failureBlock, op)); + else { + ctx1 = gen(vargs(1), ctx1); + ctx1.emit(CJUMP(successBlock, failureBlock, op)); + } + ctx1.currentBlock.addSuccessors(successBlock::failureBlock::Nil); + } + + case ACode$Apply(AFunction$Primitive(APrimitive$Negation(_)),_,vargs) => + ctx1 = genCond(vargs(0), failureBlock, successBlock, ctx1); + // ??? Test with TestOp opposite ? + case _ => { + ctx1 = gen(cond, ctx1); + ctx1.emit(CONSTANT(AConstant.INT(1))); + ctx1.emit(CJUMP(successBlock, failureBlock, ATestOp.EQ)); + ctx1.currentBlock.addSuccessors(successBlock::failureBlock::Nil); + } + } + ctx1; + } + + /* This method generate the type stacks of all the blocks + * of this method.*/ + private def genTypeStack = { + icTraverse((bb: IBasicBlock) => { + global.log("// Typing block #"+bb.label); + global.log("\t-> "+bb.endStack); + bb.typeBlock; + // !!! Here we must test if all the meeting blocks have the + // !!! same stack. + bb.successors.foreach((suc: IBasicBlock) => suc.initStack(bb.endStack)); + }); + } + +} + + /* This class represents the context of generating icode */ + class GenContext(current: IBasicBlock, next: IBasicBlock) { + + // ################################################## + // Public fields + + /* The block in which we add instructions */ + val currentBlock = current; + + /* The block were we jump at the end of the current block + * It can be the *null* value */ + var nextBlock = next; + + // ################################################## + // Constructor code + + assert(current != null, "No current block"); + + // ################################################## + // Public methods + + /* append an instruction at the end of the current block */ + def emit(instr: ICInstruction) : GenContext = { + currentBlock.emit(instr); + this; + } + + /* Split the current block. The next block is still the same */ + def changeBlock(bb: IBasicBlock) = { + new GenContext(bb, next); + } + + /* Close the current block. If there's a known next block + * It append a jump instruction to the block. + * This method do something only the first time it's called */ + def closeBlock = { + if (!currentBlock.isClosedBlock) { + if (nextBlock != null) { + emit(JUMP(nextBlock)); + currentBlock.addSuccessor(nextBlock); + } + currentBlock.close; + } + } + } + +} // package diff --git a/sources/scala/tools/scalac/icode/ICodePhase.scala b/sources/scala/tools/scalac/icode/ICodePhase.scala new file mode 100644 index 0000000000..ab55131066 --- /dev/null +++ b/sources/scala/tools/scalac/icode/ICodePhase.scala @@ -0,0 +1,65 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +import scalac.transformer.{ICodePhase => scalac_ICodePhase} +import scalac.{Global => scalac_Global} +import scalac.{Unit => scalac_Unit} +import scalac.PhaseDescriptor; +import scalac.symtab.Symbol; +import scalac.symtab.Type; +import scalac.Phase; +import scalac.atree._; + +import ch.epfl.lamp.util.CodePrinter; + +package scala.tools.scalac.icode { + +/** This class represents the ICode phase. This phase use the ATrees + * that represents the code with some primitives backends-like + * and convert them in a inline form: The ICode (for intermediate + * code). The ICode will be produced in the icode variable + * of all AMethod objects. */ +class ICodePhase(global: scalac_Global, descriptor: PhaseDescriptor) extends scalac_ICodePhase (global, descriptor) { + + // ################################################## + // Public methods + + /* Apply the icode phase to the given units */ + override def apply(units: Array[scalac_Unit]) = { + val units_it = new IterableArray(units).elements; + + units_it.foreach(translate); + } + + /* Return a CodePrinter for the ICode */ + override def getPrinter(cp: CodePrinter) = new ICodePrinter(cp); + + // ################################################## + // Private methods + + /** This method translate a single unit, it traverses all methods and + * generate ICode for each of them */ + private def translate(u: scalac_Unit) : unit = { + val classes_it = new IterableArray(u.repository.classes()).elements; + classes_it.foreach((c: AClass) => { + val methods_it = new IterableArray(c.methods()).elements; + methods_it.foreach((m: AMethod) => { + val label : String = m.symbol().name.toString(); + + val ic : ICode = new ICode(label, global); + ic.generate(m.code()); + + // save the produced ICode + m.icode = ic; + + }); + }); + } + +} +} diff --git a/sources/scala/tools/scalac/icode/ICodePrinter.scala b/sources/scala/tools/scalac/icode/ICodePrinter.scala new file mode 100644 index 0000000000..7021f54566 --- /dev/null +++ b/sources/scala/tools/scalac/icode/ICodePrinter.scala @@ -0,0 +1,72 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +import ch.epfl.lamp.util.CodePrinter; + +import scalac.Unit; +import scalac.{Global => scalac_Global} +import scalac.atree._; + +import scalac.symtab.{Symbol => scalac_Symbol} + +package scala.tools.scalac.icode { + +/* This class implements a Printer for the ICode */ +class ICodePrinter (printer: CodePrinter) extends ATreePrinter (printer: CodePrinter) { + + // ################################################## + // Public methods + + /* This method print all ICode produced after the ICode phase */ + override def printGlobal(global: scalac_Global) : ATreePrinter = { // !!! Extends ATree !!! + val phase = global.currentPhase; + + printer.println("[[ICode at "+phase+" (after "+phase.prev+")]]"); + val units_it = new IterableArray(global.units).elements; + units_it.foreach(printAnUnit); + + this; + } + + /* This method print a single unit. */ + def printAnUnit(unit: Unit) = { // ??? Private + printer.println ("// Scala source: "+unit.source); + val classes_it = new IterableArray(unit.repository.classes()).elements; + classes_it.foreach((c: AClass) => { + printer.println (""); + printer.println ("// ICode for class <"+c.symbol().name+">"); + val methods_it = new IterableArray(c.methods()).elements; + methods_it.foreach((m: AMethod) => { + val icode : ICode = m.icode.asInstanceOf[ICode]; + printer.println ("// ::"+m.symbol().name); + icode.icTraverse((bb: IBasicBlock) => { + printer.println("Block #"+bb.label); + printer.println("Initial stack -> "+bb.initialStack); + printer.println("Substituable variables : "); + if (bb.substituteVars != null) + bb.substituteVars.foreach((s: scalac_Symbol) => printer.print(s.name.toString())); + else + printer.println(" {Empty} "); + printer.println("Instructions:"); + printer.indent(); + bb.bbTraverse((ici: ICInstruction) => + printer.println(ici.toString())); + printer.undent(); + printer.println("End stack -> "+bb.endStack); + printer.print ("Successors: "); + bb.successors.foreach((bb: IBasicBlock) => printer.print(bb.label+", ")); + printer.println (""); // ?? Del + printer.println (); + }); + }); + }); + + + } +} +} diff --git a/sources/scalac/CompilerPhases.java b/sources/scalac/CompilerPhases.java index dae13e6c91..adac32a95e 100644 --- a/sources/scalac/CompilerPhases.java +++ b/sources/scalac/CompilerPhases.java @@ -37,8 +37,10 @@ public class CompilerPhases { public final PhaseDescriptor EXPANDMIXIN; public final PhaseDescriptor MAKEBOXINGEXPLICIT; public final PhaseDescriptor ERASURE; + public final PhaseDescriptor ICODE; public final PhaseDescriptor GENMSIL; public final PhaseDescriptor GENJVM; + public final PhaseDescriptor GENJVMFROMICODE; public final PhaseDescriptor TERMINAL; //######################################################################## @@ -62,8 +64,10 @@ public class CompilerPhases { protected Class ADDINTERFACES_PHASE() { return scalac.transformer.AddInterfacesPhase.class; } protected Class EXPANDMIXIN_PHASE() { return scalac.transformer.ExpandMixinsPhase.class; } protected Class ERASURE_PHASE() { return scalac.transformer.ErasurePhase.class; } + protected Class ICODE_PHASE() { return scalac.util.EmptyPhase.class; } // No java version protected Class GENMSIL_PHASE() { return scalac.backend.msil.GenMSILPhase.class; } protected Class GENJVM_PHASE() { return scalac.backend.jvm.GenJVMPhase.class; } + protected Class GENJVMFROMICODE_PHASE() { return scalac.util.EmptyPhase.class; } // No java version //######################################################################## // Public Constructors @@ -152,6 +156,11 @@ public class CompilerPhases { "type eraser", "erased types", ERASURE_PHASE()), + this.ICODE = new PhaseDescriptor( + "icode", + "generate icode", + "generated icode", + ICODE_PHASE()), this.GENMSIL = new PhaseDescriptor( "genmsil", "generate MSIL code", @@ -162,7 +171,12 @@ public class CompilerPhases { "generate JVM bytecodes", "generated JVM code", GENJVM_PHASE()), - this.TERMINAL = new PhaseDescriptor( + this.GENJVMFROMICODE = new PhaseDescriptor( + "genjvmfromicode", + "generate JVM bytecodes using ICode", + "generated JVM code using ICode", + GENJVMFROMICODE_PHASE()), + this.TERMINAL = new PhaseDescriptor( "terminal", "compilation terminated", "compilation terminated", diff --git a/sources/scalac/Global.java b/sources/scalac/Global.java index bd411f7439..37e2ed95d9 100644 --- a/sources/scalac/Global.java +++ b/sources/scalac/Global.java @@ -136,11 +136,13 @@ public class Global { public static final String TARGET_INT; public static final String TARGET_JVM; public static final String TARGET_MSIL; + public static final String TARGET_JVMFROMICODE; public static final String[] TARGETS = new String[] { - TARGET_INT = "int", - TARGET_JVM = "jvm", - TARGET_MSIL = "msil", + TARGET_INT = "int", + TARGET_JVM = "jvm", + TARGET_MSIL = "msil", + TARGET_JVMFROMICODE = "jvmfromicode" }; /** tree printers @@ -225,6 +227,12 @@ public class Global { // TODO: Enable TailCall for other backends when they handle LabelDefs if (target != TARGET_MSIL) args.phases.GENMSIL.addSkipFlag(); if (target != TARGET_JVM) args.phases.GENJVM.addSkipFlag(); + if (target != TARGET_JVMFROMICODE) { + args.phases.ICODE.addSkipFlag(); + args.phases.GENJVMFROMICODE.addSkipFlag(); + } else { + ;//args.phases.ERASURE.addSkipFlag(); + } PHASE.freeze(); PhaseDescriptor[] descriptors = PHASE.phases(); this.firstPhase = descriptors[0].create(this); @@ -334,6 +342,15 @@ public class Global { printer.printGlobal(this); if (next) currentPhase = currentPhase.prev; if (html) writer.println(""); + } else if (currentPhase.id == PHASE.ICODE.id()) { + boolean html = args.printer.value.equals(PRINTER_HTML); + if (html) writer.println("
");
+	    ATreePrinter printer = ((scalac.transformer.ICodePhase)currentPhase).getPrinter(new CodePrinter(writer)); // Oh !!
+            boolean next = currentPhase.next != null;
+            if (next) currentPhase = currentPhase.next;
+            printer.printGlobal(this);
+            if (next) currentPhase = currentPhase.prev;
+            if (html) writer.println("
"); } else { // go to next phase to print symbols with their new type boolean next = currentPhase.next != null; diff --git a/sources/scalac/atree/AMethod.java b/sources/scalac/atree/AMethod.java index 332f2284d4..f904fa6b92 100644 --- a/sources/scalac/atree/AMethod.java +++ b/sources/scalac/atree/AMethod.java @@ -13,6 +13,11 @@ import scalac.symtab.Type; /** This class represents an attributed method. */ public class AMethod extends AMember { + //######################################################################## + // Public Fields + + /** Contains the Intermediate code of this Method */ + public Object icode = null; //######################################################################## // Public Constructors diff --git a/sources/scalac/atree/AShiftOp.java b/sources/scalac/atree/AShiftOp.java index 102f7963bd..fbed80fcde 100644 --- a/sources/scalac/atree/AShiftOp.java +++ b/sources/scalac/atree/AShiftOp.java @@ -16,24 +16,24 @@ public class AShiftOp { //######################################################################## // Public Cases - /** A logical shift to the left */ - public case LSL; - - /** A logical shift to the right */ - public case LSR; + /** An arithmetic shift to the left */ + public case ASL; /** An arithmetic shift to the right */ public case ASR; + /** A logical shift to the right */ + public case LSR; + //######################################################################## // Public Methods /** Returns a string representation of this operation. */ public String toString() { switch (this) { - case LSL: return "LSL"; - case LSR: return "LSR"; + case ASL: return "ASL"; case ASR: return "ASR"; + case LSR: return "LSR"; default: throw Debug.abort("unknown case", this); } } diff --git a/sources/scalac/atree/ATreeTyper.java b/sources/scalac/atree/ATreeTyper.java index 3a1a1df5c7..dead4e7c40 100644 --- a/sources/scalac/atree/ATreeTyper.java +++ b/sources/scalac/atree/ATreeTyper.java @@ -216,6 +216,36 @@ public class ATreeTyper { } } + //######################################################################## + // Public Methods - aliases of type() for scala + public Type[] computeType(ACode[] codes) { + return type(codes); + } + + public Type computeType(ACode code) { + return type(code); + } + + public Type computeType(ALocation location) { + return type(location); + } + + public Type computeType(AFunction function) { + return type(function); + } + + public Type computeType(APrimitive primitive) { + return type(primitive); + } + + public Type computeType(AConstant constant) { + return type(constant); + } + + public Type computeType(ATypeKind kind) { + return type(kind); + } + //######################################################################## // Private Methods @@ -231,7 +261,7 @@ public class ATreeTyper { } /** Returns the element type of the given array type. */ - private Type getArrayElementType(Type type) { + public Type getArrayElementType(Type type) { // !!! public / private switch (type) { case TypeRef(_, Symbol symbol, Type[] args): assert symbol == definitions.ARRAY_CLASS && args.length == 1: type; diff --git a/sources/scalac/transformer/ICodePhase.java b/sources/scalac/transformer/ICodePhase.java new file mode 100644 index 0000000000..5ed3212da1 --- /dev/null +++ b/sources/scalac/transformer/ICodePhase.java @@ -0,0 +1,59 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id: + +package scalac.transformer; + +import scalac.Global; +import scalac.Phase; +import scalac.PhaseDescriptor; +import scalac.Unit; +import scalac.checkers.TreeChecker; +import scalac.symtab.Definitions; + +import ch.epfl.lamp.util.CodePrinter; +import scalac.atree.ATreePrinter; + + +/** + * This class represents the ICode phase for the java version + * of the compiler. It doesn't do anything but permit to make + * a bridge between the java implementation of Socos et the + * scala one. See scala.tools.scalac.icode.ICodePhase for + * implementation + */ +public class ICodePhase extends Phase { + + //######################################################################## + // Private Fields + + private final Definitions definitions; + + //######################################################################## + // Public Constructors + + /** Initializes this instance. */ + public ICodePhase(Global global, PhaseDescriptor descriptor) { + super(global, descriptor); + this.definitions = global.definitions; + } + + //######################################################################## + // Public Methods + + /** Applies this phase to the given compilation units. */ + public void apply(Unit[] units) { + // This java version doesn't make anything + } + + public ATreePrinter getPrinter(CodePrinter cp) { + return new ATreePrinter(cp); + // !! Useless + } + +} + -- cgit v1.2.3