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 710ea15b authored by Kunshan Wang's avatar Kunshan Wang
Browse files

More tests and fixes on native calls.

parent 210ee90e
......@@ -8,3 +8,4 @@ target
*.swp
.tmpBin
.d8_history
hs_err_pid*.log
......@@ -275,7 +275,10 @@ class UIRTextReader(val idFactory: IDFactory) {
def mkConst(t: Type, c: ConstConstructorContext): Constant = {
val con = c match {
case cc: ConstIntContext => ConstInt(t, cc.intLiteral)
case cc: ConstIntContext => t match {
case _: TypeInt => ConstInt(t, cc.intLiteral)
case _: AbstractPointerType => ConstPointer(t, cc.intLiteral().longValue())
}
case cc: ConstFloatContext => ConstFloat(t, cc.floatLiteral)
case cc: ConstDoubleContext => ConstDouble(t, cc.doubleLiteral)
case cc: ConstStructContext => ConstStruct(t, null).later(phase2) {
......
......@@ -32,6 +32,7 @@ class ConstantPool(implicit microVM: MicroVM) {
case _:TypeStack => BoxStack(None)
}
case ConstVector(ty, elems) => BoxVector(elems.map(maybeMakeBox))
case ConstPointer(ty, addr) => BoxPointer(addr)
case gc:GlobalCell => BoxIRef(0L, microVM.memoryManager.globalMemory.addrForGlobalCell(gc))
case f:Function => BoxFunc(Some(f))
}
......
package uvm.refimpl.nat
import java.nio.ByteBuffer
import java.nio.ByteOrder
import org.slf4j.LoggerFactory
import com.kenai.jffi.{ Type => JType, Struct => JStruct, Function => JFunction, HeapInvocationBuffer, Invoker }
import com.typesafe.scalalogging.Logger
import uvm.FuncSig
import uvm.refimpl.UvmRefImplException
import uvm.refimpl.itpr._
import uvm.refimpl.itpr.ValueBox
import uvm.refimpl.mem.TypeSizes
import uvm.refimpl.mem.TypeSizes.Word
import uvm.types._
import uvm.types.{ Type => MType }
import uvm.utils.LazyPool
import javax.vecmath.Tuple2d
import uvm.refimpl.itpr._
import java.nio.ByteBuffer
import uvm.refimpl.mem.TypeSizes
import uvm.utils.HexDump
object NativeHelper {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
}
/**
* Helps calling native functions. Based on JFFI.
*/
class NativeHelper {
import NativeHelper._
val jffiTypePool: LazyPool[MType, JType] = LazyPool {
case TypeVoid() => JType.VOID
case TypeInt(8) => JType.SINT8
......@@ -55,13 +64,15 @@ class NativeHelper {
val fldvbs = vb.asInstanceOf[BoxStruct].values
for (((fty, fvb), i) <- (flds zip fldvbs).zipWithIndex) {
val off2 = TypeSizes.fieldOffsetOf(s, i)
putArgToBuf(buf, off + off2.toInt, mty, vb)
putArgToBuf(buf, off + off2.toInt, fty, fvb)
}
}
case _: AbstractPointerType => buf.putLong(off, vb.asInstanceOf[BoxPointer].addr)
}
}
private val FORCE_ALIGN_UP = 16L
private def putArg(hib: HeapInvocationBuffer, mty: MType, vb: ValueBox): Unit = {
mty match {
case TypeInt(8) => hib.putByte(vb.asInstanceOf[BoxInt].value.toByte)
......@@ -71,8 +82,10 @@ class NativeHelper {
case TypeFloat() => hib.putFloat(vb.asInstanceOf[BoxFloat].value)
case TypeDouble() => hib.putDouble(vb.asInstanceOf[BoxDouble].value)
case TypeStruct(flds) => {
val buf = ByteBuffer.allocate(TypeSizes.sizeOf(mty).toInt)
val buf = ByteBuffer.allocate(TypeSizes.alignUp(TypeSizes.sizeOf(mty), FORCE_ALIGN_UP).intValue())
buf.order(ByteOrder.LITTLE_ENDIAN)
putArgToBuf(buf, 0, mty, vb)
logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf))
hib.putStruct(buf.array(), buf.arrayOffset())
}
case _: AbstractPointerType => hib.putAddress(vb.asInstanceOf[BoxPointer].addr)
......@@ -91,6 +104,9 @@ class NativeHelper {
val inv = Invoker.getInstance
sig.retTy match {
case TypeVoid() => {
inv.invokeLong(jFunc, hib)
}
case TypeInt(8) => {
val rv = inv.invokeInt(jFunc, hib).toByte
retBox.asInstanceOf[BoxInt].value = OpHelper.trunc(BigInt(rv), 8)
......@@ -117,7 +133,8 @@ class NativeHelper {
}
case TypeStruct(flds) => {
val rv = inv.invokeStruct(jFunc, hib)
val buf = ByteBuffer.wrap(rv)
val buf = ByteBuffer.wrap(rv).order(ByteOrder.LITTLE_ENDIAN)
logger.debug("Hexdump:\n" + HexDump.dumpByteBuffer(buf))
getArgFromBuf(buf, 0, sig.retTy, retBox)
}
case _: AbstractPointerType => {
......@@ -139,7 +156,7 @@ class NativeHelper {
val fldvbs = vb.asInstanceOf[BoxStruct].values
for (((fty, fvb), i) <- (flds zip fldvbs).zipWithIndex) {
val off2 = TypeSizes.fieldOffsetOf(s, i)
getArgFromBuf(buf, off + off2.toInt, mty, vb)
getArgFromBuf(buf, off + off2.toInt, fty, fvb)
}
}
case _: AbstractPointerType => vb.asInstanceOf[BoxPointer].addr = buf.getLong(off)
......
......@@ -32,6 +32,8 @@ case class ConstNull(var constTy: Type) extends Constant
case class ConstVector(var constTy: Type, var elems: Seq[Constant]) extends Constant
case class ConstPointer(var constTy: Type, var addr: Long) extends Constant
case class GlobalCell(var cellTy: Type) extends GlobalVariable
case class ExposedFunc(var func: Function, var callConv: Flag, var cookie: ConstInt) extends GlobalVariable
......
package uvm.utils
import jnr.ffi.Pointer
import jnr.ffi.Runtime
import uvm.refimpl.mem.TypeSizes._
import scala.annotation.elidable
import scala.annotation.elidable.ASSERTION
import java.nio.ByteBuffer
/**
* Show binary data in hexadecimal bytes notations.
*/
object HexDump {
val mem = Pointer.wrap(Runtime.getSystemRuntime, 0L)
val LINE_SIZE = 16
def dumpByteBuffer(buf: ByteBuffer): String = dumpByteBuffer(buf, 0, buf.limit())
def dumpByteBuffer(buf: ByteBuffer, begin: Int, size: Int): String = {
val hd = new HexDump(begin)
for (addr <- begin until (begin + size)) {
val b = buf.get(addr)
hd.addByte(b)
}
hd.finish()
}
def dumpMemory(begin: Long, size: Long): String = {
val hd = new HexDump(begin)
for (addr <- begin until (begin + size)) {
val b = mem.getByte(addr)
hd.addByte(b)
}
hd.finish()
}
}
class HexDump(beginAddr: Long) {
import HexDump._
val buf = new Array[Byte](16)
var curLineAddr: Long = alignDown(beginAddr, 16)
var curLineSize: Int = (beginAddr - curLineAddr).toInt
var linePreSkip: Int = curLineSize
var linePostSkip: Int = 0
val sb = new StringBuilder()
def addByte(b: Byte) {
buf(curLineSize) = b
curLineSize += 1
if (curLineSize == LINE_SIZE) {
processLine()
}
}
def processLine() {
assert(curLineSize == LINE_SIZE, "Line not full: current: %d, expected: %d".format(curLineSize, LINE_SIZE))
sb ++= "%16x".format(curLineAddr)
sb ++= ": "
for ((b, i) <- buf.zipWithIndex) {
val padding = i match {
case 0 => ""
case 8 => " "
case _ => " "
}
sb ++= padding
val num = if (i < linePreSkip || i + linePostSkip >= LINE_SIZE) " "
else "%02x".format(b)
sb ++= num
}
sb ++= " "
sb ++= "|"
for ((b, i) <- buf.zipWithIndex) {
val chr = if (i < linePreSkip || i + linePostSkip >= LINE_SIZE) " "
else if (32 <= b && b <= 126) b.toChar.toString() else "."
sb ++= chr
}
sb ++= "|"
sb ++= "\n"
curLineSize = 0
linePreSkip = 0
linePostSkip = 0
curLineAddr += LINE_SIZE
}
def finish(): String = {
if (curLineSize > 0) {
if (curLineSize < 16) {
linePostSkip = (alignUp(curLineSize, 16) - curLineSize).toInt
curLineSize = 16
}
processLine()
}
sb.toString()
}
}
\ No newline at end of file
......@@ -230,6 +230,17 @@ trait TestingBundlesValidators extends Matchers with ExtraMatchers {
its fields 1 shouldBe (our globalValue "@fdummy")
}
our const "@I32P_PTR1" shouldBeA[ConstPointer] { its =>
its.constTy shouldBe (our ty "@i32_p")
its.addr shouldBe 0x123456789abcdef0L
}
our const "@SIG0FP_PTR1" shouldBeA[ConstPointer] { its =>
its.constTy shouldBe (our ty "@sig0_fp")
its.addr shouldBe 0xfedcba9876543210L
}
// Testing namespaces
val ci8 = our const "@ci8"
our globalValue "@ci8" shouldBe ci8
......
......@@ -57,7 +57,7 @@ class UvmInterpreterNativeTests extends UvmBundleTesterBase {
val func = ca.putFunction("@writetest")
val a0 = ca.putInt("@i64", funcAddr)
val a0 = ca.putPointer("@write_fp", funcAddr)
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
val Seq(fp, rv, buf, bufV0P) = ca.dumpKeepalives(st, 0)
......@@ -77,11 +77,13 @@ class UvmInterpreterNativeTests extends UvmBundleTesterBase {
val lib = Library.getDefault()
val funcAddr = lib.getSymbolAddress("memcpy")
val func = ca.putFunction("@memcpytest")
val hgfp = ca.putGlobal("@FP_MEMCPY")
val hfp = ca.putPointer("@memcpy_fp", funcAddr)
ca.store(MemoryOrder.NOT_ATOMIC, hgfp, hfp)
val a0 = ca.putInt("@i64", funcAddr)
val func = ca.putFunction("@memcpytest")
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
testFunc(ca, func, Seq()) { (ca, th, st, wp) =>
val Seq(fp, rv, ob, b0, b1, b2, b3, b4, b5) = ca.dumpKeepalives(st, 0)
fp.vb.asPointer shouldEqual funcAddr
......
package uvm.refimpl.itpr
import org.scalatest._
import java.io.FileReader
import uvm._
import uvm.types._
import uvm.ssavariables._
import uvm.refimpl._
import uvm.refimpl.itpr._
import MemoryOrder._
import AtomicRMWOptr._
import uvm.refimpl.mem.TypeSizes.Word
import ch.qos.logback.classic.Level._
import uvm.refimpl.UvmBundleTesterBase
import com.kenai.jffi.Library
import jnr.posix.POSIXFactory
import jnr.ffi.LibraryLoader
import uvm.utils.HexDump
class UvmInterpreterNativeTestsExtra extends UvmBundleTesterBase {
setLogLevels(
ROOT_LOGGER_NAME -> INFO,
"uvm.refimpl.nat" -> DEBUG,
"uvm.refimpl.itpr" -> DEBUG)
val fileName = "tests/c-snippets/structtest.so"
if (!new java.io.File(fileName).isFile()) {
throw new RuntimeException("Need to compile the structtest.so library. cd into tests/c-snippets and invoke 'make'.")
}
preloadBundles("tests/uvm-refimpl-test/native-tests.uir")
"The CCALL instruction" should "handle struct parameters in foo" in {
val ca = microVM.newClientAgent()
val lib = Library.openLibrary(fileName, Library.NOW)
val funcAddr = lib.getSymbolAddress("foo_func")
val func = ca.putFunction("@foo_func_test")
val a0 = ca.putInt("@i64", funcAddr)
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
val Seq(fp, rv, a, b, c, d) = ca.dumpKeepalives(st, 0)
fp.vb.asPointer shouldEqual funcAddr
// println("%x".format(a.vb.asUInt(64).toLong))
a.vb.asUInt(64) shouldEqual 0x55aa55aa55aa55aaL
b.vb.asUInt(32) shouldEqual 0x5a5a5a5a
c.vb.asUInt(16) shouldEqual 0xa5a5
d.vb.asUInt(8) shouldEqual 0x61
TrapRebindPassVoid(st)
}
ca.close()
}
"The CCALL instruction" should "handle struct parameters in bar involving pointers" in {
val ca = microVM.newClientAgent()
val lib = Library.openLibrary(fileName, Library.NOW)
val funcAddr = lib.getSymbolAddress("bar_func")
val func = ca.putFunction("@bar_func_test")
val a0 = ca.putInt("@i64", funcAddr)
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
val Seq(fp, rv, a, b) = ca.dumpKeepalives(st, 0)
fp.vb.asPointer shouldEqual funcAddr
// println("%x".format(a.vb.asUInt(64).toLong))
a.vb.asPointer shouldEqual 0x123456789abcdef0L
b.vb.asPointer shouldEqual 0xfedcba9876543210L
TrapRebindPassVoid(st)
}
ca.close()
}
"The CCALL instruction" should "handle struct parameters and return value in baz" in {
val ca = microVM.newClientAgent()
val lib = Library.openLibrary(fileName, Library.NOW)
val funcAddr = lib.getSymbolAddress("baz_func")
val func = ca.putFunction("@baz_func_test")
val a0 = ca.putInt("@i64", funcAddr)
testFunc(ca, func, Seq(a0)) { (ca, th, st, wp) =>
val Seq(fp, rv, a, b, c, pextra, aextra) = ca.dumpKeepalives(st, 0)
fp.vb.asPointer shouldEqual funcAddr
val Seq(rab, rbb) = rv.vb.asStruct
val Seq(raxb, rayb) = rab.asStruct
raxb.asFloat shouldEqual 4.0f
rayb.asSInt(32) shouldEqual 5
rbb.asDouble shouldEqual 6.0
// println("%x".format(a.vb.asUInt(64).toLong))
a.vb.asFloat shouldEqual 1.0f
b.vb.asSInt(32) shouldEqual 2
c.vb.asDouble shouldEqual 3.0
val ptr = pextra.vb.asPointer
println("ptr is 0x%x".format(ptr))
val (oref, off) = aextra.vb.asIRef
println("oref, off is 0x%x 0x%x".format(oref, off))
print(HexDump.dumpMemory(ptr-32, 16+64))
val m = microVM.memoryManager.memorySupport.theMemory
m.getFloat(ptr) shouldEqual 4.0f
m.getInt(ptr+4) shouldEqual 5
m.getDouble(ptr+8) shouldEqual 6.0
TrapRebindPassVoid(st)
}
ca.close()
}
}
\ No newline at end of file
......@@ -653,9 +653,11 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
val a3 = ca.putDouble("@double", 6.25d)
testFunc(ca, func, Seq(a0, a1, a2, a3)) { (ca, th, st, wp) =>
val Seq(trunc, zext, sext, fptrunc, fpext, fptoui1, fptosi1, fptoui2, fptosi2, uitofp, sitofp,
bitcast1, bitcast2, bitcast3, bitcast4, ptrcast1, ptrcast2, ptrcast3) = ca.dumpKeepalives(st, 0)
val kas = ca.dumpKeepalives(st, 0)
val Seq(trunc, zext, sext, fptrunc, fpext, fptoui1, fptosi1, fptoui2, fptosi2, uitofp, sitofp) = kas.take(11)
val Seq(bitcast1, bitcast2, bitcast3, bitcast4, ptrcast1, ptrcast2, ptrcast3, ptrcast4, ptrcast5, ptrcast6) = kas.drop(11)
trunc.vb.asUInt(32) shouldBe 0x9abcdef0L
zext.vb.asUInt(64) shouldBe 0xfedcba98L
sext.vb.asUInt(64) shouldBe BigInt("fffffffffedcba98", 16)
......@@ -679,13 +681,19 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
ptrcast2.vb.asPointer shouldBe 0x123456789abcdef0L
ptrcast3.vb.asSInt(64) shouldBe 0x123456789abcdef0L
ptrcast4.vb.asSInt(64) shouldBe 0xdeadbeef13572468L
ptrcast5.vb.asPointer shouldBe 0x55aa55aa5a5a5a5aL
ptrcast6.vb.asInt shouldBe 0x13572468L
TrapRebindPassVoid(st)
}
val a5 = ca.putInt("@i64", -0x123456789abcdef0L)
testFunc(ca, func, Seq(a0, a5, a2, a3)) { (ca, th, st, wp) =>
val Seq(trunc, zext, sext, fptrunc, fpext, fptoui1, fptosi1, fptoui2, fptosi2, uitofp, sitofp,
bitcast1, bitcast2, bitcast3, bitcast4, ptrcast1, ptrcast2, ptrcast3) = ca.dumpKeepalives(st, 0)
val kas = ca.dumpKeepalives(st, 0)
val Seq(trunc, zext, sext, fptrunc, fpext, fptoui1, fptosi1, fptoui2, fptosi2, uitofp, sitofp) = kas.take(11)
val Seq(bitcast1, bitcast2, bitcast3, bitcast4, ptrcast1, ptrcast2, ptrcast3, ptrcast4, ptrcast5, ptrcast6) = kas.drop(11)
sitofp.vb.asDouble shouldBe (-0x123456789abcdef0L).doubleValue()
......@@ -1409,7 +1417,7 @@ class UvmInterpreterSpec extends UvmBundleTesterBase {
e.vb.asSInt(64) shouldEqual 42
f.vb.asSInt(64) shouldEqual 42
val thr = th.vb.asThread.get
thr.pinSet should contain(cAddr)
......
package uvm.testutil
import org.scalatest.Matchers
import org.scalatest.FlatSpec
import uvm.utils.HexDump
class HexDumpTest extends FlatSpec with Matchers {
"HexDump" should "dump render 256 byte values" in {
val hd = new HexDump(0x420)
for (i <- 0 until 256) {
hd.addByte(i.toByte)
}
val result = hd.finish()
print(result)
}
"HexDump" should "dump render 256 byte values from non 16-byte-aligned starting point" in {
val hd = new HexDump(0x424)
for (i <- 0 until 256) {
hd.addByte(i.toByte)
}
val result = hd.finish()
print(result)
}
}
\ No newline at end of file
all: structtest.so
structtest.so: structtest.c
cc -fPIC -shared -o structtest.so structtest.c
#include <stdio.h>
struct foo {
long a;
int b;
short c;
char d;
};
struct bar {
void *a;
void (*b)();
};
struct baz {
struct baz1 {
float x;
int y;
} a;
double b;
};
void foo_func(struct foo o, long *a, int *b, short *c, char *d) {
*a = o.a;
*b = o.b;
*c = o.c;
*d = o.d;
}
void bar_func(struct bar o, void **a, void (**b)()) {
*a = o.a;
*b = o.b;
}
struct baz baz_func(struct baz o, float *x, int *y, double *b, struct baz *extra) {
*x = o.a.x;
*y = o.a.y;
*b = o.b;
struct baz p;
p.a.x = 4.0;
p.a.y = 5;
p.b = 6.0;
printf("[C:baz_func] *extra = %p\n", extra);
printf("[C:baz_func] extra->a.x = %f\n", extra->a.x);
printf("[C:baz_func] extra->a.y = %d\n", extra->a.y);
printf("[C:baz_func] extra->b = %lf\n", extra->b);
*extra = p;
printf("[C:baz_func] *extra = %p\n", extra);
printf("[C:baz_func] extra->a.x = %f\n", extra->a.x);
printf("[C:baz_func] extra->a.y = %d\n", extra->a.y);
printf("[C:baz_func] extra->b = %lf\n", extra->b);
return p;
}
......@@ -64,10 +64,10 @@
.const @F_2 <@float> = 2.0f
.const @F_3 <@float> = 3.0f
.const @F_4 <@float> = 4.0f
.const @I32_1 <@float> = 1
.const @I32_2 <@float> = 2
.const @I32_3 <@float> = 3
.const @I32_4 <@float> = 4
.const @I32_1 <@i32> = 1
.const @I32_2 <@i32> = 2
.const @I32_3 <@i32> = 3
.const @I32_4 <@i32> = 4
.const @D_1 <@float> = 1.0d
.const @D_2 <@float> = 2.0d
......@@ -82,3 +82,9 @@
.typedef @sgf_t = struct<@ii64 @func0>
.const @sgf <@sgf_t> = {@gi64 @fdummy}
.typedef @i32_p = ptr<@i32>
.typedef @sig0_fp = funcptr<@sig0>