Commit f62845b3 authored by Kunshan Wang's avatar Kunshan Wang

Boot image builder scans heap objects.

parent a4402c9d
......@@ -8,6 +8,19 @@ import uvm.refimpl._
import uvm.refimpl.mem.TypeSizes.Word
import scala.collection.mutable.{ Set, Queue }
import uvm.refimpl.itpr.BoxIRef
import scala.collection.immutable.TreeMap
import uvm.refimpl.mem.TypeSizes
import uvm.refimpl.mem.Space
import uvm.refimpl.mem.HeaderUtils
import uvm.refimpl.mem.scanning.RefFieldHandler
import uvm.refimpl.itpr.InterpreterThread
import uvm.refimpl.itpr.BoxStack
import uvm.refimpl.itpr.HasObjRef
import uvm.refimpl.itpr.InterpreterStack
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
import uvm.refimpl.mem.scanning.MemoryDataScanner
class BootImageBuilder(implicit microVM: MicroVM) {
def makeBootImage(whiteList: Seq[Int], outputFile: String): Unit = {
......@@ -41,6 +54,7 @@ class TransitiveClosure[T](initialElems: Seq[T]) {
}
object TransitiveClosureBuilder {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
implicit class RichTransitiveClosure[T <: Identified](val tc: TransitiveClosure[T]) extends AnyVal {
def ?=(elem: SSAVariable) = {
......@@ -56,11 +70,17 @@ object TransitiveClosureBuilder {
tc
}
}
case class GlobalCellRec(begin: Word, end: Word, g: GlobalCell)
}
class TransitiveClosureBuilder(initialSet: Seq[Int])(
implicit microVM: MicroVM) {
class TransitiveClosureBuilder(initialSet: Seq[Int])(implicit microVM: MicroVM) extends RefFieldHandler {
import TransitiveClosureBuilder._
private val globalMemory: Space = microVM.memoryManager.globalMemory
private val smallObjectSpace: Space = microVM.memoryManager.heap.space
private val largeObjectSpace: Space = microVM.memoryManager.heap.los
private implicit val memorySupport = microVM.memoryManager.memorySupport
/**
* Top-level definitions in the closure. Function versions are not considered top-level here. A function may have at
......@@ -71,15 +91,46 @@ class TransitiveClosureBuilder(initialSet: Seq[Int])(
/**
* Global cells + heap objects.
*/
val objs = TransitiveClosure[Word]()
val allocs = TransitiveClosure[Word]()
def getGlobalCellAddr(g: GlobalCell): Word = {
microVM.constantPool.getGlobalVarBox(g).asInstanceOf[BoxIRef].addr
}
/**
* Map the global cell's starting address to the global cell itself.
*/
val globalCellMap = {
val elems = microVM.globalBundle.globalCellNs.all.toSeq.map { g =>
val begin = getGlobalCellAddr(g)
val ty = g.cellTy
assert(!ty.isInstanceOf[TypeHybrid])
val size = TypeSizes.sizeOf(ty)
val end = begin + size
(begin, GlobalCellRec(begin, end, g))
}
TreeMap[Word, GlobalCellRec](elems: _*)
}
def getGlobalCellRec(iref: Word): GlobalCellRec = {
require(microVM.memoryManager.globalMemory.isInSpace(iref),
"Address %d 0x%x is not in the global space".format(iref, iref))
val (begin, gcr) = globalCellMap.to(iref).last
assert(begin <= iref && iref < gcr.end,
"Address %d 0x%x is in the global space, but the previous global cell's address range is %d 0x%x - %d 0x%x".format(
iref, iref, begin, begin, gcr.end, gcr.end))
gcr
}
def doTransitiveClosure(): Unit = {
while (tls.hasPending || objs.hasPending) {
while (tls.hasPending || allocs.hasPending) {
while (tls.hasPending) {
visitTopLevel(tls.dequeue())
}
while (objs.hasPending) {
visitHeapObj(objs.dequeue())
while (allocs.hasPending) {
visitAllocUnit(allocs.dequeue())
}
}
}
......@@ -124,8 +175,8 @@ class TransitiveClosureBuilder(initialSet: Seq[Int])(
private def visitGlobalCell(g: GlobalCell): Unit = {
tls += g.cellTy
// Scan this cell and visit reachable heap objects or other global cells, too.
allocs += getGlobalCellAddr(g)
}
private def visitFunction(f: Function): Unit = {
......@@ -205,7 +256,7 @@ class TransitiveClosureBuilder(initialSet: Seq[Int])(
case InstCommInst(ci, flagList, typeList, funcSigList, argList, excClause, keepalives) =>
tls ++= typeList ++= funcSigList ??= argList
case _ => throw new UvmRuntimeException("Unknown instruction: " + inst.getClass.getName)
case _ => throw new BootImageBuilderException("Unknown instruction: " + inst.getClass.getName)
}
private def visitNewStackAction(nsa: NewStackAction): Unit = nsa match {
......@@ -213,7 +264,58 @@ class TransitiveClosureBuilder(initialSet: Seq[Int])(
case ThrowExc(e) =>
}
private def visitHeapObj(ref: Word): Unit = {
???
private def visitAllocUnit(addrOrObjRef: Word): Unit = {
logger.debug("Visiting allocation unit 0x%x".format(addrOrObjRef))
if (globalMemory.isInSpace(addrOrObjRef)) {
val GlobalCellRec(begin, end, g) = getGlobalCellRec(addrOrObjRef)
logger.debug(" It's global cell. begin=0x%x, end=0x%x, type=%s".format(begin, end, g.cellTy))
tls += g
visitAllocUnitFields(begin, g.cellTy)
} else if (smallObjectSpace.isInSpace(addrOrObjRef) || largeObjectSpace.isInSpace(addrOrObjRef)) {
val objRef = addrOrObjRef
val tag = HeaderUtils.getTag(objRef)
val ty = HeaderUtils.getType(microVM, tag)
tls += ty
visitAllocUnitFields(objRef, ty)
} else {
throw new BootImageBuilderException("Address %d 0x%x is not in the heap or global space".format(addrOrObjRef, addrOrObjRef))
}
}
private def visitAllocUnitFields(begin: Word, ty: Type): Unit = {
MemoryDataScanner.scanAllocUnit(begin, this)
}
def boxToHeap(box: HasObjRef): Option[Word] =
throw new BootImageBuilderException("BUG: Box should not have been scanned during boot image building.")
def boxToStack(box: BoxStack): Option[InterpreterStack] =
throw new BootImageBuilderException("BUG: Box should not have been scanned during boot image building.")
def memToHeap(objRef: Word, iRef: Word, toObj: Word, isWeak: Boolean, isTR64: Boolean): Option[Word] = {
if (toObj != 0L) {
allocs += toObj
Some(toObj)
} else {
None
}
}
def memToStack(objRef: Word, iRef: Word, toStack: Option[InterpreterStack]): Option[InterpreterStack] = {
if (toStack.isDefined) {
throw new BootImageBuilderException(
"Error: Stack reachable during boot image building. From: obj 0x%x, iref 0x%x. To stack id: %d".format(
objRef, iRef, toStack.get.id))
}
None
}
def stackToStackMem(stack: InterpreterStack, toObj: Word): Option[Word] =
throw new BootImageBuilderException("BUG: Stack should not have been scanned during boot image building.")
def threadToStack(thread: InterpreterThread, toStack: Option[InterpreterStack]): Option[InterpreterStack] =
throw new BootImageBuilderException("BUG: Thread should not have been scanned during boot image building.")
def pinSetToMem(toObj: Word): Option[Word] =
throw new BootImageBuilderException("BUG: Pin set should not have been scanned during boot image building.")
}
\ No newline at end of file
package uvm.refimpl.bootimg
import uvm.refimpl.UvmRuntimeException
class BootImageBuilderException(message: String = null, cause: Throwable = null) extends UvmRuntimeException(message, cause)
......@@ -53,6 +53,9 @@ case class BoxIRef(var objRef: Word, var offset: Word) extends HasObjRef {
// Helper to get and set the objRef and offset at the same time
def oo: (Word, Word) = (objRef, offset)
def oo_=(newVal: (Word, Word)): Unit = { objRef = newVal._1; offset = newVal._2 }
// Helper to get the address (objRef + offset). Useful when accessing the memory.
def addr: Word = objRef + offset
}
case class BoxFunc(var func: Option[Function]) extends ObjectBox[Function] {
def obj = func
......
......@@ -111,7 +111,7 @@ class AllScanner(val handler: RefFieldHandler)(
allEmpty = false
val objRef = addrQueue.pollFirst()
logger.debug("Scanning heap object 0x%x...".format(objRef))
MemoryDataScanner.scanAllocUnit(objRef, objRef, this)
MemoryDataScanner.scanAllocUnit(objRef, this)
}
}
}
......
......@@ -17,7 +17,7 @@ object MemoryDataScanner extends StrictLogging {
* Scan an allocation unit. In this implementation, heap objects, alloca cells and global
* cells all have the same layout.
*/
def scanAllocUnit(objRef: Word, iRef: Word, handler: RefFieldHandler)(implicit microVM: MicroVM, memorySupport: MemorySupport) {
def scanAllocUnit(objRef: Word, handler: RefFieldHandler)(implicit microVM: MicroVM, memorySupport: MemorySupport) {
val tag = HeaderUtils.getTag(objRef)
logger.debug("Obj 0x%x, tag 0x%x".format(objRef, tag))
val ty = HeaderUtils.getType(microVM, tag)
......
......@@ -59,6 +59,18 @@ abstract class UvmBundleTesterBase extends UvmTestBase {
ctx.closeContext()
}
def preloadHails(fileNames: String*): Unit = {
val ctx = microVM.newContext()
for (fn <- fileNames) {
val r = new FileReader(fn)
ctx.loadHail(r)
r.close()
}
ctx.closeContext()
}
type TrapHandlerFunction = (MuCtx, MuThreadRefValue, MuStackRefValue, Int) => TrapHandlerResult
class MockTrapHandler(thf: TrapHandlerFunction) extends TrapHandler {
......
......@@ -5,28 +5,32 @@ import uvm.refimpl.VMConf
import uvm.refimpl.UvmBundleTesterBase
import uvm.ir.textinput.ExtraMatchers
import uvm.ir.textinput.TestingBundlesValidators.MagicalOur
import uvm.refimpl.mem.HeaderUtils
import uvm.types.Type
import uvm.refimpl.mem.TypeSizes.Word
class TransitiveClosureTest extends UvmBundleTesterBase with ExtraMatchers {
override def makeMicroVM = new MicroVM(new VMConf())
preloadBundles("tests/uvm-refimpl-test/transitive-closure.uir")
preloadHails("tests/uvm-refimpl-test/transitive-closure.hail")
val our = new MagicalOur(microVM.globalBundle)
behavior of "TransitiveClosureBuilder"
it should "compute transitive closure over types" in {
val tsb = new TransitiveClosureBuilder(Seq("@refi64"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our ty "@i64")
tsb.tls.set shouldNot contain(our ty "@i32")
}
it should "compute transitive closure over function signatures" in {
val tsb = new TransitiveClosureBuilder(Seq("@s1"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our ty "@i32")
tsb.tls.set should contain(our ty "@i16")
tsb.tls.set shouldNot contain(our ty "@i64")
......@@ -35,7 +39,7 @@ class TransitiveClosureTest extends UvmBundleTesterBase with ExtraMatchers {
it should "compute transitive closure over constants" in {
val tsb = new TransitiveClosureBuilder(Seq("@F", "@S"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our const "@S0")
tsb.tls.set should contain(our const "@S1")
tsb.tls.set should contain(our const "@S2")
......@@ -49,14 +53,14 @@ class TransitiveClosureTest extends UvmBundleTesterBase with ExtraMatchers {
it should "compute transitive closure over global cells" in {
val tsb = new TransitiveClosureBuilder(Seq("@gfoo"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our ty "@foo")
}
it should "compute transitive closure over function decls" in {
val tsb = new TransitiveClosureBuilder(Seq("@fd"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our sig "@s1")
tsb.tls.set should contain(our ty "@i32")
tsb.tls.set should contain(our ty "@i16")
......@@ -66,7 +70,7 @@ class TransitiveClosureTest extends UvmBundleTesterBase with ExtraMatchers {
it should "compute transitive closure over function defs" in {
val tsb = new TransitiveClosureBuilder(Seq("@gd"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our sig "@s2")
tsb.tls.set should contain(our ty "@i64")
tsb.tls.set should contain(our ty "@i16")
......@@ -79,11 +83,70 @@ class TransitiveClosureTest extends UvmBundleTesterBase with ExtraMatchers {
it should "compute transitive closure over exposed functions" in {
val tsb = new TransitiveClosureBuilder(Seq("@ef"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our func "@fd")
tsb.tls.set should contain(our sig "@s1")
tsb.tls.set should contain(our ty "@i32")
tsb.tls.set should contain(our ty "@i16")
tsb.tls.set should contain(our const "@S1")
}
def getType(obj: Word): Type = {
val tag = HeaderUtils.getTag(obj)(microVM.memoryManager.memorySupport)
val ty = HeaderUtils.getType(microVM, tag)
ty
}
it should "scan the heap for objects" in {
val tsb = new TransitiveClosureBuilder(Seq("@llhead"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our ty "@refll")
tsb.tls.set should contain(our ty "@ll")
tsb.tls.set should contain(our ty "@i64")
val objs = tsb.allocs.set.toSeq
objs.length shouldBe 4
for (obj <- objs) {
val ty = getType(obj)
Seq(ty) should contain oneOf (our ty "@ll", our ty "@refll")
}
}
it should "scan the types reachable from objects" in {
val tsb = new TransitiveClosureBuilder(Seq("@llhead_void"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our ty "@refll")
tsb.tls.set should contain(our ty "@ll")
tsb.tls.set should contain(our ty "@i64")
tsb.tls.set should contain(our ty "@refvoid")
tsb.tls.set should contain(our ty "@void")
val objs = tsb.allocs.set.toSeq
objs.length shouldBe 4
for (obj <- objs) {
val ty = getType(obj)
Seq(ty) should contain oneOf (our ty "@ll", our ty "@refvoid")
}
}
it should "scan global cells which refer to each other" in {
val tsb = new TransitiveClosureBuilder(Seq("@gr2"))(microVM)
tsb.doTransitiveClosure()
tsb.tls.set should contain(our ty "@irefi64")
tsb.tls.set should contain(our ty "@i64")
tsb.tls.set should contain(our globalCell "@gr1")
val objs = tsb.allocs.set.toSeq
objs.length shouldBe 2
for (obj <- objs) {
val ty = getType(obj)
Seq(ty) should contain oneOf (our ty "@i64", our ty "@refi64")
}
}
}
\ No newline at end of file
.new $o1 <@ll>
.new $o2 <@ll>
.new $o3 <@ll>
.init $o1 = {100 $o2}
.init $o2 = {200 $o3}
.init $o3 = {300 NULL}
.init @gr1 = 42
.init @gr2 = &@gr1
.init @llhead = $o1
.init @llhead_void = $o1
......@@ -32,3 +32,19 @@
}
.expose @ef = @fd #DEFAULT @S1
// linked list
.typedef @ll = struct <@i64 @refll>
.typedef @refll = ref<@ll>
.global @llhead <@refll>
// refer to head via refvoid. The object's concrete type should be scanned.
.typedef @void = void
.typedef @refvoid = ref<@void>
.global @llhead_void <@refvoid>
// refer to another global cell
.typedef @irefi64 = iref<@i64>
.global @gr1 <@i64>
.global @gr2 <@irefi64>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment