Commit 5eb904bb authored by Kunshan Wang's avatar Kunshan Wang

Merge branch 'issue60-symbol-reloc'

parents 25c6d5b9 f0c46abf
......@@ -265,11 +265,6 @@ struct MuVM {
// Set handlers
void (*set_trap_handler)(MuVM *mvm, MuTrapHandler trap_handler, MuCPtr userdata);
// Build boot image
void (*make_boot_image)(MuVM *mvm,
MuID* whitelist, MuArraySize whitelist_sz,
MuCString output_file); /// MUAPIPARSER whitelist:array:whitelist_sz
// Proprietary API of refimpl2: Let the micro VM execute.
void (*execute)(MuVM *mvm);
......@@ -426,8 +421,9 @@ struct MuCtx {
void (*disable_watchpoint)(MuCtx *ctx, MuWPID wpid);
// Mu memory pinning, usually object pinning
MuUPtrValue (*pin )(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
void (*unpin)(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
MuUPtrValue (*pin )(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
void (*unpin )(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
MuUPtrValue (*get_addr)(MuCtx *ctx, MuValue loc); // loc is either MuRefValue or MuIRefValue
// Expose Mu functions as native callable things, usually function pointers
MuValue (*expose )(MuCtx *ctx, MuFuncRefValue func, MuCallConv call_conv, MuIntValue cookie);
......@@ -435,6 +431,15 @@ struct MuCtx {
// Create an IR builder and start building.
MuIRBuilder* (*new_ir_builder)(MuCtx *ctx);
// Build boot image
void (*make_boot_image)(MuCtx *ctx,
MuID* whitelist, MuArraySize whitelist_sz,
MuFuncRefValue primordial_func, MuStackRefValue primordial_stack,
MuRefValue primordial_threadlocal,
MuIRefValue *sym_fields, MuCString *sym_strings, MuArraySize nsyms,
MuIRefValue *reloc_fields, MuCString *reloc_strings, MuArraySize nrelocs,
MuCString output_file); /// MUAPIPARSER whitelist:array:whitelist_sz;sym_fields:array:nsyms;sym_strings:array:nsyms;reloc_fields:array:nrelocs;reloc_strings:array:nrelocs;primordial_func:optional;primordial_stack:optional;primordial_threadlocal:optional
};
// These types are all aliases of MuID. They are provided to guide C
......@@ -758,9 +763,10 @@ struct MuIRBuilder {
#define MU_CI_UVM_KILL_DEPENDENCY ((MuCommInst)0x230) /// MUAPIPARSER muname:@uvm.kill_dependency
#define MU_CI_UVM_NATIVE_PIN ((MuCommInst)0x240) /// MUAPIPARSER muname:@uvm.native.pin
#define MU_CI_UVM_NATIVE_UNPIN ((MuCommInst)0x241) /// MUAPIPARSER muname:@uvm.native.unpin
#define MU_CI_UVM_NATIVE_EXPOSE ((MuCommInst)0x242) /// MUAPIPARSER muname:@uvm.native.expose
#define MU_CI_UVM_NATIVE_UNEXPOSE ((MuCommInst)0x243) /// MUAPIPARSER muname:@uvm.native.unexpose
#define MU_CI_UVM_NATIVE_GET_COOKIE ((MuCommInst)0x244) /// MUAPIPARSER muname:@uvm.native.get_cookie
#define MU_CI_UVM_NATIVE_GET_ADDR ((MuCommInst)0x242) /// MUAPIPARSER muname:@uvm.native.get_addr
#define MU_CI_UVM_NATIVE_EXPOSE ((MuCommInst)0x243) /// MUAPIPARSER muname:@uvm.native.expose
#define MU_CI_UVM_NATIVE_UNEXPOSE ((MuCommInst)0x244) /// MUAPIPARSER muname:@uvm.native.unexpose
#define MU_CI_UVM_NATIVE_GET_COOKIE ((MuCommInst)0x245) /// MUAPIPARSER muname:@uvm.native.get_cookie
#define MU_CI_UVM_META_ID_OF ((MuCommInst)0x250) /// MUAPIPARSER muname:@uvm.meta.id_of
#define MU_CI_UVM_META_NAME_OF ((MuCommInst)0x251) /// MUAPIPARSER muname:@uvm.meta.name_of
#define MU_CI_UVM_META_LOAD_BUNDLE ((MuCommInst)0x252) /// MUAPIPARSER muname:@uvm.meta.load_bundle
......
......@@ -11,6 +11,7 @@ const char *hw_string = "Hello world!\n";
const char *hw2_string = "Goodbye world!\n";
const char *gc_conf =
"vmLog=DEBUG\n"
"sosSize=524288\n"
"losSize=524288\n"
"globalSize=1048576\n"
......@@ -18,8 +19,10 @@ const char *gc_conf =
;
int main() {
printf("Creating micro VM...\n");
MuVM *mvm = mu_refimpl2_new_ex(gc_conf);
printf("Creating client context...\n");
MuCtx *ctx = mvm->new_context(mvm);
char *bundle1 =
......
......@@ -188,6 +188,7 @@ _array_converters = {
"uint64_t*" : "readLongArray",
"MuFlag*" : "readFlagArray",
"MuID*" : "readIntArray",
"MuCString*": "readCStringArray",
}
_special_converters = {
......
......@@ -363,9 +363,10 @@ common_instruction_opcodes = {
'@uvm.kill_dependency': 0x230,
'@uvm.native.pin': 0x240,
'@uvm.native.unpin': 0x241,
'@uvm.native.expose': 0x242,
'@uvm.native.unexpose': 0x243,
'@uvm.native.get_cookie': 0x244,
'@uvm.native.get_addr': 0x242,
'@uvm.native.expose': 0x243,
'@uvm.native.unexpose': 0x244,
'@uvm.native.get_cookie': 0x245,
'@uvm.meta.id_of': 0x250,
'@uvm.meta.name_of': 0x251,
'@uvm.meta.load_bundle': 0x252,
......@@ -1499,7 +1500,6 @@ _initialize_methods(MuVM, [
('id_of', CMuID, [CMuName]),
('name_of', CMuName, [CMuID]),
('set_trap_handler_', None, [CMuTrapHandler, CMuCPtr]),
('make_boot_image_', None, [ctypes.c_void_p, CMuArraySize, CMuCString]),
('execute', None, []),
('get_mu_error_ptr', ctypes.c_void_p, []),
## GEN:END:MuVM
......@@ -1591,9 +1591,11 @@ _initialize_methods(MuCtx, [
('disable_watchpoint', None, [CMuWPID]),
('pin', MuUPtrValue, [MuValue]),
('unpin', None, [MuValue]),
('get_addr', MuUPtrValue, [MuValue]),
('expose', MuValue, [MuFuncRefValue, CMuCallConv, MuIntValue]),
('unexpose', None, [CMuCallConv, MuValue]),
('new_ir_builder', MuIRBuilder, []),
('make_boot_image_', None, [ctypes.c_void_p, CMuArraySize, MuFuncRefValue, MuStackRefValue, MuRefValue, ctypes.c_void_p, ctypes.c_void_p, CMuArraySize, ctypes.c_void_p, ctypes.c_void_p, CMuArraySize, CMuCString]),
## GEN:END:MuCtx
])
......
......@@ -22,6 +22,7 @@ import uvm.refimpl.nat.NativeLibraryHolder
import uvm.staticanalysis.StaticAnalyzer
import uvm.utils.IDFactory
import uvm.utils.WithUtils.tryWithResource
import uvm.refimpl.bootimg.PrimordialInfo
object MicroVM {
val DEFAULT_SOS_SIZE: Word = 2L * 1024L * 1024L; // 2MiB
......@@ -31,16 +32,29 @@ object MicroVM {
def apply(): MicroVM = {
val vmConf = new VMConf()
new MicroVM(vmConf)
MicroVM(vmConf)
}
def apply(confStr: String): MicroVM = {
val vmConf = VMConf(confStr)
new MicroVM(vmConf)
MicroVM(vmConf)
}
def apply(confStr: String, appArgs: Seq[String]): MicroVM = {
val vmConf = VMConf(confStr)
MicroVM(vmConf, appArgs)
}
def apply(vmConf: VMConf): MicroVM = {
new MicroVM(vmConf, None)
}
def apply(vmConf: VMConf, appArgs: Seq[String]): MicroVM = {
new MicroVM(vmConf, Some(appArgs))
}
}
class MicroVM(val vmConf: VMConf) extends IRBuilderListener {
class MicroVM private (val vmConf: VMConf, val appArgs: Option[Seq[String]]) extends IRBuilderListener {
// implicitly injected resources
private implicit val microVM = this
......@@ -177,17 +191,9 @@ class MicroVM(val vmConf: VMConf) extends IRBuilderListener {
* Make boot image.
*/
def makeBootImage(whiteList: Seq[MuID], outputFile: String): Unit = {
val whiteListObjs = whiteList.map { id =>
val obj = globalBundle.allNs(id)
obj match {
case tl: TopLevel => tl
case _ => throw new UvmRuntimeException(
"White list may only contain top-level definitions. Entity ID %d%s is a %s".format(
obj.id, obj.name.map(n => s" (${n})").getOrElse(""), obj.getClass.getName))
}
}
val whiteListObjs = whiteList.map(globalBundle.topLevelNs.apply)
bootImageBuilder.makeBootImage(whiteListObjs, outputFile)
bootImageBuilder.makeBootImage(whiteListObjs, PrimordialInfo.NO_PRIMORDIAL, Seq(), Seq(), outputFile)
}
/**
......@@ -209,7 +215,7 @@ class MicroVM(val vmConf: VMConf) extends IRBuilderListener {
* Load from a boot image.
*/
def loadBootImage(file: String): Unit = {
tryWithResource(new BootImageLoader(file)) { bil =>
tryWithResource(new BootImageLoader(file, appArgs)) { bil =>
bil.load()
}
}
......
......@@ -20,6 +20,8 @@ import uvm.ssavariables.AtomicRMWOptr._
import uvm.ssavariables.HasKeepaliveClause
import uvm.ssavariables.MemoryOrder._
import uvm.types._
import uvm.refimpl.bootimg.FieldAndSymbol
import uvm.refimpl.bootimg.PrimordialInfo
object MuCtx {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
......@@ -189,9 +191,9 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
/** Compare general reference types for equality. */
def refEq(lhs: MuGenRefValue, rhs: MuGenRefValue): Boolean = (lhs, rhs) match {
case (l: MuRefValue, r: MuRefValue) => l.vb.objRef == r.vb.objRef
case (l: MuIRefValue, r: MuIRefValue) => l.vb.oo == r.vb.oo
case (l: MuOpaqueRefValue[_], r: MuOpaqueRefValue[_]) => l.vb.obj == r.vb.obj
case (l: MuRefValue, r: MuRefValue) => l.vb.objRef == r.vb.objRef
case (l: MuIRefValue, r: MuIRefValue) => l.vb.oo == r.vb.oo
case (l: MuOpaqueRefValue[_], r: MuOpaqueRefValue[_]) => l.vb.obj == r.vb.obj
case (l, r) => {
throw new IllegalArgumentException("Bad types for refEq: %s and %s".format(
l.showTy, r.showTy))
......@@ -269,11 +271,11 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
val nt = microVM.globalBundle.typeNs(newType)
val nh = (opnd, nt) match {
case (MuRefValue(ty, vb), ty2 @ TypeRef(_)) => MuRefValue(ty2, vb)
case (MuIRefValue(ty, vb), ty2 @ TypeIRef(_)) => MuIRefValue(ty2, vb)
case (MuRefValue(ty, vb), ty2 @ TypeRef(_)) => MuRefValue(ty2, vb)
case (MuIRefValue(ty, vb), ty2 @ TypeIRef(_)) => MuIRefValue(ty2, vb)
case (MuOpaqueRefValue(ty, vb), ty2 @ TypeFuncRef(_)) => {
require(ty.isInstanceOf[TypeFuncRef] && vb.isInstanceOf[BoxOpaque[_]],
"Attempt to cast %s to funcref. funcref value required".format(opnd.showTy))
"Attempt to cast %s to funcref. funcref value required".format(opnd.showTy))
MuFuncRefValue(ty2, vb.asInstanceOf[BoxFunc])
}
case _ => {
......@@ -680,6 +682,24 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
unpin(objRef)
}
def getAddr(loc: MuValue): MuUPtrValue = {
val (objTy, (objRef, offset)) = loc match {
case MuRefValue(t, vb) => (t, (vb.objRef, 0L))
case MuIRefValue(t, vb) => (t, vb.oo)
case _ => {
throw new IllegalArgumentException("loc must be ref or iref. Found %s".format(loc.ty))
}
}
// do nothing
val addr = objRef + offset
assert(microVM.memoryManager.globalMemory.isInSpace(addr) || pinSet.contains(objRef),
"Attempt to get address of objref %d 0x%x, offset %d, 0x%x, which is neither a global cell nor pinned by the current thread.".format(
objRef, objRef, offset, offset))
val ptrTy = InternalTypePool.ptrOf(objTy)
val box = new BoxPointer(addr)
addHandle(MuUPtrValue(ptrTy, box))
}
def expose(func: MuFuncRefValue, callConv: Flag, cookie: MuIntValue): MuUFPValue = {
val TypeFuncRef(sig) = func.ty
val f = func.vb.obj.getOrElse {
......@@ -701,6 +721,37 @@ class MuCtx(val ctxID: MuInternalID, _mutator: Mutator)(
microVM.newIRBuilder()
}
def makeBootImage(whiteList: Seq[MuID],
primordialFunc: Option[MuFuncRefValue], primordialStack: Option[MuStackRefValue], primordialThreadLocal: Option[MuRefValue],
symFields: Seq[MuIRefValue], symStrings: Seq[String], relocFields: Seq[MuIRefValue], relocStrings: Seq[String],
outputFile: String): Unit = {
val whiteListObjs = whiteList.map(microVM.globalBundle.topLevelNs.apply)
val maybePF = primordialFunc.flatMap(_.vb.obj)
val maybePS = primordialStack.flatMap(_.vb.obj)
val maybeTL = primordialThreadLocal.flatMap { h =>
h.vb.objRef match {
case 0 => None
case a => Some(a)
}
}
val primordial = PrimordialInfo(maybePF, maybePS, maybeTL)
val symFlds = symFields.map { h => (h.vb.objRef, h.vb.offset) }
val relocFlds = relocFields.map { h =>
assert(h.ty.ty.isInstanceOf[AbstractPointerType], "Reloc field %d 0x%x is not a pointer field. field type: %s".format(
h.vb.addr, h.vb.addr, h.ty.ty))
(h.vb.objRef, h.vb.offset)
}
val syms = for (((obj, off), name) <- symFlds zip symStrings) yield FieldAndSymbol(obj, off, name)
val relocs = for (((obj, off), name) <- relocFlds zip relocStrings) yield FieldAndSymbol(obj, off, name)
microVM.bootImageBuilder.makeBootImage(whiteListObjs, primordial, syms, relocs, outputFile)
}
// Internal methods for the micro VM
def handleFromInterpreterThread(thr: Option[InterpreterThread]): MuThreadRefValue = {
......
......@@ -33,63 +33,30 @@ import uvm.utils.IOHelpers
import uvm.utils.WithUtils.tryWithResource
class BootImageBuilder(implicit microVM: MicroVM) {
def makeBootImage(whiteList: Seq[TopLevel], outputFile: String): Unit = {
implicit val tcb = new TransitiveClosureBuilder(whiteList)
def makeBootImage(whiteList: Seq[TopLevel], primordial: PrimordialInfo,
syms: Seq[FieldAndSymbol], relocs: Seq[FieldAndSymbol], outputFile: String): Unit = {
implicit val tcb = new TransitiveClosureBuilder(whiteList, primordial)
tcb.doTransitiveClosure()
val biw = new BootImageWriter(tcb, outputFile)
val biw = new BootImageWriter(tcb, syms, relocs, outputFile)
biw.writeFile()
}
}
case class PrimordialInfo(maybeFunc: Option[Function], maybeStack: Option[InterpreterStack], maybeThreadLocal: Option[Word])
object PrimordialInfo {
val NO_PRIMORDIAL = PrimordialInfo(None, None, None)
}
case class FieldAndSymbol(objRef: Word, offset: Word, symbol: String) {
def toAddrSymPair: (Word, String) = ((objRef + offset), symbol)
}
object BootImageWriter {
import BootImageFile._
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
val FILEEXT_DATA = ".data"
val FILEEXT_ALLOC = ".alloc"
val FILEEXT_RELOC = ".reloc"
val UIRBUNDLE_FILE = "bundle.uir"
val IDNAMEMAP_FILE = "idnamemap"
val AS_REF = "R"
val AS_IREF = "I"
val AS_TAGREF = "T"
val AS_FUNCREF = "F"
val TO_GLOBAL = "G"
val TO_HEAP = "H"
val TO_FUNC = "F"
val NO_OFFSET = 0L
/**
* Allocation record. For global and heap.
*
* @param num An integer to identify the unit in a .data file. For global, it is the ID of the global cell; for heap
* object, it is a sequential number.
* @param fileOffset The offset from the beginning of the .data file.
* @param ty The type of the allocation unit.
* @param varLen The length of the variable part if it is a hybrid; otherwise 0.
*
* @param addr The address in the current micro VM instance. Not persisted.
*/
case class UnitAllocRecord(num: Long, fileOffset: Long, ty: Type, varLen: Long, addr: Word)
/**
* Relocation record. One for each field of ref<T>, iref<T>, weakref<T> or funcref<sig> types.
*
* @param num The source allocation unit number.
* @param fieldOffset The offset of the field from the beginning of the allocation unit.
* @param targetKind Whether the target is in the global memory, heap, or is a function.
* @param targetNum To identify the target. For memory, it is the beginning of the allocation unit; for function, it
* is the ID of the function.
* @param targetOffset If the source is an iref<T>, it is the offset from the beginning of the target allocation unit;
* otherwise 0.
*/
case class FieldRelocRecord(num: Long, fieldOffset: Long, fieldKind: String, targetKind: String, targetNum: Long, targetOffset: Long)
class FileGroup(baseName: String, dir: Path) {
val dataFile = dir.resolve(baseName + FILEEXT_DATA)
val allocFile = dir.resolve(baseName + FILEEXT_ALLOC)
......@@ -102,8 +69,10 @@ object BootImageWriter {
}
}
class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implicit microVM: MicroVM) {
class BootImageWriter(tcb: TransitiveClosureBuilder, syms: Seq[FieldAndSymbol],
relocs: Seq[FieldAndSymbol], outputFile: String)(implicit microVM: MicroVM) {
import BootImageWriter._
import BootImageFile._
val bundleSerializer = new BundleSerializer(microVM.globalBundle, tcb.tls.set)
......@@ -113,13 +82,22 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
val globalGroup = new FileGroup("global", tempDir)
val heapGroup = new FileGroup("heap", tempDir)
val symsMap = syms.map(_.toAddrSymPair).toMap
val symsRevMap = syms.map(_.toAddrSymPair.swap).toMap
val relocsMap = relocs.map(_.toAddrSymPair).toMap
{
logger.debug("Relocation map: " + relocsMap.toString)
}
val idNameMapPath = tempDir.resolve(IDNAMEMAP_FILE)
val uirBundlePath = tempDir.resolve(UIRBUNDLE_FILE)
val metaInfoPath = tempDir.resolve(METAINFO_FILE)
val zipPath = Paths.get(outputFile)
val allZipContents = Seq(globalGroup.dataFile, heapGroup.dataFile, globalGroup.allocFile, heapGroup.allocFile,
globalGroup.relocFile, heapGroup.relocFile, idNameMapPath, uirBundlePath)
globalGroup.relocFile, heapGroup.relocFile, idNameMapPath, uirBundlePath, metaInfoPath)
private val globalMemory: Space = microVM.memoryManager.globalMemory
private val smallObjectSpace: Space = microVM.memoryManager.heap.space
......@@ -149,6 +127,8 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
scanForRelocs(globalGroup)
scanForRelocs(heapGroup)
addPointerRelocs()
writeRelocRecs(globalGroup)
writeRelocRecs(heapGroup)
......@@ -156,6 +136,8 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
writeIDNameMap()
writeUIRBundle()
writeMetaInfo()
makeZipPackage()
}
......@@ -200,17 +182,53 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
val begin = alloc.addr
MemoryDataScanner.scanAllocUnit(begin, new MemoryFieldHandler {
def visitUPtrField(objRef: Word, iRef: Word, toAddr: Word): Unit = {
relocsMap.get(iRef) match {
case Some(symbol) => {
// by symbol
val fieldOffset = iRef - objRef
val reloc = FieldRelocRecord(num, fieldOffset, AS_UPTR, TO_SYM, 0, 0, symbol)
group.relocRecs += reloc
logger.debug("External uptr relocation: %d 0x%x -> %s".format(iRef, iRef, symbol))
}
case None => {
// maybe a "reasonable" pointer to a mu global cell. Relocate it too.
val fieldOffset = iRef - objRef
val maybeGlobalCell = tcb.maybeGetGlobalCellRec(toAddr)
maybeGlobalCell match {
case Some(gcr) =>
val targetNum = gcr.g.id.toLong
val targetAddr = gcr.begin
val targetOffset = toAddr - targetAddr
val reloc = FieldRelocRecord(num, fieldOffset, AS_UPTR, TO_GLOBAL, targetNum, targetOffset, NO_SYM)
group.relocRecs += reloc
logger.info("Relocation entry for uptr field automatically generated: %d 0x%x -> %d 0x%x".format(
iRef, iRef, toAddr, toAddr))
case None => }
}
}
}
def visitUFPField(objRef: Word, iRef: Word, toAddr: Word): Unit = {
relocsMap.get(iRef).foreach { symbol =>
// by symbol
val fieldOffset = iRef - objRef
val reloc = FieldRelocRecord(num, fieldOffset, AS_UPTR, TO_SYM, 0, 0, symbol)
group.relocRecs += reloc
logger.debug("External ufuncptr relocation: %d 0x%x -> %s".format(iRef, iRef, symbol))
}
}
def visitRefField(objRef: Word, iRef: Word, toObj: Word, isWeak: Boolean): Unit = if (toObj != 0L) {
val fieldOffset = iRef - objRef
val targetNum = addrToHeapObjNum(toObj)
val reloc = FieldRelocRecord(num, fieldOffset, AS_REF, TO_HEAP, targetNum, NO_OFFSET)
val reloc = FieldRelocRecord(num, fieldOffset, AS_REF, TO_HEAP, targetNum, NO_OFFSET, NO_SYM)
group.relocRecs += reloc
}
def visitIRefField(objRef: Word, iRef: Word, toObj: Word, toOffset: Word): Unit = if (toObj != 0L) {
val fieldOffset = iRef - objRef
val targetNum = addrToHeapObjNum(toObj)
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_HEAP, targetNum, toOffset)
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_HEAP, targetNum, toOffset, NO_SYM)
group.relocRecs += reloc
} else {
val fieldOffset = iRef - objRef
......@@ -218,21 +236,21 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
val targetNum = gcr.g.id.toLong
val targetAddr = gcr.begin
val targetOffset = toOffset - targetAddr
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_GLOBAL, targetNum, targetOffset)
val reloc = FieldRelocRecord(num, fieldOffset, AS_IREF, TO_GLOBAL, targetNum, targetOffset, NO_SYM)
group.relocRecs += reloc
}
def visitTagRefField(objRef: Word, iRef: Word, toObj: Word): Unit = if (toObj != 0L) {
val fieldOffset = iRef - objRef
val targetNum = addrToHeapObjNum(toObj)
val reloc = FieldRelocRecord(num, fieldOffset, AS_TAGREF, TO_HEAP, targetNum, NO_OFFSET)
val reloc = FieldRelocRecord(num, fieldOffset, AS_TAGREF, TO_HEAP, targetNum, NO_OFFSET, NO_SYM)
group.relocRecs += reloc
}
def visitFuncRefField(objRef: Word, iRef: Word, toFunc: Option[Function]): Unit = toFunc.foreach { func =>
val fieldOffset = iRef - objRef
val targetNum = func.id.toLong
val reloc = FieldRelocRecord(num, fieldOffset, AS_FUNCREF, TO_FUNC, targetNum, NO_OFFSET)
val reloc = FieldRelocRecord(num, fieldOffset, AS_FUNCREF, TO_FUNC, targetNum, NO_OFFSET, NO_SYM)
group.relocRecs += reloc
}
......@@ -244,11 +262,16 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
})
}
}
private def addPointerRelocs(): Unit = {
// Seems uptrs are already handled when scanning fields. No further processing is needed at this moment.
}
private def writeRelocRecs(group: FileGroup): Unit = {
tryWithResource(Files.newBufferedWriter(group.relocFile, StandardCharsets.UTF_8)) { writer =>
for (rr <- group.relocRecs) {
writer.write("%d,%d,%s,%s,%d,%d\n".format(rr.num, rr.fieldOffset, rr.fieldKind, rr.targetKind, rr.targetNum, rr.targetOffset))
writer.write("%d,%d,%s,%s,%d,%d,%s\n".format(
rr.num, rr.fieldOffset, rr.fieldKind, rr.targetKind, rr.targetNum, rr.targetOffset, rr.targetString))
}
}
}
......@@ -264,6 +287,22 @@ class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implici
bundleSerializer.writeUIR(writer)
}
}
private def writeMetaInfo(): Unit = {
tryWithResource(Files.newBufferedWriter(metaInfoPath, StandardCharsets.UTF_8)) { writer =>
val sb = new StringBuilder()
tcb.primordial.maybeFunc.foreach { func =>
sb.append("%s=%d\n".format(KEY_INITFUNC, func.id))
tcb.primordial.maybeThreadLocal.foreach { addr =>
val heapObjNum = heapGroup.allocRecs.find(_.addr == addr).getOrElse {
throw new BootImageBuilderException("BUG: Thread local object is not in the boot image")
}.num
sb.append("%s=%d\n".format(KEY_THREADLOCAL, heapObjNum))
}
}
writer.write(sb.toString)
}
}
private def makeZipPackage(): Unit = {
tryWithResource(new ZipOutputStream(Files.newOutputStream(zipPath))) { zos =>
......
package uvm.refimpl.bootimg
import uvm.types.Type
import uvm.refimpl.Word
object BootImageFile {
val FILEEXT_DATA = ".data"
val FILEEXT_ALLOC = ".alloc"
val FILEEXT_RELOC = ".reloc"
val UIRBUNDLE_FILE = "bundle.uir"
val IDNAMEMAP_FILE = "idnamemap"
val METAINFO_FILE = "metainfo"
val KEY_INITFUNC = "initfunc"
val KEY_THREADLOCAL = "threadlocal"
val KEY_EXTRALIBS = "extralibs"
val AS_REF = "R"
val AS_IREF = "I"
val AS_TAGREF = "T"
val AS_FUNCREF = "F"
val AS_UPTR = "P"
val TO_GLOBAL = "G"
val TO_HEAP = "H"
val TO_FUNC = "F"
val TO_SYM = "S"
val NO_OFFSET = 0L
val NO_SYM = ""
/**
* Allocation record. For global and heap.
*
* @param num An integer to identify the unit in a .data file. For global, it is the ID of the global cell; for heap
* object, it is a sequential number.
* @param fileOffset The offset from the beginning of the .data file.
* @param ty The type of the allocation unit.
* @param varLen The length of the variable part if it is a hybrid; otherwise 0.
*
* @param addr The address in the current micro VM instance. Not persisted.
*/
case class UnitAllocRecord(num: Long, fileOffset: Long, ty: Type, varLen: Long, addr: Word)
/**
* Relocation record. One for each field of ref<T>, iref<T>, weakref<T> or funcref<sig> types.
*
* @param num The source allocation unit number.
* @param fieldOffset The offset of the field from the beginning of the allocation unit.
* @param targetKind Whether the target is in the global memory, heap, or is a function.
* @param targetNum To identify the target. For memory, it is the beginning of the allocation unit; for function, it
* is the ID of the function.
* @param targetOffset If the source is an iref<T>, it is the offset from the beginning of the target allocation unit;
* otherwise 0.
*/
case class FieldRelocRecord(num: Long, fieldOffset: Long, fieldKind: String, targetKind: String, targetNum: Long, targetOffset: Long, targetString: String)
}
\ No newline at end of file
......@@ -24,15 +24,28 @@ import uvm.types.TypeHybrid
import uvm.utils.IOHelpers.forEachLine
import uvm.utils.MappedIDFactory
import uvm.utils.WithUtils.tryWithResource
import scala.collection.mutable.ArrayBuffer
import uvm.refimpl.itpr.HowToResume
import uvm.refimpl.cmdline.NativeArgv