GitLab will be upgraded to the 12.10.14-ce.0 on 28 Sept 2020 at 2.00pm (AEDT) to 2.30pm (AEDT). During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to us at N110 (b) CSIT building.

Commit fc3a0458 authored by Kunshan Wang's avatar Kunshan Wang

Porting reference implementation

parent 5a852f01
......@@ -10,6 +10,8 @@ scalaVersion := "2.11.4"
libraryDependencies := Seq(
"org.antlr" % "antlr4" % "4.3",
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"ch.qos.logback" % "logback-classic" % "1.1.2",
"org.scalatest" %% "scalatest" % "2.2.0"
)
......
package uvm.refimpl
import uvm._
import uvm.refimpl.itpr._
import uvm.refimpl.mem._
import uvm.refimpl.mem.TypeSizes.Word
import scala.collection.mutable.HashSet
import uvm.ir.textinput.UIRTextReader
import uvm.ir.textinput.IDFactory
object MicroVM {
val DEFAULT_HEAP_SIZE: Word = 4L * 1024L * 1024L; // 4MiB
val DEFAULT_GLOBAL_SIZE: Word = 1L * 1024L * 1024L; // 1MiB
val DEFAULT_STACK_SIZE: Word = 63L * 1024L; // 60KiB per stack
}
class MicroVM(heapSize: Word = MicroVM.DEFAULT_HEAP_SIZE,
globalSize: Word = MicroVM.DEFAULT_GLOBAL_SIZE,
stackSize: Word = MicroVM.DEFAULT_STACK_SIZE) {
val globalBundle = new Bundle()
val constantPool = new ConstantPool(this)
val memoryManager = new MemoryManager(heapSize, globalSize, stackSize, this)
//val threadStackManager = new ThreadStackManager(this)
//val trapManager = new TrapManager(this)
val clientAgents = new HashSet[ClientAgent]()
val irReader = new UIRTextReader(new IDFactory())
/**
* Add things from a bundle to the Micro VM.
*/
def addBundle(bundle: Bundle) {
globalBundle.merge(bundle);
for (gc <- bundle.globalCellNs.all) {
memoryManager.globalMemory.addGlobalCell(gc)
}
for (g <- bundle.globalVarNs.all) {
constantPool.addGlobalVar(g)
}
}
def newClientAgent(): ClientAgent = new ClientAgent(this)
def addClientAgent(ca: ClientAgent): Unit = {
clientAgents.add(ca)
}
def removeClientAgent(ca: ClientAgent): Unit = {
clientAgents.remove(ca)
}
}
\ No newline at end of file
package uvm.refimpl
import uvm.types._
import uvm.refimpl.itpr._
import java.io.Reader
import scala.collection.mutable.HashSet
case class Handle(ty: Type, vb: ValueBox)
class ClientAgent(microVM: MicroVM) {
val handles = new HashSet[Handle]()
microVM.addClientAgent(this)
/** Help the Client look up the ID of a name */
def idOf(name: String): Int = {
microVM.globalBundle.allNs(name).id
}
/** Help the Client look up the optional name of an ID */
def nameOf(id: Int): Option[String] = {
microVM.globalBundle.allNs(id).name
}
def close(): Unit = {
handles.clear()
microVM.removeClientAgent(this)
}
def loadBundle(r: Reader): Unit = {
val bundle = microVM.irReader.read(r, microVM.globalBundle)
microVM.addBundle(bundle)
}
private def newHandle(t: Type, vb: ValueBox): Handle = {
val handle = Handle(t, vb)
handles.add(handle)
return handle
}
def putInt(typeID: Int, v: BigInt): Handle = {
val t = microVM.globalBundle.typeNs(typeID).asInstanceOf[TypeInt]
val preparedV = OpHelper.trunc(v, t.length)
newHandle(t, BoxInt(preparedV))
}
def putFloat(typeID: Int, v: Float): Handle = {
val t = microVM.globalBundle.typeNs(typeID).asInstanceOf[TypeFloat]
newHandle(t, BoxFloat(v))
}
def putDouble(typeID: Int, v: Double): Handle = {
val t = microVM.globalBundle.typeNs(typeID).asInstanceOf[TypeDouble]
newHandle(t, BoxDouble(v))
}
def putIntVec(typeID: Int, vs: Seq[BigInt]): Handle = {
val t = microVM.globalBundle.typeNs(typeID).asInstanceOf[TypeVector]
val et = t.elemTy.asInstanceOf[TypeInt]
val preparedVs = for (v <- vs) yield OpHelper.trunc(v, et.length)
newHandle(t, BoxVector(preparedVs.map(BoxInt)))
}
def putFloatVec(typeID: Int, vs: Seq[Float]): Handle = {
val t = microVM.globalBundle.typeNs(typeID).asInstanceOf[TypeVector]
val et = t.elemTy.asInstanceOf[TypeFloat]
newHandle(t, BoxVector(vs.map(BoxFloat)))
}
def putDoubleVec(typeID: Int, vs: Seq[Double]): Handle = {
val t = microVM.globalBundle.typeNs(typeID).asInstanceOf[TypeVector]
val et = t.elemTy.asInstanceOf[TypeDouble]
newHandle(t, BoxVector(vs.map(BoxDouble)))
}
def putConstant(id: Int): Handle = {
val c = microVM.globalBundle.constantNs(id)
val t = c.constTy
val box = microVM.constantPool.getGlobalVarBox(c)
newHandle(t, box)
}
def putGlobal(id: Int): Handle = {
val g = microVM.globalBundle.globalCellNs(id)
val t = InternalTypePool.irefOf(g.cellTy)
val a = microVM.memoryManager.globalMemory.addrForGlobalCell(g)
val box = BoxIRef(0L, a)
newHandle(t, box)
}
def putFunction(id: Int): Handle = {
val f = microVM.globalBundle.funcNs(id)
val t = InternalTypePool.funcOf(f.sig)
val box = BoxFunc(Some(f))
newHandle(t, box)
}
def deleteHandle(h: Handle): Unit = {
handles.remove(h)
}
def toInt(h: Handle, signExt: Boolean = false): BigInt = {
val t = h.ty.asInstanceOf[TypeInt]
val ib = h.vb.asInstanceOf[BoxInt]
if (signExt) OpHelper.prepareSigned(ib.value, t.length) else OpHelper.prepareUnsigned(ib.value, t.length)
}
def toFloat(h: Handle): Float = {
h.vb.asInstanceOf[BoxFloat].value
}
def toDouble(h: Handle): Double = {
h.vb.asInstanceOf[BoxDouble].value
}
def toIntVec(h: Handle, signExt: Boolean = false): Seq[BigInt] = {
val t = h.ty.asInstanceOf[TypeVector]
val et = t.elemTy.asInstanceOf[TypeInt]
val bv = h.vb.asInstanceOf[BoxVector]
for (b <- bv.values) yield {
val ib = b.asInstanceOf[BoxInt]
if (signExt) OpHelper.prepareSigned(ib.value, et.length) else OpHelper.prepareUnsigned(ib.value, et.length)
}
}
def toFloatVec(h: Handle): Seq[Float] = {
h.vb.asInstanceOf[BoxVector].values.map(b => b.asInstanceOf[BoxFloat].value)
}
def toDoubleVec(h: Handle): Seq[Double] = {
h.vb.asInstanceOf[BoxVector].values.map(b => b.asInstanceOf[BoxDouble].value)
}
def extractValue(str: Handle, index: Int): Handle = {
val st = str.ty.asInstanceOf[TypeStruct]
val sb = str.vb.asInstanceOf[BoxStruct]
val et = st.fieldTy(index)
val eb = sb.values(index)
newHandle(et, eb)
}
def insertValue(str: Handle, index: Int, newVal: Handle): Handle = {
val st = str.ty.asInstanceOf[TypeStruct]
val sb = str.vb.asInstanceOf[BoxStruct]
val nsb = BoxStruct(for ((b, i) <- sb.values.zipWithIndex) yield if (i == index) newVal.vb else b)
newHandle(st, nsb)
}
}
package uvm.refimpl
import uvm.UvmException
case class UvmRefImplException(message: String = null, cause: Throwable = null) extends UvmException(message, cause)
package uvm.refimpl
import uvm.types._
import uvm.ir.textinput.IDFactory
import scala.collection.mutable.HashMap
import uvm.FuncSig
object InternalIDFactory extends IDFactory(32768) // IDs from 32768-65535 are for implementation internal use.
object InternalTypes {
val VOID_TYPE = TypeVoid()
VOID_TYPE.id = InternalIDFactory.getID()
VOID_TYPE.name = Some("@uvm.internal.void_type")
val BYTE_TYPE = TypeInt(8)
BYTE_TYPE.id = InternalIDFactory.getID()
BYTE_TYPE.name = Some("@uvm.internal.byte_type")
val BYTE_ARRAY_TYPE = TypeHybrid(VOID_TYPE, BYTE_TYPE)
BYTE_ARRAY_TYPE.id = InternalIDFactory.getID()
BYTE_ARRAY_TYPE.name = Some("@uvm.internal.byte_array_type")
}
object InternalTypePool {
val irefPool = HashMap[Type, TypeIRef]()
def irefOf(t: Type): TypeIRef = irefPool.get(t).getOrElse(TypeIRef(t))
val funcPool = HashMap[FuncSig, TypeFunc]()
def funcOf(s: FuncSig): TypeFunc = funcPool.get(s).getOrElse(TypeFunc(s))
}
\ No newline at end of file
package uvm.refimpl.itpr
import uvm._
import uvm.types._
import uvm.ssavariables._
import uvm.refimpl.MicroVM
import scala.collection.mutable.HashMap
class ConstantPool(microVM: MicroVM) {
val globalVarBoxes = HashMap[GlobalVariable, ValueBox]()
def addGlobalVar(g: GlobalVariable) {
maybeMakeBox(g)
}
def maybeMakeBox(g: GlobalVariable): ValueBox = {
val box = globalVarBoxes.get(g).getOrElse(makeBox(g))
globalVarBoxes.put(g, box)
box
}
def makeBox(g: GlobalVariable): ValueBox = g match {
case ConstInt(ty, num) => BoxInt(num & (1 << ty.asInstanceOf[TypeInt].length))
case ConstFloat(ty, num) => BoxFloat(num)
case ConstDouble(ty, num) => BoxDouble(num)
case ConstStruct(ty, flds) => BoxStruct(flds.map(maybeMakeBox))
case ConstNull(ty) => ty match {
case _:TypeRef => BoxRef(0L)
case _:TypeIRef => BoxIRef(0L, 0L)
case _:TypeFunc => BoxFunc(None)
case _:TypeThread => BoxThread(None)
case _:TypeStack => BoxStack(None)
}
case gc:GlobalCell => BoxIRef(0L, microVM.memoryManager.globalMemory.addrForGlobalCell(gc))
case f:Function => BoxFunc(Some(f))
}
def getGlobalVarBox(g: GlobalVariable): ValueBox = globalVarBoxes(g)
}
\ No newline at end of file
package uvm.refimpl.itpr
object OpHelper {
val ONE = BigInt(1)
def mask(n: Int): BigInt = {
(ONE << n) - ONE
}
def truncFromBigInt(n: BigInt, len: Int): BigInt = n & mask(len)
def zextToBigInt(n: BigInt, len: Int): BigInt = n & mask(len)
def sextToBigInt(n: BigInt, len: Int): BigInt = {
val bit = n.testBit(len - 1)
if (bit) {
n | (~mask(len - 1))
} else {
n & (mask(len - 1))
}
}
def prepareUnsigned(n: BigInt, len: Int): BigInt = truncFromBigInt(n, len)
def prepareSigned(n: BigInt, len: Int): BigInt = {
sextToBigInt(truncFromBigInt(n, len), len)
}
def unprepare(n: BigInt, len: Int): BigInt = truncFromBigInt(n, len)
def trunc(n: BigInt, toLen: Int): BigInt = truncFromBigInt(n, toLen)
def zext(n: BigInt, fromLen: Int, toLen: Int): BigInt = truncFromBigInt(n, fromLen)
def sext(n: BigInt, fromLen: Int, toLen: Int): BigInt = {
truncFromBigInt(sextToBigInt(n, fromLen), toLen)
}
def tr64IsInt(opnd: Long): Boolean = {
(opnd & 0x7ff0000000000001L) == 0x7ff0000000000001L
}
def tr64IsFp(opnd: Long): Boolean = {
(opnd & 0x7ff0000000000001L) != 0x7ff0000000000001L &&
(opnd & 0x7ff0000000000003L) != 0x7ff0000000000002L
}
def tr64IsRef(opnd: Long): Boolean = {
(opnd & 0x7ff0000000000003L) == 0x7ff0000000000002L
}
def intToTr64(opnd: Long): Long = {
(0x7ff0000000000001L | ((opnd & 0x7ffffffffffffL) << 1) |
((opnd & 0x8000000000000L) << 12))
}
def fpToTr64(opnd: Double): Long = {
var bits = java.lang.Double.doubleToRawLongBits(opnd)
if (java.lang.Double.isNaN(opnd)) {
bits = bits & 0xfff8000000000000L | 0x0000000000000008L
}
bits
}
def refToTr64(opnd: Long, tag: Long): Long = {
(0x7ff0000000000002L | (opnd & 0x7ffffffffff8L) | ((opnd & 0x800000000000L) << 16) |
((tag & 0x3eL) << 46) |
((tag & 0x1) << 2))
}
def tr64ToInt(opnd: Long): Long = {
(((opnd & 0xffffffffffffeL) >> 1) | ((opnd & 0x8000000000000000L) >> 12) & (1L << 51))
}
def tr64ToFp(opnd: Long): Double = java.lang.Double.longBitsToDouble(opnd)
def tr64ToRef(opnd: Long): Long = {
((opnd & 0x7ffffffffff8L) |
(((~(((opnd & 0x8000000000000000L) << 1) - 1)) >> 17) &
0xffff800000000000L))
}
def tr64ToTag(opnd: Long): Long = {
(((opnd & 0x000f800000000000L) >> 46) | ((opnd & 0x4) >> 2))
}
}
package uvm.refimpl.itpr
import uvm._
import uvm.refimpl.mem.TypeSizes.Word
abstract class ValueBox
abstract class ObjectBox[T](obj: Option[T]) extends ValueBox
case class BoxInt(value: BigInt) extends ValueBox
case class BoxFloat(value: Float) extends ValueBox
case class BoxDouble(value: Double) extends ValueBox
case class BoxVector(values: Seq[ValueBox]) extends ValueBox
case class BoxRef(addr: Word) extends ValueBox
case class BoxIRef(base: Word, offset: Word) extends ValueBox
case class BoxStruct(values: Seq[ValueBox]) extends ValueBox
case class BoxVoid() extends ValueBox
case class BoxFunc(func: Option[Function]) extends ObjectBox[Function](func)
case class BoxThread(thread: Option[InterpreterThread]) extends ObjectBox[InterpreterThread](thread)
case class BoxStack(stack: Option[InterpreterStack]) extends ObjectBox[InterpreterStack](stack)
case class BoxTagRef64(raw: Long) extends ValueBox {
}
package uvm.refimpl.itpr
class InterpreterThread {
}
class InterpreterStack {
}
class InterpreterFrame {
}
\ No newline at end of file
package uvm.refimpl.mem
import uvm.refimpl.mem.TypeSizes.Word
trait Allocator {
def alloc(size: Word, align: Word, headerSize: Word): Word
}
package uvm.refimpl.mem
import uvm.types._
import uvm.ssavariables.GlobalCell
import uvm.refimpl._
import uvm.refimpl.mem.TypeSizes._
//import uvm.refimpl.mem.bumppointer.RewindableBumpPointerAllocator
import java.util.HashMap
class GlobalMemory(begin: Word, size: Word, microVM: MicroVM) extends Space("GlobalSpace", begin, size) {
private val allocator: Allocator = null// = new RewindableBumpPointerAllocator(begin, size, microVM)
private val locationMap = new HashMap[GlobalCell, Word]()
def addGlobalCell(gc: GlobalCell) {
val ty = gc.cellTy
if (ty.isInstanceOf[TypeHybrid]) {
throw new UvmRefImplException("It does not make sense to make global hybrid (use array and any fixed types). global data: " + gc.repr)
}
val addr = allocateGlobalCellMemory(ty)
locationMap.put(gc, addr)
}
private def allocateGlobalCellMemory(ty: Type): Word = {
val tag = ty.id
val size = sizeOf(ty)
val align = alignOf(ty)
val objAddr = allocator.alloc(size, align, TypeSizes.GC_HEADER_SIZE_SCALAR)
HeaderUtils.postAllocScalar(objAddr, tag)
objAddr
}
def addrForGlobalCell(gc: GlobalCell): Word = locationMap.get(gc)
}
package uvm.refimpl.mem
import uvm.types.Type
import uvm.refimpl.MicroVM
import uvm.refimpl.mem.TypeSizes.Word
import com.typesafe.scalalogging._
import org.slf4j.LoggerFactory
object HeaderUtils extends StrictLogging {
def postAllocScalar(addr: Word, tag: Long) {
setTag(addr, tag)
}
def postAllocHybrid(addr: Word, tag: Long, len: Long) {
postAllocScalar(addr, tag)
setVarLength(addr, len)
}
def getTag(objRef: Word): Long = {
MemorySupport.loadLong(objRef + TypeSizes.GC_HEADER_OFFSET_TAG)
}
def getVarLength(objRef: Word): Long = {
MemorySupport.loadLong(objRef + TypeSizes.GC_HEADER_OFFSET_HYBRID_LENGTH)
}
def setTag(objRef: Word, tag: Long) {
logger.debug(s"Storing tag ${tag} at addr ${TypeSizes.GC_HEADER_OFFSET_TAG}")
MemorySupport.storeLong(objRef + TypeSizes.GC_HEADER_OFFSET_TAG, tag)
}
def setVarLength(objRef: Word, len: Long) {
MemorySupport.storeLong(objRef + TypeSizes.GC_HEADER_OFFSET_HYBRID_LENGTH, len)
}
def getTypeID(tag: Long): Int = (tag & 0x00000000ffffffffL).toInt
def getType(microVM: MicroVM, tag: Long): Type = {
val typeID = getTypeID(tag)
microVM.globalBundle.typeNs(typeID)
}
def getForwardedDest(oldHeader: Long): Long = oldHeader & 0x0000ffffffffffffL
}
package uvm.refimpl.mem
import com.typesafe.scalalogging.StrictLogging
import TypeSizes._
object MemUtils extends StrictLogging {
def zeroRegion(start: Word, length: Word) {
val end = start + length
logger.debug(s"Zeroing [${start} -> ${end}] ${length} bytes")
var a = start
while (a < end) {
MemorySupport.storeLong(a, 0)
a += WORD_SIZE_BYTES
}
}
def memcpy(src: Long, dst: Word, length: Word) {
logger.debug("Copying [${src} -> ${dst}] ${length} bytes")
var a: Word = 0
while (a < length) {
val oldWord = MemorySupport.loadLong(src + a)
MemorySupport.storeLong(dst + a, oldWord)
a += WORD_SIZE_BYTES
}
}
}
package uvm.refimpl.mem
import uvm.refimpl.MicroVM
//import uvm.refimpl.mem.MicroVMInternalTypes
//import uvm.refimpl.mem.simpleimmix.SimpleImmixHeap
import MemoryManager._
object MemoryManager {
val MEMORY_BEGIN = 0x100000L
}
class MemoryManager(val heapSize: Long,
val globalSize: Long,
val stackSize: Long,
val microVM: MicroVM) {
// val heap = new SimpleImmixHeap(MEMORY_BEGIN, heapSize, microVM)
val globalMemory = new GlobalMemory(MEMORY_BEGIN + heapSize, globalSize, microVM)
// private val stacks = new ArrayList[StackMemory]()
// private val internalMutator = heap.makeMutator()
// def makeMutator(): Mutator = heap.makeMutator()
// def makeStackMemory(): StackMemory = {
// val objRef = internalMutator.newHybrid(MicroVMInternalTypes.BYTE_ARRAY_TYPE, stackSize)
// val stackMemory = new StackMemory(objRef, stackSize, microVM)
// stackMemory
// }
}
package uvm.refimpl.mem
import uvm.refimpl.mem.TypeSizes.Word
import java.nio.ByteBuffer
import uvm.ssavariables.AtomicRMWOptr._
object MemorySupport {
val MEMORY_SIZE: Word = 64L * 1024L * 1024L
val bb: ByteBuffer = ByteBuffer.allocateDirect(MEMORY_SIZE.toInt)
bb.order(java.nio.ByteOrder.LITTLE_ENDIAN)
def loadByte(loc: Word): Byte = bb.get(loc.toInt)
def loadShort(loc: Word): Short = bb.getShort(loc.toInt)
def loadInt(loc: Word): Int = bb.getInt(loc.toInt)
def loadLong(loc: Word): Long = bb.getLong(loc.toInt)
def loadFloat(loc: Word): Float = bb.getFloat(loc.toInt)
def loadDouble(loc: Word): Double = bb.getDouble(loc.toInt)
def storeByte(loc: Word, v: Byte): Unit = bb.put(loc.toInt, v)
def storeShort(loc: Word, v: Short): Unit = bb.putShort(loc.toInt, v)
def storeInt(loc: Word, v: Int): Unit = bb.putInt(loc.toInt, v)
def storeLong(loc: Word, v: Long): Unit = bb.putLong(loc.toInt, v)
def storeFloat(loc: Word, v: Float): Unit = bb.putFloat(loc.toInt, v)
def storeDouble(loc: Word, v: Double): Unit = bb.putDouble(loc.toInt, v)
def cmpXchgInt(loc: Word, expected: Int, desired: Int): (Boolean, Int) = {
val oldVal = loadInt(loc)
if (oldVal == expected) {
storeInt(loc, desired)
return (true, oldVal)
} else {
return (false, oldVal)
}
}
def cmpXchgLong(loc: Word, expected: Long, desired: Long): (Boolean, Long) = {
val oldVal = loadLong(loc)
if (oldVal == expected) {
storeLong(loc, desired)
return (true, oldVal)
} else {
return (false, oldVal)
}
}
def atomicRMWInt(optr: AtomicRMWOptr, loc: Word, opnd: Int): Int = {
val oldVal = loadInt(loc)
val newVal = optr match {
case XCHG => opnd
case ADD => oldVal + opnd
case SUB => oldVal - opnd
case AND => oldVal & opnd
case NAND => ~(oldVal & opnd)
case OR => oldVal | opnd
case XOR => oldVal ^ opnd
case MAX => Math.max(oldVal, opnd)
case MIN => Math.min(oldVal, opnd)
case UMAX => Math.max(oldVal - Int.MinValue, opnd - Int.MinValue) + Int.MinValue
case UMIN => Math.min(oldVal - Int.MinValue, opnd - Int.MinValue) + Int.MinValue
}
storeInt(loc, newVal)
return oldVal
}
def atomicRMWLong(optr: AtomicRMWOptr, loc: Word, opnd: Long): Long = {
val oldVal = loadLong(loc)
val newVal = optr match {
case XCHG => opnd
case ADD => oldVal + opnd
case SUB => oldVal - opnd
case AND => oldVal & opnd
case NAND => ~(oldVal & opnd)
case OR => oldVal | opnd
case XOR => oldVal ^ opnd
case MAX => Math.max(oldVal, opnd)
case MIN => Math.min(oldVal, opnd)
case UMAX => Math.max(oldVal - Int.MinValue, opnd - Int.MinValue) + Int.MinValue
case UMIN => Math.min(oldVal - Int.MinValue, opnd - Int.MinValue) + Int.MinValue
}
storeLong(loc, newVal)
return oldVal
}
}
\ No newline at end of file
package uvm.refimpl.mem
import uvm.refimpl.mem.TypeSizes.Word
import Space._
object Space {
private var spaces: List[Space] = Nil
private def addSpace(space: Space) {
spaces = space :: spaces
}
def getSpaceForAddress(addr: Long): Option[Space] = {
spaces.find(_.isInSpace(addr))
}
}
class Space(val name: String, val begin: Long, val extend: Long) {
addSpace(this)
def isInSpace(addr: Word): Boolean = begin <= addr && addr < begin + extend
}
package uvm.refimpl.mem
import uvm.types._
import uvm.refimpl.UvmRefImplException
/**