From a84e0a9b9e5104f146853b89ce8b7ba56fbb03d8 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Fri, 2 Dec 2005 17:16:52 +0000 Subject: Improved generated code and line number informa... Improved generated code and line number information. --- .../tools/nsc/backend/icode/BasicBlocks.scala | 25 ++++-- .../scala/tools/nsc/backend/icode/GenICode.scala | 98 ++++++++++++++++++++-- .../scala/tools/nsc/backend/icode/Members.scala | 15 ++++ sources/scala/tools/nsc/backend/jvm/GenJVM.scala | 7 +- 4 files changed, 129 insertions(+), 16 deletions(-) diff --git a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala index 50f3c75a3a..77877c1dc0 100644 --- a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -70,7 +70,11 @@ trait BasicBlocks: ICodes { } /** The number of instructions in this basic block so far. */ - def size: Int = instrs.length; + def size: Int = + if (isClosed) + instrs.length; + else + instructionList.length; /** Initialize the stack of the block, must be done before evaluation * the type stack */ @@ -139,6 +143,8 @@ trait BasicBlocks: ICodes { instructionList = subst(instructionList); } + ////////////////////// Emit ////////////////////// + /** Add a new instruction at the end of the block, * using the same source position as the last emitted instruction @@ -166,12 +172,11 @@ trait BasicBlocks: ICodes { assert(instructionList.length > 0, "Empty block."); closed = true; - -// Console.println("block " + label + " "); - instrs = toInstructionArray(instructionList.reverse); } + def isEmpty: Boolean = instructionList.isEmpty; + /** Enter ignore mode: new 'emit'ted instructions will not be * added to this basic block. It makes the generation of THROW * and RETURNs easier. @@ -190,6 +195,12 @@ trait BasicBlocks: ICodes { else instructionList.head; + def firstInstruction = + if (closed) + instrs(0) + else + instructionList.last; + /** Convert the list to an array */ def toInstructionArray(l: List[Instruction]): Array[Instruction] = { var array = new Array[Instruction](l.length); @@ -202,7 +213,7 @@ trait BasicBlocks: ICodes { def isClosed = closed; // TODO: Take care of exception handlers! - def successors : List[BasicBlock] = // here order will count + def successors : List[BasicBlock] = if (isEmpty) Nil else lastInstruction match { case JUMP (where) => List(where); case CJUMP(success, failure, _, _) => failure::success::Nil; @@ -211,7 +222,9 @@ trait BasicBlocks: ICodes { case RETURN(_) => Nil; case THROW() => Nil; case _ => - global.abort("The last instruction is not a control flow instruction: " + lastInstruction); + if (isClosed) + global.abort("The last instruction is not a control flow instruction: " + lastInstruction); + else Nil; } /** Returns the precessors of this block, in the current 'code' chunk. diff --git a/sources/scala/tools/nsc/backend/icode/GenICode.scala b/sources/scala/tools/nsc/backend/icode/GenICode.scala index e2ac18a65d..330302b184 100644 --- a/sources/scala/tools/nsc/backend/icode/GenICode.scala +++ b/sources/scala/tools/nsc/backend/icode/GenICode.scala @@ -113,9 +113,13 @@ abstract class GenICode extends SubComponent { rhs match { case Block(_, Return(_)) => (); case Return(_) => (); - case _ => ctx1.bb.emit(RETURN(m.returnType), rhs.pos); + case _ => if (ctx1.bb.isEmpty) + ctx1.bb.emit(RETURN(m.returnType), rhs.pos); + else + ctx1.bb.emit(RETURN(m.returnType)); } ctx1.bb.close; + prune(ctx1.method); } else ctx1.method.setCode(null); ctx1; @@ -349,6 +353,14 @@ abstract class GenICode extends SubComponent { if (settings.debug.value) log("Adding label " + tree.symbol); } + +// if (!isTailCallLabel(tree.asInstanceOf[LabelDef], ctx)) { +// log("Non-tail call label found (" + tree.symbol + "), initializing arguments to default values."); +// genLoadLabelArguments(params map { p => zeroOf(toTypeKind(p.symbol.tpe)) }, +// ctx1.labels(tree.symbol), +// ctx); +// } + ctx.bb.emit(JUMP(ctx1.bb), tree.pos); ctx.bb.close; genLoad(rhs, ctx1, expectedType /*toTypeKind(tree.symbol.info.resultType)*/); @@ -400,10 +412,11 @@ abstract class GenICode extends SubComponent { elseCtx = genLoad(elsep, elseCtx, ifKind); } - thenCtx.bb.emit(JUMP(contCtx.bb), thenp.pos); + thenCtx.bb.emit(JUMP(contCtx.bb)); thenCtx.bb.close; - elseCtx.bb.emit(JUMP(contCtx.bb), elsep.pos); + elseCtx.bb.emit(JUMP(contCtx.bb)); elseCtx.bb.close; + contCtx; case Return(expr) => @@ -822,7 +835,7 @@ abstract class GenICode extends SubComponent { } private def adapt(from: TypeKind, to: TypeKind, ctx: Context, tree: Tree): Unit = { - if (!(from <:< to)) { + if (!(from <:< to) && !(from == SCALA_ALLREF && to == SCALA_ALL)) { to match { case UNIT => ctx.bb.emit(DROP(from), tree.pos); @@ -1272,6 +1285,70 @@ abstract class GenICode extends SubComponent { abort("Malformed parameter list: " + vparamss); } + /** + * If the block consists of a single unconditional jump, prune + * it by replacing the instructions in the predecessor to jump + * directly to the JUMP target of the block. + */ + def prune(method: IMethod) = { + var changed = false; + var n = 0; + + def prune0(block: BasicBlock): Unit = { + val optCont = block.lastInstruction match { + case JUMP(b) if (b != block) => Some(b); + case _ => None + } + if (block.size == 1 && optCont != None) { + val Some(cont) = optCont; + val pred = block.predecessors; + log("Preds: " + pred + " of " + block); + pred foreach { p => + p.lastInstruction match { + case CJUMP(succ, fail, cond, kind) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + if (block == succ) + CJUMP(cont, fail, cond, kind) + else if (block == fail) + CJUMP(succ, cont, cond, kind) + else + abort("Could not find block in preds")); + + case CZJUMP(succ, fail, cond, kind) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + if (block == succ) + CZJUMP(cont, fail, cond, kind) + else if (block == fail) + CZJUMP(succ, cont, cond, kind) + else + abort("Could not find block in preds")); + + case JUMP(b) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, JUMP(cont)); + } + } + if (changed) + method.code.removeBlock(block); + } + } + + do { + changed = false; + n = n + 1; + method.code traverse prune0; + } while (changed); + + log("Prune fixpoint reached in " + n + " iterations."); + } def getMaxType(ts: List[Type]): TypeKind = { def maxType(a: TypeKind, b: TypeKind): TypeKind = @@ -1281,6 +1358,15 @@ abstract class GenICode extends SubComponent { kinds reduceLeft maxType; } + /** Check weather a given label definition is introduced by the tail call phase + * It is considered to be so if all value parameters of the label are the + * same as the value parameters of the current method. + */ + def isTailCallLabel(tree: LabelDef, ctx: Context) = + tree.params.length == ctx.defdef.vparamss.head && + List.forall2(tree.params, ctx.defdef.vparamss.head) + { (x, y) => x.symbol == y.symbol } + /////////////////////// Context //////////////////////////////// @@ -1455,6 +1541,7 @@ abstract class GenICode extends SubComponent { afterCtx } } + } /** * Represent a label in the current method code. In order @@ -1544,7 +1631,7 @@ abstract class GenICode extends SubComponent { * 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 { + abstract class PseudoJUMP(label: Label) extends Instruction { override def toString(): String ="PJUMP " + label.symbol.simpleName; override def consumed = 0; @@ -1571,6 +1658,5 @@ abstract class GenICode extends SubComponent { failure.addCallingInstruction(this); } - } } diff --git a/sources/scala/tools/nsc/backend/icode/Members.scala b/sources/scala/tools/nsc/backend/icode/Members.scala index 1a8c15aa05..35fe48bcbe 100644 --- a/sources/scala/tools/nsc/backend/icode/Members.scala +++ b/sources/scala/tools/nsc/backend/icode/Members.scala @@ -37,6 +37,21 @@ trait Members: ICodes { startBlock = newBlock; startBlock.initStack(new TypeStack); + + def removeBlock(b: BasicBlock) = { + if (settings.debug.value) { + assert(blocks.forall(p => !(p.successors.contains(b))), + "Removing block that is still referenced in method code " + label); + if (b == startBlock) + assert(b.successors.length == 1, + "Removing start block with more than one successor."); + } + + if (b == startBlock) + startBlock = b.successors.head; + blocks -= b; + } + /** * Apply a function to all basic blocks, for side-effects. It starts at * the given startBlock and checks that are no predecessors of the given node. diff --git a/sources/scala/tools/nsc/backend/jvm/GenJVM.scala b/sources/scala/tools/nsc/backend/jvm/GenJVM.scala index fc9bd73608..c1e299b6ea 100644 --- a/sources/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/sources/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -312,7 +312,7 @@ abstract class GenJVM extends SubComponent { log("Generating code for block: " + b + " at pc: " + labels(b).getAnchor()); var lastMappedPC = 0; - var lastLineNr = 0; + var lastLineNr =0; var crtPC = 0; b traverse ( instr => { @@ -559,13 +559,12 @@ abstract class GenJVM extends SubComponent { crtPC = jcode.getPC(); val crtLine = try { clasz.cunit.position(instr.pos).line; } catch { - case _: Error => Position.NOPOS; + case _: Error => lastLineNr; } //System.err.println("CRTLINE: " + instr.pos + " " + // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine); - if (crtLine != lastLineNr && - crtPC > lastMappedPC) { + if (crtPC > lastMappedPC) { jcode.completeLineNumber(lastMappedPC, crtPC, crtLine); lastMappedPC = crtPC; lastLineNr = crtLine; -- cgit v1.2.3