Skip to content

Specialize Functions #1807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
abef3fd
Add phases and initial replacement for super
felixmulder Dec 2, 2016
33ee0f3
Replace all existing combinations of Function1 with specialized version
felixmulder Dec 2, 2016
2e35558
Do transformations on symbol level too
felixmulder Dec 13, 2016
aa790fa
Refactor transformations to be more idiomatic
felixmulder Dec 14, 2016
b5fa2bf
Add dispatch to specialized applys
felixmulder Dec 14, 2016
a46ade9
Add forwarding method for generic case
felixmulder Dec 14, 2016
8f3d2fd
Don't specialize Function1 tree when invalid to
felixmulder Dec 14, 2016
7858540
Write test to check for specialized apply
felixmulder Dec 14, 2016
8c0ecd1
Remove `DispatchToSpecializedApply` phase
felixmulder Dec 23, 2016
fd66d93
SpecializeFunction1: don't roll over parents, use mapConserve
felixmulder Dec 30, 2016
281628f
Rewrite to handle all specialized functions
felixmulder Feb 14, 2017
16a09dd
Don't remove parents not being specialized
felixmulder Feb 16, 2017
8aeaa83
Add plain function tests to NameOps and Definitions
felixmulder Feb 16, 2017
4a84b0c
Rewrite `SpecializeFunctions` from `DenotTransformer` to `InfoTransfo…
felixmulder Feb 16, 2017
5df2904
Add `MiniPhaseTransform` to add specialized methods to FunctionN
felixmulder Feb 21, 2017
a206d77
Add synthetic bridge when compiling FunctionN
felixmulder Feb 21, 2017
662631d
Fix ordering of specialized names and type parameterized apply
felixmulder Feb 21, 2017
e3b7eef
Add parent types explicitly when specializing
felixmulder Feb 22, 2017
ff4e8d5
Make `ThisName` recursive on `self.ThisName`
felixmulder Apr 12, 2017
e17672b
Make sure specialized functions get the correct name
felixmulder Apr 12, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,16 @@ class Compiler {
new ByNameClosures, // Expand arguments to by-name parameters to closures
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
new SpecializedApplyMethods, // Adds specialized methods to FunctionN
new ClassOf), // Expand `Predef.classOf` calls.
List(new TryCatchPatterns, // Compile cases in try/catch
new PatternMatcher, // Compile pattern matches
new ExplicitOuter, // Add accessors to outer classes from nested ones.
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
new ShortcutImplicits, // Allow implicit functions without creating closures
new CrossCastAnd, // Normalize selections involving intersection types.
new Splitter), // Expand selections involving union types into conditionals
new Splitter, // Expand selections involving union types into conditionals
new SpecializeFunctions), // Specialized Function1 by replacing super with specialized super
List(new VCInlineMethods, // Inlines calls to value class methods
new IsInstanceOfEvaluator, // Issues warnings when unreachable statements are present in match/if expressions
new SeqLiterals, // Express vararg arguments as arrays
Expand Down
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -736,12 +736,17 @@ class Definitions {
def isBottomType(tp: Type) =
tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass)

/** Is a function class.
/** Is any function class that satisfies:
* - FunctionN for N >= 0
* - ImplicitFunctionN for N >= 0
*/
def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction

/** Is a function class where
* - FunctionN for N >= 0
*/
def isPlainFunctionClass(cls: Symbol) = scalaClassName(cls).isPlainFunction

/** Is an implicit function class.
* - ImplicitFunctionN for N >= 0
*/
Expand Down Expand Up @@ -879,9 +884,9 @@ class Definitions {
lazy val ScalaNumericValueTypeList = List(
ByteType, ShortType, CharType, IntType, LongType, FloatType, DoubleType)

private lazy val ScalaNumericValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypeList.toSet
private lazy val ScalaValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypes + UnitType + BooleanType
private lazy val ScalaBoxedTypes = ScalaValueTypes map (t => boxedTypes(t.name))
lazy val ScalaNumericValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypeList.toSet
lazy val ScalaValueTypes: collection.Set[TypeRef] = ScalaNumericValueTypes + UnitType + BooleanType
lazy val ScalaBoxedTypes = ScalaValueTypes map (t => boxedTypes(t.name))

val ScalaNumericValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaNumericValueTypes.map(_.symbol))
val ScalaValueClasses = new PerRun[collection.Set[Symbol]](implicit ctx => ScalaValueTypes.map(_.symbol))
Expand Down
50 changes: 33 additions & 17 deletions compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,17 @@ object NameOps {
def functionArity: Int =
functionArityFor(str.Function) max functionArityFor(str.ImplicitFunction)

/** Is a function name
/** Is any function name that satisfies
* - FunctionN for N >= 0
* - ImplicitFunctionN for N >= 0
* - false otherwise
*/
def isFunction: Boolean = functionArity >= 0

/** Is a function name
* - FunctionN for N >= 0
*/
def isPlainFunction: Boolean = functionArityFor(str.Function) >= 0

/** Is a implicit function name
* - ImplicitFunctionN for N >= 0
* - false otherwise
Expand Down Expand Up @@ -209,23 +213,24 @@ object NameOps {
case nme.clone_ => nme.clone_
}

def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = {

def typeToTag(tp: Types.Type): Name = {
tp.classSymbol match {
case t if t eq defn.IntClass => nme.specializedTypeNames.Int
case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean
case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte
case t if t eq defn.LongClass => nme.specializedTypeNames.Long
case t if t eq defn.ShortClass => nme.specializedTypeNames.Short
case t if t eq defn.FloatClass => nme.specializedTypeNames.Float
case t if t eq defn.UnitClass => nme.specializedTypeNames.Void
case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double
case t if t eq defn.CharClass => nme.specializedTypeNames.Char
case _ => nme.specializedTypeNames.Object
}
private def typeToTag(tp: Types.Type)(implicit ctx: Context): Name =
tp.classSymbol match {
case t if t eq defn.IntClass => nme.specializedTypeNames.Int
case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean
case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte
case t if t eq defn.LongClass => nme.specializedTypeNames.Long
case t if t eq defn.ShortClass => nme.specializedTypeNames.Short
case t if t eq defn.FloatClass => nme.specializedTypeNames.Float
case t if t eq defn.UnitClass => nme.specializedTypeNames.Void
case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double
case t if t eq defn.CharClass => nme.specializedTypeNames.Char
case _ => nme.specializedTypeNames.Object
}

/** This method is to be used on **type parameters** from a class, since
* this method does sorting based on their names
*/
def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = {
val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1))
val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1))

Expand All @@ -234,6 +239,17 @@ object NameOps {
classTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix)
}

/** Use for specializing function names ONLY and use it if you are **not**
* creating specialized name from type parameters. The order of names will
* be:
*
* `<return type><first type><second type><...>`
*/
def specializedFunction(ret: Types.Type, args: List[Types.Type])(implicit ctx: Context): name.ThisName =
name ++ nme.specializedTypeNames.prefix ++
nme.specializedTypeNames.separator ++ typeToTag(ret) ++
args.map(typeToTag).fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix

/** If name length exceeds allowable limit, replace part of it by hash */
def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString))

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ object Names {
* in a name table. A derived term name adds a tag, and possibly a number
* or a further simple name to some other name.
*/
abstract class Name extends DotClass with PreName {
abstract class Name extends DotClass with PreName { self =>

/** A type for names of the same kind as this name */
type ThisName <: Name
type ThisName <: Name { type ThisName = self.ThisName }

/** Is this name a type name? */
def isTypeName: Boolean
Expand Down
190 changes: 190 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package dotty.tools.dotc
package transform

import TreeTransforms.{ MiniPhaseTransform, TransformerInfo }
import ast.Trees._, ast.tpd, core._
import Contexts.Context, Types._, Decorators._, Symbols._, DenotTransformers._
import SymDenotations._, Scopes._, StdNames._, NameOps._, Names._

import scala.collection.mutable

/** Specializes classes that inherit from `FunctionN` where there exists a
* specialized form.
*/
class SpecializeFunctions extends MiniPhaseTransform with InfoTransformer {
import ast.tpd._
val phaseName = "specializeFunctions"

private[this] var _blacklistedSymbols: List[Symbol] = _

private def blacklistedSymbols(implicit ctx: Context): List[Symbol] = {
if (_blacklistedSymbols eq null) _blacklistedSymbols = List(
ctx.getClassIfDefined("scala.math.Ordering").asClass.membersNamed("Ops".toTypeName).first.symbol
)

_blacklistedSymbols
}

/** Transforms the type to include decls for specialized applys and replace
* the class parents with specialized versions.
*/
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match {
case tp: ClassInfo if !sym.is(Flags.Package) && (tp.decls ne EmptyScope) => {
var newApplys = Map.empty[Name, Symbol]

val newParents = tp.parents.mapConserve { parent =>
List(0, 1, 2, 3).flatMap { arity =>
val func = defn.FunctionClass(arity)
if (!parent.derivesFrom(func)) Nil
else {
val typeParams = tp.typeRef.baseArgInfos(func)
val interface = specInterface(typeParams)

if (interface.exists) {
if (tp.decls.lookup(nme.apply).exists) {
val specializedMethodName = nme.apply.specializedFunction(typeParams.last, typeParams.init)
newApplys = newApplys + (specializedMethodName -> interface)
}

if (parent.isRef(func)) List(interface.typeRef)
else Nil
}
else Nil
}
}
.headOption
.getOrElse(parent)
}

def newDecls =
if (newApplys.isEmpty) tp.decls
else
newApplys.toList.map { case (name, interface) =>
ctx.newSymbol(
sym,
name,
Flags.Override | Flags.Method,
interface.info.decls.lookup(name).info
)
}
.foldLeft(tp.decls.cloneScope) {
(scope, sym) => scope.enter(sym); scope
}

tp.derivedClassInfo(
classParents = newParents,
decls = newDecls
)
}

case _ => tp
}

/** Transforms the `Template` of the classes to contain forwarders from the
* generic applys to the specialized ones. Also replaces parents of the
* class on the tree level and inserts the specialized applys in the
* template body.
*/
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = {
val applyBuf = new mutable.ListBuffer[Tree]
val newBody = tree.body.mapConserve {
case dt: DefDef if dt.name == nme.apply && dt.vparamss.length == 1 => {
val specName = nme.apply.specializedFunction(
dt.tpe.widen.finalResultType,
dt.vparamss.head.map(_.symbol.info)
)

val specializedApply = tree.symbol.enclosingClass.info.decls.lookup(specName)//member(specName).symbol
//val specializedApply = tree.symbol.enclosingClass.info.member(specName).symbol

if (false) {
println(tree.symbol.enclosingClass.show)
println("'" + specName.show + "'")
println(specializedApply)
println(specializedApply.exists)
}


if (specializedApply.exists) {
val apply = specializedApply.asTerm
val specializedDecl =
polyDefDef(apply, trefs => vrefss => {
dt.rhs
.changeOwner(dt.symbol, apply)
.subst(dt.vparamss.flatten.map(_.symbol), vrefss.flatten.map(_.symbol))
})
applyBuf += specializedDecl

// create a forwarding to the specialized apply
cpy.DefDef(dt)(rhs = {
tpd
.ref(apply)
.appliedToArgs(dt.vparamss.head.map(vparam => ref(vparam.symbol)))
})
} else dt
}
case x => x
}

val missing: List[TypeTree] = List(0, 1, 2, 3).flatMap { arity =>
val func = defn.FunctionClass(arity)
val tr = tree.symbol.enclosingClass.typeRef

if (!tr.parents.exists(_.isRef(func))) Nil
else {
val typeParams = tr.baseArgInfos(func)
val interface = specInterface(typeParams)

if (interface.exists) List(interface.info)
else Nil
}
}.map(TypeTree)

cpy.Template(tree)(
parents = tree.parents ++ missing,
body = applyBuf.toList ++ newBody
)
}

/** Dispatch to specialized `apply`s in user code when available */
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
tree match {
case app @ Apply(fun, args)
if fun.symbol.name == nme.apply &&
fun.symbol.owner.derivesFrom(defn.FunctionClass(args.length))
=> {
val params = (fun.tpe.widen.firstParamTypes :+ tree.tpe).map(_.widenSingleton.dealias)
val specializedApply = specializedName(nme.apply, params)

if (!params.exists(_.isInstanceOf[ExprType]) && fun.symbol.owner.info.decls.lookup(specializedApply).exists) {
val newSel = fun match {
case Select(qual, _) =>
qual.select(specializedApply)
case _ => {
(fun.tpe: @unchecked) match {
case TermRef(prefix: ThisType, name) =>
tpd.This(prefix.cls).select(specializedApply)
case TermRef(prefix: NamedType, name) =>
tpd.ref(prefix).select(specializedApply)
}
}
}

newSel.appliedToArgs(args)
}
else tree
}
case _ => tree
}

@inline private def specializedName(name: Name, args: List[Type])(implicit ctx: Context) =
name.specializedFor(args, args.map(_.typeSymbol.name), Nil, Nil)

@inline private def specInterface(typeParams: List[Type])(implicit ctx: Context) = {
val specName =
("JFunction" + (typeParams.length - 1)).toTermName
.specializedFunction(typeParams.last, typeParams.init)

ctx.getClassIfDefined("scala.compat.java8.".toTermName ++ specName)
}
}
Loading