diff options
author | Iulian Dragos <jaguarul@gmail.com> | 2005-07-25 09:30:48 +0000 |
---|---|---|
committer | Iulian Dragos <jaguarul@gmail.com> | 2005-07-25 09:30:48 +0000 |
commit | 5cf7d39061bdf671311c17ba139186af53a1d5aa (patch) | |
tree | 46e5a5f878de5c992db6aaa67fc23fbfa9d17759 | |
parent | 3e90b7175a58a1b50801b3ad7cf0bb22da2146b2 (diff) | |
download | scala-5cf7d39061bdf671311c17ba139186af53a1d5aa.tar.gz scala-5cf7d39061bdf671311c17ba139186af53a1d5aa.tar.bz2 scala-5cf7d39061bdf671311c17ba139186af53a1d5aa.zip |
Added experimental support for ICode.
-rwxr-xr-x | sources/scala/tools/nsc/Global.scala | 26 | ||||
-rw-r--r-- | sources/scala/tools/nsc/Settings.scala | 1 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/BasicBlocks.scala | 169 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/GenICode.scala | 638 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/ICodes.scala | 22 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/Members.scala | 166 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/Opcodes.scala | 419 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/Primitives.scala | 234 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/Printers.scala | 97 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/TypeKind.scala | 79 | ||||
-rw-r--r-- | sources/scala/tools/nsc/backend/icode/TypeStacks.scala | 131 |
11 files changed, 1981 insertions, 1 deletions
diff --git a/sources/scala/tools/nsc/Global.scala b/sources/scala/tools/nsc/Global.scala index 06898a1297..4d63ec2a78 100755 --- a/sources/scala/tools/nsc/Global.scala +++ b/sources/scala/tools/nsc/Global.scala @@ -18,8 +18,12 @@ import ast.parser._; import typechecker._; import matching.TransMatcher; import transform._; +import backend.icode.{ICodes, GenICode}; -class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable with Trees with CompilationUnits { +class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable + with Trees + with CompilationUnits + with ICodes { // sub-components -------------------------------------------------- @@ -64,6 +68,8 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable def log(msg: Object): unit = if (settings.log contains phase.name) inform("[log " + phase + "] " + msg); + def abort(msg: String) = throw new Error(msg); + // file interface ------------------------------------------------------- private val reader: SourceReader = { @@ -167,6 +173,14 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable val global: Global.this.type = Global.this; } + object genicode extends GenICode { + val global: Global.this.type = Global.this; + } + + object icodePrinter extends backend.icode.Printers { + val global: Global.this.type = Global.this; + } + def phaseDescriptors: List[SubComponent] = List( analyzer.namerFactory, // needs to be first analyzer.typerFactory, // needs to be second @@ -193,6 +207,10 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable } } + // temporary: icode is turned on explicitely + if (settings.Xshowicode.value) + p = genicode.newPhase(p); + val terminalPhase = new GlobalPhase(p) { def name = "terminal"; def apply(unit: CompilationUnit): unit = {} @@ -256,6 +274,7 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable } if (settings.Xshowcls.value != "") showDef(newTermName(settings.Xshowcls.value), false); if (settings.Xshowobj.value != "") showDef(newTermName(settings.Xshowobj.value), true); + if (settings.Xshowicode.value) printICode(); if (reporter.errors() == 0) { for (val Pair(sym, pickled) <- symData.elements.toList) { @@ -351,6 +370,11 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable } } + private def printICode(): Unit = { + val printer = new icodePrinter.TextPrinter(new PrintWriter(System.out, true)); + classes.foreach(printer.printClass); + } + private def informStatistics = { inform("#identifiers : " + analyzer.idcnt); inform("#selections : " + analyzer.selcnt); diff --git a/sources/scala/tools/nsc/Settings.scala b/sources/scala/tools/nsc/Settings.scala index e9bb2cbea8..e8496fc683 100644 --- a/sources/scala/tools/nsc/Settings.scala +++ b/sources/scala/tools/nsc/Settings.scala @@ -47,6 +47,7 @@ class Settings(error: String => unit) { val Xshowcls = StringSetting ("-Xshowcls", "class", "Show class info", ""); val Xshowobj = StringSetting ("-Xshowobj", "object", "Show object info", ""); + val Xshowicode = BooleanSetting("-Xshowicode", "Print the generated ICode"); /** A list of all settings */ def allSettings: List[Setting] = allsettings.reverse; diff --git a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala new file mode 100644 index 0000000000..ffe9d7cd33 --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -0,0 +1,169 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.ast._; +import scala.collection.mutable.Map; + +trait BasicBlocks: Global { + import opcodes._; + + /** This class represents a basic block. Each + * basic block contains a list of instructions that are + * either executed all, or none. No jumps + * to/from the "middle" of the basic block are allowed. + */ + class BasicBlock (theLabel: int) { + + /** The type stack at the begining of the block */ + var initialStack : TypeStack = 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 : TypeStack = null; + + + /** ICode instructions */ + private var instructionList: List[Instruction] = Nil; + + private var lastInstruction: Instruction = null; + + private var closed: boolean = false; + + private var instrs: Array[Instruction] = _; + + // public: + + /** Compute an hashCode for the block */ + override def hashCode() = label; + + /** Apply a function to all the instructions of the block*/ + def traverse(f: Instruction => unit) = + instructionList.reverse.foreach(f); + + /** The number of instructions in this basic block so far. */ + def size: Int = instructionList.length; + + /** Initialize the stack of the block, must be done before evaluation + * the type stack */ + def initStack(stack : TypeStack) = { + if (initialStack == null) { + initialStack = stack; + endStack = null; + } + } + + ///////////////////// Substitutions /////////////////////// + + /** + * Replace the instruction at the given position. Used by labels when + * they are anchored. + */ + def replaceInstruction(pos: Int, instr: Instruction): Boolean = { + assert(closed, "Instructions can be replaced only after the basic block is closed"); + + instrs(pos) = instr; + true + } + + /** + * Replace the given instruction with the new one. + * Returns `true' if it actually changed something. + */ + def replaceInstruction(oldInstr: Instruction, newInstr: Instruction): Boolean = { + assert(closed, "Instructions can be replaced only after the basic block is closed"); + + var i = 0; + var changed = false; + while (i < instrs.length) { + if (instrs(i) == oldInstr) { + instrs(i) = newInstr; + changed = true; + } + i = i + 1; + } + changed + } + + /** Replace all instructions found in the map. */ + def subst(map: Map[Instruction, Instruction]) = { + var i = 0; + while (i < instrs.length) { + map get instrs(i) match { + case Some(instr) => replaceInstruction(i, instr); + case None => (); + } + i = i + 1; + } + } + + /** Add a new instruction at the end of the block */ + def emit(instr: Instruction) = { + assert (!closed, "BasicBlock closed."); + instructionList = instr :: instructionList; + lastInstruction = instr; + } + + /** Compute the type stack of the block */ + def typeBlock = { + assert(initialStack != null, "Stack not initialized"); + endStack = initialStack; + traverse((ic : Instruction) => endStack = endStack.eval(ic)); + } + + /** Close the block */ + def close = { + closed = true; + instrs = toInstructionArray(instructionList); + } + + /** Convert the list to an array */ + def toInstructionArray(l: List[Instruction]): Array[Instruction] = { + var array = new Array[Instruction](l.length); + var i: Int = 0; + + l foreach (x => { array(i) = x; i = i + 1 }); + array + } + + def isClosed = closed; + + def getLastInstruction = lastInstruction; + + def successors : List[BasicBlock] = // here order will count + lastInstruction match { + case JUMP (where) => List(where); + case CJUMP(success, failure, _) => failure::success::Nil; + case CZJUMP(success, failure, _) => failure::success::Nil; + case SWITCH(_,labels) => labels; + case RETURN() => Nil; + case _ => + abort("The last instruction is not a control flow instruction"); + } + + // Instead of it, rather use a printer + def print() : unit = print(System.out); + + def print(out: java.io.PrintStream) : unit = { + out.println("block #"+label+" :"); + instructionList.reverse.foreach( + (i: Instruction) => out.println(" "+i)); + out.print("Successors: "); + successors.foreach((x: BasicBlock) => out.print(" "+x.label.toString())); + out.println(); + } + + } + +} diff --git a/sources/scala/tools/nsc/backend/icode/GenICode.scala b/sources/scala/tools/nsc/backend/icode/GenICode.scala new file mode 100644 index 0000000000..727202ca72 --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/GenICode.scala @@ -0,0 +1,638 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.collection.mutable.{Map, HashMap}; + +abstract class GenICode extends SubComponent { + import global._; + import opcodes._; + + val phaseName = "genicode"; + + override def newPhase(prev: Phase) = new ICodePhase(prev); + + class ICodePhase(prev: Phase) extends StdPhase(prev) { + import Primitives._; + + override def description = "Generate ICode from the AST"; + + var unit: CompilationUnit = _; + + /** A map from scala primitive Types to ICode TypeKinds */ + private var primitiveTypeMap: Map[Symbol, TypeKind] = null; + + private var primitiveMethods: Map[Symbol, Primitive] = null; + + initPrimitiveTypeMap; + + override def apply(unit: CompilationUnit): Unit = { + this.unit = unit; + gen(unit.body); + } + + def gen(tree: Tree): Context = gen(tree, new Context()); + + def gen(trees: List[Tree], ctx: Context): Context = { + var ctx1 = ctx; + for (val t <- trees) + ctx1 = gen(t, ctx1); + + ctx1 + } + + def gen(tree: Tree, ctx: Context): Context = tree match { + case EmptyTree => ctx; + + case PackageDef(name, stats) => gen(stats, ctx setPackage name); + + case ClassDef(mods, name, tparams, tpt, impl) => + ctx setClass (new IClass(tree.symbol) setCompilationUnit unit); + addClassFields(ctx, tree.symbol); + classes = ctx.clazz :: classes; + gen(impl, ctx); + ctx setClass null; + + // !! modules should be eliminated by refcheck... or not? + case ModuleDef(mods, name, impl) => + ctx setClass (new IClass(tree.symbol) setCompilationUnit unit); + classes = ctx.clazz :: classes; + gen(impl, ctx); + ctx setClass null; + + case ValDef(mods, name, tpt, rhs) => ctx; // we use the symbol to add fields + + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + log("Entering method " + name); + val m = new IMethod(tree.symbol); + ctx.clazz.addMethod(m); + var ctx1 = ctx.enterMethod(m, tree.asInstanceOf[DefDef]); + addMethodParams(ctx1, vparamss); + ctx1 = genLoad(rhs, + ctx1, + toTypeKind(ctx1.method.symbol.info.resultType)); + ctx1.bb.emit(RETURN()); + ctx1; + + case Template(parents, body) => + gen(body, ctx); + + case Block(stats, expr) => + abort("Block is not allowed in gen " + tree); + + +/* case AbsTypeDef(mods, name, lo, hi) => (eliminated by erasure) + case AliasTypeDef(mods, name, tparams, rhs) => (eliminated by erasure) + case Import(expr, selectors) => (eliminated by typecheck) + case Attributed(attribute, definition) => (eliminated by typecheck) + case DocDef(comment, definition) => (eliminated by typecheck) + case CaseDef(pat, guard, body) => (eliminated by transmatch) + case Sequence(trees) => (eliminated by transmatch) + case Alternative(trees) => (eliminated by transmatch) + case Star(elem) => (eliminated by transmatch) + case Bind(name, body) => (eliminated by transmatch) + case ArrayValue(elemtpt, trees) => (introduced by uncurry) + case Function(vparams, body) => (eliminated by typecheck) + case Typed(expr, tpt) => (eliminated by erasure) + case TypeApply(fun, args) => + case SingletonTypeTree(ref) => (eliminated by typecheck) + case SelectFromTypeTree(qualifier, selector) => (eliminated by typecheck) + case CompoundTypeTree(templ: Template) => (eliminated by typecheck) + case AppliedTypeTree(tpt, args) => (eliminated by typecheck) +*/ + } + + private def genStat(trees: List[Tree], ctx: Context): Context = { + var currentCtx = ctx; + + for (val t <- trees) + currentCtx = genStat(t, currentCtx); + + currentCtx + } + + /** + * Generate code for the given tree. The trees should contain statements + * and not produce any value. Use genLoad for expressions which leave + * a value on top of the stack. + * + * @return a new context. This is necessary for control flow instructions + * which may change the current basic block. + */ + private def genStat(tree: Tree, ctx: Context): Context = tree match { + case Assign(lhs @ Select(_, _), rhs) => + if (lhs.symbol.isStatic) { + val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info)); + ctx1.bb.emit(STORE_FIELD(lhs.symbol, true)); + ctx1 + } else { + var ctx1 = genLoadQualifier(lhs, ctx); + ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info)); + ctx1.bb.emit(STORE_FIELD(lhs.symbol, false)); + ctx1 + } + + case Assign(lhs, rhs) => + assert(ctx.method.locals.contains(lhs.symbol), + "Assignment to inexistent local: " + lhs.symbol); + val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info)); + ctx1.bb.emit(STORE_LOCAL(lhs.symbol, lhs.symbol.isValueParameter)); + ctx1 + + case _ => + log("Passing " + tree + " to genLoad"); + genLoad(tree, ctx, UNIT); + } + + private def genLoad(tree: Tree, ctx: Context, expectedType: TypeKind): Context = { + tree match { + case LabelDef(name, params, rhs) => + ctx.method.addLocals(params map (.symbol)); + val ctx1 = ctx.newBlock; + ctx1.labels.get(tree.symbol) match { + case Some(label) => label.anchor(ctx1.bb); + + case None => + ctx.labels += tree.symbol -> (new Label(tree.symbol) anchor ctx1.bb setParams (params map (.symbol))); + } + genLoad(rhs, ctx1, toTypeKind(tree.symbol.info.resultType)); + + case ValDef(_, _, _, rhs) => + val sym = tree.symbol; + ctx.method.addLocal(sym); + val ctx1 = genLoad(rhs, ctx, toTypeKind(sym.info)); + ctx1.bb.emit(STORE_LOCAL(sym, false)); + ctx1 + + case If(cond, thenp, elsep) => + var thenCtx = ctx.newBlock; + var elseCtx = ctx.newBlock; + val contCtx = ctx.newBlock; + genCond(cond, ctx, thenCtx, elseCtx); + thenCtx = genLoad(thenp, thenCtx, toTypeKind(tree.tpe)); + elseCtx = genLoad(elsep, elseCtx, toTypeKind(tree.tpe)); + thenCtx.bb.emit(JUMP(contCtx.bb)); + elseCtx.bb.emit(JUMP(contCtx.bb)); + contCtx; + + case Return(expr) => + val ctx1 = genLoad(expr, ctx, expectedType); + ctx1.bb.emit(RETURN()); + ctx1 + + case Try(block, catches, finalizer) => + abort("Try blocks not handled yet"); + + case Throw(expr) => + val ctx1 = genLoad(expr, ctx, expectedType); + ctx1.bb.emit(THROW()); + ctx; + + case New(tpt) => + ctx.bb.emit(NEW(tree.symbol)); + ctx; + + case Apply(TypeApply(fun, targs), _) => + val sym = fun.symbol; + val ctx1 = genLoadQualifier(fun, ctx); + + if (sym == definitions.Any_isInstanceOfErased) + ctx1.bb.emit(IS_INSTANCE(targs.head.tpe)); + else if (sym == definitions.Any_asInstanceOfErased) + ctx1.bb.emit(CHECK_CAST(targs.head.tpe)); + else + abort("Unexpected type application"); + ctx1 + + case Apply(fun @ Select(Super(_, mixin), _), args) => + val invokeStyle = SuperCall(mixin); + val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx); + + ctx1.bb.emit(CALL_METHOD(fun.symbol, invokeStyle)); + ctx1 + + + case Apply(fun, args) => + val sym = fun.symbol; + if (sym.isLabel) { + + val label = ctx.labels.get(sym) match { + case Some(l) => l; + + // it is a forward jump, scan for labels + case None => + scanForLabels(ctx.defdef, ctx); + ctx.labels.get(sym) match { + case Some(l) => l; + case _ => abort("Unknown label target: " + sym); + } + } + val ctx1 = genLoadLabelArguments(args, label, ctx); + if (label.anchored) + ctx1.bb.emit(JUMP(label.block)); + else + ctx1.bb.emit(PJUMP(label)); + + ctx1.bb.close; + ctx1.newBlock; + } else if (isPrimitive(fun.symbol)) { + + abort("Primitive operations not handled yet"); + + } else { + // normal method call + + var invokeStyle = + if (sym.isConstructor) + NewInstance; + else if (sym.isStatic) + Static(false) + else + Dynamic; + + var ctx1 = genLoadQualifier(fun, ctx); + ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx1); + + ctx1.bb.emit(CALL_METHOD(sym, invokeStyle)); + ctx1 + } + + case This(qual) => + assert(tree.symbol == ctx.clazz.symbol, + "Trying to access the this of another class: " + + "tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol); + ctx.bb.emit(THIS(ctx.clazz.symbol)); + ctx; + + case Select(qualifier, selector) => + val sym = tree.symbol; + if (sym.isStatic) { + ctx.bb.emit(LOAD_FIELD(sym, true)); + ctx + } else { + val ctx1 = genLoadQualifier(tree, ctx); // !! attention + ctx1.bb.emit(LOAD_FIELD(sym, false)); + ctx1 + } + + case Ident(name) => + if (tree.symbol.isModule) + abort("Modules are not handled yet"); + else + ctx.bb.emit(LOAD_LOCAL(tree.symbol, tree.symbol.isValueParameter )); + ctx + + case Literal(value) => + ctx.bb.emit(CONSTANT(value)); + ctx + + case Block(stats, expr) => + log("Entering block"); + assert(!(ctx.method eq null), "Block outside method"); + val ctx1 = genStat(stats, ctx); + genLoad(expr, ctx1, expectedType); + + case EmptyTree => ctx; + + case Assign(_, _) => genStat(tree, ctx); + + case _ => abort("Unexpected tree in genLoad: " + tree); + } + } + + /** Load the qualifier of `tree' on top of the stack. */ + private def genLoadQualifier(tree: Tree, ctx: Context): Context = + tree match { + case Select(qualifier, _) => + genLoad(qualifier, ctx, REF); + case _ => + abort("Unknown qualifier " + tree); + } + + /** + * Generate code that loads args into label parameters. + */ + private def genLoadLabelArguments(args: List[Tree], label: Label, ctx: Context): Context = { + assert(args.length == label.params.length, + "Wrong number of arguments in call to label " + label.symbol); + var ctx1 = ctx; + var arg = args; + var param = label.params; + + while (arg != Nil) { + ctx1 = genLoad(arg.head, ctx1, toTypeKind(param.head.info)); + ctx1.bb.emit(STORE_LOCAL(param.head, param.head.isValueParameter)); + arg = arg.tail; + param = param.tail; + } + ctx1 + } + + private def genLoadArguments(args: List[Tree], tpes: List[Type], ctx: Context): Context = { + assert(args.length == tpes.length, "Wrong number of arguments in call " + ctx); + + var ctx1 = ctx; + var arg = args; + var tpe = tpes; + while (arg != Nil) { + ctx1 = genLoad(arg.head, ctx1, toTypeKind(tpe.head)); + arg = arg.tail; + tpe = tpe.tail; + } + ctx1 + } + + /** Is the given symbol a primitive operation? */ + def isPrimitive(fun: Symbol): Boolean = false; + + /** + * Traverse the tree and store label stubs in the contxt. This is + * necessary to handle forward jumps, because at a label application + * with arguments, the symbols of the corresponding LabelDef parameters + * are not yet known. + * + * Attention! Might be expensive to traverse each method twice! An + * optimization would be to make it lazily, only if forward jumps + * really happen. + */ + private def scanForLabels(tree: Tree, ctx: Context): Unit = + new Traverser() { + override def traverse(tree: Tree): Unit = tree match { + + case LabelDef(name, params, _) => + ctx.labels += tree.symbol -> (new Label(tree.symbol) setParams(params map (.symbol))); + + case _ => super.traverse(tree); + } + } traverse(tree); + + /** + * Generate code for conditional expressions. The two basic blocks + * represent the continuation in case of success/failure of the + * test. + */ + private def genCond(tree: Tree, + ctx: Context, + thenCtx: Context, + elseCtx: Context): Unit = { + + + } + + /** + * Add all fields of the given class symbol to the current ICode + * class. + */ + private def addClassFields(ctx: Context, cls: Symbol): Unit = { + assert(ctx.clazz.symbol eq cls, + "Classes are not the same: " + ctx.clazz.symbol + ", " + cls); + + for (val f <- cls.info.decls.elements) + if (!f.isMethod && f.isTerm) + ctx.clazz.addField(new IField(f)); + } + + /** + * Add parameters to the current ICode method. It is assumed the methods + * have been uncurried, so the list of lists contains just one list. + */ + private def addMethodParams(ctx: Context, vparamss: List[List[ValDef]]): Unit = + vparamss match { + case Nil => () + + case vparams :: Nil => + for (val p <- vparams) + ctx.method.addParam(p.symbol); + + case _ => + abort("Malformed parameter list: " + vparamss); + } + + def toTypeKind(t: Type): TypeKind = t match { + case ThisType(_) => REF; + + case SingleType(pre, sym) => + primitiveTypeMap get sym match { + case Some(k) => k; + case None => REF; + } + + case ConstantType(value) => + toTypeKind(value.tpe); + + case TypeRef(_, sym, _) => REF; + case _ => abort("Unknown type"); + } + + /** Initialize the map from scala primitive types to ICode types */ + private def initPrimitiveTypeMap = { + log("Initializing primitive map"); + primitiveTypeMap = new HashMap(); + primitiveTypeMap += definitions.UnitClass -> UNIT; + primitiveTypeMap += definitions.BooleanClass -> BOOL; + primitiveTypeMap += definitions.ByteClass -> I1; + primitiveTypeMap += definitions.ShortClass -> I2; + primitiveTypeMap += definitions.CharClass -> I2; + primitiveTypeMap += definitions.IntClass -> I4; + primitiveTypeMap += definitions.LongClass -> I8; + primitiveTypeMap += definitions.FloatClass -> R4; + primitiveTypeMap += definitions.DoubleClass -> R8; + primitiveTypeMap += definitions.StringClass -> STRING; + } + + /////////////////////// Context //////////////////////////////// + + + /** + * The Context class keeps information relative to the current state + * in code generation + */ + class Context { + + /** The current package. */ + var packg: Name = _; + + /** The current class. */ + var clazz: IClass = _; + + /** The current method. */ + var method: IMethod = _; + + /** The current basic block. */ + var bb: BasicBlock = _; + + /** Map from label symbols to label objects. */ + var labels: HashMap[Symbol, Label] = new HashMap(); + + /** Current method definition. */ + var defdef: DefDef = _; + + /** TODO: add a current exception handler */ + + def this(other: Context) = { + this(); + this.packg = other.packg; + this.clazz = other.clazz; + this.method = other.method; + this.bb = other.bb; + this.labels = other.labels; + this.defdef = other.defdef; + } + + def setPackage(p: Name): this.type = { + this.packg = p; + this + } + + def setClass(c: IClass): this.type = { + this.clazz = c; + this + } + + def setMethod(m: IMethod): this.type = { + this.method = m; + this + } + + def setBasicBlock(b: BasicBlock): this.type = { + this.bb = b; + this + } + + /** Prepare a new context upon entry into a method */ + def enterMethod(m: IMethod, d: DefDef): Context = { + val ctx1 = new Context(this) setMethod(m); + ctx1.labels = new HashMap(); + ctx1.method.code = new Code(m.symbol.simpleName.toString()); + ctx1.bb = ctx1.method.code.startBlock; + ctx1.defdef = d; + ctx1 + } + + /** Return a new context for a new basic block. */ + def newBlock: Context = + new Context(this) setBasicBlock (method.code.newBlock); + } + + /** + * Represent a label in the current method code. In order + * to support forward jumps, labels can be created without + * having a deisgnated target block. They can later be attached + * by calling `anchor'. + */ + class Label(val symbol: Symbol) { + var anchored = false; + var block: BasicBlock = _; + var params: List[Symbol] = _; + + private var toPatch: List[Instruction] = Nil; + + /** Fix this label to the given basic block. */ + def anchor(b: BasicBlock): Label = { + assert(!anchored, "Cannot anchor an already anchored label!"); + this.block = b; + this + } + + def setParams(p: List[Symbol]): Label = { + assert(params == null, "Cannot set label parameters twice!"); + params = p; + this + } + + /** Add an instruction that refers to this label. */ + def addCallingInstruction(i: Instruction) = + toPatch = i :: toPatch; + + /** + * Patch the code by replacing pseudo call instructions with + * jumps to the given basic block. + */ + def patch(code: Code): Unit = { + def substMap: Map[Instruction, Instruction] = { + val map = new HashMap[Instruction, Instruction](); + + toPatch foreach (i => map += i -> patch(i)); + map + } + + val map = substMap; + code traverse (.subst(map)); + } + + /** + * Return the patched instruction. If the given instruction + * jumps to this label, replace it with the basic block. Otherwise, + * return the same instruction. Conditional jumps have more than one + * label, so they are replaced only if all labels are anchored. + */ + def patch(instr: Instruction): Instruction = { + assert(anchored, "Cannot patch until this label is anchored: " + this); + + instr match { + case PJUMP(self) + if (self == this) => JUMP(block); + + case PCJUMP(self, failure, cond) + if (self == this && failure.anchored) => + CJUMP(block, failure.block, cond); + + case PCJUMP(success, self, cond) + if (self == this && success.anchored) => + CJUMP(success.block, block, cond); + + case PCZJUMP(self, failure, cond) + if (self == this && failure.anchored) => + CZJUMP(block, failure.block, cond); + + case PCZJUMP(success, self, cond) + if (self == this && success.anchored) => + CZJUMP(success.block, block, cond); + + case _ => instr; + } + } + } + + ///////////////// Fake instructions ////////////////////////// + + /** + * Pseudo jump: it takes a Label instead of a basick block. + * It is used temporarily during code generation. It is replaced + * by a real JUMP instruction when all labels are resolved. + */ + class PseudoJUMP(label: Label) extends Instruction { + override def toString(): String ="PJUMP " + label.symbol.simpleName; + + override def consumed = 0; + override def produced = 0; + + // register with the given label + if (!label.anchored) + label.addCallingInstruction(this); + } + + case class PJUMP(where: Label) extends PseudoJUMP(where); + + case class PCJUMP(success: Label, failure: Label, cond: TestOp) + extends PseudoJUMP(success) { + + if (!failure.anchored) + failure.addCallingInstruction(this); + } + + case class PCZJUMP(success: Label, failure: Label, cond: TestOp) + extends PseudoJUMP(success) { + + if (!failure.anchored) + failure.addCallingInstruction(this); + } + + } +} + diff --git a/sources/scala/tools/nsc/backend/icode/ICodes.scala b/sources/scala/tools/nsc/backend/icode/ICodes.scala new file mode 100644 index 0000000000..14406bce1a --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/ICodes.scala @@ -0,0 +1,22 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ + +package scala.tools.nsc.backend.icode; + +/** Glue together ICode parts. + */ +abstract class ICodes: Global extends AnyRef + with Members + with BasicBlocks + with Opcodes + with TypeStacks +{ + import opcodes._; + + /** The ICode representation of classes */ + var classes: List[IClass] = Nil; +} + diff --git a/sources/scala/tools/nsc/backend/icode/Members.scala b/sources/scala/tools/nsc/backend/icode/Members.scala new file mode 100644 index 0000000000..7dac967952 --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/Members.scala @@ -0,0 +1,166 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.collection.mutable.HashMap; +import scala.collection.mutable.HashSet; + +trait Members: Global { + + /** This class represents the intermediate code of a method. + */ + class Code(label: String) { + + /** The set of all blocks */ + val blocks: HashSet[BasicBlock] = new HashSet; + + /** The start block of the method */ + var startBlock: BasicBlock = null; + + /** The stack produced by this method */ + var producedStack: TypeStack = null; + + private var currentLabel: int = 0; + private var aTreeLabels: HashMap[Symbol, BasicBlock] = new HashMap; + + // Constructor code + startBlock = newBlock; + startBlock.initStack(new TypeStack); + + /** Apply a function to all basic blocks, for side-effects. */ + def traverse(f: BasicBlock => Unit) = + traverseFeedBack((bb: BasicBlock, hm: HashMap[BasicBlock, boolean]) => f(bb)); + + /* This method applies the given function to each basic block. */ + def traverseFeedBack(f: (BasicBlock, HashMap[BasicBlock, Boolean]) => Unit) = { + val visited : HashMap[BasicBlock, Boolean] = new HashMap; + visited ++= blocks.elements.map(x => Pair(x, false)); + + var blockToVisit : List[BasicBlock] = startBlock::Nil; + + while (!blockToVisit.isEmpty) { + blockToVisit match { + case b::xs => + if (!visited(b)) { + f(b, visited); + 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 + "'"; + + /** This method print the code */ + def print() : unit = print(System.out); + + def print(out: java.io.PrintStream) : unit = { + traverse((bb: BasicBlock) => { + 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.traverse((ici: Instruction) => + out.println(" "+ici.toString())); + out.print ("Successors: "); + bb.successors.foreach((bb: BasicBlock) => out.print(bb.label+", ")); + out.println (""); // ?? Del + out.println (); + }); + } + + def logType = { + log ("// Typing " + toString()); + traverse( (bb: BasicBlock) => { + log ("Typing block #" + bb.label); + var typer = new TypeStack; + bb.traverse((ic: Instruction) => { + typer = typer.eval(ic); + log(ic.toString()+" -> "+typer.toString()); + }); + + }); + } + + /* Compute a unique new label */ + def nextLabel = { + currentLabel = currentLabel + 1; + currentLabel; + } + + /* Create a new block and append it to the list + */ + def newBlock : BasicBlock = { + val block = new BasicBlock(nextLabel); + blocks += block; + block; + } + } + + /** Represent a class in ICode */ + class IClass(val symbol: Symbol) { + var fields: List[IField] = Nil; + var methods: List[IMethod] = Nil; + var cunit: CompilationUnit = _; + + def addField(f: IField): this.type = { + fields = f :: fields; + this + } + + def addMethod(m: IMethod): this.type = { + methods = m :: methods; + this + } + + def setCompilationUnit(unit: CompilationUnit): this.type = { + this.cunit = unit; + this + } + } + + /** Represent a field in ICode */ + class IField(val symbol: Symbol) { + } + + /** Represent a method in ICode */ + class IMethod(val symbol: Symbol) { + var code: Code = null; + + /** local variables and method parameters */ + var locals: List[Symbol] = Nil; + + /** method parameters */ + var params: List[Symbol] = Nil; + + def setCode(code: Code): IMethod = { + this.code = code; + this + } + + def addLocal(sym: Symbol): Unit = + if (!(locals contains sym)) + locals = sym :: locals; + + def addLocals(ls: List[Symbol]): Unit = + ls foreach addLocal; + + def addParam(sym: Symbol): Unit = + if (!(params contains sym)) + params = sym :: params; + + def addParams(as: List[Symbol]): Unit = + as.foreach( (a: Symbol) => { addParam(a); addLocal(a); } ) + } +} diff --git a/sources/scala/tools/nsc/backend/icode/Opcodes.scala b/sources/scala/tools/nsc/backend/icode/Opcodes.scala new file mode 100644 index 0000000000..0313bd8cc8 --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/Opcodes.scala @@ -0,0 +1,419 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.ast._; +import scala.tools.nsc.backend.icode.Primitives._; + + +/** + * 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: Global { + + /** 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() extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "LOAD_ARRAY_ITEM"; + + 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 "+field.toString(); //+isStatic?" (static)":""; + + override def consumed = 1; + 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() extends Instruction { + /** Returns a string representation of this instruction */ + override def toString(): String = "STORE_ARRAY_ITEM"; + + 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.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: new - unused by jvm + * Stack: ...:arg1:arg2:...:argn + * ->: ...:ref + * + * 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.toString()+" ("+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. + * Stack: ...: + * ->: ...:ref + */ + case class NEW(clasz: Symbol) extends Instruction { + /** 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 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: Type) 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: Type) 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) + * ->: ...: + */ + case class SWITCH(tags: Array[Array[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) extends Instruction { + + /** 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: BasicBlock, failureBlock: BasicBlock, cond: TestOp) extends Instruction { + /** 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 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: Type) 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: Type) 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 new object creation? */ + def isNew: Boolean = this match { + case NewInstance => true; + case _ => false; + } + + /** 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 _ => false; + } + + /** Returns a string representation of this style. */ + override def toString(): String = this match { + case NewInstance => "new"; + case Dynamic => "dynamic"; + case Static(false) => "static-class"; + case Static(true) => "static-instance"; + case SuperCall(mixin) => "super(" + mixin + ")"; + } + } + + case object NewInstance extends InvokeStyle; + case object Dynamic extends InvokeStyle; + + /** + * Special invoke. Static(true) is used for constructor, + * priavate and super calls. + */ + case class Static(onInstance: Boolean) extends InvokeStyle; + + /** Call through super[mixin]. */ + case class SuperCall(mixin: Name) extends InvokeStyle; + + } +} diff --git a/sources/scala/tools/nsc/backend/icode/Primitives.scala b/sources/scala/tools/nsc/backend/icode/Primitives.scala new file mode 100644 index 0000000000..3fc765069e --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/Primitives.scala @@ -0,0 +1,234 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import java.io.PrintWriter; + +object Primitives { + + /** This class represents a primitive operation. */ + class Primitive { + + /** Returns a string representation of this primitive. */ + override def toString(): String = { +// val x: StringBuffer = new StringBuffer(); +// new PrimitivePrinter(new PrintWriter(x)).printPrimitive(this); +// x.toString(); + "" + } + } + + + // type : (type) => type + // range: type <- { BOOL, Ix, Ux, Rx } + // jvm : {i, l, f, d}neg + case class Negation(kind: TypeKind) extends Primitive; + + // type : zero ? (type) => BOOL : (type,type) => BOOL + // range: type <- { BOOL, Ix, Ux, Rx, REF } + // jvm : if{eq, ne, lt, ge, le, gt}, if{null, nonnull} + // if_icmp{eq, ne, lt, ge, le, gt}, if_acmp{eq,ne} + case class Test(op: TestOp, kind: TypeKind, zero: boolean) extends Primitive; + + // type : (type,type) => I4 + // range: type <- { Ix, Ux, Rx } + // jvm : lcmp, {f, d}cmp{l, g} + case class Comparison(op: ComparisonOp, kind: TypeKind) extends Primitive; + + // type : (type,type) => type + // range: type <- { Ix, Ux, Rx } + // jvm : {i, l, f, d}{add, sub, mul, div, rem} + case class Arithmetic(op: ArithmeticOp, kind: TypeKind) extends Primitive; + + // type : (type,type) => type + // range: type <- { BOOL, Ix, Ux } + // jvm : {i, l}{and, or, xor} + case class Logical(op: LogicalOp, kind: TypeKind) extends Primitive; + + // type : (type,I4) => type + // range: type <- { Ix, Ux } + // jvm : {i, l}{shl, ushl, shr} + case class Shift(op: ShiftOp, kind: TypeKind) extends Primitive; + + // type : (src) => dst + // range: src,dst <- { Ix, Ux, Rx } + // jvm : i2{l, f, d}, l2{i, f, d}, f2{i, l, d}, d2{i, l, f}, i2{b, c, s} + case class Conversion(src: TypeKind, dst: TypeKind) extends Primitive; + + // type : (Array[REF]) => I4 + // range: type <- { BOOL, Ix, Ux, Rx, REF } + // jvm : arraylength + case class ArrayLength(kind: TypeKind) extends Primitive; + + // type : (lf,rg) => STR + // range: lf,rg <- { BOOL, Ix, Ux, Rx, REF, STR } + // jvm : - + case class StringConcat(lf: TypeKind, rg: TypeKind) extends Primitive; + + + /** Pretty printer for primitives */ + class PrimitivePrinter(out: PrintWriter) { + + def print(s: String): PrimitivePrinter = { + out.print(s); + this + } + + def print(o: AnyRef): PrimitivePrinter = print(o.toString()); + + def printPrimitive(prim: Primitive) = prim match { + case Negation(kind) => + print("!"); + + case Test(op, kind, zero) => + print(op).print(kind); + + case Comparison(op, kind) => + print(op).print("(").print(kind); + + } + } + + /** This class represents a comparison operation. */ + class ComparisonOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case CMPL => "CMPL"; + case CMP => "CMP"; + case CMPG => "CMPG"; + case _ => throw new RuntimeException("ComparisonOp unknown case"); + } + } + + /** A comparison operation with -1 default for NaNs */ + case object CMPL extends ComparisonOp; + + /** A comparison operation with no default for NaNs */ + case object CMP extends ComparisonOp; + + /** A comparison operation with +1 default for NaNs */ + case object CMPG extends ComparisonOp; + + + /** This class represents a test operation. */ + class TestOp { + + /** Returns the negation of this operation. */ + def negate(): TestOp = this match { + case EQ => NE; + case NE => EQ; + case LT => GE; + case GE => LT; + case LE => GT; + case GT => LE; + case _ => throw new RuntimeException("TestOp unknown case"); + } + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case EQ => "EQ"; + case NE => "NE"; + case LT => "LT"; + case GE => "GE"; + case LE => "LE"; + case GT => "GT"; + case _ => throw new RuntimeException("TestOp unknown case"); + } + } + /** An equality test */ + case object EQ extends TestOp; + + /** A non-equality test */ + case object NE extends TestOp; + + /** A less-than test */ + case object LT extends TestOp; + + /** A greater-than-or-equal test */ + case object GE extends TestOp; + + /** A less-than-or-equal test */ + case object LE extends TestOp; + + /** A greater-than test */ + case object GT extends TestOp; + + /** This class represents an arithmetic operation. */ + class ArithmeticOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case ADD => "ADD"; + case SUB => "SUB"; + case MUL => "MUL"; + case DIV => "DIV"; + case REM => "REM"; + case _ => throw new RuntimeException("ArithmeticOp unknown case"); + } + } + + /** An arithmetic addition operation */ + case object ADD extends ArithmeticOp; + + /** An arithmetic subtraction operation */ + case object SUB extends ArithmeticOp; + + /** An arithmetic multiplication operation */ + case object MUL extends ArithmeticOp; + + /** An arithmetic division operation */ + case object DIV extends ArithmeticOp; + + /** An arithmetic remainder operation */ + case object REM extends ArithmeticOp; + + /** This class represents a shift operation. */ + class ShiftOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case LSL => "LSL"; + case ASR => "ASR"; + case LSR => "LSR"; + case _ => throw new RuntimeException("ShitOp unknown case"); + } + } + + /** A logical shift to the left */ + case object LSL extends ShiftOp; + + /** An arithmetic shift to the right */ + case object ASR extends ShiftOp; + + /** A logical shift to the right */ + case object LSR extends ShiftOp; + + /** This class represents a logical operation. */ + class LogicalOp { + + /** Returns a string representation of this operation. */ + override def toString(): String = this match { + case AND => return "AND"; + case OR => return "OR"; + case XOR => return "XOR"; + case _ => throw new RuntimeException("LogicalOp unknown case"); + } + } + + /** A bitwise AND operation */ + case object AND extends LogicalOp; + + /** A bitwise OR operation */ + case object OR extends LogicalOp; + + /** A bitwise XOR operation */ + case object XOR extends LogicalOp; +} + diff --git a/sources/scala/tools/nsc/backend/icode/Printers.scala b/sources/scala/tools/nsc/backend/icode/Printers.scala new file mode 100644 index 0000000000..4b174847ae --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/Printers.scala @@ -0,0 +1,97 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc.backend.icode; + +import java.io.PrintWriter; + +abstract class Printers { + val global: Global; + import global._; + import opcodes._; + + class TextPrinter(out: PrintWriter) { + var margin = 0; + + final val TAB = 2; + + def indent = margin = margin + TAB; + def undent = margin = margin - TAB; + + def print(s: String) = out.print(s); + def print(o: Any): Unit = print(o.toString()); + + def println(s: String): Unit = { + print(s); + println + } + + def println = { + out.println(); + var i = 0; + while (i < margin) { + print(" "); + i = i + 1; + } + } + + def printList[a](l: List[a], sep: String): Unit = l match { + case Nil => (); + case x :: Nil => print(x); + case x :: xs => print(x); print(sep); printList(xs, sep); + } + + def printList[a](pr: a => Unit)(l: List[a], sep: String): Unit = l match { + case Nil => (); + case x :: Nil => pr(x); + case x :: xs => pr(x); print(sep); printList(pr)(xs, sep); + } + + + def printClass(cls: IClass): Unit = { + print(cls.symbol.toString()); print(" extends "); + printList(cls.symbol.info.parents, ", "); + indent; println(" {"); + println("// fields:"); + cls.fields.foreach(printField); println; + println("// methods"); + cls.methods.foreach(printMethod); + undent; println; + println("}"); + } + + def printField(f: IField): Unit = { + print(f.symbol.keyString); print(" "); + print(f.symbol.nameString); print(": "); + println(f.symbol.info.toString()); + } + + def printMethod(m: IMethod): Unit = { + print("def "); print(m.symbol.name); + print("("); printList(printParam)(m.params.reverse, ", "); print(")"); + print(": "); print(m.symbol.info.resultType); println(" {"); + printCode(m.code); + println("}"); + } + + def printParam(p: Symbol): Unit = { + print(p.name); print(": "); print(p.info); + } + + def printCode(code: Code): Unit = { + code traverse printBlock; + } + + def printBlock(bb: BasicBlock): Unit = { + print(bb.label); print(": "); indent; println; + bb traverse printInstruction; + undent; println; + } + + def printInstruction(i: Instruction): Unit = { + println(i.toString()); + } + } +} diff --git a/sources/scala/tools/nsc/backend/icode/TypeKind.scala b/sources/scala/tools/nsc/backend/icode/TypeKind.scala new file mode 100644 index 0000000000..dae8820264 --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/TypeKind.scala @@ -0,0 +1,79 @@ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +/** This class represents a type kind. Type kinds + * represent the types that the VM know (or the ICode + * view of what VMs know. + */ +class TypeKind { + + /** Returns a string representation of this type kind. */ + override def toString(): String = this match { + case UNIT => "UNIT"; + case BOOL => "BOOL"; + case U1 => "U1"; + case U2 => "U2"; + case U4 => "U4"; + case U8 => "U8"; + case I1 => "I1"; + case I2 => "I2"; + case I4 => "I4"; + case I8 => "I8"; + case R4 => "R4"; + case R8 => "R8"; + case REF => "REF"; + case STRING => "STR"; + case NULL => "NULL"; + case ZERO => "ZERO"; + } +} + +/** The unit value */ +case object UNIT extends TypeKind; + +/** A boolean value */ +case object BOOL extends TypeKind; + +/** A 1-byte unsigned integer */ +case object U1 extends TypeKind; + +/** A 2-byte unsigned integer */ +case object U2 extends TypeKind; + +/** A 4-byte unsigned integer */ +case object U4 extends TypeKind; + +/** An 8-byte unsigned integer */ +case object U8 extends TypeKind; + +/** A 1-byte signed integer */ +case object I1 extends TypeKind; + +/** A 2-byte signed integer */ +case object I2 extends TypeKind; + +/** A 4-byte signed integer */ +case object I4 extends TypeKind; + +/** An 8-byte signed integer */ +case object I8 extends TypeKind; + +/** A 4-byte floating point number */ +case object R4 extends TypeKind; + +/** An 8-byte floating point number */ +case object R8 extends TypeKind; + +/** An object reference */ +case object REF extends TypeKind; + +/** A string reference */ +case object STRING extends TypeKind; + +/** The null reference */ +case object NULL extends TypeKind; + +/** The zero value */ +case object ZERO extends TypeKind; diff --git a/sources/scala/tools/nsc/backend/icode/TypeStacks.scala b/sources/scala/tools/nsc/backend/icode/TypeStacks.scala new file mode 100644 index 0000000000..6398636517 --- /dev/null +++ b/sources/scala/tools/nsc/backend/icode/TypeStacks.scala @@ -0,0 +1,131 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.tools.nsc.backend.icode.Primitives; + +trait TypeStacks: Global { + import opcodes._; + + /* This class simulates the type of the opperand + * stack of the ICode. + */ + class TypeStack { + + private var stack : List[Type] = Nil; +// private val definitions = global.definitions; + // TODO: + // private val typer = definitions.atyper; // !!! Using atree typer ! + + private def this(stack : List[Type]) = { + this(); + this.stack = stack; + } + + /** Simulate the effects of an Instruction + * on the stack. It returns the new stack + */ + def eval(instr: Instruction) : TypeStack = { + log(instr.toString()); + assert(stack.length >= instr.consumed, "Invalid instruction :"+instr+" for stack "+toString()); + + val result = new TypeStack(erase(production(instr)):::stack.drop(instr.consumed)); /// !!! Opt + 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; + + /* Compute what type(s) the instruction produce on the stack */ + private def production(instr: Instruction) : List[Type] = instr match { + case THIS(clasz) => clasz.thisType :: Nil; + + // TODO: + // case CONSTANT(aConstant) => typer.computeType(aConstant)::Nil; + + // TODO: + // case LOAD_ARRAY_ITEM() => typer.getArrayElementType(stack.tail.head)::Nil; + + case LOAD_LOCAL(local, _) => local.tpe :: Nil; + + case LOAD_FIELD(field, _) => stack.head.memberType(field) :: Nil; + + case STORE_ARRAY_ITEM() => Nil; + + case STORE_LOCAL(_,_) => Nil; + + case STORE_FIELD(_,_) => Nil; + + // TODO: + // case CALL_PRIMITIVE(p) => typer.computeType(p)::Nil; + + case CALL_METHOD(method, style) => style match { + case NewInstance => method.owner.thisType :: Nil; + case _ => method.tpe.resultType :: Nil; + } + case NEW(clasz) => clasz.tpe :: Nil; + + case CREATE_ARRAY(element) => + appliedType(definitions.ArrayClass.typeConstructor, + List(element)) :: Nil; + + case IS_INSTANCE(_) => definitions.BooleanClass.tpe :: 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 == definitions.UnitClass.tpe) + ); + + /* This method returns a String representation of the stack */ + override def toString() = { + var ret : String = ""; + stack.foreach((t: Type) => ret=(t.toString()+"::")+ret); + ret; + + } + } + +} |