Skip to content

Commit bd2fd57

Browse files
committed
Cache quote unpickling
1 parent 6d0817d commit bd2fd57

File tree

4 files changed

+76
-18
lines changed

4 files changed

+76
-18
lines changed

compiler/src/dotty/tools/dotc/quoted/MacroExpansion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ object MacroExpansion {
1414
ctx.property(MacroExpansionPosition)
1515

1616
def context(inlinedFrom: tpd.Tree)(using Context): Context =
17-
ctx.fresh.setProperty(MacroExpansionPosition, SourcePosition(inlinedFrom.source, inlinedFrom.span)).setTypeAssigner(new Typer).withSource(inlinedFrom.source)
17+
QuotesCache.init(ctx.fresh).setProperty(MacroExpansionPosition, SourcePosition(inlinedFrom.source, inlinedFrom.span)).setTypeAssigner(new Typer).withSource(inlinedFrom.source)
1818
}
1919

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,24 @@ object PickledQuotes {
5454

5555
/** Unpickle the tree contained in the TastyExpr */
5656
def unpickleTerm(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?])(using Context): Tree = {
57+
val t0 = System.nanoTime()
5758
val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = false))
5859
val Inlined(call, Nil, expnasion) = unpickled
60+
val t1 = System.nanoTime()
5961
val inlineCtx = inlineContext(call)
62+
val t2 = System.nanoTime()
6063
val expansion1 = spliceTypes(expnasion, typeHole, termHole)(using inlineCtx)
64+
val t3 = System.nanoTime()
6165
val expansion2 = spliceTerms(expansion1, typeHole, termHole)(using inlineCtx)
62-
cpy.Inlined(unpickled)(call, Nil, expansion2)
66+
val t4 = System.nanoTime()
67+
val res = cpy.Inlined(unpickled)(call, Nil, expansion2)
68+
val t5 = System.nanoTime()
69+
// println(s"""
70+
// |unpickle> ${ (t1 - t0).toDouble / 1000 }μs
71+
// |spliceTypes> ${(t3 - t2).toDouble / 1000 }μs
72+
// |spliceTerms> ${(t4 - t3).toDouble / 1000 }μs
73+
// |""".stripMargin)
74+
res
6375
}
6476

6577
/** Unpickle the tree contained in the TastyType */
@@ -181,25 +193,39 @@ object PickledQuotes {
181193

182194
/** Unpickle TASTY bytes into it's tree */
183195
private def unpickle(pickled: String | List[String], isType: Boolean)(using Context): Tree = {
184-
val bytes = pickled match
185-
case pickled: String => TastyString.unpickle(pickled)
186-
case pickled: List[String] => TastyString.unpickle(pickled)
196+
QuotesCache.getTree(pickled) match
197+
case Some(tree) =>
198+
quotePickling.println(s"**** Using cached quote for TASTY\n$tree")
199+
tree
200+
case _ =>
201+
val bytes = pickled match
202+
case pickled: String => TastyString.unpickle(pickled)
203+
case pickled: List[String] => TastyString.unpickle(pickled)
204+
205+
quotePickling.println(s"**** unpickling quote from TASTY\n${TastyPrinter.show(bytes)}")
187206

188-
quotePickling.println(s"**** unpickling quote from TASTY\n${TastyPrinter.show(bytes)}")
207+
val mode = if (isType) UnpickleMode.TypeTree else UnpickleMode.Term
208+
val unpickler = new DottyUnpickler(bytes, mode)
209+
unpickler.enter(Set.empty)
189210

190-
val mode = if (isType) UnpickleMode.TypeTree else UnpickleMode.Term
191-
val unpickler = new DottyUnpickler(bytes, mode)
192-
unpickler.enter(Set.empty)
211+
val tree = unpickler.tree
193212

194-
val tree = unpickler.tree
213+
var cacheable = true // TODO: can we remove this?
195214

196-
// Make sure trees and positions are fully loaded
197-
new TreeTraverser {
198-
def traverse(tree: Tree)(using Context): Unit = traverseChildren(tree)
199-
}.traverse(tree)
215+
// Make sure trees and positions are fully loaded
216+
new TreeTraverser {
217+
def traverse(tree: Tree)(using Context): Unit =
218+
tree match
219+
case _: DefTree => cacheable = false
220+
case _ =>
221+
traverseChildren(tree)
222+
}.traverse(tree)
200223

201-
quotePickling.println(i"**** unpickled quote\n$tree")
202-
tree
224+
quotePickling.println(i"**** unpickled quote\n$tree")
225+
if cacheable then
226+
QuotesCache(pickled) = tree
227+
228+
tree
203229
}
204230

205231
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import dotty.tools.dotc.core.Contexts._
4+
import dotty.tools.dotc.util.Property
5+
import dotty.tools.dotc.reporting.trace
6+
import dotty.tools.dotc.ast.tpd
7+
8+
import scala.collection.mutable
9+
10+
object QuotesCache {
11+
import tpd._
12+
13+
/** A key to be used in a context property that caches the unpickled trees */
14+
private val QuotesCacheKey = new Property.Key[collection.mutable.Map[String | List[String], Tree]]
15+
16+
17+
/** Get the cached tree of the quote */
18+
def getTree(pickled: String | List[String])(using Context): Option[Tree] =
19+
ctx.property(QuotesCacheKey).get.get(pickled)
20+
21+
/** Update the cached tree of the quote */
22+
def update(pickled: String | List[String], tree: Tree)(using Context): Unit =
23+
ctx.property(QuotesCacheKey).get.update(pickled, tree)
24+
25+
/** Context with a cache for quote trees and tasty bytes */
26+
def init(ctx: FreshContext): ctx.type =
27+
ctx.setProperty(QuotesCacheKey, collection.mutable.Map.empty)
28+
}

staging/src/scala/quoted/staging/QuoteDriver.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package staging
44
import dotty.tools.dotc.ast.tpd
55
import dotty.tools.dotc.Driver
66
import dotty.tools.dotc.core.Contexts.{Context, ContextBase, FreshContext}
7+
import dotty.tools.dotc.quoted.QuotesCache
78
import dotty.tools.io.{AbstractFile, Directory, PlainDirectory, VirtualDirectory}
89
import dotty.tools.repl.AbstractFileClassLoader
910
import dotty.tools.dotc.reporting._
@@ -33,8 +34,11 @@ private class QuoteDriver(appClassloader: ClassLoader) extends Driver:
3334
new VirtualDirectory("<quote compilation output>")
3435
end outDir
3536

36-
val ctx0 = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh).get._2
37-
val ctx = setCompilerSettings(ctx0.fresh.setSetting(ctx0.settings.outputDir, outDir), settings)
37+
val ctx = {
38+
val ctx0 = QuotesCache.init(initCtx.fresh)
39+
val ctx1 = setup(settings.compilerArgs.toArray :+ "dummy.scala", ctx0).get._2
40+
setCompilerSettings(ctx1.fresh.setSetting(ctx1.settings.outputDir, outDir), settings)
41+
}
3842

3943
new QuoteCompiler().newRun(ctx).compileExpr(exprBuilder) match
4044
case Right(value) =>

0 commit comments

Comments
 (0)