WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

Commit f62845b3 authored by Kunshan Wang's avatar Kunshan Wang
Browse files

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