Skip to content

Commit c126865

Browse files
committed
Avoid some boxing of state ids during transform
1 parent 1fe789a commit c126865

File tree

3 files changed

+59
-22
lines changed

3 files changed

+59
-22
lines changed

src/main/scala/scala/async/internal/ExprBuilder.scala

+13-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44
package scala.async.internal
55

6+
import java.util.function.IntUnaryOperator
7+
68
import scala.collection.mutable
79
import scala.collection.mutable.ListBuffer
810
import language.existentials
@@ -23,7 +25,7 @@ trait ExprBuilder {
2325
trait AsyncState {
2426
def state: Int
2527

26-
def nextStates: List[Int]
28+
def nextStates: Array[Int]
2729

2830
def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef
2931

@@ -55,8 +57,8 @@ trait ExprBuilder {
5557
final class SimpleAsyncState(var stats: List[Tree], val state: Int, nextState: Int, symLookup: SymLookup)
5658
extends AsyncState {
5759

58-
def nextStates: List[Int] =
59-
List(nextState)
60+
val nextStates: Array[Int] =
61+
Array(nextState)
6062

6163
def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef = {
6264
mkHandlerCase(state, treesThenStats(mkStateTree(nextState, symLookup) :: Nil))
@@ -69,7 +71,7 @@ trait ExprBuilder {
6971
/** A sequence of statements with a conditional transition to the next state, which will represent
7072
* a branch of an `if` or a `match`.
7173
*/
72-
final class AsyncStateWithoutAwait(var stats: List[Tree], val state: Int, val nextStates: List[Int]) extends AsyncState {
74+
final class AsyncStateWithoutAwait(var stats: List[Tree], val state: Int, val nextStates: Array[Int]) extends AsyncState {
7375
override def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef =
7476
mkHandlerCase(state, stats)
7577

@@ -84,8 +86,8 @@ trait ExprBuilder {
8486
val awaitable: Awaitable, symLookup: SymLookup)
8587
extends AsyncState {
8688

87-
def nextStates: List[Int] =
88-
List(nextState)
89+
val nextStates: Array[Int] =
90+
Array(nextState)
8991

9092
override def mkHandlerCaseForState[T: WeakTypeTag]: CaseDef = {
9193
val fun = This(tpnme.EMPTY)
@@ -191,7 +193,7 @@ trait ExprBuilder {
191193
def resultWithIf(condTree: Tree, thenState: Int, elseState: Int): AsyncState = {
192194
def mkBranch(state: Int) = mkStateTree(state, symLookup)
193195
this += If(condTree, mkBranch(thenState), mkBranch(elseState))
194-
new AsyncStateWithoutAwait(stats.toList, state, List(thenState, elseState))
196+
new AsyncStateWithoutAwait(stats.toList, state, Array(thenState, elseState))
195197
}
196198

197199
/**
@@ -204,7 +206,7 @@ trait ExprBuilder {
204206
* @param caseStates starting state of the right-hand side of the each case
205207
* @return an `AsyncState` representing the match expression
206208
*/
207-
def resultWithMatch(scrutTree: Tree, cases: List[CaseDef], caseStates: List[Int], symLookup: SymLookup): AsyncState = {
209+
def resultWithMatch(scrutTree: Tree, cases: List[CaseDef], caseStates: Array[Int], symLookup: SymLookup): AsyncState = {
208210
// 1. build list of changed cases
209211
val newCases = for ((cas, num) <- cases.zipWithIndex) yield cas match {
210212
case CaseDef(pat, guard, rhs) =>
@@ -218,7 +220,7 @@ trait ExprBuilder {
218220

219221
def resultWithLabel(startLabelState: Int, symLookup: SymLookup): AsyncState = {
220222
this += mkStateTree(startLabelState, symLookup)
221-
new AsyncStateWithoutAwait(stats.toList, state, List(startLabelState))
223+
new AsyncStateWithoutAwait(stats.toList, state, Array(startLabelState))
222224
}
223225

224226
override def toString: String = {
@@ -299,7 +301,8 @@ trait ExprBuilder {
299301
case Match(scrutinee, cases) if containsAwait(stat) =>
300302
checkForUnsupportedAwait(scrutinee)
301303

302-
val caseStates = cases.map(_ => nextState())
304+
val caseStates = new Array[Int](cases.length)
305+
java.util.Arrays.setAll(caseStates, (_ => nextState()): IntUnaryOperator)
303306
val afterMatchState = nextState()
304307

305308
asyncStates +=

src/main/scala/scala/async/internal/LiveVariables.scala

+32-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package scala.async.internal
22

3+
import scala.collection.immutable.IntMap
4+
35
trait LiveVariables {
46
self: AsyncMacro =>
57
import c.universe._
@@ -48,7 +50,7 @@ trait LiveVariables {
4850
*/
4951
def liveVars(asyncStates: List[AsyncState], liftables: List[Tree]): Map[Tree, Set[Int]] = {
5052
val liftedSyms: Set[Symbol] = // include only vars
51-
liftables.filter {
53+
liftables.iterator.filter {
5254
case ValDef(mods, _, _, _) => mods.hasFlag(MUTABLE)
5355
case _ => false
5456
}.map(_.symbol).toSet
@@ -122,20 +124,30 @@ trait LiveVariables {
122124
* A state `i` is contained in the list that is the value to which
123125
* key `j` maps iff control can flow from state `j` to state `i`.
124126
*/
125-
val cfg: Map[Int, List[Int]] = asyncStates.map(as => as.state -> as.nextStates).toMap
127+
val cfg: Map[Int, Array[Int]] = {
128+
var res = IntMap.empty[Array[Int]]
129+
130+
for (as <- asyncStates) res = res.updated(as.state, as.nextStates)
131+
res
132+
}
126133

127134
/** Tests if `state1` is a predecessor of `state2`.
128135
*/
129136
def isPred(state1: Int, state2: Int): Boolean = {
130-
val seen = scala.collection.mutable.HashSet[Int]()
137+
val seen = new StateSet()
131138

132139
def isPred0(state1: Int, state2: Int): Boolean =
133140
if(state1 == state2) false
134-
else if (seen(state1)) false // breaks cycles in the CFG
141+
else if (seen.contains(state1)) false // breaks cycles in the CFG
135142
else cfg get state1 match {
136143
case Some(nextStates) =>
137144
seen += state1
138-
nextStates.contains(state2) || nextStates.exists(isPred0(_, state2))
145+
var i = 0
146+
while (i < nextStates.length) {
147+
if (nextStates(i) == state2 || isPred0(nextStates(i), state2)) return true
148+
i += 1
149+
}
150+
false
139151
case None =>
140152
false
141153
}
@@ -164,8 +176,8 @@ trait LiveVariables {
164176
* 7. repeat if something has changed
165177
*/
166178

167-
var LVentry = Map[Int, Set[Symbol]]() withDefaultValue Set[Symbol]()
168-
var LVexit = Map[Int, Set[Symbol]]() withDefaultValue Set[Symbol]()
179+
var LVentry = IntMap[Set[Symbol]]() withDefaultValue Set[Symbol]()
180+
var LVexit = IntMap[Set[Symbol]]() withDefaultValue Set[Symbol]()
169181

170182
// All fields are declared to be dead at the exit of the final async state, except for the ones
171183
// that cannot be nulled out at all (those in noNull), because they have been captured by a nested def.
@@ -174,6 +186,14 @@ trait LiveVariables {
174186
var currStates = List(finalState) // start at final state
175187
var captured: Set[Symbol] = Set()
176188

189+
def contains(as: Array[Int], a: Int): Boolean = {
190+
var i = 0
191+
while (i < as.length) {
192+
if (as(i) == a) return true
193+
i += 1
194+
}
195+
false
196+
}
177197
while (!currStates.isEmpty) {
178198
var entryChanged: List[AsyncState] = Nil
179199

@@ -183,19 +203,19 @@ trait LiveVariables {
183203
captured ++= referenced.captured
184204
val LVentryNew = LVexit(cs.state) ++ referenced.used
185205
if (!LVentryNew.sameElements(LVentryOld)) {
186-
LVentry = LVentry + (cs.state -> LVentryNew)
206+
LVentry = LVentry.updated(cs.state, LVentryNew)
187207
entryChanged ::= cs
188208
}
189209
}
190210

191-
val pred = entryChanged.flatMap(cs => asyncStates.filter(_.nextStates.contains(cs.state)))
211+
val pred = entryChanged.flatMap(cs => asyncStates.filter(state => contains(state.nextStates, cs.state)))
192212
var exitChanged: List[AsyncState] = Nil
193213

194214
for (p <- pred) {
195215
val LVexitOld = LVexit(p.state)
196216
val LVexitNew = p.nextStates.flatMap(succ => LVentry(succ)).toSet
197217
if (!LVexitNew.sameElements(LVexitOld)) {
198-
LVexit = LVexit + (p.state -> LVexitNew)
218+
LVexit = LVexit.updated(p.state, LVexitNew)
199219
exitChanged ::= p
200220
}
201221
}
@@ -223,7 +243,7 @@ trait LiveVariables {
223243
Set(at.state)
224244
case _ =>
225245
avoid += at
226-
val preds = asyncStates.filter(_.nextStates.contains(at.state)).toSet
246+
val preds = asyncStates.filter(state => contains(state.nextStates, at.state)).toSet
227247
preds.flatMap(p => lastUsagesOf0(field, p))
228248
}
229249
}
@@ -248,7 +268,7 @@ trait LiveVariables {
248268
val succNums = lastAsyncState.nextStates
249269
// all successor states that are not indirect predecessors
250270
// filter out successor states where the field is live at the entry
251-
succNums.filter(num => !isPred(num, s)).filterNot(num => LVentry(num).contains(fld.symbol))
271+
succNums.iterator.filter(num => !isPred(num, s)).filterNot(num => LVentry(num).contains(fld.symbol))
252272
}
253273
}
254274
(fld, killAt)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package scala.async.internal
2+
3+
import java.util
4+
5+
import scala.collection.mutable
6+
7+
// Set for StateIds, which are either small positive integers or -symbolID.
8+
final class StateSet {
9+
private var bitSet = mutable.BitSet()
10+
private var caseSet = new util.HashSet[Integer]()
11+
def +=(stateId: Int): Unit = if (stateId > 0) bitSet += stateId else caseSet.add(stateId)
12+
def contains(stateId: Int): Boolean = if (stateId > 0) bitSet.contains(stateId) else caseSet.contains(stateId)
13+
14+
}

0 commit comments

Comments
 (0)