@@ -16,7 +16,7 @@ import parsley.internal.deepembedding.ContOps, ContOps.{perform, ContAdapter}
16
16
import parsley .internal .machine .instructions , instructions .{Instr , Label }
17
17
18
18
import StrictParsley .*
19
- import org .typelevel .scalaccompat .annotation .{ nowarn , nowarn3 }
19
+ import org .typelevel .scalaccompat .annotation .nowarn3
20
20
21
21
/** This is the root type of the parsley "backend": it represents a combinator tree
22
22
* where the join-points in the tree (recursive or otherwise) have been factored into
@@ -44,14 +44,15 @@ private [deepembedding] trait StrictParsley[+A] {
44
44
* @param state the code generator state
45
45
* @return the final array of instructions for this parser
46
46
*/
47
- final private [deepembedding] def generateInstructions [M [_, + _]: ContOps ](numRegsUsedByParent : Int , usedRefs : Set [Ref [_]],
47
+ final private [deepembedding] def generateInstructions [M [_, + _]: ContOps ](minRef : Int , usedRefs : Set [Ref [_]],
48
48
bodyMap : Map [Let [_], StrictParsley [_]])
49
49
(implicit state : CodeGenState ): Array [Instr ] = {
50
50
implicit val instrs : InstrBuffer = newInstrBuffer
51
51
perform {
52
- generateCalleeSave[M , Array [Instr ]](numRegsUsedByParent, this .codeGen(producesResults = true ), usedRefs) |> {
53
- // When `numRegsUsedByParent` is -1 this is top level, otherwise it is a flatMap
54
- instrs += (if (numRegsUsedByParent >= 0 ) instructions.Return else instructions.Halt )
52
+ allocateAndExpandRefs(minRef, usedRefs)
53
+ this .codeGen[M , Array [Instr ]](producesResults = true ) |> {
54
+ // When `minRef` is -1 this is top level, otherwise it is a flatMap
55
+ instrs += (if (minRef >= 0 ) instructions.Return else instructions.Halt )
55
56
val letRets = finaliseLets(bodyMap)
56
57
generateHandlers(state.handlers)
57
58
finaliseInstrs(instrs, state.nlabels, letRets)
@@ -98,51 +99,6 @@ private [deepembedding] object StrictParsley {
98
99
/** Make a fresh instruction buffer */
99
100
private def newInstrBuffer : InstrBuffer = new ResizableArray ()
100
101
101
- /** Given a set of in-use registers, this function will allocate those that are currented
102
- * unallocated, giving them addresses not currently in use by the allocated registers
103
- *
104
- * @param unallocatedRegs the set of registers that need allocating
105
- * @param regs the set of all registers used by a specific parser
106
- * @return the list of slots that have been freshly allocated to
107
- */
108
- private def allocateRegisters (unallocatedRegs : Set [Ref [_]], regs : Set [Ref [_]]): List [Int ] = {
109
- // Global registers cannot occupy the same slot as another global register
110
- // In a flatMap, that means a newly discovered global register must be allocated to a new slot: this may resize the register pool
111
- assert(unallocatedRegs == regs.filterNot(_.allocated))
112
- if (unallocatedRegs.nonEmpty) {
113
- val usedSlots = regs.collect {
114
- case reg if reg.allocated => reg.addr
115
- }: @ nowarn
116
- val freeSlots = (0 until regs.size).filterNot(usedSlots)
117
- applyAllocation(unallocatedRegs, freeSlots)
118
- }
119
- else Nil
120
- }
121
-
122
- /** Given a set of unallocated registers and a supply of unoccupied slots, allocates each
123
- * register to one of the slots.
124
- *
125
- * @param regs the set of registers that require allocation
126
- * @param freeSlots the supply of slots that are currently not in-use
127
- * @return the slots that were used for allocation
128
- */
129
- private def applyAllocation (refs : Set [Ref [_]] @ nowarn3, freeSlots : Iterable [Int ]): List [Int ] = {
130
- val allocatedSlots = mutable.ListBuffer .empty[Int ]
131
- // TODO: For scala 2.12, use lazyZip and foreach!
132
- /* for ((ref, addr) <- refs.zip(freeSlots)) {
133
- ref.allocate(addr)
134
- allocatedSlots += addr
135
- }*/ // FIXME: until 5.0.0 we need to suppress warnings, and Scala 3 is being annoying (refreshing change)
136
- type Ref_ = Ref [_]
137
- refs.zip(freeSlots).foreach { (refAndAddr : (Ref [_], Int ) @ nowarn3) =>
138
- val ref : Ref_ @ nowarn3 = refAndAddr._1
139
- val addr = refAndAddr._2
140
- ref.allocate(addr)
141
- allocatedSlots += addr
142
- }
143
- allocatedSlots.toList
144
- }
145
-
146
102
/** If required, generates callee-save around a main body of instructions.
147
103
*
148
104
* This is needed when using `flatMap`, as it is unaware of the register
@@ -160,23 +116,19 @@ private [deepembedding] object StrictParsley {
160
116
* @param instrs the instruction buffer
161
117
* @param state the code generation state, for label generation
162
118
*/
163
- private def generateCalleeSave [M [_, + _]: ContOps , R ](numRegsUsedByParent : Int , bodyGen : => M [R , Unit ], usedRefs : Set [Ref [_]])
164
- (implicit instrs : InstrBuffer , state : CodeGenState ): M [R , Unit ] = {
165
- val reqRegs = usedRefs.size
166
- val localRegs : Set [Ref [_]] @ nowarn3 = usedRefs.filterNot(_.allocated): @ nowarn3
167
- val allocatedRegs = allocateRegisters(localRegs, usedRefs)
168
- val calleeSaveRequired = numRegsUsedByParent >= 0 // if this is -1, then we are the top level and have no parent, otherwise it needs to be done
169
- if (calleeSaveRequired && localRegs.nonEmpty) {
170
- val end = state.freshLabel()
171
- val calleeSave = state.freshLabel()
172
- instrs += new instructions.Label (calleeSave)
173
- instrs += new instructions.CalleeSave (end, localRegs, reqRegs, allocatedRegs, numRegsUsedByParent)
174
- bodyGen |> {
175
- instrs += new instructions.Jump (calleeSave)
176
- instrs += new instructions.Label (end)
119
+ private def allocateAndExpandRefs (minRef : Int , usedRefs : Set [Ref [_]])(implicit instrs : InstrBuffer ): Unit = {
120
+ var nextSlot = math.max(minRef, 0 )
121
+ usedRefs.foreach { (r : Ref [_]) =>
122
+ if (! r.allocated) {
123
+ r.allocate(nextSlot)
124
+ nextSlot += 1
177
125
}
178
126
}
179
- else bodyGen
127
+ val totalSlotsRequired = nextSlot
128
+ // if this is -1, then we are the top level and have no parent, otherwise it needs to be done
129
+ if (minRef >= 0 && (minRef < totalSlotsRequired)) {
130
+ instrs += new instructions.ExpandRefs (totalSlotsRequired)
131
+ }
180
132
}
181
133
182
134
/** Generates each of the shared, non-recursive, parsers that have been ''used'' by
0 commit comments