inliners.scala
来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 505 行 · 第 1/2 页
SCALA
505 行
/* NSC -- new Scala compiler * Copyright 2005-2008 LAMP/EPFL * @author Iulian Dragos */// $Id: Inliners.scala 14416 2008-03-19 01:17:25Z mihaylov $package scala.tools.nsc.backend.optimport scala.collection.mutable.{Map, HashMap, Set, HashSet}import scala.tools.nsc.symtab._/** * @author Iulian Dragos */abstract class Inliners extends SubComponent { import global._ import icodes._ import icodes.opcodes._ val phaseName = "inliner" /** The maximum size in basic blocks of methods considered for inlining. */ final val MAX_INLINE_SIZE = 16 /** Create a new phase */ override def newPhase(p: Phase) = new InliningPhase(p) /** The Inlining phase. */ class InliningPhase(prev: Phase) extends ICodePhase(prev) { def name = phaseName val inliner = new Inliner override def apply(c: IClass) { inliner.analyzeClass(c) } } /** * Simple inliner. * */ class Inliner { val fresh = new HashMap[String, Int] /* fresh name counter */ var count = 0 def freshName(s: String) = fresh.get(s) match { case Some(count) => fresh(s) = count + 1 s + count case None => fresh(s) = 1 s + "0" } lazy val ScalaInlineAttr = definitions.getClass("scala.inline") lazy val ScalaNoInlineAttr = definitions.getClass("scala.noinline") /** Inline the 'callee' method inside the 'caller' in the given * basic block, at the given instruction (which has to be a CALL_METHOD). */ def inline(caller: IMethod, block: BasicBlock, instr: Instruction, callee: IMethod) { log("Inlining " + callee + " in " + caller + " at pos: " + (try { instr.pos.offset.get } catch { case _ => "<nopos>" })); val targetPos = instr.pos val a = new analysis.MethodTFA(callee) /* The exception handlers that are active at the current block. */ val activeHandlers = caller.exh.filter(_.covered.contains(block)) /* Map 'original' blocks to the ones inlined in the caller. */ val inlinedBlock: Map[BasicBlock, BasicBlock] = new HashMap val varsInScope: Set[Local] = new HashSet[Local] ++ block.varsInScope.elements val instrBefore = block.toList.takeWhile { case i @ SCOPE_ENTER(l) => varsInScope += l i ne instr case i => i ne instr } val instrAfter = block.toList.drop(instrBefore.length + 1); assert(!instrAfter.isEmpty, "CALL_METHOD cannot be the last instrcution in block!"); // store the '$this' into the special local val inlinedThis = new Local(caller.symbol.newVariable(instr.pos, freshName("$inlThis")), REFERENCE(definitions.ObjectClass), false); /** buffer for the returned value */ val retVal = if (callee.returnType != UNIT) new Local(caller.symbol.newVariable(instr.pos, freshName("$retVal")), callee.returnType, false); else null; /** Add a new block in the current context. */ def newBlock = { val b = caller.code.newBlock activeHandlers.foreach (_.addCoveredBlock(b)) if (retVal ne null) b.varsInScope += retVal b.varsInScope += inlinedThis b.varsInScope ++= varsInScope b } def translateExh(e: ExceptionHandler) = { var handler: ExceptionHandler = e.dup handler.covered = handler.covered.map(inlinedBlock) handler.setStartBlock(inlinedBlock(e.startBlock)) handler } var inlinedLocals: Map[Local, Local] = new HashMap /** alfa-rename `l' in caller's context. */ def dupLocal(l: Local): Local = { val sym = caller.symbol.newVariable(l.sym.pos, freshName(l.sym.name.toString()));// sym.setInfo(l.sym.tpe); val dupped = new Local(sym, l.kind, false) inlinedLocals(l) = dupped dupped } def addLocals(m: IMethod, ls: List[Local]) = m.locals = m.locals ::: ls; def addLocal(m: IMethod, l: Local): Unit = addLocals(m, List(l)); val afterBlock = newBlock; /** Map from nw.init instructions to their matching NEW call */ val pending: collection.jcl.Map[Instruction, NEW] = new collection.jcl.HashMap /** Map an instruction from the callee to one suitable for the caller. */ def map(i: Instruction): Instruction = { val newInstr = i match { case THIS(clasz) => LOAD_LOCAL(inlinedThis); case JUMP(whereto) => JUMP(inlinedBlock(whereto)); case CJUMP(success, failure, cond, kind) => CJUMP(inlinedBlock(success), inlinedBlock(failure), cond, kind); case CZJUMP(success, failure, cond, kind) => CZJUMP(inlinedBlock(success), inlinedBlock(failure), cond, kind); case SWITCH(tags, labels) => SWITCH(tags, labels map inlinedBlock); case RETURN(kind) => JUMP(afterBlock); case LOAD_LOCAL(l) if inlinedLocals.isDefinedAt(l) => LOAD_LOCAL(inlinedLocals(l)) case STORE_LOCAL(l) if inlinedLocals.isDefinedAt(l) => STORE_LOCAL(inlinedLocals(l)) case LOAD_LOCAL(l) => assert(caller.locals contains l, "Could not find local '" + l + "' in locals, nor in inlinedLocals: " + inlinedLocals) i case STORE_LOCAL(l) => assert(caller.locals contains l, "Could not find local '" + l + "' in locals, nor in inlinedLocals: " + inlinedLocals) i case SCOPE_ENTER(l) if inlinedLocals.isDefinedAt(l) => SCOPE_ENTER(inlinedLocals(l)) case SCOPE_EXIT(l) if inlinedLocals.isDefinedAt(l) => SCOPE_EXIT(inlinedLocals(l)) case nw @ NEW(sym) => val r = NEW(sym) pending(nw.init) = r r case CALL_METHOD(meth, Static(true)) if (meth.isClassConstructor) => CALL_METHOD(meth, Static(true)) case _ => i } // check any pending NEW's if (pending isDefinedAt i) { pending(i).init = newInstr.asInstanceOf[CALL_METHOD] pending -= i } newInstr } addLocals(caller, callee.locals map dupLocal); addLocal(caller, inlinedThis); if (retVal ne null) addLocal(caller, retVal); callee.code.blocks.foreach { b => inlinedBlock += (b -> newBlock) inlinedBlock(b).varsInScope ++= (b.varsInScope map inlinedLocals) } // analyse callee a.run // re-emit the instructions before the call block.open block.clear instrBefore.foreach(i => block.emit(i, i.pos)) // store the arguments into special locals callee.params.reverse.foreach { param => block.emit(STORE_LOCAL(inlinedLocals(param)), targetPos); } block.emit(STORE_LOCAL(inlinedThis), targetPos); // jump to the start block of the callee block.emit(JUMP(inlinedBlock(callee.code.startBlock)), targetPos); block.close // duplicate the other blocks in the callee linearizer.linearize(callee).foreach { bb => var info = a.in(bb); bb traverse { i => i match { case RETURN(kind) => kind match { case UNIT => if (!info.stack.types.isEmpty) { info.stack.types foreach { t => inlinedBlock(bb).emit(DROP(t), targetPos); } } case _ => if (info.stack.length > 1) { inlinedBlock(bb).emit(STORE_LOCAL(retVal), targetPos); info.stack.types.drop(1) foreach { t => inlinedBlock(bb).emit(DROP(t), targetPos); } inlinedBlock(bb).emit(LOAD_LOCAL(retVal), targetPos); } } case _ => (); } inlinedBlock(bb).emit(map(i), targetPos); info = a.interpret(info, i);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?