Various improvements to the optimiser: more aggresive inlining for monad methods, bytecode reading in more cases, better copy propagation during closure elimination.

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@17722 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
dragos 2009-05-13 15:11:55 +00:00
parent f35a52954a
commit 9064ccb93e
17 changed files with 182 additions and 87 deletions

View File

@ -1638,7 +1638,7 @@ TEST AND DISTRIBUTION BUNDLE (ALL)
<target name="all.done" depends="dist.done, test.done"/>
<target name="all.clean" depends="locker.clean, docs.clean, dist.clean"/>
<target name="all.clean" depends="locker.clean, docs.clean, dist.clean, optimised.clean"/>
<!-- ===========================================================================
STABLE REFERENCE (STARR)

View File

@ -445,7 +445,7 @@ trait BasicBlocks {
case _ => false
}
override def hashCode = label
override def hashCode = label * 41 + code.hashCode
// Instead of it, rather use a printer
def print() { print(java.lang.System.out) }

View File

@ -302,6 +302,8 @@ trait Members { self: ICodes =>
other.isInstanceOf[Local] &&
other.asInstanceOf[Local].sym == this.sym
);
override def hashCode = sym.hashCode
override def toString(): String = sym.toString()
}

View File

@ -116,7 +116,9 @@ trait Printers { self: ICodes =>
def printBlock(bb: BasicBlock) {
print(bb.label)
if (bb.loopHeader) print("[loop header]")
print(": "); indent; println
print(": ");
if (settings.debug.value) print("pred: " + bb.predecessors + " succs: " + bb.successors)
indent; println
bb.toList foreach printInstruction
undent; println
}
@ -125,7 +127,7 @@ trait Printers { self: ICodes =>
// if (settings.Xdce.value)
// print(if (i.useful) " " else " * ");
if (settings.debug.value)
print(i.pos.line.map(_.toString).getOrElse("No line"))
print(i.pos.line.map(_.toString).getOrElse(""))
println(i.toString())
}
}

View File

@ -34,6 +34,7 @@ trait Repository {
def icode(sym: Symbol, force: Boolean): IClass =
if (available(sym)) icode(sym).get
else {
log("loading " + sym)
load(sym)
assert(available(sym))
loaded(sym)

View File

@ -44,7 +44,7 @@ trait TypeKinds { self: ICodes =>
case LONG => definitions.LongClass.tpe
case FLOAT => definitions.FloatClass.tpe
case DOUBLE => definitions.DoubleClass.tpe
case REFERENCE(cls) => typeRef(cls.typeConstructor.prefix, cls, Nil)
case REFERENCE(cls) => cls.tpe //typeRef(cls.typeConstructor.prefix, cls, Nil)
//case VALUE(cls) => typeRef(cls.typeConstructor.prefix, cls, Nil);
case ARRAY(elem) => typeRef(definitions.ArrayClass.typeConstructor.prefix,
definitions.ArrayClass,

View File

@ -121,15 +121,41 @@ abstract class CopyPropagation {
* binding of that local.
*/
def getFieldValue(r: Record, f: Symbol): Option[Value] = {
if(!r.bindings.isDefinedAt(f)) None else {
var target: Value = r.bindings(f)
target match {
case Deref(LocalVar(l)) => Some(getBinding(l))
case Deref(Field(r1, f1)) => getFieldValue(r1, f1) orElse Some(target)
// case Deref(This) => Some(target)
// case Const(k) => Some(target)
case _ => Some(target)
}
}
}
/** The same as getFieldValue, but never returns Record/Field values. Use
* this when you want to find a replacement for a field value (either a local,
* or a constant/this value).
*/
def getFieldNonRecordValue(r: Record, f: Symbol): Option[Value] = {
assert(r.bindings.isDefinedAt(f),
"Record " + r + " does not contain a field " + f);
var target: Value = r.bindings(f)
target match {
case Deref(LocalVar(l)) => Some(Deref(LocalVar(getAlias(l))))
case Deref(This) => Some(target)
case Const(k) => Some(target)
case _ => None
case Deref(LocalVar(l)) =>
val alias = getAlias(l)
getBinding(alias) match {
case Record(_, _) => Some(Deref(LocalVar(alias)))
case Deref(Field(r1, f1)) =>
getFieldNonRecordValue(r1, f1) orElse Some(Deref(LocalVar(alias)))
case v => Some(v)
}
case Deref(Field(r1, f1)) =>
getFieldNonRecordValue(r1, f1) orElse None
case Deref(This) => Some(target)
case Const(k) => Some(target)
case _ => None
}
}
@ -159,8 +185,8 @@ abstract class CopyPropagation {
if (a.stack eq exceptionHandlerStack) a.stack
else if (b.stack eq exceptionHandlerStack) b.stack
else {
if (a.stack.length != b.stack.length)
throw new LubError(a, b, "Invalid stacks in states: ");
// if (a.stack.length != b.stack.length)
// throw new LubError(a, b, "Invalid stacks in states: ");
List.map2(a.stack, b.stack) { (v1, v2) =>
if (v1 == v2) v1 else Unknown
}
@ -172,9 +198,9 @@ abstract class CopyPropagation {
if (v1 == v2) v1 else Unknown
}
*/
val commonPairs = a.bindings.toList intersect (b.bindings.toList)
val resBindings = new HashMap[Location, Value]
for ((k, v) <- commonPairs)
for ((k, v) <- a.bindings if b.bindings.isDefinedAt(k) && v == b.bindings(k))
resBindings += (k -> v);
new State(resBindings, resStack)
}
@ -253,7 +279,21 @@ abstract class CopyPropagation {
val v1 = in.stack match {
case (r @ Record(cls, bindings)) :: xs =>
Deref(Field(r, field))
case Deref(LocalVar(l)) :: _ =>
in.getBinding(l) match {
case r @ Record(cls, bindings) => Deref(Field(r, field))
case _ => Unknown
}
case Deref(Field(r, f)) :: _ =>
val fld = in.getFieldValue(r, f)
fld match {
case Some(r @ Record(cls, bindings)) if bindings.isDefinedAt(f) =>
in.getFieldValue(r, f).getOrElse(Unknown)
case _ => Unknown
}
case _ => Unknown
}
out.stack = v1 :: out.stack.drop(1)
@ -474,17 +514,22 @@ abstract class CopyPropagation {
* @param state ...
*/
final def invalidateRecords(state: copyLattice.State) {
def shouldRetain(sym: Symbol): Boolean = {
if (sym.hasFlag(symtab.Flags.MUTABLE))
log("dropping binding for " + sym.fullNameString)
!sym.hasFlag(symtab.Flags.MUTABLE)
}
state.stack = state.stack map { v => v match {
case Record(cls, bindings) =>
bindings.retain { (sym: Symbol, v: Value) => !sym.hasFlag(symtab.Flags.MUTABLE) }
bindings.retain { (sym: Symbol, v: Value) => shouldRetain(sym) }
Record(cls, bindings)
case _ => v
}}
state.bindings retain {(loc, value) =>
value match {
case Deref(Field(_, _)) => false
case Boxed(Field(_, _)) => false
case Deref(Field(rec, sym)) => shouldRetain(sym)
case Boxed(Field(rec, sym)) => shouldRetain(sym)
case _ => true
}
}

View File

@ -55,19 +55,21 @@ trait DataFlowAnalysis[L <: CompleteLattice] {
def forwardAnalysis(f: (P, lattice.Elem) => lattice.Elem): Unit = try {
while (!worklist.isEmpty) {
if (stat) iterations += 1
// Console.println("worklist in: " + worklist);
//Console.println("worklist in: " + worklist);
val point = worklist.elements.next; worklist -= point; visited += point;
//Console.println("taking out point: " + point + " worklist out: " + worklist);
val output = f(point, in(point))
if ((lattice.bottom == out(point)) || output != out(point)) {
// Console.println("Output changed at " + point + " from: " + out(point) + " to: " + output + " and they are different: " + (output != out(point)))
// Console.println("Output changed at " + point
// + " from: " + out(point) + " to: " + output
// + " for input: " + in(point) + " and they are different: " + (output != out(point)))
out(point) = output
val succs = point.successors
succs foreach { p =>
if (!worklist.contains(p))
worklist += p;
in(p) = lattice.lub(in(p) :: (p.predecessors map out.apply))
in(p) = lattice.lub(/*in(p) :: */(p.predecessors map out.apply))
}
}
}

View File

@ -229,7 +229,7 @@ abstract class ReachingDefinitions {
} else {
val stack = this.in(bb).stack
assert(stack.length >= m, "entry stack is too small, expected: " + m + " found: " + stack)
stack.take(m) flatMap (_.toList)
stack.drop(depth).take(m) flatMap (_.toList)
}
/** Return the definitions that produced the topmost 'm' elements on the stack,

View File

@ -51,8 +51,8 @@ abstract class TypeFlowAnalysis {
else if (s1 eq exceptionHandlerStack) s1
else if (s2 eq exceptionHandlerStack) s2
else {
if (s1.length != s2.length)
throw new CheckerError("Incompatible stacks: " + s1 + " and " + s2);
// if (s1.length != s2.length)
// throw new CheckerError("Incompatible stacks: " + s1 + " and " + s2);
new TypeStack(List.map2(s1.types, s2.types) (icodes.lub))
}
}
@ -89,12 +89,12 @@ abstract class TypeFlowAnalysis {
for (binding1 <- env1.elements) {
val tp2 = env2(binding1._1)
resultingLocals += (binding1._1 -> typeLattice.lub2(binding1._2, tp2))
resultingLocals += ((binding1._1, typeLattice.lub2(binding1._2, tp2)))
}
for (binding2 <- env2.elements if resultingLocals(binding2._1) eq typeLattice.bottom) {
val tp1 = env1(binding2._1)
resultingLocals += (binding2._1 -> typeLattice.lub2(binding2._2, tp1))
resultingLocals += ((binding2._1, typeLattice.lub2(binding2._2, tp1)))
}
IState(resultingLocals, typeStackLattice.lub2(a.stack, b.stack))

View File

@ -123,31 +123,36 @@ abstract class ClosureElimination extends SubComponent {
}
case LOAD_FIELD(f, false) if accessible(f, m.symbol) =>
case LOAD_FIELD(f, false) /* if accessible(f, m.symbol) */ =>
def replaceFieldAccess(r: Record) {
val Record(cls, bindings) = r
info.getFieldNonRecordValue(r, f) match {
case Some(v) =>
bb.replaceInstruction(i,
DROP(REFERENCE(cls)) :: valueToInstruction(v) :: Nil);
log("Replaced " + i + " with " + info.getFieldNonRecordValue(r, f));
case None => ();
}
}
info.stack(0) match {
case r @ Record(cls, bindings) if bindings.isDefinedAt(f) =>
info.getFieldValue(r, f) match {
case Some(v) =>
bb.replaceInstruction(i,
DROP(REFERENCE(cls)) :: valueToInstruction(v) :: Nil);
log("Replaced " + i + " with " + info.getBinding(r, f));
case None => ();
}
case r @ Record(_, bindings) if bindings.isDefinedAt(f) =>
replaceFieldAccess(r)
case Deref(LocalVar(l)) =>
info.getBinding(l) match {
case r @ Record(cls, bindings) if bindings.isDefinedAt(f) =>
info.getFieldValue(r, f) match {
case Some(v) =>
bb.replaceInstruction(i,
DROP(REFERENCE(cls)) :: valueToInstruction(v) :: Nil);
log("Replaced " + i + " with " + info.getBinding(r, f));
case None => ();
}
case _ => ();
case r @ Record(_, bindings) if bindings.isDefinedAt(f) =>
replaceFieldAccess(r)
case _ =>
}
case Deref(Field(r1, f1)) =>
info.getFieldValue(r1, f1) match {
case Some(r @ Record(_, bindings)) if bindings.isDefinedAt(f) =>
replaceFieldAccess(r)
case _ =>
}
case _ => ();
case _ =>
}
case UNBOX(_) =>

View File

@ -277,20 +277,22 @@ abstract class Inliners extends SubComponent {
val tfa = new analysis.MethodTFA();
tfa.stat = settings.statistics.value
def analyzeMethod(m: IMethod): Unit = try {
// how many times have we already inlined this method here?
private val inlinedMethods: Map[Symbol, Int] = new HashMap[Symbol, Int] {
override def default(k: Symbol) = 0
}
def analyzeMethod(m: IMethod): Unit = {//try {
var retry = false
var count = 0
fresh.clear
// how many times have we already inlined this method here?
val inlinedMethods: Map[Symbol, Int] = new HashMap[Symbol, Int] {
override def default(k: Symbol) = 0
}
inlinedMethods.clear
do {
retry = false;
if (m.code ne null) {
if (settings.debug.value)
log("Analyzing " + m + " count " + count);
log("Analyzing " + m + " count " + count + " with " + m.code.blocks.length + " blocks");
tfa.init(m)
tfa.run
for (bb <- linearizer.linearize(m)) {
@ -314,8 +316,7 @@ abstract class Inliners extends SubComponent {
log("\tlooked up method: " + concreteMethod.fullNameString)
}
if (receiver == definitions.PredefModule.moduleClass) {
log("loading predef")
if (shouldLoad(receiver, concreteMethod)) {
icodes.icode(receiver, true)
}
if (settings.debug.value)
@ -331,12 +332,11 @@ abstract class Inliners extends SubComponent {
icodes.icode(receiver).get.lookupMethod(concreteMethod) match {
case Some(inc) =>
if (inc.symbol != m.symbol
&& (inlinedMethods(inc.symbol) < 2)
&& (inc.code ne null)
&& shouldInline(m, inc)
&& isSafeToInline(m, inc, info.stack)) {
retry = true;
if (!isClosureClass(receiver)) // only count non-closures
if (!(isClosureClass(receiver) && (concreteMethod.name == nme.apply))) // only count non-closures
count = count + 1;
inline(m, bb, i, inc);
inlinedMethods(inc.symbol) = inlinedMethods(inc.symbol) + 1
@ -353,7 +353,8 @@ abstract class Inliners extends SubComponent {
+ "\n\tshouldInline heuristics: " + shouldInline(m, inc) else ""));
}
case None =>
();
if (settings.debug.value)
log("could not find icode\n\treceiver: " + receiver + "\n\tmethod: " + concreteMethod)
}
}
@ -364,14 +365,29 @@ abstract class Inliners extends SubComponent {
if (tfa.stat) log(m.symbol.fullNameString + " iterations: " + tfa.iterations + " (size: " + m.code.blocks.length + ")")
}} while (retry && count < 15)
m.normalize
} catch {
case e =>
Console.println("############# Caught exception: " + e + " #################");
Console.println("\nMethod: " + m +
"\nMethod owner: " + m.symbol.owner);
e.printStackTrace();
m.dump
throw e
// } catch {
// case e =>
// Console.println("############# Caught exception: " + e + " #################");
// Console.println("\nMethod: " + m +
// "\nMethod owner: " + m.symbol.owner);
// e.printStackTrace();
// m.dump
// throw e
}
def isMonadMethod(method: Symbol): Boolean =
(method.name == nme.foreach
|| method.name == nme.filter
|| method.name == nme.map
|| method.name == nme.flatMap)
/** Should the given method be loaded from disk? */
def shouldLoad(receiver: Symbol, method: Symbol): Boolean = {
if (settings.debug.value) log("shouldLoad: " + receiver + "." + method)
((method.isFinal && isMonadMethod(method) && isHigherOrderMethod(method))
|| (receiver.enclosingPackage == definitions.ScalaRunTimeModule.enclosingPackage)
|| (receiver == definitions.PredefModule.moduleClass))
}
/** Cache whether a method calls private members. */
@ -407,7 +423,8 @@ abstract class Inliners extends SubComponent {
case LOAD_FIELD(f, _) =>
if (f.hasFlag(Flags.PRIVATE))
if (f.hasFlag(Flags.SYNTHETIC) || f.hasFlag(Flags.PARAMACCESSOR)) {
if ((callee.sourceFile ne null)
&& (f.hasFlag(Flags.SYNTHETIC) || f.hasFlag(Flags.PARAMACCESSOR))) {
if (settings.debug.value)
log("Making not-private symbol out of synthetic: " + f);
f.setFlag(Flags.notPRIVATE)
@ -416,7 +433,8 @@ abstract class Inliners extends SubComponent {
case STORE_FIELD(f, _) =>
if (f.hasFlag(Flags.PRIVATE))
if (f.hasFlag(Flags.SYNTHETIC) || f.hasFlag(Flags.PARAMACCESSOR)) {
if ((callee.sourceFile ne null)
&& (f.hasFlag(Flags.SYNTHETIC) || f.hasFlag(Flags.PARAMACCESSOR))) {
if (settings.debug.value)
log("Making not-private symbol out of synthetic: " + f);
f.setFlag(Flags.notPRIVATE)
@ -456,7 +474,7 @@ abstract class Inliners extends SubComponent {
}
/** small method size (in blocks) */
val SMALL_METHOD_SIZE = 4
val SMALL_METHOD_SIZE = 1
/** Decide whether to inline or not. Heuristics:
* - it's bad to make the caller larger (> SMALL_METHOD_SIZE)
@ -483,14 +501,15 @@ abstract class Inliners extends SubComponent {
if (callee.code.blocks.length > MAX_INLINE_SIZE)
score -= 1
if (callee.symbol.tpe.paramTypes.exists(t => definitions.FunctionClass.contains(t.typeSymbol))) {
if (settings.debug.value)
log("increased score to: " + score)
if (isMonadMethod(callee.symbol))
score += 2
}
else if (isHigherOrderMethod(callee.symbol))
score += 1
if (isClosureClass(callee.symbol.owner))
score += 2
if (inlinedMethods(callee.symbol) > 2) score -= 2
if (settings.debug.value) log("shouldInline(" + callee + ") score: " + score)
score > 0
}
} /* class Inliner */
@ -504,4 +523,10 @@ abstract class Inliners extends SubComponent {
}
res
}
/** Does 'sym' denote a higher order method? */
def isHigherOrderMethod(sym: Symbol): Boolean =
(sym.isMethod
&& atPhase(currentRun.erasurePhase.prev)(sym.info.paramTypes exists definitions.isFunctionType))
} /* class Inliners */

View File

@ -424,7 +424,7 @@ trait Definitions {
clazz.setInfo(
PolyType(
List(tparam),
ClassInfoType(List(p), newClassScope(clazz), clazz)))
ClassInfoType(List(AnyRefClass.tpe, p), newClassScope(clazz), clazz)))
}
private def newAlias(owner: Symbol, name: Name, alias: Type): Symbol = {

View File

@ -166,8 +166,8 @@ abstract class SymbolLoaders {
def refresh() {
/** Is the given name a valid input file base name? */
def isValid(name: String): Boolean =
name.length() > 0 && !name.endsWith("$class") &&
(/*settings.XO.value*/true || name.indexOf("$anon") == -1)
name.length() > 0 /*&& !name.endsWith("$class") &&
(/*settings.XO.value*/true || name.indexOf("$anon") == -1) */
val classes = new HashMap[String, global.classPath0.Context]
val packages = new HashMap[String, global.classPath0.Context]

View File

@ -51,8 +51,10 @@ abstract class ClassfileParser {
}
def parse(file: AbstractFile, root: Symbol) = try {
def handleMissing(e: MissingRequirementError) =
def handleMissing(e: MissingRequirementError) = {
if (settings.debug.value) e.printStackTrace
throw new IOException("Missing dependency '" + e.req + "', required by " + in.file)
}
def handleError(e: Exception) = {
if (settings.debug.value) e.printStackTrace()
@ -217,15 +219,22 @@ abstract class ClassfileParser {
//assert(name.endsWith("$"), "Not a module class: " + name)
f = definitions.getModule(name.subName(0, name.length - 1))
} else {
val origName = nme.originalName(name)
val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol
// println("\t" + owner.info.member(name).tpe.widen + " =:= " + tpe)
f = owner.info.member(name).suchThat(_.tpe.widen =:= tpe)
f = owner.info.member(origName).suchThat(_.tpe.widen =:= tpe)
if (f == NoSymbol)
f = owner.info.member(newTermName(name.toString + nme.LOCAL_SUFFIX)).suchThat(_.tpe =:= tpe)
f = owner.info.member(newTermName(origName.toString + nme.LOCAL_SUFFIX)).suchThat(_.tpe =:= tpe)
if (f == NoSymbol) {
// if it's an impl class, try to find it's static member inside the class
assert(ownerTpe.typeSymbol.isImplClass, "Not an implementation class: " + owner + " couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe.members);
f = ownerTpe.member(name).suchThat(_.tpe =:= tpe)
if (ownerTpe.typeSymbol.isImplClass)
f = ownerTpe.member(origName).suchThat(_.tpe =:= tpe)
else {
log("Couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe)
f = if (tpe.isInstanceOf[MethodType]) owner.newMethod(owner.pos, name).setInfo(tpe)
else owner.newValue(owner.pos, name).setInfo(tpe).setFlag(MUTABLE)
log("created fake member " + f.fullNameString)
}
// println("\townerTpe.decls: " + ownerTpe.decls)
// println("Looking for: " + name + ": " + tpe + " inside: " + ownerTpe.typeSymbol + "\n\tand found: " + ownerTpe.members)
}

View File

@ -44,6 +44,7 @@ abstract class ICodeReader extends ClassfileParser {
var classFile: AbstractFile = null;
var sym = cls
sym.info // ensure accurate type information
isScalaModule = cls.isModule && !cls.hasFlag(JAVA)
log("Reading class: " + cls + " isScalaModule?: " + isScalaModule)
val name = cls.fullNameString(java.io.File.separatorChar) + (if (sym.hasFlag(MODULE)) "$" else "")
@ -191,8 +192,8 @@ abstract class ICodeReader extends ClassfileParser {
definitions.NullClass
else if (name.endsWith("$class")) {
val iface = definitions.getClass(name.subName(0, name.length - "$class".length))
log("forcing " + iface)
iface.info // force the mixin type-transformer
log("forcing " + iface.owner + " at phase: " + phase + " impl: " + iface.implClass)
iface.owner.info // force the mixin type-transformer
definitions.getClass(name)
} else if (name.endsWith("$"))
definitions.getModule(name.subName(0, name.length - 1))
@ -926,16 +927,19 @@ abstract class ICodeReader extends ClassfileParser {
for ((i, idx) <- bb.toList.zipWithIndex) i match {
case CALL_METHOD(m, Static(true)) if m.isClassConstructor =>
val defs = rdef.findDefs(bb, idx, 1, m.info.paramTypes.length)
//println("ctor: " + i + " found defs: " + defs)
assert(defs.length == 1)
if (settings.debug.value) log("ctor: " + i + " found defs: " + defs)
assert(defs.length == 1, "wrong defs at bb " + bb + "\n" + method.dump + rdef)
val (bb1, idx1) = defs.head
var producer = bb1(idx1)
while (producer.isInstanceOf[DUP]) {
val (bb2, idx2) = rdef.findDefs(bb1, idx1, 1).head
producer = bb2(idx2)
}
assert(producer.isInstanceOf[NEW], producer)
producer.asInstanceOf[NEW].init = i.asInstanceOf[CALL_METHOD]
producer match {
case nw: NEW => nw.init = i.asInstanceOf[CALL_METHOD]
case _: THIS => () // super constructor call
case _ => assert(false, producer + "\n" + method.dump)
}
case _ =>
}
}

View File

@ -12,7 +12,7 @@
package scala.runtime
final class RichInt(start: Int) extends Proxy with Ordered[Int] {
final class RichInt(val start: Int) extends Proxy with Ordered[Int] {
// Proxy
def self: Any = start