FFI at CT (#10150)
* enable FFI at CT * rename useFFI=>nimHasLibFFI; improve formatting rawExecute traceCode * disable libffi on windows (works for win32, not yet win64)
This commit is contained in:
parent
30ab7e6bdd
commit
adbabf145c
9 changed files with 250 additions and 136 deletions
|
|
@ -34,7 +34,7 @@ bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
|
||||||
bootSwitch(usedNativeStacktrace,
|
bootSwitch(usedNativeStacktrace,
|
||||||
defined(nativeStackTrace) and nativeStackTraceSupported,
|
defined(nativeStackTrace) and nativeStackTraceSupported,
|
||||||
"-d:nativeStackTrace")
|
"-d:nativeStackTrace")
|
||||||
bootSwitch(usedFFI, hasFFI, "-d:useFFI")
|
bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI")
|
||||||
|
|
||||||
type
|
type
|
||||||
TCmdLinePass* = enum
|
TCmdLinePass* = enum
|
||||||
|
|
|
||||||
|
|
@ -9,43 +9,47 @@
|
||||||
|
|
||||||
## This file implements the FFI part of the evaluator for Nim code.
|
## This file implements the FFI part of the evaluator for Nim code.
|
||||||
|
|
||||||
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
|
import ast, astalgo, ropes, types, options, tables, dynlib, msgs, os, lineinfos
|
||||||
|
import pkg/libffi
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
const libcDll = "msvcrt.dll"
|
const libcDll = "msvcrt.dll"
|
||||||
else:
|
elif defined(linux):
|
||||||
const libcDll = "libc.so(.6|.5|)"
|
const libcDll = "libc.so(.6|.5|)"
|
||||||
|
elif defined(osx):
|
||||||
|
const libcDll = "/usr/lib/libSystem.dylib"
|
||||||
|
else:
|
||||||
|
{.error: "`libcDll` not implemented on this platform".}
|
||||||
|
|
||||||
type
|
type
|
||||||
TDllCache = tables.TTable[string, TLibHandle]
|
TDllCache = tables.Table[string, LibHandle]
|
||||||
var
|
var
|
||||||
gDllCache = initTable[string, TLibHandle]()
|
gDllCache = initTable[string, LibHandle]()
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var gExeHandle = loadLib(os.getAppFilename())
|
var gExeHandle = loadLib(os.getAppFilename())
|
||||||
else:
|
else:
|
||||||
var gExeHandle = loadLib()
|
var gExeHandle = loadLib()
|
||||||
|
|
||||||
proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
|
proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
|
||||||
result = cache[dll]
|
if dll in cache:
|
||||||
|
return cache[dll]
|
||||||
|
var libs: seq[string]
|
||||||
|
libCandidates(dll, libs)
|
||||||
|
for c in libs:
|
||||||
|
result = loadLib(c)
|
||||||
|
if not result.isNil: break
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
var libs: seq[string] = @[]
|
globalError(conf, info, "cannot load: " & dll)
|
||||||
libCandidates(dll, libs)
|
cache[dll] = result
|
||||||
for c in libs:
|
|
||||||
result = loadLib(c)
|
|
||||||
if not result.isNil: break
|
|
||||||
if result.isNil:
|
|
||||||
globalError(info, "cannot load: " & dll)
|
|
||||||
cache[dll] = result
|
|
||||||
|
|
||||||
const
|
const
|
||||||
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
|
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
|
||||||
|
|
||||||
var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable
|
var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable
|
||||||
|
|
||||||
proc importcSymbol*(sym: PSym): PNode =
|
proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
|
||||||
let name = ropeToStr(sym.loc.r)
|
let name = $sym.loc.r
|
||||||
|
|
||||||
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
||||||
# that contains the address instead:
|
# that contains the address instead:
|
||||||
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
|
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
|
||||||
|
|
@ -57,28 +61,28 @@ proc importcSymbol*(sym: PSym): PNode =
|
||||||
else:
|
else:
|
||||||
let lib = sym.annex
|
let lib = sym.annex
|
||||||
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
|
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
|
||||||
globalError(sym.info, "dynlib needs to be a string lit for the REPL")
|
globalError(conf, sym.info, "dynlib needs to be a string lit")
|
||||||
var theAddr: pointer
|
var theAddr: pointer
|
||||||
if lib.isNil and not gExehandle.isNil:
|
if (lib.isNil or lib.kind == libHeader) and not gExehandle.isNil:
|
||||||
# first try this exe itself:
|
# first try this exe itself:
|
||||||
theAddr = gExehandle.symAddr(name)
|
theAddr = gExehandle.symAddr(name)
|
||||||
# then try libc:
|
# then try libc:
|
||||||
if theAddr.isNil:
|
if theAddr.isNil:
|
||||||
let dllhandle = gDllCache.getDll(libcDll, sym.info)
|
let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
|
||||||
theAddr = dllhandle.symAddr(name)
|
theAddr = dllhandle.symAddr(name)
|
||||||
elif not lib.isNil:
|
elif not lib.isNil:
|
||||||
let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
|
let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
|
||||||
else: lib.path.strVal, sym.info)
|
let dllhandle = getDll(conf, gDllCache, dll, sym.info)
|
||||||
theAddr = dllhandle.symAddr(name)
|
theAddr = dllhandle.symAddr(name)
|
||||||
if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s)
|
if theAddr.isNil: globalError(conf, sym.info, "cannot import: " & sym.name.s)
|
||||||
result.intVal = cast[ByteAddress](theAddr)
|
result.intVal = cast[ByteAddress](theAddr)
|
||||||
|
|
||||||
proc mapType(t: ast.PType): ptr libffi.TType =
|
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.TType =
|
||||||
if t == nil: return addr libffi.type_void
|
if t == nil: return addr libffi.type_void
|
||||||
|
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
|
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
|
||||||
case t.getSize
|
case getSize(conf, t)
|
||||||
of 1: result = addr libffi.type_uint8
|
of 1: result = addr libffi.type_uint8
|
||||||
of 2: result = addr libffi.type_sint16
|
of 2: result = addr libffi.type_sint16
|
||||||
of 4: result = addr libffi.type_sint32
|
of 4: result = addr libffi.type_sint32
|
||||||
|
|
@ -90,87 +94,87 @@ proc mapType(t: ast.PType): ptr libffi.TType =
|
||||||
tyStmt, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
|
tyStmt, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
|
||||||
result = addr libffi.type_pointer
|
result = addr libffi.type_pointer
|
||||||
of tyDistinct, tyAlias, tySink:
|
of tyDistinct, tyAlias, tySink:
|
||||||
result = mapType(t.sons[0])
|
result = mapType(conf, t.sons[0])
|
||||||
else:
|
else:
|
||||||
result = nil
|
result = nil
|
||||||
# too risky:
|
# too risky:
|
||||||
#of tyFloat128: result = addr libffi.type_longdouble
|
#of tyFloat128: result = addr libffi.type_longdouble
|
||||||
|
|
||||||
proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
|
proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI =
|
||||||
case cc
|
case cc
|
||||||
of ccDefault: result = DEFAULT_ABI
|
of ccDefault: result = DEFAULT_ABI
|
||||||
of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
|
of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
|
||||||
of ccCDecl: result = DEFAULT_ABI
|
of ccCDecl: result = DEFAULT_ABI
|
||||||
else:
|
else:
|
||||||
globalError(info, "cannot map calling convention to FFI")
|
globalError(conf, info, "cannot map calling convention to FFI")
|
||||||
|
|
||||||
template rd(T, p: untyped): untyped = (cast[ptr T](p))[]
|
template rd(T, p: untyped): untyped = (cast[ptr T](p))[]
|
||||||
template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v
|
template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v
|
||||||
template `+!`(x, y: untyped): untyped =
|
template `+!`(x, y: untyped): untyped =
|
||||||
cast[pointer](cast[ByteAddress](x) + y)
|
cast[pointer](cast[ByteAddress](x) + y)
|
||||||
|
|
||||||
proc packSize(v: PNode, typ: PType): int =
|
proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
|
||||||
## computes the size of the blob
|
## computes the size of the blob
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tyPtr, tyRef, tyVar, tyLent:
|
of tyPtr, tyRef, tyVar, tyLent:
|
||||||
if v.kind in {nkNilLit, nkPtrLit}:
|
if v.kind in {nkNilLit, nkPtrLit}:
|
||||||
result = sizeof(pointer)
|
result = sizeof(pointer)
|
||||||
else:
|
else:
|
||||||
result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
|
result = sizeof(pointer) + packSize(conf, v.sons[0], typ.lastSon)
|
||||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
||||||
result = packSize(v, typ.sons[0])
|
result = packSize(conf, v, typ.sons[0])
|
||||||
of tyArray:
|
of tyArray:
|
||||||
# consider: ptr array[0..1000_000, int] which is common for interfacing;
|
# consider: ptr array[0..1000_000, int] which is common for interfacing;
|
||||||
# we use the real length here instead
|
# we use the real length here instead
|
||||||
if v.kind in {nkNilLit, nkPtrLit}:
|
if v.kind in {nkNilLit, nkPtrLit}:
|
||||||
result = sizeof(pointer)
|
result = sizeof(pointer)
|
||||||
elif v.len != 0:
|
elif v.len != 0:
|
||||||
result = v.len * packSize(v.sons[0], typ.sons[1])
|
result = v.len * packSize(conf, v.sons[0], typ.sons[1])
|
||||||
else:
|
else:
|
||||||
result = typ.getSize.int
|
result = getSize(conf, typ).int
|
||||||
|
|
||||||
proc pack(v: PNode, typ: PType, res: pointer)
|
proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
|
||||||
|
|
||||||
proc getField(n: PNode; position: int): PSym =
|
proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
result = getField(n.sons[i], position)
|
result = getField(conf, n.sons[i], position)
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
result = getField(n.sons[0], position)
|
result = getField(conf, n.sons[0], position)
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
case n.sons[i].kind
|
case n.sons[i].kind
|
||||||
of nkOfBranch, nkElse:
|
of nkOfBranch, nkElse:
|
||||||
result = getField(lastSon(n.sons[i]), position)
|
result = getField(conf, lastSon(n.sons[i]), position)
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
else: internalError(n.info, "getField(record case branch)")
|
else: internalError(conf, n.info, "getField(record case branch)")
|
||||||
of nkSym:
|
of nkSym:
|
||||||
if n.sym.position == position: result = n.sym
|
if n.sym.position == position: result = n.sym
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc packObject(x: PNode, typ: PType, res: pointer) =
|
proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
|
||||||
internalAssert x.kind in {nkObjConstr, nkPar, nkTupleConstr}
|
internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
|
||||||
# compute the field's offsets:
|
# compute the field's offsets:
|
||||||
discard typ.getSize
|
discard getSize(conf, typ)
|
||||||
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
|
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
|
||||||
var it = x.sons[i]
|
var it = x.sons[i]
|
||||||
if it.kind == nkExprColonExpr:
|
if it.kind == nkExprColonExpr:
|
||||||
internalAssert it.sons[0].kind == nkSym
|
internalAssert conf, it.sons[0].kind == nkSym
|
||||||
let field = it.sons[0].sym
|
let field = it.sons[0].sym
|
||||||
pack(it.sons[1], field.typ, res +! field.offset)
|
pack(conf, it.sons[1], field.typ, res +! field.offset)
|
||||||
elif typ.n != nil:
|
elif typ.n != nil:
|
||||||
let field = getField(typ.n, i)
|
let field = getField(conf, typ.n, i)
|
||||||
pack(it, field.typ, res +! field.offset)
|
pack(conf, it, field.typ, res +! field.offset)
|
||||||
else:
|
else:
|
||||||
# XXX: todo
|
# XXX: todo
|
||||||
globalError(x.info, "cannot pack unnamed tuple")
|
globalError(conf, x.info, "cannot pack unnamed tuple")
|
||||||
|
|
||||||
const maxPackDepth = 20
|
const maxPackDepth = 20
|
||||||
var packRecCheck = 0
|
var packRecCheck = 0
|
||||||
|
|
||||||
proc pack(v: PNode, typ: PType, res: pointer) =
|
proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
|
||||||
template awr(T, v: untyped): untyped =
|
template awr(T, v: untyped): untyped =
|
||||||
wr(T, res, v)
|
wr(T, res, v)
|
||||||
|
|
||||||
|
|
@ -188,13 +192,13 @@ proc pack(v: PNode, typ: PType, res: pointer) =
|
||||||
of tyUInt32: awr(uint32, v.intVal.uint32)
|
of tyUInt32: awr(uint32, v.intVal.uint32)
|
||||||
of tyUInt64: awr(uint64, v.intVal.uint64)
|
of tyUInt64: awr(uint64, v.intVal.uint64)
|
||||||
of tyEnum, tySet:
|
of tyEnum, tySet:
|
||||||
case v.typ.getSize
|
case getSize(conf, v.typ)
|
||||||
of 1: awr(uint8, v.intVal.uint8)
|
of 1: awr(uint8, v.intVal.uint8)
|
||||||
of 2: awr(uint16, v.intVal.uint16)
|
of 2: awr(uint16, v.intVal.uint16)
|
||||||
of 4: awr(int32, v.intVal.int32)
|
of 4: awr(int32, v.intVal.int32)
|
||||||
of 8: awr(int64, v.intVal.int64)
|
of 8: awr(int64, v.intVal.int64)
|
||||||
else:
|
else:
|
||||||
globalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
|
globalError(conf, v.info, "cannot map value to FFI (tyEnum, tySet)")
|
||||||
of tyFloat: awr(float, v.floatVal)
|
of tyFloat: awr(float, v.floatVal)
|
||||||
of tyFloat32: awr(float32, v.floatVal)
|
of tyFloat32: awr(float32, v.floatVal)
|
||||||
of tyFloat64: awr(float64, v.floatVal)
|
of tyFloat64: awr(float64, v.floatVal)
|
||||||
|
|
@ -208,7 +212,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
|
||||||
elif v.kind in {nkStrLit..nkTripleStrLit}:
|
elif v.kind in {nkStrLit..nkTripleStrLit}:
|
||||||
awr(cstring, cstring(v.strVal))
|
awr(cstring, cstring(v.strVal))
|
||||||
else:
|
else:
|
||||||
globalError(v.info, "cannot map pointer/proc value to FFI")
|
globalError(conf, v.info, "cannot map pointer/proc value to FFI")
|
||||||
of tyPtr, tyRef, tyVar, tyLent:
|
of tyPtr, tyRef, tyVar, tyLent:
|
||||||
if v.kind == nkNilLit:
|
if v.kind == nkNilLit:
|
||||||
# nothing to do since the memory is 0 initialized anyway
|
# nothing to do since the memory is 0 initialized anyway
|
||||||
|
|
@ -218,44 +222,44 @@ proc pack(v: PNode, typ: PType, res: pointer) =
|
||||||
else:
|
else:
|
||||||
if packRecCheck > maxPackDepth:
|
if packRecCheck > maxPackDepth:
|
||||||
packRecCheck = 0
|
packRecCheck = 0
|
||||||
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||||
inc packRecCheck
|
inc packRecCheck
|
||||||
pack(v.sons[0], typ.lastSon, res +! sizeof(pointer))
|
pack(conf, v.sons[0], typ.lastSon, res +! sizeof(pointer))
|
||||||
dec packRecCheck
|
dec packRecCheck
|
||||||
awr(pointer, res +! sizeof(pointer))
|
awr(pointer, res +! sizeof(pointer))
|
||||||
of tyArray:
|
of tyArray:
|
||||||
let baseSize = typ.sons[1].getSize
|
let baseSize = getSize(conf, typ.sons[1])
|
||||||
for i in 0 ..< v.len:
|
for i in 0 ..< v.len:
|
||||||
pack(v.sons[i], typ.sons[1], res +! i * baseSize)
|
pack(conf, v.sons[i], typ.sons[1], res +! i * baseSize)
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
packObject(v, typ, res)
|
packObject(conf, v, typ, res)
|
||||||
of tyNil:
|
of tyNil:
|
||||||
discard
|
discard
|
||||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
||||||
pack(v, typ.sons[0], res)
|
pack(conf, v, typ.sons[0], res)
|
||||||
else:
|
else:
|
||||||
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||||
|
|
||||||
proc unpack(x: pointer, typ: PType, n: PNode): PNode
|
proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode
|
||||||
|
|
||||||
proc unpackObjectAdd(x: pointer, n, result: PNode) =
|
proc unpackObjectAdd(conf: ConfigRef, x: pointer, n, result: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
unpackObjectAdd(x, n.sons[i], result)
|
unpackObjectAdd(conf, x, n.sons[i], result)
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
globalError(result.info, "case objects cannot be unpacked")
|
globalError(conf, result.info, "case objects cannot be unpacked")
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var pair = newNodeI(nkExprColonExpr, result.info, 2)
|
var pair = newNodeI(nkExprColonExpr, result.info, 2)
|
||||||
pair.sons[0] = n
|
pair.sons[0] = n
|
||||||
pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
|
pair.sons[1] = unpack(conf, x +! n.sym.offset, n.sym.typ, nil)
|
||||||
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
|
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
|
||||||
result.add pair
|
result.add pair
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
|
proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||||
# compute the field's offsets:
|
# compute the field's offsets:
|
||||||
discard typ.getSize
|
discard getSize(conf, typ)
|
||||||
|
|
||||||
# iterate over any actual field of 'n' ... if n is nil we need to create
|
# iterate over any actual field of 'n' ... if n is nil we need to create
|
||||||
# the nkPar node:
|
# the nkPar node:
|
||||||
|
|
@ -263,36 +267,36 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
result = newNode(nkTupleConstr)
|
result = newNode(nkTupleConstr)
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
if typ.n.isNil:
|
if typ.n.isNil:
|
||||||
internalError("cannot unpack unnamed tuple")
|
internalError(conf, "cannot unpack unnamed tuple")
|
||||||
unpackObjectAdd(x, typ.n, result)
|
unpackObjectAdd(conf, x, typ.n, result)
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}:
|
if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}:
|
||||||
globalError(n.info, "cannot map value from FFI")
|
globalError(conf, n.info, "cannot map value from FFI")
|
||||||
if typ.n.isNil:
|
if typ.n.isNil:
|
||||||
globalError(n.info, "cannot unpack unnamed tuple")
|
globalError(conf, n.info, "cannot unpack unnamed tuple")
|
||||||
for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
|
for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
if it.kind == nkExprColonExpr:
|
if it.kind == nkExprColonExpr:
|
||||||
internalAssert it.sons[0].kind == nkSym
|
internalAssert conf, it.sons[0].kind == nkSym
|
||||||
let field = it.sons[0].sym
|
let field = it.sons[0].sym
|
||||||
it.sons[1] = unpack(x +! field.offset, field.typ, it.sons[1])
|
it.sons[1] = unpack(conf, x +! field.offset, field.typ, it.sons[1])
|
||||||
else:
|
else:
|
||||||
let field = getField(typ.n, i)
|
let field = getField(conf, typ.n, i)
|
||||||
n.sons[i] = unpack(x +! field.offset, field.typ, it)
|
n.sons[i] = unpack(conf, x +! field.offset, field.typ, it)
|
||||||
|
|
||||||
proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
|
proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||||
if n.isNil:
|
if n.isNil:
|
||||||
result = newNode(nkBracket)
|
result = newNode(nkBracket)
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
newSeq(result.sons, lengthOrd(typ).int)
|
newSeq(result.sons, lengthOrd(conf, typ).int)
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
if result.kind != nkBracket:
|
if result.kind != nkBracket:
|
||||||
globalError(n.info, "cannot map value from FFI")
|
globalError(conf, n.info, "cannot map value from FFI")
|
||||||
let baseSize = typ.sons[1].getSize
|
let baseSize = getSize(conf, typ.sons[1])
|
||||||
for i in 0 ..< result.len:
|
for i in 0 ..< result.len:
|
||||||
result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
|
result.sons[i] = unpack(conf, x +! i * baseSize, typ.sons[1], result.sons[i])
|
||||||
|
|
||||||
proc canonNodeKind(k: TNodeKind): TNodeKind =
|
proc canonNodeKind(k: TNodeKind): TNodeKind =
|
||||||
case k
|
case k
|
||||||
|
|
@ -301,7 +305,7 @@ proc canonNodeKind(k: TNodeKind): TNodeKind =
|
||||||
of nkStrLit..nkTripleStrLit: result = nkStrLit
|
of nkStrLit..nkTripleStrLit: result = nkStrLit
|
||||||
else: result = k
|
else: result = k
|
||||||
|
|
||||||
proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||||
template aw(k, v, field: untyped): untyped =
|
template aw(k, v, field: untyped): untyped =
|
||||||
if n.isNil:
|
if n.isNil:
|
||||||
result = newNode(k)
|
result = newNode(k)
|
||||||
|
|
@ -313,7 +317,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
#echo "expected ", k, " but got ", result.kind
|
#echo "expected ", k, " but got ", result.kind
|
||||||
#debug result
|
#debug result
|
||||||
return newNodeI(nkExceptBranch, n.info)
|
return newNodeI(nkExceptBranch, n.info)
|
||||||
#globalError(n.info, "cannot map value from FFI")
|
#globalError(conf, n.info, "cannot map value from FFI")
|
||||||
result.field = v
|
result.field = v
|
||||||
|
|
||||||
template setNil() =
|
template setNil() =
|
||||||
|
|
@ -344,13 +348,13 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
|
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
|
||||||
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
|
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
case typ.getSize
|
case getSize(conf, typ)
|
||||||
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
|
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
|
||||||
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
|
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
|
||||||
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
|
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
|
||||||
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
|
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
|
||||||
else:
|
else:
|
||||||
globalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
|
globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
|
||||||
of tyFloat: awf(nkFloatLit, rd(float, x))
|
of tyFloat: awf(nkFloatLit, rd(float, x))
|
||||||
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
|
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
|
||||||
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
|
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
|
||||||
|
|
@ -371,15 +375,15 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
elif n == nil or n.kind == nkPtrLit:
|
elif n == nil or n.kind == nkPtrLit:
|
||||||
awi(nkPtrLit, cast[ByteAddress](p))
|
awi(nkPtrLit, cast[ByteAddress](p))
|
||||||
elif n != nil and n.len == 1:
|
elif n != nil and n.len == 1:
|
||||||
internalAssert n.kind == nkRefTy
|
internalAssert(conf, n.kind == nkRefTy)
|
||||||
n.sons[0] = unpack(p, typ.lastSon, n.sons[0])
|
n.sons[0] = unpack(conf, p, typ.lastSon, n.sons[0])
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
result = unpackObject(x, typ, n)
|
result = unpackObject(conf, x, typ, n)
|
||||||
of tyArray:
|
of tyArray:
|
||||||
result = unpackArray(x, typ, n)
|
result = unpackArray(conf, x, typ, n)
|
||||||
of tyCString, tyString:
|
of tyCString, tyString:
|
||||||
let p = rd(cstring, x)
|
let p = rd(cstring, x)
|
||||||
if p.isNil:
|
if p.isNil:
|
||||||
|
|
@ -389,12 +393,12 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
of tyNil:
|
of tyNil:
|
||||||
setNil()
|
setNil()
|
||||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
||||||
result = unpack(x, typ.lastSon, n)
|
result = unpack(conf, x, typ.lastSon, n)
|
||||||
else:
|
else:
|
||||||
# XXX what to do with 'array' here?
|
# XXX what to do with 'array' here?
|
||||||
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
|
||||||
|
|
||||||
proc fficast*(x: PNode, destTyp: PType): PNode =
|
proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||||
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer,
|
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer,
|
||||||
tyProc, tyCString, tyString,
|
tyProc, tyCString, tyString,
|
||||||
tySequence}:
|
tySequence}:
|
||||||
|
|
@ -404,93 +408,94 @@ proc fficast*(x: PNode, destTyp: PType): PNode =
|
||||||
result = newNodeIT(x.kind, x.info, destTyp)
|
result = newNodeIT(x.kind, x.info, destTyp)
|
||||||
else:
|
else:
|
||||||
# we play safe here and allocate the max possible size:
|
# we play safe here and allocate the max possible size:
|
||||||
let size = max(packSize(x, x.typ), packSize(x, destTyp))
|
let size = max(packSize(conf, x, x.typ), packSize(conf, x, destTyp))
|
||||||
var a = alloc0(size)
|
var a = alloc0(size)
|
||||||
pack(x, x.typ, a)
|
pack(conf, x, x.typ, a)
|
||||||
# cast through a pointer needs a new inner object:
|
# cast through a pointer needs a new inner object:
|
||||||
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
||||||
else: x.copyTree
|
else: x.copyTree
|
||||||
y.typ = x.typ
|
y.typ = x.typ
|
||||||
result = unpack(a, destTyp, y)
|
result = unpack(conf, a, destTyp, y)
|
||||||
dealloc a
|
dealloc a
|
||||||
|
|
||||||
proc callForeignFunction*(call: PNode): PNode =
|
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
|
||||||
internalAssert call.sons[0].kind == nkPtrLit
|
internalAssert conf, call.sons[0].kind == nkPtrLit
|
||||||
|
|
||||||
var cif: TCif
|
var cif: TCif
|
||||||
var sig: TParamList
|
var sig: TParamList
|
||||||
# use the arguments' types for varargs support:
|
# use the arguments' types for varargs support:
|
||||||
for i in 1..call.len-1:
|
for i in 1..call.len-1:
|
||||||
sig[i-1] = mapType(call.sons[i].typ)
|
sig[i-1] = mapType(conf, call.sons[i].typ)
|
||||||
if sig[i-1].isNil:
|
if sig[i-1].isNil:
|
||||||
globalError(call.info, "cannot map FFI type")
|
globalError(conf, call.info, "cannot map FFI type")
|
||||||
|
|
||||||
let typ = call.sons[0].typ
|
let typ = call.sons[0].typ
|
||||||
if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
|
if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
|
||||||
mapType(typ.sons[0]), sig) != OK:
|
mapType(conf, typ.sons[0]), sig) != OK:
|
||||||
globalError(call.info, "error in FFI call")
|
globalError(conf, call.info, "error in FFI call")
|
||||||
|
|
||||||
var args: TArgList
|
var args: TArgList
|
||||||
let fn = cast[pointer](call.sons[0].intVal)
|
let fn = cast[pointer](call.sons[0].intVal)
|
||||||
for i in 1 .. call.len-1:
|
for i in 1 .. call.len-1:
|
||||||
var t = call.sons[i].typ
|
var t = call.sons[i].typ
|
||||||
args[i-1] = alloc0(packSize(call.sons[i], t))
|
args[i-1] = alloc0(packSize(conf, call.sons[i], t))
|
||||||
pack(call.sons[i], t, args[i-1])
|
pack(conf, call.sons[i], t, args[i-1])
|
||||||
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
|
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
|
||||||
else: alloc(typ.sons[0].getSize.int)
|
else: alloc(getSize(conf, typ.sons[0]).int)
|
||||||
|
|
||||||
libffi.call(cif, fn, retVal, args)
|
libffi.call(cif, fn, retVal, args)
|
||||||
|
|
||||||
if retVal.isNil:
|
if retVal.isNil:
|
||||||
result = newNode(nkEmpty)
|
result = newNode(nkEmpty)
|
||||||
else:
|
else:
|
||||||
result = unpack(retVal, typ.sons[0], nil)
|
result = unpack(conf, retVal, typ.sons[0], nil)
|
||||||
result.info = call.info
|
result.info = call.info
|
||||||
|
|
||||||
if retVal != nil: dealloc retVal
|
if retVal != nil: dealloc retVal
|
||||||
for i in 1 .. call.len-1:
|
for i in 1 .. call.len-1:
|
||||||
call.sons[i] = unpack(args[i-1], typ.sons[i], call[i])
|
call.sons[i] = unpack(conf, args[i-1], typ.sons[i], call[i])
|
||||||
dealloc args[i-1]
|
dealloc args[i-1]
|
||||||
|
|
||||||
proc callForeignFunction*(fn: PNode, fntyp: PType,
|
proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||||
args: var TNodeSeq, start, len: int,
|
args: var TNodeSeq, start, len: int,
|
||||||
info: TLineInfo): PNode =
|
info: TLineInfo): PNode =
|
||||||
internalAssert fn.kind == nkPtrLit
|
internalAssert conf, fn.kind == nkPtrLit
|
||||||
|
|
||||||
var cif: TCif
|
var cif: TCif
|
||||||
var sig: TParamList
|
var sig: TParamList
|
||||||
for i in 0..len-1:
|
for i in 0..len-1:
|
||||||
var aTyp = args[i+start].typ
|
var aTyp = args[i+start].typ
|
||||||
if aTyp.isNil:
|
if aTyp.isNil:
|
||||||
internalAssert i+1 < fntyp.len
|
internalAssert conf, i+1 < fntyp.len
|
||||||
aTyp = fntyp.sons[i+1]
|
aTyp = fntyp.sons[i+1]
|
||||||
args[i+start].typ = aTyp
|
args[i+start].typ = aTyp
|
||||||
sig[i] = mapType(aTyp)
|
sig[i] = mapType(conf, aTyp)
|
||||||
if sig[i].isNil: globalError(info, "cannot map FFI type")
|
if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
|
||||||
|
|
||||||
if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len),
|
if prep_cif(cif, mapCallConv(conf, fntyp.callConv, info), cuint(len),
|
||||||
mapType(fntyp.sons[0]), sig) != OK:
|
mapType(conf, fntyp.sons[0]), sig) != OK:
|
||||||
globalError(info, "error in FFI call")
|
globalError(conf, info, "error in FFI call")
|
||||||
|
|
||||||
var cargs: TArgList
|
var cargs: TArgList
|
||||||
let fn = cast[pointer](fn.intVal)
|
let fn = cast[pointer](fn.intVal)
|
||||||
for i in 0 .. len-1:
|
for i in 0 .. len-1:
|
||||||
let t = args[i+start].typ
|
let t = args[i+start].typ
|
||||||
cargs[i] = alloc0(packSize(args[i+start], t))
|
cargs[i] = alloc0(packSize(conf, args[i+start], t))
|
||||||
pack(args[i+start], t, cargs[i])
|
pack(conf, args[i+start], t, cargs[i])
|
||||||
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
|
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
|
||||||
else: alloc(fntyp.sons[0].getSize.int)
|
else: alloc(getSize(conf, fntyp.sons[0]).int)
|
||||||
|
|
||||||
libffi.call(cif, fn, retVal, cargs)
|
libffi.call(cif, fn, retVal, cargs)
|
||||||
|
|
||||||
if retVal.isNil:
|
if retVal.isNil:
|
||||||
result = newNode(nkEmpty)
|
result = newNode(nkEmpty)
|
||||||
else:
|
else:
|
||||||
result = unpack(retVal, fntyp.sons[0], nil)
|
result = unpack(conf, retVal, fntyp.sons[0], nil)
|
||||||
result.info = info
|
result.info = info
|
||||||
|
|
||||||
if retVal != nil: dealloc retVal
|
if retVal != nil: dealloc retVal
|
||||||
for i in 0 .. len-1:
|
for i in 0 .. len-1:
|
||||||
let t = args[i+start].typ
|
let t = args[i+start].typ
|
||||||
args[i+start] = unpack(cargs[i], t, args[i+start])
|
args[i+start] = unpack(conf, cargs[i], t, args[i+start])
|
||||||
dealloc cargs[i]
|
dealloc cargs[i]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ when not defined(leanCompiler):
|
||||||
proc interactivePasses(graph: ModuleGraph) =
|
proc interactivePasses(graph: ModuleGraph) =
|
||||||
initDefines(graph.config.symbols)
|
initDefines(graph.config.symbols)
|
||||||
defineSymbol(graph.config.symbols, "nimscript")
|
defineSymbol(graph.config.symbols, "nimscript")
|
||||||
|
# note: seems redundant with -d:nimHasLibFFI
|
||||||
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
|
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
|
||||||
registerPass(graph, verbosePass)
|
registerPass(graph, verbosePass)
|
||||||
registerPass(graph, semPass)
|
registerPass(graph, semPass)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const
|
||||||
hasTinyCBackend* = defined(tinyc)
|
hasTinyCBackend* = defined(tinyc)
|
||||||
useEffectSystem* = true
|
useEffectSystem* = true
|
||||||
useWriteTracking* = false
|
useWriteTracking* = false
|
||||||
hasFFI* = defined(useFFI)
|
hasFFI* = defined(nimHasLibFFI)
|
||||||
copyrightYear* = "2018"
|
copyrightYear* = "2018"
|
||||||
|
|
||||||
type # please make sure we have under 32 options
|
type # please make sure we have under 32 options
|
||||||
|
|
@ -128,6 +128,10 @@ type
|
||||||
forLoopMacros,
|
forLoopMacros,
|
||||||
caseStmtMacros,
|
caseStmtMacros,
|
||||||
codeReordering,
|
codeReordering,
|
||||||
|
compiletimeFFI,
|
||||||
|
## This requires building nim with `-d:nimHasLibFFI`
|
||||||
|
## which itself requires `nimble install libffi`, see #10150
|
||||||
|
## Note: this feature can't be localized with {.push.}
|
||||||
|
|
||||||
SymbolFilesOption* = enum
|
SymbolFilesOption* = enum
|
||||||
disabledSf, writeOnlySf, readOnlySf, v2Sf
|
disabledSf, writeOnlySf, readOnlySf, v2Sf
|
||||||
|
|
|
||||||
|
|
@ -498,7 +498,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
let ra = instr.regA
|
let ra = instr.regA
|
||||||
|
|
||||||
when traceCode:
|
when traceCode:
|
||||||
echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC
|
template regDescr(name, r): string =
|
||||||
|
let kind = if r < regs.len: $regs[r].kind else: ""
|
||||||
|
let ret = name & ": " & $r & " " & $kind
|
||||||
|
alignLeft(ret, 15)
|
||||||
|
echo "PC:$pc $opcode $ra $rb $rc" % [
|
||||||
|
"pc", $pc, "opcode", alignLeft($c.code[pc].opcode, 15),
|
||||||
|
"ra", regDescr("ra", ra), "rb", regDescr("rb", instr.regB),
|
||||||
|
"rc", regDescr("rc", instr.regC)]
|
||||||
|
|
||||||
case instr.opcode
|
case instr.opcode
|
||||||
of opcEof: return regs[ra]
|
of opcEof: return regs[ra]
|
||||||
|
|
@ -1072,15 +1079,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
currentException: c.currentExceptionA,
|
currentException: c.currentExceptionA,
|
||||||
currentLineInfo: c.debug[pc]))
|
currentLineInfo: c.debug[pc]))
|
||||||
elif sfImportc in prc.flags:
|
elif sfImportc in prc.flags:
|
||||||
if allowFFI notin c.features:
|
if compiletimeFFI notin c.config.features:
|
||||||
globalError(c.config, c.debug[pc], "VM not allowed to do FFI")
|
globalError(c.config, c.debug[pc], "VM not allowed to do FFI, see `compiletimeFFI`")
|
||||||
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep
|
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep
|
||||||
# 'regs' in a register:
|
# 'regs' in a register:
|
||||||
when hasFFI:
|
when hasFFI:
|
||||||
let prcValue = c.globals.sons[prc.position-1]
|
let prcValue = c.globals.sons[prc.position-1]
|
||||||
if prcValue.kind == nkEmpty:
|
if prcValue.kind == nkEmpty:
|
||||||
globalError(c.config, c.debug[pc], "cannot run " & prc.name.s)
|
globalError(c.config, c.debug[pc], "cannot run " & prc.name.s)
|
||||||
let newValue = callForeignFunction(prcValue, prc.typ, tos.slots,
|
var slots2: TNodeSeq
|
||||||
|
slots2.setLen(tos.slots.len)
|
||||||
|
for i in 0..<tos.slots.len:
|
||||||
|
slots2[i] = regToNode(tos.slots[i])
|
||||||
|
let newValue = callForeignFunction(c.config, prcValue, prc.typ, slots2,
|
||||||
rb+1, rc-1, c.debug[pc])
|
rb+1, rc-1, c.debug[pc])
|
||||||
if newValue.kind != nkEmpty:
|
if newValue.kind != nkEmpty:
|
||||||
assert instr.opcode == opcIndCallAsgn
|
assert instr.opcode == opcIndCallAsgn
|
||||||
|
|
@ -1611,9 +1622,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal)
|
regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal)
|
||||||
regs[ra].node.flags.incl nfIsRef
|
regs[ra].node.flags.incl nfIsRef
|
||||||
of opcSetType:
|
of opcSetType:
|
||||||
|
let typ = c.types[instr.regBx - wordExcess]
|
||||||
if regs[ra].kind != rkNode:
|
if regs[ra].kind != rkNode:
|
||||||
internalError(c.config, c.debug[pc], "cannot set type")
|
let temp = regToNode(regs[ra])
|
||||||
regs[ra].node.typ = c.types[instr.regBx - wordExcess]
|
ensureKind(rkNode)
|
||||||
|
regs[ra].node = temp
|
||||||
|
regs[ra].node.info = c.debug[pc]
|
||||||
|
regs[ra].node.typ = typ
|
||||||
of opcConv:
|
of opcConv:
|
||||||
let rb = instr.regB
|
let rb = instr.regB
|
||||||
inc pc
|
inc pc
|
||||||
|
|
@ -1633,8 +1648,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
let srctyp = c.types[c.code[pc].regBx - wordExcess]
|
let srctyp = c.types[c.code[pc].regBx - wordExcess]
|
||||||
|
|
||||||
when hasFFI:
|
when hasFFI:
|
||||||
let dest = fficast(regs[rb], desttyp)
|
let dest = fficast(c.config, regs[rb].node, desttyp)
|
||||||
asgnRef(regs[ra], dest)
|
# todo: check whether this is correct
|
||||||
|
# asgnRef(regs[ra], dest)
|
||||||
|
putIntoReg(regs[ra], dest)
|
||||||
else:
|
else:
|
||||||
globalError(c.config, c.debug[pc], "cannot evaluate cast")
|
globalError(c.config, c.debug[pc], "cannot evaluate cast")
|
||||||
of opcNSetIntVal:
|
of opcNSetIntVal:
|
||||||
|
|
@ -1921,14 +1938,12 @@ proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
|
||||||
|
|
||||||
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||||
#var c = newEvalContext(module, emRepl)
|
#var c = newEvalContext(module, emRepl)
|
||||||
#c.features = {allowCast, allowFFI, allowInfiniteLoops}
|
#c.features = {allowCast, allowInfiniteLoops}
|
||||||
#pushStackFrame(c, newStackFrame())
|
#pushStackFrame(c, newStackFrame())
|
||||||
|
|
||||||
# XXX produce a new 'globals' environment here:
|
# XXX produce a new 'globals' environment here:
|
||||||
setupGlobalCtx(module, graph)
|
setupGlobalCtx(module, graph)
|
||||||
result = PCtx graph.vm
|
result = PCtx graph.vm
|
||||||
when hasFFI:
|
|
||||||
PCtx(graph.vm).features = {allowFFI, allowCast}
|
|
||||||
|
|
||||||
proc myProcess(c: PPassContext, n: PNode): PNode =
|
proc myProcess(c: PPassContext, n: PNode): PNode =
|
||||||
let c = PCtx(c)
|
let c = PCtx(c)
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,6 @@ type
|
||||||
|
|
||||||
TSandboxFlag* = enum ## what the evaluation engine should allow
|
TSandboxFlag* = enum ## what the evaluation engine should allow
|
||||||
allowCast, ## allow unsafe language feature: 'cast'
|
allowCast, ## allow unsafe language feature: 'cast'
|
||||||
allowFFI, ## allow the FFI
|
|
||||||
allowInfiniteLoops ## allow endless loops
|
allowInfiniteLoops ## allow endless loops
|
||||||
TSandboxFlags* = set[TSandboxFlag]
|
TSandboxFlags* = set[TSandboxFlag]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,9 @@ proc getTemp(cc: PCtx; tt: PType): TRegister =
|
||||||
|
|
||||||
proc freeTemp(c: PCtx; r: TRegister) =
|
proc freeTemp(c: PCtx; r: TRegister) =
|
||||||
let c = c.prc
|
let c = c.prc
|
||||||
if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: c.slots[r].inUse = false
|
if c.slots[r].kind in {slotSomeTemp..slotTempComplex}:
|
||||||
|
# this seems to cause https://github.com/nim-lang/Nim/issues/10647
|
||||||
|
c.slots[r].inUse = false
|
||||||
|
|
||||||
proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister =
|
proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister =
|
||||||
# if register pressure is high, we re-use more aggressively:
|
# if register pressure is high, we re-use more aggressively:
|
||||||
|
|
@ -1540,8 +1542,8 @@ proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
|
||||||
|
|
||||||
proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
|
proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
|
||||||
when hasFFI:
|
when hasFFI:
|
||||||
if allowFFI in c.features:
|
if compiletimeFFI in c.config.features:
|
||||||
c.globals.add(importcSymbol(s))
|
c.globals.add(importcSymbol(c.config, s))
|
||||||
s.position = c.globals.len
|
s.position = c.globals.len
|
||||||
else:
|
else:
|
||||||
localError(c.config, info, "VM is not allowed to 'importc'")
|
localError(c.config, info, "VM is not allowed to 'importc'")
|
||||||
|
|
|
||||||
7
koch.nim
7
koch.nim
|
|
@ -463,11 +463,18 @@ proc runCI(cmd: string) =
|
||||||
# as that would weaken our testing efforts.
|
# as that would weaken our testing efforts.
|
||||||
when defined(posix): # appveyor (on windows) didn't run this
|
when defined(posix): # appveyor (on windows) didn't run this
|
||||||
kochExecFold("Boot", "boot")
|
kochExecFold("Boot", "boot")
|
||||||
|
# boot without -d:nimHasLibFFI to make sure this still works
|
||||||
kochExecFold("Boot in release mode", "boot -d:release")
|
kochExecFold("Boot in release mode", "boot -d:release")
|
||||||
|
|
||||||
## build nimble early on to enable remainder to depend on it if needed
|
## build nimble early on to enable remainder to depend on it if needed
|
||||||
kochExecFold("Build Nimble", "nimble")
|
kochExecFold("Build Nimble", "nimble")
|
||||||
|
|
||||||
|
when not defined(windows):
|
||||||
|
# pending https://github.com/Araq/libffi/pull/2
|
||||||
|
# also, that PR works on win32 but not yet win64
|
||||||
|
execFold("nimble install -y libffi", "nimble install -y libffi")
|
||||||
|
kochExecFold("boot -d:release -d:nimHasLibFFI", "boot -d:release -d:nimHasLibFFI")
|
||||||
|
|
||||||
if getEnv("NIM_TEST_PACKAGES", "false") == "true":
|
if getEnv("NIM_TEST_PACKAGES", "false") == "true":
|
||||||
execFold("Test selected Nimble packages", "nim c -r testament/tester cat nimble-extra")
|
execFold("Test selected Nimble packages", "nim c -r testament/tester cat nimble-extra")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
81
tests/vm/tevalffi.nim
Normal file
81
tests/vm/tevalffi.nim
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c --experimental:compiletimeFFI $file"
|
||||||
|
nimout: '''
|
||||||
|
foo
|
||||||
|
foo:100
|
||||||
|
foo:101
|
||||||
|
foo:102:103
|
||||||
|
foo:102:103:104
|
||||||
|
foo:0.03:asdf:103:105
|
||||||
|
ret={s1:foobar s2:foobar age:25 pi:3.14}
|
||||||
|
'''
|
||||||
|
output: '''
|
||||||
|
foo
|
||||||
|
foo:100
|
||||||
|
foo:101
|
||||||
|
foo:102:103
|
||||||
|
foo:102:103:104
|
||||||
|
foo:0.03:asdf:103:105
|
||||||
|
ret={s1:foobar s2:foobar age:25 pi:3.14}
|
||||||
|
'''
|
||||||
|
disabled: "windows"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# re-enable for windows once libffi can be installed in koch.nim
|
||||||
|
# With win32 (not yet win64), libffi on windows works and this test passes.
|
||||||
|
|
||||||
|
when defined(linux):
|
||||||
|
{.passL: "-lm".} # for exp
|
||||||
|
proc c_exp(a: float64): float64 {.importc: "exp", header: "<math.h>".}
|
||||||
|
|
||||||
|
proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>", varargs, discardable.}
|
||||||
|
|
||||||
|
const snprintfName = when defined(windows): "_snprintf" else: "snprintf"
|
||||||
|
proc c_snprintf*(buffer: pointer, buf_size: uint, format: cstring): cint {.importc: snprintfName, header: "<stdio.h>", varargs .}
|
||||||
|
|
||||||
|
proc c_malloc(size:uint):pointer {.importc:"malloc", header: "<stdlib.h>".}
|
||||||
|
proc c_free(p: pointer) {.importc:"free", header: "<stdlib.h>".}
|
||||||
|
|
||||||
|
proc fun() =
|
||||||
|
block: # c_exp
|
||||||
|
var x = 0.3
|
||||||
|
let b = c_exp(x)
|
||||||
|
let b2 = int(b*1_000_000) # avoids floating point equality
|
||||||
|
doAssert b2 == 1349858
|
||||||
|
doAssert c_exp(0.3) == c_exp(x)
|
||||||
|
const x2 = 0.3
|
||||||
|
doAssert c_exp(x2) == c_exp(x)
|
||||||
|
|
||||||
|
block: # c_printf
|
||||||
|
c_printf("foo\n")
|
||||||
|
c_printf("foo:%d\n", 100)
|
||||||
|
c_printf("foo:%d\n", 101.cint)
|
||||||
|
c_printf("foo:%d:%d\n", 102.cint, 103.cint)
|
||||||
|
let temp = 104.cint
|
||||||
|
c_printf("foo:%d:%d:%d\n", 102.cint, 103.cint, temp)
|
||||||
|
var temp2 = 105.cint
|
||||||
|
c_printf("foo:%g:%s:%d:%d\n", 0.03, "asdf", 103.cint, temp2)
|
||||||
|
|
||||||
|
block: # c_snprintf, c_malloc, c_free
|
||||||
|
let n: uint = 50
|
||||||
|
var buffer2: pointer = c_malloc(n)
|
||||||
|
var s: cstring = "foobar"
|
||||||
|
var age: cint = 25
|
||||||
|
let j = c_snprintf(buffer2, n, "s1:%s s2:%s age:%d pi:%g", s, s, age, 3.14)
|
||||||
|
c_printf("ret={%s}\n", buffer2)
|
||||||
|
c_free(buffer2) # not sure it has an effect
|
||||||
|
|
||||||
|
block: # c_printf bug
|
||||||
|
var a = 123
|
||||||
|
var a2 = a.addr
|
||||||
|
#[
|
||||||
|
bug: different behavior between CT RT in this case:
|
||||||
|
at CT, shows foo2:a=123
|
||||||
|
at RT, shows foo2:a=<address as int>
|
||||||
|
]#
|
||||||
|
if false:
|
||||||
|
c_printf("foo2:a=%d\n", a2)
|
||||||
|
|
||||||
|
static:
|
||||||
|
fun()
|
||||||
|
fun()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue