Skip to content

Optimize constant folding #9895

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

Merged
merged 1 commit into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -583,14 +583,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
case tree: Select if qualifier.tpe eq tree.qualifier.tpe =>
tree1.withTypeUnchecked(tree.tpe)
case _ =>
val tree2 = tree.tpe match {
val tree2: Select = tree.tpe match {
case tpe: NamedType =>
val qualType = qualifier.tpe.widenIfUnstable
if qualType.isBottomType then tree1.withTypeUnchecked(tree.tpe)
else tree1.withType(tpe.derivedSelect(qualType))
case _ => tree1.withTypeUnchecked(tree.tpe)
}
ConstFold(tree2)
ConstFold.Select(tree2)
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ object Types {
case TypeAlias(alias) => alias.dealias1(keep)
case _ => tp
}
case app @ AppliedType(tycon, args) =>
case app @ AppliedType(tycon, _) =>
val tycon1 = tycon.dealias1(keep)
if (tycon1 ne tycon) app.superType.dealias1(keep)
else this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,7 @@ class TreeUnpickler(reader: TastyReader,
case name: TypeName => TypeRef(qualType, name, denot)
case name: TermName => TermRef(qualType, name, denot)
}
ConstFold(untpd.Select(qual, name).withType(tpe))
ConstFold.Select(untpd.Select(qual, name).withType(tpe))

def completeSelect(name: Name, sig: Signature): Select =
val qual = readTerm()
Expand Down
116 changes: 61 additions & 55 deletions compiler/src/dotty/tools/dotc/typer/ConstFold.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,56 +14,63 @@ import StdNames._
import Contexts._
import transform.TypeUtils._

object ConstFold {
object ConstFold:

import tpd._

/** If tree is a constant operation, replace with result. */
def apply[T <: Tree](tree: T)(using Context): T = finish(tree) {
tree match {
case Apply(Select(xt, op), yt :: Nil) =>
private val foldedBinops = Set[Name](
nme.ZOR, nme.OR, nme.XOR, nme.ZAND, nme.AND, nme.EQ, nme.NE,
nme.LT, nme.GT, nme.LE, nme.GE, nme.LSL, nme.LSR, nme.ASR,
nme.ADD, nme.SUB, nme.MUL, nme.DIV, nme.MOD)

private val foldedUnops = Set[Name](
nme.UNARY_!, nme.UNARY_~, nme.UNARY_+, nme.UNARY_-)

def Apply[T <: Apply](tree: T)(using Context): T =
tree.fun match
case Select(xt, op) if foldedBinops.contains(op) =>
xt.tpe.widenTermRefExpr.normalized match
case ConstantType(x) =>
yt.tpe.widenTermRefExpr match
case ConstantType(y) => foldBinop(op, x, y)
case _ => null
case _ => null
case Select(xt, op) =>
xt.tpe.widenTermRefExpr match {
case ConstantType(x) => foldUnop(op, x)
case _ => null
}
case TypeApply(_, List(targ)) if tree.symbol eq defn.Predef_classOf =>
Constant(targ.tpe)
case Apply(TypeApply(Select(qual, nme.getClass_), _), Nil)
if qual.tpe.widen.isPrimitiveValueType =>
Constant(qual.tpe.widen)
case _ => null
}
}
tree.args match
case yt :: Nil =>
yt.tpe.widenTermRefExpr.normalized match
case ConstantType(y) => tree.withFoldedType(foldBinop(op, x, y))
case _ => tree
case _ => tree
case _ => tree
case TypeApply(Select(qual, nme.getClass_), _)
if qual.tpe.widen.isPrimitiveValueType && tree.args.isEmpty =>
tree.withFoldedType(Constant(qual.tpe.widen))
case _ =>
tree

def Select[T <: Select](tree: T)(using Context): T =
if foldedUnops.contains(tree.name) then
tree.qualifier.tpe.widenTermRefExpr.normalized match
case ConstantType(x) => tree.withFoldedType(foldUnop(tree.name, x))
case _ => tree
else tree

/** If tree is a constant operation, replace with result. */
def apply[T <: Tree](tree: T)(using Context): T = tree match
case tree: Apply => Apply(tree)
case tree: Select => Select(tree)
case TypeApply(_, targ :: Nil) if tree.symbol eq defn.Predef_classOf =>
tree.withFoldedType(Constant(targ.tpe))
case _ => tree

/** If tree is a constant value that can be converted to type `pt`, perform
* the conversion.
*/
def apply[T <: Tree](tree: T, pt: Type)(using Context): T =
finish(apply(tree)) {
tree.tpe.widenTermRefExpr.normalized match {
case ConstantType(x) => x convertTo pt
case _ => null
}
}

inline private def finish[T <: Tree](tree: T)(compX: => Constant)(using Context): T =
try {
val x = compX
if (x ne null) tree.withType(ConstantType(x)).asInstanceOf[T]
else tree
}
catch {
case _: ArithmeticException => tree // the code will crash at runtime,
// but that is better than the
// compiler itself crashing
}
val tree1 = apply(tree)
tree.tpe.widenTermRefExpr.normalized match
case ConstantType(x) => tree1.withFoldedType(x.convertTo(pt))
case _ => tree1

extension [T <: Tree](tree: T)(using Context)
private def withFoldedType(c: Constant | Null): T =
if c == null then tree else tree.withType(ConstantType(c)).asInstanceOf[T]

private def foldUnop(op: Name, x: Constant): Constant = (op, x.tag) match {
case (nme.UNARY_!, BooleanTag) => Constant(!x.booleanValue)
Expand Down Expand Up @@ -166,23 +173,22 @@ object ConstFold {
case _ => null
}

private def foldBinop(op: Name, x: Constant, y: Constant): Constant = {
private def foldBinop(op: Name, x: Constant, y: Constant): Constant =
val optag =
if (x.tag == y.tag) x.tag
else if (x.isNumeric && y.isNumeric) math.max(x.tag, y.tag)
else NoTag

try optag match {
case BooleanTag => foldBooleanOp(op, x, y)
case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y)
case LongTag => foldLongOp(op, x, y)
case FloatTag => foldFloatOp(op, x, y)
case DoubleTag => foldDoubleOp(op, x, y)
case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue)
case _ => null
}
catch {
case ex: ArithmeticException => null
}
}
}
try optag match
case BooleanTag => foldBooleanOp(op, x, y)
case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y)
case LongTag => foldLongOp(op, x, y)
case FloatTag => foldFloatOp(op, x, y)
case DoubleTag => foldDoubleOp(op, x, y)
case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue)
case _ => null
catch case ex: ArithmeticException => null // the code will crash at runtime,
// but that is better than the
// compiler itself crashing
end foldBinop
end ConstFold
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ trait TypeAssigner {

case _ => accessibleSelectionType(tree, qual)
}
ConstFold(tree.withType(tp))
ConstFold.Select(tree.withType(tp))
}

/** Normalize type T appearing in a new T by following eta expansions to
Expand Down Expand Up @@ -301,7 +301,7 @@ trait TypeAssigner {
if (ctx.settings.Ydebug.value) new FatalError("").printStackTrace()
errorType(err.takesNoParamsStr(fn, ""), tree.srcPos)
}
ConstFold(tree.withType(ownType))
ConstFold.Apply(tree.withType(ownType))
}

def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(using Context): TypeApply = {
Expand Down