optimizing type tests and related stuff

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@26052 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
moors 2011-11-22 23:10:23 +00:00
parent 2121b0f097
commit 06f234e3e4
2 changed files with 39 additions and 10 deletions

View File

@ -311,7 +311,11 @@ abstract class UnCurry extends InfoTransform
else if (IntClass.tpe <:< a.tpe) Literal(Constant(0))
else if (LongClass.tpe <:< a.tpe) Literal(Constant(0L))
else if (CharClass.tpe <:< a.tpe) Literal(Constant(0.toChar))
else gen.mkCast(NULL, a.tpe) // must cast, at least when a.tpe <:< NothingClass.tpe
else {
val tpA = a.tpe.normalize
if (NullClass.tpe <:< tpA) NULL
else gen.mkCast(NULL, tpA) // must cast, at least when a.tpe <:< NothingClass.tpe
}
Apply(fun.duplicate, List(zero))
case _ =>
super.transform(tree)

View File

@ -843,8 +843,18 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// repack existential types, otherwise they sometimes get unpacked in the wrong location (type inference comes up with an unexpected skolem)
// TODO: I don't really know why this happens -- maybe because the owner hierarchy changes?
// the other workaround (besides repackExistential) is to explicitly pass expectedTp as the type argument for the call to guard, but repacking the existential somehow feels more robust
// TODO: check if optimization makes a difference, try something else if necessary (cache?)
def repackExistential(tp: Type): Type = if(tp == NoType) tp
else existentialAbstraction((tp filter {t => t.typeSymbol.isExistentiallyBound}) map (_.typeSymbol), tp)
else {
val existentials = new collection.mutable.ListBuffer[Symbol]
tp foreach { t =>
val sym = t.typeSymbol
if (sym.isExistentiallyBound) existentials += sym
}
if (existentials isEmpty) tp
else existentialAbstraction(existentials toList, tp)
// existentialAbstraction((tp filter {t => t.typeSymbol.isExistentiallyBound}) map (_.typeSymbol), tp)
}
object vpmName {
val caseResult = "caseResult".toTermName
@ -866,6 +876,9 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
import CODE._
// cf. !needsTypeTest from above
def typesConform(tp: Type, pt: Type) = (tp eq pt) || (tp <:< pt)
trait CommonCodeGen extends AbsCodeGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen =>
def fun(arg: Symbol, body: Tree): Tree = Function(List(ValDef(arg)), body)
def tupleSel(binder: Symbol)(i: Int): Tree = (REF(binder) DOT vpmName.tupleIndex(i)) // make tree that accesses the i'th component of the tuple referenced by binder
@ -873,10 +886,22 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n))
def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya
def and(a: Tree, b: Tree): Tree = a AND b
def _asInstanceOf(t: Tree, tp: Type): Tree = gen.mkAsInstanceOf(t, repackExistential(tp), true, false)
// TODO: optimize to if (!needsTypeTest(b.info.widen, repackExistential(tp))) REF(b) else ...
def _asInstanceOf(b: Symbol, tp: Type): Tree = gen.mkAsInstanceOf(REF(b), repackExistential(tp), true, false)
def _asInstanceOf(t: Tree, tp: Type): Tree = { val tpX = repackExistential(tp)
if ((t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX)
else gen.mkAsInstanceOf(t, tpX, true, false)
}
def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false)
// { val tpX = repackExistential(tp)
// if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE }
// else gen.mkIsInstanceOf(REF(b), tpX, true, false)
// }
def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = repackExistential(tp)
if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX)
else gen.mkAsInstanceOf(REF(b), tpX, true, false)
}
}
trait MatchingStrategyGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen =>
@ -887,7 +912,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
def or(f: Tree, as: List[Tree]): Tree = (matchingStrategy DOT vpmName.or)((f :: as): _*) // matchingStrategy.or(f, as)
def guard(c: Tree): Tree = (matchingStrategy DOT vpmName.guard)(c, UNIT) // matchingStrategy.guard(c, then) -- a user-defined guard
// TODO: get rid of the cast when it's unnecessary, but this requires type checking `body` -- maybe this should be one of the optimisations we perform after generating the tree
def caseResult(res: Tree, tp: Type): Tree = (matchingStrategy DOT vpmName.caseResult) (pmgen._asInstanceOf(res, tp)) // matchingStrategy.caseResult(res), like one, but blow this one away for isDefinedAt (since it's the RHS of a case)
def caseResult(res: Tree, tp: Type): Tree = (matchingStrategy DOT vpmName.caseResult) (_asInstanceOf(res, tp)) // matchingStrategy.caseResult(res), like one, but blow this one away for isDefinedAt (since it's the RHS of a case)
// an internal guard TODO: use different method call so exhaustiveness can distinguish it from user-defined guards
def cond(c: Tree, then: Tree = UNIT, tp: Type = NoType): Tree = genTypeApply((matchingStrategy DOT vpmName.guard), repackExistential(tp)) APPLY (c, then) // matchingStrategy.guard(c, then)
@ -898,7 +923,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
trait MonadInstGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen =>
// methods in the monad instance -- used directly in translation
def flatMap(a: Tree, b: Tree): Tree = (a DOT vpmName.flatMap)(b)
def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (genTyped(thisCase, pt) DOT vpmName.orElse)(genTyped(elseCase, pt))
def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (genTypeApply(thisCase DOT vpmName.orElse, pt)) APPLY (elseCase)
}
// when we know we're targetting Option, do some inlining the optimizer won't do
@ -947,16 +972,16 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
override def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = {
val vs = freshSym(thisCase.pos, pt, "o")
val isEmpty = pt member vpmName.isEmpty
val v = VAL(vs) === genTyped(thisCase, pt)
val v = VAL(vs) === thisCase // genTyped(, pt)
BLOCK(
v,
IF (vs DOT isEmpty) THEN genTyped(elseCase, pt) ELSE REF(vs)
IF (vs DOT isEmpty) THEN elseCase /*genTyped(, pt)*/ ELSE REF(vs)
)
}
}
def genTypeApply(tfun: Tree, args: Type*): Tree = if(args contains NoType) tfun else TypeApply(tfun, args.toList map TypeTree)
def genTyped(t: Tree, tp: Type): Tree = if(tp == NoType) t else Typed(t, TypeTree(repackExistential(tp)))
// def genTyped(t: Tree, tp: Type): Tree = if(tp == NoType) t else Typed(t, TypeTree(repackExistential(tp)))
}
}