Skip to content

Commit 2e7294c

Browse files
committed
Perform switch optimization in pattern matcher
1 parent 003301b commit 2e7294c

File tree

1 file changed

+42
-2
lines changed

1 file changed

+42
-2
lines changed

compiler/src/dotty/tools/dotc/transform/PatMat.scala

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ object PatMat {
127127
override def toString = i"TypeTest($scrutinee: $tpt)"
128128
}
129129

130-
class EqualTest(scrut: Symbol, tree: Tree, ons: Node, onf: Node) extends Test(scrut, ons, onf) {
130+
class EqualTest(scrut: Symbol, val tree: Tree, ons: Node, onf: Node) extends Test(scrut, ons, onf) {
131131
def pos = tree.pos
132132
def condition = applyOverloaded(tree, nme.EQ, scrutinee :: Nil, Nil, defn.BooleanType)
133133
override def toString = i"EqualTest($tree == $scrutinee)"
@@ -404,14 +404,54 @@ object PatMat {
404404

405405
val emitted = mutable.Set[Int]()
406406

407+
/** Collect longest list of nodes that represent possible cases of
408+
* a switch, including a last default case, by starting with this
409+
* node on following onSuccess nodes.
410+
*/
411+
def collectSwitchCases(node: Test): List[Node] = {
412+
def isSwitchableType(tpe: Type): Boolean =
413+
(tpe isRef defn.IntClass) ||
414+
(tpe isRef defn.ByteClass) ||
415+
(tpe isRef defn.ShortClass) ||
416+
(tpe isRef defn.CharClass)
417+
418+
val scrutinee = node.scrutinee
419+
420+
def isIntConst(tree: Tree) = tree match {
421+
case Literal(const) => const.isIntRange
422+
case _ => false
423+
}
424+
425+
def recur(node: Node): List[Node] = node match {
426+
case node: EqualTest if node.scrutinee === scrutinee && isIntConst(node.tree) =>
427+
node :: recur(node.onFailure)
428+
case _ =>
429+
node :: Nil
430+
}
431+
432+
recur(node)
433+
}
434+
435+
/** Emit cases of a switch */
436+
def emitSwitchCases(cases: List[Node]): List[CaseDef] = cases match {
437+
case (test: EqualTest) :: cases1 =>
438+
CaseDef(test.tree, EmptyTree, emit(test.onSuccess)) :: emitSwitchCases(cases1)
439+
case (default: Node) :: Nil =>
440+
CaseDef(Underscore(defn.IntType), EmptyTree, emit(default)) :: Nil
441+
}
442+
407443
def emit(node: Node): Tree = {
408444
if (selfCheck) {
409445
assert(node.isInstanceOf[CallNode] || !emitted.contains(node.id), node.id)
410446
emitted += node.id
411447
}
412448
node match {
413449
case node: Test =>
414-
If(node.condition, emit(node.onSuccess), emit(node.onFailure)).withPos(node.pos)
450+
val switchCases = collectSwitchCases(node)
451+
if (switchCases.lengthCompare(4) >= 0) // at least 3 cases + default
452+
Match(node.scrutinee, emitSwitchCases(switchCases))
453+
else
454+
If(node.condition, emit(node.onSuccess), emit(node.onFailure)).withPos(node.pos)
415455
case node @ LetNode(sym, body) =>
416456
val symDef =
417457
if (sym.is(Label)) DefDef(sym, emit(labelled(sym)))

0 commit comments

Comments
 (0)