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._