Skip to content

Commit 067c250

Browse files
committed
Implement Tuple operation directly in the compiler
To increase the performance of compiling code with Tuple/NonEmptyTuple operations
1 parent 77bce31 commit 067c250

File tree

6 files changed

+103
-22
lines changed

6 files changed

+103
-22
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class Compiler {
8686
new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition.
8787
new ResolveSuper, // Implement super accessors
8888
new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method
89+
new GenericTuples, // TODO
8990
new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify.
9091
List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
9192
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,10 @@ class Definitions {
683683
lazy val Product_productPrefixR: TermRef = ProductClass.requiredMethodRef(nme.productPrefix)
684684
def Product_productPrefix(implicit ctx: Context): Symbol = Product_productPrefixR.symbol
685685

686+
lazy val IteratorType: TypeRef = ctx.requiredClassRef("scala.collection.Iterator")
687+
def IteratorClass(implicit ctx: Context): ClassSymbol = IteratorType.symbol.asClass
688+
def IteratorModule(implicit ctx: Context): Symbol = IteratorClass.companionModule
689+
686690
lazy val ModuleSerializationProxyType: TypeRef = ctx.requiredClassRef("scala.runtime.ModuleSerializationProxy")
687691
def ModuleSerializationProxyClass(implicit ctx: Context): ClassSymbol = ModuleSerializationProxyType.symbol.asClass
688692
lazy val ModuleSerializationProxyConstructor: TermSymbol =
@@ -783,6 +787,8 @@ class Definitions {
783787

784788
lazy val TupleTypeRef: TypeRef = ctx.requiredClassRef("scala.Tuple")
785789
def TupleClass(implicit ctx: Context): ClassSymbol = TupleTypeRef.symbol.asClass
790+
lazy val Tuple_consR: TermRef = TupleClass.requiredMethod("*:").termRef
791+
def Tuple_cons: Symbol = Tuple_consR.symbol
786792
lazy val NonEmptyTupleTypeRef: TypeRef = ctx.requiredClassRef("scala.NonEmptyTuple")
787793
def NonEmptyTupleClass(implicit ctx: Context): ClassSymbol = NonEmptyTupleTypeRef.symbol.asClass
788794

@@ -794,6 +800,10 @@ class Definitions {
794800

795801
def TupleXXL_apply(implicit ctx: Context): Symbol =
796802
TupleXXLModule.info.member(nme.apply).requiredSymbol("method", nme.apply, TupleXXLModule)(_.info.isVarArgsMethod)
803+
def TupleXXL_fromIterator(implicit ctx: Context): Symbol = TupleXXLModule.requiredMethod("fromIterator")
804+
805+
lazy val DynamicTupleModule: Symbol = ctx.requiredModule("scala.runtime.DynamicTuple")
806+
lazy val DynamicTuple_consIterator: Symbol = DynamicTupleModule.requiredMethod("consIterator")
797807

798808
// Annotation base classes
799809
lazy val AnnotationType: TypeRef = ctx.requiredClassRef("scala.annotation.Annotation")
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import Constants.Constant
6+
import Contexts.Context
7+
import Decorators._
8+
import Flags._
9+
import ast.Trees._
10+
import Definitions._
11+
import DenotTransformers._
12+
import StdNames._
13+
import Symbols._
14+
import MegaPhase._
15+
import Types._
16+
import dotty.tools.dotc.ast.tpd
17+
18+
19+
import scala.annotation.tailrec
20+
21+
/** TODO
22+
*/
23+
class GenericTuples extends MiniPhase with IdentityDenotTransformer {
24+
import tpd._
25+
26+
def phaseName: String = "genericTuples"
27+
28+
override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree = {
29+
if (tree.symbol == defn.Tuple_cons) transformTupleCons(tree)
30+
else super.transformApply(tree)
31+
}
32+
33+
private def transformTupleCons(tree: tpd.Apply)(implicit ctx: Context): Tree = {
34+
val TypeApply(Select(qual, _), headType :: tailType :: Nil) = tree.fun
35+
tupleTypes(tree.tpe) match {
36+
case Some(tpes) =>
37+
val size = tpes.size
38+
if (size <= 5) {
39+
val tailType =
40+
if (size == 1) defn.UnitType
41+
else defn.TupleType(size - 1).appliedTo(tpes.tail)
42+
evalOnce(Typed(qual, TypeTree(tailType))) { tup =>
43+
val elements = tree.args.head :: (0 until size - 1).map(i => tup.select(nme.selectorName(i))).toList
44+
knownTupleFromElements(tpes, elements)
45+
}
46+
} else {
47+
val fullIterator = ref(defn.DynamicTuple_consIterator).appliedToArgs(tree.args.head :: qual :: Nil)
48+
evalOnce(fullIterator) { it =>
49+
knownTupleFromIterator(tpes.length, it).asInstance(tree.tpe)
50+
}
51+
}
52+
case _ =>
53+
ref(defn.DynamicTupleModule).select("dynamic_*:".toTermName).appliedToTypeTrees(tailType :: headType :: Nil).appliedToArgs(qual :: tree.args).asInstance(tree.tpe)
54+
}
55+
}
56+
57+
/** Create a TupleN (1 <= N < 23) from the elements */
58+
private def knownTupleFromElements(tpes: List[Type], elements: List[Tree])(implicit ctx: Context) = {
59+
val size = elements.size
60+
assert(0 < size && size <= Definitions.MaxTupleArity)
61+
val tupleModule = defn.TupleType(size).classSymbol.companionModule
62+
ref(tupleModule).select(nme.apply).appliedToTypes(tpes).appliedToArgs(elements)
63+
}
64+
65+
private def knownTupleFromIterator(size: Int, it: Tree)(implicit ctx: Context): Tree = {
66+
if (size == 0) Literal(Constant(())) // TODO should this code be here? Or assert(size > specializedSize)
67+
else if (size <= Definitions.MaxTupleArity) {
68+
// TODO outline this code for the 22 alternatives (or less, may not need the smallest ones)?
69+
// This would yield smaller bytecode at the cost of an extra (easily JIT inlinable) call.
70+
// def dynamicTupleN(it: Iterator[Any]): TupleN[Any, ..., Any] = Tuple(it.next(), ..., it.next())
71+
val tpes = List.fill(size)(defn.AnyType)
72+
val elements = (0 until size).map(_ => it.select(nme.next)).toList
73+
knownTupleFromElements(tpes, elements)
74+
} else {
75+
ref(defn.TupleXXL_fromIterator).appliedTo(it)
76+
}
77+
}
78+
79+
private def tupleTypes(tp: Type)(implicit ctx: Context): Option[List[Type]] = {
80+
@tailrec def rec(tp: Type, acc: List[Type]): Option[List[Type]] = tp match {
81+
case tp: AppliedType if defn.PairClass == tp.classSymbol => rec(tp.args(1), tp.args(0) :: acc)
82+
case tp if tp.classSymbol == defn.UnitClass => Some(acc.reverse)
83+
case _ => None
84+
}
85+
rec(tp.stripTypeVar, Nil)
86+
}
87+
}

library/src-3.x/scala/Tuple.scala

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,7 @@ sealed trait Tuple extends Any {
3232
DynamicTuple.dynamicToArray(this)
3333
}
3434

35-
inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This = {
36-
type Result = H *: This
37-
inline constValueOpt[BoundedSize[this.type]] match {
38-
case Some(0) =>
39-
Tuple1(x).asInstanceOf[Result]
40-
case Some(1) =>
41-
Tuple2(x, asInstanceOf[Tuple1[_]]._1).asInstanceOf[Result]
42-
case Some(2) =>
43-
val t = asInstanceOf[Tuple2[_, _]]
44-
Tuple3(x, t._1, t._2).asInstanceOf[Result]
45-
case Some(3) =>
46-
val t = asInstanceOf[Tuple3[_, _, _]]
47-
Tuple4(x, t._1, t._2, t._3).asInstanceOf[Result]
48-
case Some(4) =>
49-
val t = asInstanceOf[Tuple4[_, _, _, _]]
50-
Tuple5(x, t._1, t._2, t._3, t._4).asInstanceOf[Result]
51-
case Some(n) =>
52-
knownTupleFromItrator[H *: this.type](n + 1, Iterator.single(x) ++ this.asInstanceOf[Product].productIterator)
53-
case _ =>
54-
DynamicTuple.dynamic_*:[This, H](this, x)
55-
}
56-
}
35+
def *: [H, This >: this.type <: Tuple] (x: H): H *: This = ???
5736

5837
inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = {
5938
type Result = Concat[This, that.type]

library/src-3.x/scala/runtime/DynamicTuple.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,7 @@ object DynamicTuple {
269269
}
270270
res.asInstanceOf[Result]
271271
}
272+
273+
def consIterator(head: Any, tail: Tuple): Iterator[Any] =
274+
Iterator.single(head) ++ tail.asInstanceOf[Product].productIterator
272275
}

library/src/scala/TupleXXL.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ final class TupleXXL private (es: Array[Object]) extends Product {
2121
def elems: Array[Object] = es
2222
}
2323
object TupleXXL {
24+
def fromIterator(elems: Iterator[Any]) = new TupleXXL(elems.map(_.asInstanceOf[Object]).toArray)
2425
def apply(elems: Array[Object]) = new TupleXXL(elems.clone)
2526
def apply(elems: Any*) = new TupleXXL(elems.asInstanceOf[Seq[Object]].toArray)
2627
def unapplySeq(x: TupleXXL): Option[Seq[Any]] = Some(x.elems.toSeq)

0 commit comments

Comments
 (0)