next steps for FFI at compile time
This commit is contained in:
parent
23b90eaa3a
commit
88a441ea8e
6 changed files with 215 additions and 182 deletions
|
|
@ -279,6 +279,13 @@ proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
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(x +! i * baseSize, typ.sons[1], result.sons[i])
|
||||||
|
|
||||||
|
proc canonNodeKind(k: TNodeKind): TNodeKind =
|
||||||
|
case k
|
||||||
|
of nkCharLit..nkUInt64Lit: result = nkIntLit
|
||||||
|
of nkFloatLit..nkFloat128Lit: result = nkFloatLit
|
||||||
|
of nkStrLit..nkTripleStrLit: result = nkStrLit
|
||||||
|
else: result = k
|
||||||
|
|
||||||
proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
template aw(k, v, field: expr) {.immediate, dirty.} =
|
template aw(k, v, field: expr) {.immediate, dirty.} =
|
||||||
if n.isNil:
|
if n.isNil:
|
||||||
|
|
@ -287,7 +294,8 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
# check we have the right field:
|
# check we have the right field:
|
||||||
result = n
|
result = n
|
||||||
if result.kind != k:
|
if result.kind.canonNodeKind != k.canonNodeKind:
|
||||||
|
echo "expected ", k, " but got ", result.kind
|
||||||
GlobalError(n.info, "cannot map value from FFI")
|
GlobalError(n.info, "cannot map value from FFI")
|
||||||
result.field = v
|
result.field = v
|
||||||
|
|
||||||
|
|
@ -309,15 +317,15 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
of tyBool: awi(nkIntLit, rd(bool, x).ord)
|
of tyBool: awi(nkIntLit, rd(bool, x).ord)
|
||||||
of tyChar: awi(nkIntLit, rd(char, x).ord)
|
of tyChar: awi(nkIntLit, rd(char, x).ord)
|
||||||
of tyInt: awi(nkIntLit, rd(int, x))
|
of tyInt: awi(nkIntLit, rd(int, x))
|
||||||
of tyInt8: awi(nkIntLit, rd(int8, x))
|
of tyInt8: awi(nkInt8Lit, rd(int8, x))
|
||||||
of tyInt16: awi(nkIntLit, rd(int16, x))
|
of tyInt16: awi(nkInt16Lit, rd(int16, x))
|
||||||
of tyInt32: awi(nkIntLit, rd(int32, x))
|
of tyInt32: awi(nkInt32Lit, rd(int32, x))
|
||||||
of tyInt64: awi(nkIntLit, rd(int64, x))
|
of tyInt64: awi(nkInt64Lit, rd(int64, x))
|
||||||
of tyUInt: awi(nkIntLit, rd(uint, x).biggestInt)
|
of tyUInt: awi(nkUIntLit, rd(uint, x).biggestInt)
|
||||||
of tyUInt8: awi(nkIntLit, rd(uint8, x).biggestInt)
|
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).biggestInt)
|
||||||
of tyUInt16: awi(nkIntLit, rd(uint16, x).biggestInt)
|
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).biggestInt)
|
||||||
of tyUInt32: awi(nkIntLit, rd(uint32, x).biggestInt)
|
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).biggestInt)
|
||||||
of tyUInt64: awi(nkIntLit, rd(uint64, x).biggestInt)
|
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).biggestInt)
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
case typ.getSize
|
case typ.getSize
|
||||||
of 1: awi(nkIntLit, rd(uint8, x).biggestInt)
|
of 1: awi(nkIntLit, rd(uint8, x).biggestInt)
|
||||||
|
|
@ -327,8 +335,8 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
GlobalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
|
GlobalError(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(nkFloatLit, rd(float32, x))
|
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
|
||||||
of tyFloat64: awf(nkFloatLit, rd(float64, x))
|
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
|
||||||
of tyPointer, tyProc:
|
of tyPointer, tyProc:
|
||||||
let p = rd(pointer, x)
|
let p = rd(pointer, x)
|
||||||
if p.isNil:
|
if p.isNil:
|
||||||
|
|
@ -339,7 +347,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
let p = rd(pointer, x)
|
let p = rd(pointer, x)
|
||||||
if p.isNil:
|
if p.isNil:
|
||||||
setNil()
|
setNil()
|
||||||
elif n != nil and n.kind == nkPtrLit:
|
elif n == nil or n.kind == nkPtrLit:
|
||||||
awi(nkPtrLit, cast[TAddress](p))
|
awi(nkPtrLit, cast[TAddress](p))
|
||||||
elif n != nil and n.len == 1:
|
elif n != nil and n.len == 1:
|
||||||
n.sons[0] = unpack(rd(pointer, x), typ.sons[0], n.sons[0])
|
n.sons[0] = unpack(rd(pointer, x), typ.sons[0], n.sons[0])
|
||||||
|
|
@ -363,6 +371,14 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||||
# 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(n.info, "cannot map value from FFI " & typeToString(typ))
|
||||||
|
|
||||||
|
proc fficast*(x: PNode, destTyp: PType): PNode =
|
||||||
|
# we play safe here and allocate the max possible size:
|
||||||
|
let allocSize = max(packSize(x, x.typ), packSize(x, destTyp))
|
||||||
|
var a = alloc0(allocSize)
|
||||||
|
pack(x, x.typ, a)
|
||||||
|
result = unpack(a, destTyp, nil)
|
||||||
|
dealloc a
|
||||||
|
|
||||||
proc callForeignFunction*(call: PNode): PNode =
|
proc callForeignFunction*(call: PNode): PNode =
|
||||||
InternalAssert call.sons[0].kind == nkPtrLit
|
InternalAssert call.sons[0].kind == nkPtrLit
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ proc newEvalContext*(module: PSym, filename: string,
|
||||||
new(result)
|
new(result)
|
||||||
result.module = module
|
result.module = module
|
||||||
result.mode = mode
|
result.mode = mode
|
||||||
|
result.features = {allowFFI}
|
||||||
initIdNodeTable(result.globals)
|
initIdNodeTable(result.globals)
|
||||||
|
|
||||||
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
|
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
|
||||||
|
|
@ -351,7 +352,7 @@ proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode =
|
||||||
result = copyTree(result)
|
result = copyTree(result)
|
||||||
else:
|
else:
|
||||||
when hasFFI:
|
when hasFFI:
|
||||||
if sfImportc in s.flags:
|
if sfImportc in s.flags and allowFFI in c.features:
|
||||||
result = importcSymbol(s)
|
result = importcSymbol(s)
|
||||||
IdNodeTablePut(c.globals, s, result)
|
IdNodeTablePut(c.globals, s, result)
|
||||||
return result
|
return result
|
||||||
|
|
@ -398,7 +399,7 @@ proc evalCall(c: PEvalContext, n: PNode): PNode =
|
||||||
if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info)
|
if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info)
|
||||||
|
|
||||||
when hasFFI:
|
when hasFFI:
|
||||||
if sfImportc in prc.sym.flags:
|
if sfImportc in prc.sym.flags and allowFFI in c.features:
|
||||||
var newCall = newNodeI(nkCall, n.info, n.len)
|
var newCall = newNodeI(nkCall, n.info, n.len)
|
||||||
newCall.sons[0] = evalGlobalVar(c, prc.sym, {})
|
newCall.sons[0] = evalGlobalVar(c, prc.sym, {})
|
||||||
for i in 1 .. <n.len:
|
for i in 1 .. <n.len:
|
||||||
|
|
@ -549,7 +550,8 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
of skConst: result = s.ast
|
of skConst: result = s.ast
|
||||||
of skEnumField: result = newIntNodeT(s.position, n)
|
of skEnumField: result = newIntNodeT(s.position, n)
|
||||||
else: result = nil
|
else: result = nil
|
||||||
const mask = when hasFFI: {sfForward} else: {sfImportc, sfForward}
|
let mask = if hasFFI and allowFFI in c.features: {sfForward}
|
||||||
|
else: {sfImportc, sfForward}
|
||||||
if result == nil or mask * s.flags != {}:
|
if result == nil or mask * s.flags != {}:
|
||||||
result = raiseCannotEval(c, n.info)
|
result = raiseCannotEval(c, n.info)
|
||||||
|
|
||||||
|
|
@ -650,8 +652,12 @@ proc evalConv(c: PEvalContext, n: PNode): PNode =
|
||||||
|
|
||||||
proc evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
proc evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
if allowCast in c.features:
|
if allowCast in c.features:
|
||||||
# XXX we need better checking here and the new pack/unpack stuff should
|
when hasFFI:
|
||||||
# be useful for some casts too:
|
result = evalAux(c, n.sons[1], {efLValue})
|
||||||
|
if isSpecial(result): return
|
||||||
|
InternalAssert result.typ != nil
|
||||||
|
result = fficast(result, n.typ)
|
||||||
|
else:
|
||||||
result = evalConv(c, n)
|
result = evalConv(c, n)
|
||||||
else:
|
else:
|
||||||
result = raiseCannotEval(c, n.info)
|
result = raiseCannotEval(c, n.info)
|
||||||
|
|
@ -1429,7 +1435,7 @@ proc eval*(c: PEvalContext, n: PNode): PNode =
|
||||||
if sonsLen(result) >= 1:
|
if sonsLen(result) >= 1:
|
||||||
stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ))
|
stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ))
|
||||||
else:
|
else:
|
||||||
stackTrace(c, n, errCannotInterpretNodeX, renderTree(n))
|
stackTrace(c, result, errCannotInterpretNodeX, renderTree(n))
|
||||||
|
|
||||||
proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode =
|
proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode =
|
||||||
var p = newEvalContext(module, "", mode)
|
var p = newEvalContext(module, "", mode)
|
||||||
|
|
@ -1474,6 +1480,7 @@ proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode =
|
||||||
|
|
||||||
proc myOpen(module: PSym, filename: string): PPassContext =
|
proc myOpen(module: PSym, filename: string): PPassContext =
|
||||||
var c = newEvalContext(module, filename, emRepl)
|
var c = newEvalContext(module, filename, emRepl)
|
||||||
|
c.features = {allowCast, allowFFI, allowInfiniteLoops}
|
||||||
pushStackFrame(c, newStackFrame())
|
pushStackFrame(c, newStackFrame())
|
||||||
result = c
|
result = c
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,6 @@ proc CommandCompileToEcmaScript =
|
||||||
|
|
||||||
proc CommandInteractive =
|
proc CommandInteractive =
|
||||||
msgs.gErrorMax = high(int) # do not stop after first error
|
msgs.gErrorMax = high(int) # do not stop after first error
|
||||||
incl(gGlobalOptions, optSafeCode)
|
|
||||||
#setTarget(osNimrodVM, cpuNimrodVM)
|
#setTarget(osNimrodVM, cpuNimrodVM)
|
||||||
initDefines()
|
initDefines()
|
||||||
DefineSymbol("nimrodvm")
|
DefineSymbol("nimrodvm")
|
||||||
|
|
|
||||||
|
|
@ -1547,6 +1547,7 @@ when false:
|
||||||
|
|
||||||
# ----------------- GC interface ---------------------------------------------
|
# ----------------- GC interface ---------------------------------------------
|
||||||
|
|
||||||
|
when not defined(nimrodVM):
|
||||||
proc GC_disable*() {.rtl, inl.}
|
proc GC_disable*() {.rtl, inl.}
|
||||||
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
|
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
|
||||||
## reactivate the GC. Note that in most circumstances one should only disable
|
## reactivate the GC. Note that in most circumstances one should only disable
|
||||||
|
|
@ -1707,9 +1708,10 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
|
||||||
## get type information for `x`. Ordinary code should not use this, but
|
## get type information for `x`. Ordinary code should not use this, but
|
||||||
## the `typeinfo` module instead.
|
## the `typeinfo` module instead.
|
||||||
|
|
||||||
when not defined(EcmaScript) and not defined(NimrodVM):
|
when not defined(EcmaScript): #and not defined(NimrodVM):
|
||||||
{.push stack_trace: off, profiler:off.}
|
{.push stack_trace: off, profiler:off.}
|
||||||
|
|
||||||
|
when not defined(NimrodVM):
|
||||||
proc initGC()
|
proc initGC()
|
||||||
when not defined(boehmgc) and not defined(useMalloc):
|
when not defined(boehmgc) and not defined(useMalloc):
|
||||||
proc initAllocator() {.inline.}
|
proc initAllocator() {.inline.}
|
||||||
|
|
@ -1736,6 +1738,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
result = int(c_strcmp(x, y))
|
result = int(c_strcmp(x, y))
|
||||||
|
|
||||||
const pccHack = if defined(pcc): "_" else: "" # Hack for PCC
|
const pccHack = if defined(pcc): "_" else: "" # Hack for PCC
|
||||||
|
when not defined(NimrodVM):
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
# work-around C's sucking abstraction:
|
# work-around C's sucking abstraction:
|
||||||
# BUGFIX: stdin and stdout should be binary files!
|
# BUGFIX: stdin and stdout should be binary files!
|
||||||
|
|
@ -1957,6 +1960,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
inc(i)
|
inc(i)
|
||||||
dealloc(a)
|
dealloc(a)
|
||||||
|
|
||||||
|
when not defined(NimrodVM):
|
||||||
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
|
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
|
||||||
## atomic increment of `memLoc`. Returns the value after the operation.
|
## atomic increment of `memLoc`. Returns the value after the operation.
|
||||||
|
|
||||||
|
|
@ -1979,10 +1983,11 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
include "system/syslocks"
|
include "system/syslocks"
|
||||||
include "system/threads"
|
include "system/threads"
|
||||||
elif not defined(nogc):
|
elif not defined(nogc) and not defined(NimrodVM):
|
||||||
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
|
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
|
||||||
initGC()
|
initGC()
|
||||||
|
|
||||||
|
when not defined(NimrodVM):
|
||||||
proc setControlCHook*(hook: proc () {.noconv.})
|
proc setControlCHook*(hook: proc () {.noconv.})
|
||||||
## allows you to override the behaviour of your application when CTRL+C
|
## allows you to override the behaviour of your application when CTRL+C
|
||||||
## is pressed. Only one such hook is supported.
|
## is pressed. Only one such hook is supported.
|
||||||
|
|
@ -2010,7 +2015,9 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
{.pop.} # stack trace
|
{.pop.} # stack trace
|
||||||
{.pop.} # stack trace
|
{.pop.} # stack trace
|
||||||
|
|
||||||
when hostOS != "standalone": include "system/dyncalls"
|
when hostOS != "standalone" and not defined(NimrodVM):
|
||||||
|
include "system/dyncalls"
|
||||||
|
when not defined(NimrodVM):
|
||||||
include "system/sets"
|
include "system/sets"
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -2044,6 +2051,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
include "system/sysio"
|
include "system/sysio"
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
include "system/channels"
|
include "system/channels"
|
||||||
|
else:
|
||||||
|
include "system/sysio"
|
||||||
|
|
||||||
iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} =
|
iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} =
|
||||||
## Iterate over any line in the file named `filename`.
|
## Iterate over any line in the file named `filename`.
|
||||||
|
|
@ -2058,7 +2067,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
var res = TaintedString(newStringOfCap(80))
|
var res = TaintedString(newStringOfCap(80))
|
||||||
while f.readLine(res): yield TaintedString(res)
|
while f.readLine(res): yield TaintedString(res)
|
||||||
|
|
||||||
when hostOS != "standalone":
|
when hostOS != "standalone" and not defined(NimrodVM):
|
||||||
include "system/assign"
|
include "system/assign"
|
||||||
include "system/repr"
|
include "system/repr"
|
||||||
|
|
||||||
|
|
@ -2083,13 +2092,14 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
excHandler.raiseAction = action
|
excHandler.raiseAction = action
|
||||||
|
|
||||||
{.push stack_trace: off, profiler:off.}
|
{.push stack_trace: off, profiler:off.}
|
||||||
when defined(endb):
|
when defined(endb) and not defined(NimrodVM):
|
||||||
include "system/debugger"
|
include "system/debugger"
|
||||||
|
|
||||||
when defined(profiler) or defined(memProfiler):
|
when defined(profiler) or defined(memProfiler):
|
||||||
include "system/profiler"
|
include "system/profiler"
|
||||||
{.pop.} # stacktrace
|
{.pop.} # stacktrace
|
||||||
|
|
||||||
|
when not defined(NimrodVM):
|
||||||
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
||||||
## can be used to mark a condition to be likely. This is a hint for the
|
## can be used to mark a condition to be likely. This is a hint for the
|
||||||
## optimizer.
|
## optimizer.
|
||||||
|
|
@ -2118,7 +2128,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
`result` = *((NI*) `x`.ClEnv) < 0;
|
`result` = *((NI*) `x`.ClEnv) < 0;
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
elif defined(ecmaScript) or defined(NimrodVM):
|
elif defined(ecmaScript):
|
||||||
# Stubs:
|
# Stubs:
|
||||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
|
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ elif defined(macosx):
|
||||||
ImageLibName = "libSDL_image-1.2.0.dylib"
|
ImageLibName = "libSDL_image-1.2.0.dylib"
|
||||||
else:
|
else:
|
||||||
const
|
const
|
||||||
ImageLibName = "libSDL_image.so"
|
ImageLibName = "libSDL_image(.so|-1.2.so.0)"
|
||||||
const
|
const
|
||||||
IMAGE_MAJOR_VERSION* = 1
|
IMAGE_MAJOR_VERSION* = 1
|
||||||
IMAGE_MINOR_VERSION* = 2
|
IMAGE_MINOR_VERSION* = 2
|
||||||
|
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -27,6 +27,7 @@ version 0.9.X
|
||||||
- optimize genericAssign in the code generator
|
- optimize genericAssign in the code generator
|
||||||
- better support for macros that rewrite procs
|
- better support for macros that rewrite procs
|
||||||
- macros need access to types and symbols (partially implemented)
|
- macros need access to types and symbols (partially implemented)
|
||||||
|
- result = result shr 8 for the "system()" wrapper
|
||||||
|
|
||||||
- rethink the syntax/grammar:
|
- rethink the syntax/grammar:
|
||||||
* parser is not strict enough with newlines
|
* parser is not strict enough with newlines
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue