Skip to content

Commit e93b499

Browse files
Add -Yopt-phases, -Yopt-fuel, bisect.sh, update main Simplify loop
1 parent 328357c commit e93b499

File tree

7 files changed

+94
-31
lines changed

7 files changed

+94
-31
lines changed

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ class ScalaSettings extends Settings.SettingGroup {
107107
val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.")
108108

109109
/** Linker specific flags */
110-
val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize"
110+
val YoptPhases = PhasesSetting("-Yopt-phases", "Restrict the optimisation phases to execute under -optimise.")
111+
val YoptFuel = IntSetting("-Yopt-fuel", "Maximum number of optimisations performed under -optimise.", -1)
112+
val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying local optimisations to the .program") withAbbreviation "-optimize"
111113

112114
/** Dottydoc specific settings */
113115
val siteRoot = StringSetting(

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dotty.tools.dotc
22
package transform
3+
34
import core.Contexts.Context
45

56
/** Utility class for lazy values whose evaluation depends on a context.
@@ -20,4 +21,4 @@ class CtxLazy[T](expr: Context => T) {
2021
}
2122
myValue
2223
}
23-
}
24+
}

compiler/src/dotty/tools/dotc/transform/localopt/BubbleUpNothing.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import ast.Trees._
77

88
/** Every pure statement preceding a ??? can be removed.
99
*
10-
* This optimisation makes it rather tricky meaningful examples since the
11-
* compiler will often be able to reduce them to a single main with ???...
10+
* This optimisation makes it rather tricky to write meaningful examples
11+
* since the compiler will often be able to reduce them to a single main
12+
* method with body = ???.
1213
*/
1314
class BubbleUpNothing(implicit val ctx: Context) extends Optimisation {
1415
import ast.tpd._

compiler/src/dotty/tools/dotc/transform/localopt/Optimisation.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ trait Optimisation {
1010
def visitor: Tree => Unit
1111

1212
/** Does the actual Tree => Tree transformation, possibly using a different
13-
* context from the one using in Optimisation.
13+
* context from the one used in Optimisation.
1414
*/
1515
def transformer(localCtx: Context): Tree => Tree
1616

17-
def name: String = this.getClass.getName.init
17+
def name: String = this.getClass.getName.split('.').last
1818

1919
val NoVisitor: Tree => Unit = _ => ()
2020
}

compiler/src/dotty/tools/dotc/transform/localopt/Simplify.scala

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {
2020
override def phaseName: String = "simplify"
2121
override val cpy = tpd.cpy
2222

23-
def beforeErasure(implicit ctx: Context): List[Optimisation] =
23+
private def beforeErasure(implicit ctx: Context): List[Optimisation] =
2424
new InlineCaseIntrinsics ::
2525
new RemoveUnnecessaryNullChecks ::
2626
new InlineOptions ::
@@ -36,50 +36,73 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {
3636
new ConstantFold ::
3737
Nil
3838

39-
def afterErasure(implicit ctx: Context): List[Optimisation] =
40-
// new InlineCaseIntrinsics ::
41-
// new RemoveUnnecessaryNullChecks ::
42-
// new InlineOptions ::
43-
// new InlineLabelsCalledOnce ::
39+
private def afterErasure(implicit ctx: Context): List[Optimisation] =
4440
new Valify(this) ::
4541
new Devalify ::
4642
new Jumpjump ::
4743
new DropGoodCasts ::
48-
// new DropNoEffects(this) ::
49-
// new InlineLocalObjects :: // followCases needs to be fixed, see ./tests/pos/rbtree.scala
50-
// new Varify :: // varify could stop other transformations from being applied. postponed.
51-
// new BubbleUpNothing ::
5244
new ConstantFold ::
5345
Nil
5446

47+
/** Optimisation fuel, for debugging. Decremented every time Simplify
48+
* applies an optimisation until fuel == 0. Original idea from Automatic
49+
* Isolation of Compiler Errors by David Whalley. Unable with -Yopt-fuel.
50+
*
51+
* The fuel can be used to do a bisection on large test cases that fail
52+
* -optimise. See compiler/test/bisect.sh for a shell script to automates
53+
* the bisection search.
54+
*/
55+
var fuel: Int = -1
56+
57+
override def prepareForUnit(tree: Tree)(implicit ctx: Context) = {
58+
val maxFuel = ctx.settings.YoptFuel.value
59+
if (fuel < 0 && maxFuel > 0) // Both defaults are at -1
60+
fuel = maxFuel
61+
this
62+
}
63+
5564
// The entry point of local optimisation: DefDefs
5665
override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
5766
val ctx0 = ctx
5867
if (ctx.settings.optimise.value && !tree.symbol.is(Label)) {
5968
implicit val ctx: Context = ctx0.withOwner(tree.symbol(ctx0))
60-
val optimisations = if (ctx.erasedTypes) afterErasure else beforeErasure
69+
val optimisations = {
70+
val o = if (ctx.erasedTypes) afterErasure else beforeErasure
71+
val p = ctx.settings.YoptPhases.value
72+
if (p.isEmpty) o else o.filter(x => p.contains(x.name))
73+
}
6174

6275
var rhs0 = tree.rhs
6376
var rhs1: Tree = null
6477
while (rhs1 ne rhs0) {
6578
rhs1 = rhs0
6679
val context = ctx.withOwner(tree.symbol)
67-
// TODO: fuse for performance
68-
optimisations.foreach { optimisation =>
80+
optimisations.foreach { optimisation => // TODO: fuse for performance
81+
// Visit
6982
rhs0.foreachSubTree(optimisation.visitor)
7083

71-
val rhst = new TreeMap() {
84+
// Transform
85+
rhs0 = new TreeMap() {
7286
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
7387
val innerCtx = if (tree.isDef && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx
74-
optimisation.transformer(ctx)(super.transform(tree)(innerCtx))
88+
val childOptimizedTree = super.transform(tree)(innerCtx)
89+
90+
if (fuel == 0)
91+
childOptimizedTree
92+
else {
93+
val fullyOptimizedTree = optimisation.transformer(ctx)(childOptimizedTree)
94+
95+
if (tree ne fullyOptimizedTree) {
96+
if (fuel > 0) fuel -= 1
97+
if (fuel != -1 && fuel < 10) {
98+
println(s"${tree.symbol} was simplified by ${optimisation.name} (fuel=$fuel): ${tree.show}")
99+
println(s"became after ${optimisation.name}: (fuel=$fuel) ${fullyOptimizedTree.show}")
100+
}
101+
}
102+
fullyOptimizedTree
103+
}
75104
}
76105
}.transform(rhs0)
77-
78-
if (rhst ne rhs0) {
79-
simplify.println(s"${tree.symbol} was simplified by ${optimisation.name}: ${rhs0.show}")
80-
simplify.println(s"became: ${rhst.show}")
81-
}
82-
rhs0 = rhst
83106
}
84107
}
85108
if (rhs0 ne tree.rhs) tpd.cpy.DefDef(tree)(rhs = rhs0)

compiler/test/bisect.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/sh
2+
3+
# Bisect a shell command. Takes a `xargs -I{}` style lambda as argument, finds
4+
# the first value of `{}` < 65536 such that the command succeeds. Usage:
5+
#
6+
# $ sh bisect.sh test {} -lt 42
7+
8+
cmd="$@"
9+
10+
# Example of bisection to isolate -optimise bootstrap errors using -Yopt-fuel.
11+
# See comments in dotty/tools/dotc/transform/localopt/Simplify.scala
12+
13+
# cmd=$(cat <<EOF
14+
# sbt '
15+
# ; project dotty-compiler-bootstrapped
16+
# ; clean
17+
# ; set scalacOptions ++= Seq(
18+
# "-optimise",
19+
# "-Yopt-phases:InlineCaseIntrinsics",
20+
# "-Yopt-fuel", "{}",
21+
# "-pagewidth", "1024")
22+
# ; test
23+
# '
24+
# EOF
25+
# )
26+
27+
lo=1
28+
hi=65536 # ~16 steps
29+
30+
while [ "$(echo "$hi - $lo" | bc)" -ne 1 ]; do
31+
mid=$(echo "$lo + ($hi - $lo) / 2" | bc)
32+
run=$(echo "$cmd" | sed "s/{}/$mid/g")
33+
34+
echo "Now trying with {} = $mid (lo=$lo, hi=$hi)"
35+
echo "$run"
36+
sh -c "$run"
37+
38+
if [ $? -eq 0 ]; then lo=$mid; else hi=$mid; fi
39+
done
40+
echo "First failure at $hi"

compiler/test/partest

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)