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.

MemorySupport.scala 10.2 KB
Newer Older
1 2
package uvm.refimpl.mem

3
import scala.annotation.implicitNotFound
4 5 6 7

import jnr.ffi.Memory
import uvm._
import uvm.refimpl._
8
import uvm.refimpl.itpr.FrameCursor
9 10 11 12 13 14
import uvm.refimpl.itpr.InterpreterStack
import uvm.refimpl.itpr.InterpreterThread

import uvm.refimpl.mem.TypeSizes.Word
import uvm.refimpl.nat.NativeSupport
import uvm.ssavariables.AtomicRMWOptr._
Kunshan Wang's avatar
Kunshan Wang committed
15
import uvm.ir.irbuilder.IRBuilder
16

17 18 19 20
/**
 * Support for native memory access. Backed by JNR-FFI.
 */
class MemorySupport(val muMemorySize: Word) {
21 22
  import EntityAccessors._

Kunshan Wang's avatar
Kunshan Wang committed
23 24 25
  val jnrRuntime = NativeSupport.jnrRuntime
  val theMemory = NativeSupport.theMemory

26
  val SIZE_LIMIT: Word = Int.MaxValue.toLong
27

28
  if (muMemorySize > SIZE_LIMIT) {
29 30
    throw new UvmRuntimeException(("Memory too large (%d bytes requested)." +
      " Due to the limitation of JNR-FFI, the maximum available memory size is %d bytes.").format(muMemorySize, SIZE_LIMIT))
31 32 33 34 35 36 37 38 39 40 41 42 43
  }

  val muMemory = Memory.allocateDirect(jnrRuntime, muMemorySize.toInt, true)
  val muMemoryBegin = muMemory.address()
  val muMemoryEnd = muMemoryBegin + muMemorySize

  def isInMuMemory(addr: Word): Boolean = muMemoryBegin <= addr && addr < muMemoryEnd

  def assertInMuMemory(inMu: Boolean, addr: Word): Unit = {
    if (inMu && !isInMuMemory(addr)) {
      throw new UvmIllegalMemoryAccessException("Accessed address 0x%x outside the Mu memory [0x%x-0x%x].".format(addr, muMemoryBegin, muMemoryEnd))
    }
  }
44

45 46 47 48 49 50 51
  def loadByte(addr: Word, inMu: Boolean = true): Byte = { assertInMuMemory(inMu, addr); theMemory.getByte(addr) }
  def loadShort(addr: Word, inMu: Boolean = true): Short = { assertInMuMemory(inMu, addr); theMemory.getShort(addr) }
  def loadInt(addr: Word, inMu: Boolean = true): Int = { assertInMuMemory(inMu, addr); theMemory.getInt(addr) }
  def loadLong(addr: Word, inMu: Boolean = true): Long = { assertInMuMemory(inMu, addr); theMemory.getLong(addr) }
  def loadI128(addr: Word, inMu: Boolean = true): (Long, Long) = { assertInMuMemory(inMu, addr); (theMemory.getLong(addr), theMemory.getLong(addr + 8)) }
  def loadFloat(addr: Word, inMu: Boolean = true): Float = { assertInMuMemory(inMu, addr); theMemory.getFloat(addr) }
  def loadDouble(addr: Word, inMu: Boolean = true): Double = { assertInMuMemory(inMu, addr); theMemory.getDouble(addr) }
52

53 54 55 56 57 58 59
  def storeByte(addr: Word, v: Byte, inMu: Boolean = true): Unit = { assertInMuMemory(inMu, addr); theMemory.putByte(addr, v) }
  def storeShort(addr: Word, v: Short, inMu: Boolean = true): Unit = { assertInMuMemory(inMu, addr); theMemory.putShort(addr, v) }
  def storeInt(addr: Word, v: Int, inMu: Boolean = true): Unit = { assertInMuMemory(inMu, addr); theMemory.putInt(addr, v) }
  def storeLong(addr: Word, v: Long, inMu: Boolean = true): Unit = { assertInMuMemory(inMu, addr); theMemory.putLong(addr, v) }
  def storeI128(addr: Word, v: (Long, Long), inMu: Boolean = true): Unit = { assertInMuMemory(inMu, addr); val (low, high) = v; theMemory.putLong(addr, low); theMemory.putLong(addr + 8, high) }
  def storeFloat(addr: Word, v: Float, inMu: Boolean = true): Unit = { assertInMuMemory(inMu, addr); theMemory.putFloat(addr, v) }
  def storeDouble(addr: Word, v: Double, inMu: Boolean = true): Unit = { assertInMuMemory(inMu, addr); theMemory.putDouble(addr, v) }
60

61 62
  def cmpXchgInt(addr: Word, expected: Int, desired: Int, inMu: Boolean = true): (Boolean, Int) = {
    assertInMuMemory(inMu, addr)
63
    val oldVal = loadInt(addr, inMu)
64
    if (oldVal == expected) {
65
      storeInt(addr, desired, inMu)
66 67 68 69 70 71
      return (true, oldVal)
    } else {
      return (false, oldVal)
    }
  }

72 73
  def cmpXchgLong(addr: Word, expected: Long, desired: Long, inMu: Boolean = true): (Boolean, Long) = {
    assertInMuMemory(inMu, addr)
74
    val oldVal = loadLong(addr, inMu)
75
    if (oldVal == expected) {
76
      storeLong(addr, desired, inMu)
77 78 79 80 81 82
      return (true, oldVal)
    } else {
      return (false, oldVal)
    }
  }

83 84
  def cmpXchgI128(addr: Word, expected: (Long, Long), desired: (Long, Long), inMu: Boolean = true): (Boolean, (Long, Long)) = {
    assertInMuMemory(inMu, addr)
85
    val oldVal = loadI128(addr, inMu)
86
    if (oldVal == expected) {
87
      storeI128(addr, desired, inMu)
88 89 90 91 92 93
      return (true, oldVal)
    } else {
      return (false, oldVal)
    }
  }

94 95
  def atomicRMWInt(optr: AtomicRMWOptr, addr: Word, opnd: Int, inMu: Boolean = true): Int = {
    assertInMuMemory(inMu, addr)
96
    val oldVal = loadInt(addr, inMu)
97 98
    val newVal = optr match {
      case XCHG => opnd
99 100 101
      case ADD  => oldVal + opnd
      case SUB  => oldVal - opnd
      case AND  => oldVal & opnd
102
      case NAND => ~(oldVal & opnd)
103 104 105 106
      case OR   => oldVal | opnd
      case XOR  => oldVal ^ opnd
      case MAX  => Math.max(oldVal, opnd)
      case MIN  => Math.min(oldVal, opnd)
107 108 109
      case UMAX => Math.max(oldVal - Int.MinValue, opnd - Int.MinValue) + Int.MinValue
      case UMIN => Math.min(oldVal - Int.MinValue, opnd - Int.MinValue) + Int.MinValue
    }
110
    storeInt(addr, newVal, inMu)
111 112
    return oldVal
  }
113

114 115
  def atomicRMWLong(optr: AtomicRMWOptr, addr: Word, opnd: Long, inMu: Boolean = true): Long = {
    assertInMuMemory(inMu, addr)
116
    val oldVal = loadLong(addr, inMu)
117 118
    val newVal = optr match {
      case XCHG => opnd
119 120 121
      case ADD  => oldVal + opnd
      case SUB  => oldVal - opnd
      case AND  => oldVal & opnd
122
      case NAND => ~(oldVal & opnd)
123 124 125 126
      case OR   => oldVal | opnd
      case XOR  => oldVal ^ opnd
      case MAX  => Math.max(oldVal, opnd)
      case MIN  => Math.min(oldVal, opnd)
127 128
      case UMAX => Math.max(oldVal - Long.MinValue, opnd - Long.MinValue) + Long.MinValue
      case UMIN => Math.min(oldVal - Long.MinValue, opnd - Long.MinValue) + Long.MinValue
129
    }
130
    storeLong(addr, newVal, inMu)
131 132
    return oldVal
  }
133

134 135
  def xchgI128(addr: Word, desired: (Long, Long), inMu: Boolean = true): (Long, Long) = {
    assertInMuMemory(inMu, addr)
136 137
    val oldVal = loadI128(addr, inMu)
    storeI128(addr, desired, inMu)
138 139
    return oldVal
  }
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
  def loadEntity[T: CanBeIntegerized](addr: Word)(implicit microVM: MicroVM): Option[T] = {
    val num = loadLong(addr, inMu = true)
    val obj = try {
      implicitly[CanBeIntegerized[T]].deIntegerize(num)
    } catch {
      case e: UvmUnknownIDException =>
        throw new UvmRefImplException("Memory location 0x%x referring to non-existing entity %d".format(addr, num), e)
    }
    obj
  }

  def storeEntity[T: CanBeIntegerized](addr: Word, obj: Option[T])(implicit microVM: MicroVM): Unit = {
    val num = implicitly[CanBeIntegerized[T]].integerize(obj)
    storeLong(addr, num, inMu = true)
  }

  def cmpXchgEntity[T: CanBeIntegerized](addr: Word, expected: Option[T], desired: Option[T])(
    implicit microVM: MicroVM): (Boolean, Option[T]) = {
    val numExpected = implicitly[CanBeIntegerized[T]].integerize(expected)
160
    val numDesired = implicitly[CanBeIntegerized[T]].integerize(desired)
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    val (succ, oldNum) = cmpXchgLong(addr, numExpected, numDesired, inMu = true)
    val oldObj = try {
      implicitly[CanBeIntegerized[T]].deIntegerize(oldNum)
    } catch {
      case e: UvmUnknownIDException =>
        throw new UvmRefImplException("Memory location 0x%x referring to non-existing entity %d".format(addr, oldNum), e)
    }
    (succ, oldObj)
  }

  def xchgEntity[T: CanBeIntegerized](addr: Word, desired: Option[T])(implicit microVM: MicroVM): Option[T] = {
    val oldVal = loadEntity[T](addr)
    storeEntity[T](addr, desired)
    return oldVal
  }

Kunshan Wang's avatar
Kunshan Wang committed
177 178 179
  def memset(addr: Word, size: Word, value: Byte): Unit = {
    theMemory.setMemory(addr, size, value)
  }
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

  def loadBytes(addr: Word, dest: Array[Byte], index: Int, len: Word, inMu: Boolean = true): Unit = {
    assertInMuMemory(inMu, addr)
    assertInMuMemory(inMu, addr + len - 1)
    assert(index >= 0, "Index is negative")
    assert(index + len <= dest.length, "array too small. dest.size=%d, len=%d".format(dest.size, len))
    theMemory.get(addr, dest, 0, len.toInt)
  }

  def storeBytes(addr: Word, dest: Array[Byte], index: Int, len: Word, inMu: Boolean = true): Unit = {
    assertInMuMemory(inMu, addr)
    assertInMuMemory(inMu, addr + len - 1)
    assert(index >= 0, "Index is negative")
    assert(index + len <= dest.length, "array too small. dest.size=%d, len=%d".format(dest.size, len))
    theMemory.put(addr, dest, 0, len.toInt)
  }
196
}
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234

object EntityAccessors {
  @implicitNotFound("Mu entity type ${T} cannot be represented as an integer, thus cannot be loaded/stored from the memory.")
  trait CanBeIntegerized[T] {
    protected def _integerize(obj: T)(implicit microVM: MicroVM): Word
    protected def _deIntegerize(num: Word)(implicit microVM: MicroVM): T

    def integerize(obj: Option[T])(implicit microVM: MicroVM): Word =
      obj.map(_integerize).getOrElse(0L)
    def deIntegerize(num: Word)(implicit microVM: MicroVM): Option[T] = {
      if (num == 0L) {
        None
      } else {
        Some(_deIntegerize(num))
      }
    }
  }

  implicit object FunctionIntegerizer extends CanBeIntegerized[Function] {
    def _integerize(obj: Function)(implicit microVM: MicroVM): Word = obj.id.toLong
    def _deIntegerize(num: Word)(implicit microVM: MicroVM): Function = microVM.globalBundle.funcNs(num.toInt)
  }

  implicit object ThreadIntegerizer extends CanBeIntegerized[InterpreterThread] {
    def _integerize(obj: InterpreterThread)(implicit microVM: MicroVM): Word = obj.id.toLong
    def _deIntegerize(num: Word)(implicit microVM: MicroVM): InterpreterThread = microVM.threadStackManager.threadRegistry(num.toInt)
  }

  implicit object StackIntegerizer extends CanBeIntegerized[InterpreterStack] {
    def _integerize(obj: InterpreterStack)(implicit microVM: MicroVM): Word = obj.id.toLong
    def _deIntegerize(num: Word)(implicit microVM: MicroVM): InterpreterStack = microVM.threadStackManager.stackRegistry(num.toInt)
  }

  implicit object FrameCursorIntegerizer extends CanBeIntegerized[FrameCursor] {
    def _integerize(obj: FrameCursor)(implicit microVM: MicroVM): Word = obj.id.toLong
    def _deIntegerize(num: Word)(implicit microVM: MicroVM): FrameCursor = microVM.threadStackManager.frameCursorRegistry(num.toInt)
  }

Kunshan Wang's avatar
Kunshan Wang committed
235 236 237
  implicit object IRBuilderIntegerizer extends CanBeIntegerized[IRBuilder] {
    def _integerize(obj: IRBuilder)(implicit microVM: MicroVM): Word = microVM.irBuilderRegistry.objGetLong(obj)
    def _deIntegerize(num: Word)(implicit microVM: MicroVM): IRBuilder = microVM.irBuilderRegistry.longToObj(num)
238 239
  }
}