Skip to content

Commit

Permalink
feat: added new ExpandRefs system
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mie6 committed Jan 4, 2025
1 parent 5655dbe commit 2d9e1c6
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,36 +104,15 @@ private [deepembedding] object StrictParsley {
* @param regs the set of all registers used by a specific parser
* @return the list of slots that have been freshly allocated to
*/
private def allocateRegisters(minRef: Int, unallocatedRegs: Set[Ref[_]], regs: Set[Ref[_]]): List[Int] = {
private def allocateRegisters(minRef: Int, unallocatedRegs: Set[Ref[_]]): Int = {
// Global registers cannot occupy the same slot as another global register
// In a flatMap, that means a newly discovered global register must be allocated to a new slot: this may resize the register pool
assert(unallocatedRegs == regs.filterNot(_.allocated))
if (unallocatedRegs.nonEmpty) {
/*val usedSlots = regs.collect {
case reg if reg.allocated => reg.addr
}*/
val minSlot = math.max(minRef, 0)
val freeSlots = minSlot until (minSlot + unallocatedRegs.size)//(0 until regs.size).filterNot(usedSlots)
applyAllocation(unallocatedRegs, freeSlots)
var nextSlot = math.max(minRef, 0)
for (reg <- unallocatedRegs) {
reg.allocate(nextSlot)
nextSlot += 1
}
else Nil
}

/** Given a set of unallocated registers and a supply of unoccupied slots, allocates each
* register to one of the slots.
*
* @param regs the set of registers that require allocation
* @param freeSlots the supply of slots that are currently not in-use
* @return the slots that were used for allocation
*/
private def applyAllocation(refs: Set[Ref[_]], freeSlots: Iterable[Int]): List[Int] = {
val allocatedSlots = mutable.ListBuffer.empty[Int]
// TODO: For scala 2.12, use lazyZip and foreach!
for ((ref, addr) <- refs.zip(freeSlots)) {
ref.allocate(addr)
allocatedSlots += addr
}
allocatedSlots.toList
nextSlot
}

/** If required, generates callee-save around a main body of instructions.
Expand All @@ -153,25 +132,27 @@ private [deepembedding] object StrictParsley {
* @param instrs the instruction buffer
* @param state the code generation state, for label generation
*/
private def generateCalleeSave[M[_, +_]: ContOps, R](numRegsUsedByParent: Int, minRef: Int, bodyGen: =>M[R, Unit], usedRefs: Set[Ref[_]])
(implicit instrs: InstrBuffer, state: CodeGenState): M[R, Unit] = {
val reqRegs = usedRefs.size
private def generateCalleeSave[M[_, +_], R](@scala.annotation.unused numRegsUsedByParent: Int, minRef: Int, bodyGen: =>M[R, Unit], usedRefs: Set[Ref[_]])
(implicit instrs: InstrBuffer, @scala.annotation.unused state: CodeGenState): M[R, Unit] = {
//val reqRegs = usedRefs.size
val localRegs = usedRefs.filterNot(_.allocated)
val allocatedRegs = allocateRegisters(minRef, localRegs, usedRefs)
val calleeSaveRequired = numRegsUsedByParent >= 0 // if this is -1, then we are the top level and have no parent, otherwise it needs to be done
if (calleeSaveRequired && localRegs.nonEmpty) {
val end = state.freshLabel()
val calleeSave = state.freshLabel()
instrs += new instructions.Push(false) // callee-save is not active
instrs += new instructions.Label(calleeSave)
instrs += new instructions.CalleeSave(end, localRegs, reqRegs, allocatedRegs, numRegsUsedByParent)
bodyGen |> {
instrs += new instructions.Push(true) // callee-save is active
instrs += new instructions.Jump(calleeSave)
instrs += new instructions.Label(end)
}
val totalSlotsRequired = allocateRegisters(minRef, localRegs)
//val calleeSaveRequired = numRegsUsedByParent >= 0 // if this is -1, then we are the top level and have no parent, otherwise it needs to be done
val refExpandRequired = minRef >= 0 // if this is -1, then we are the top level and have no parent, otherwise it needs to be done
if (refExpandRequired && localRegs.nonEmpty) {
//val end = state.freshLabel()
//val calleeSave = state.freshLabel()
//instrs += new instructions.Push(false) // callee-save is not active
//instrs += new instructions.Label(calleeSave)
//instrs += new instructions.CalleeSave(end, localRegs, reqRegs, allocatedRegs, numRegsUsedByParent)
//bodyGen
//instrs += new instructions.Push(true) // callee-save is active
//instrs += new instructions.Jump(calleeSave)
//instrs += new instructions.Label(end)
instrs += new instructions.ExpandRefs(totalSlotsRequired)
}
else bodyGen
//else bodyGen
bodyGen
}

/** Generates each of the shared, non-recursive, parsers that have been ''used'' by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
package parsley.internal.machine.instructions

import parsley.state.Ref
//import parsley.state.Ref
import parsley.token.errors.LabelConfig

import parsley.internal.errors.ExpectDesc
Expand Down Expand Up @@ -161,7 +161,7 @@ private [internal] object Span extends Instr {
// same mapping for a let-bound parser)
// TODO: unit test to demonstrate the above issue!
// This instruction holds mutable state, but it is safe to do so, because it's always the first instruction of a DynCall.
private [parsley] final class CalleeSave(var label: Int, localRefs: Set[Ref[_]], reqSize: Int, slots: List[(Int, Int)], saveArray: Array[AnyRef])
/*private [parsley] final class CalleeSave(var label: Int, localRefs: Set[Ref[_]], reqSize: Int, slots: List[(Int, Int)], saveArray: Array[AnyRef])
extends InstrWithLabel {
private def this(label: Int, localRefs: Set[Ref[_]], reqSize: Int, slots: List[Int]) =
this(label, localRefs, reqSize, slots.zipWithIndex, new Array[AnyRef](slots.length))
Expand Down Expand Up @@ -222,4 +222,13 @@ private [parsley] final class CalleeSave(var label: Int, localRefs: Set[Ref[_]],
// $COVERAGE-OFF$
override def toString: String = s"CalleeSave($label, newSz = $reqSize, slotsToSave = $slots)"
// $COVERAGE-ON$
}*/

private [parsley] final class ExpandRefs(newSz: Int) extends Instr {
override def apply(ctx: Context): Unit = {
if (newSz > ctx.regs.size) {
ctx.regs = java.util.Arrays.copyOf(ctx.regs, newSz)
}
ctx.inc()
}
}
6 changes: 3 additions & 3 deletions parsley/shared/src/test/scala/parsley/CoreTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class CoreTests extends ParsleyTest {
val p = "hello :)".makeRef(r2 => q *> q *> r2.get)
p.parse("aa") shouldBe Success("hello :)")
}
they should "be preserved by callee-save in flatMap" ignore {
they should "be preserved by callee-save in flatMap" in {
val p = "hello world".makeRef(r2 => {
6.makeRef(r1 => {
unit.flatMap(_ => 4.makeRef(_ => r2.set("hi"))) *>
Expand All @@ -239,7 +239,7 @@ class CoreTests extends ParsleyTest {
})
p.parse("") shouldBe Success((6, "hi"))
}
they should "be preserved by callee-save in flatMap even when it fails" ignore {
they should "be preserved by callee-save in flatMap even when it fails" in {
val p = "hello world".makeRef(r2 => {
6.makeRef(r1 => {
combinator.optional(unit.flatMap(_ => r2.set("hi") *> 4.makeRef(_ => Parsley.empty))) *>
Expand Down Expand Up @@ -392,7 +392,7 @@ class CoreTests extends ParsleyTest {
q.parse("aaaabbb") shouldBe a [Success[_]]
}

"flatMap" should "consistently generate a callee-save instruction if needed" ignore {
"flatMap" should "consistently generate a callee-save instruction if needed" in {
import parsley.state._
val r = Ref.make[Int]
val p = unit.flatMap { _ =>
Expand Down

0 comments on commit 2d9e1c6

Please sign in to comment.