Skip to content

Commit 6ffaa23

Browse files
committed
next steps for FFI support
1 parent bd74576 commit 6ffaa23

File tree

9 files changed

+179
-63
lines changed

9 files changed

+179
-63
lines changed

compiler/commands.nim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,13 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
398398
if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg)
399399
of "cincludes":
400400
expectArg(switch, arg, pass, info)
401-
if pass in {passCmd2, passPP}: cIncludes.add arg
401+
if pass in {passCmd2, passPP}: cIncludes.add arg.processPath
402402
of "clibdir":
403403
expectArg(switch, arg, pass, info)
404-
if pass in {passCmd2, passPP}: cLibs.add arg
404+
if pass in {passCmd2, passPP}: cLibs.add arg.processPath
405405
of "clib":
406406
expectArg(switch, arg, pass, info)
407-
if pass in {passCmd2, passPP}: cLinkedLibs.add arg
407+
if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath
408408
of "header":
409409
headerFile = arg
410410
incl(gGlobalOptions, optGenIndex)

compiler/evalffi.nim

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,46 @@ proc callForeignFunction*(call: PNode): PNode =
441441
for i in 1 .. call.len-1:
442442
call.sons[i] = unpack(args[i-1], typ.sons[i], call[i])
443443
dealloc args[i-1]
444+
445+
proc callForeignFunction*(fn: PNode, fntyp: PType,
446+
args: var TNodeSeq, start, len: int,
447+
info: TLineInfo): PNode =
448+
internalAssert fn.kind == nkPtrLit
449+
450+
var cif: TCif
451+
var sig: TParamList
452+
for i in 0..len-1:
453+
var aTyp = args[i+start].typ
454+
if aTyp.isNil:
455+
internalAssert i+1 < fntyp.len
456+
aTyp = fntyp.sons[i+1]
457+
args[i+start].typ = aTyp
458+
sig[i] = mapType(aTyp)
459+
if sig[i].isNil: globalError(info, "cannot map FFI type")
460+
461+
if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len),
462+
mapType(fntyp.sons[0]), sig) != OK:
463+
globalError(info, "error in FFI call")
464+
465+
var cargs: TArgList
466+
let fn = cast[pointer](fn.intVal)
467+
for i in 0 .. len-1:
468+
let t = args[i+start].typ
469+
cargs[i] = alloc0(packSize(args[i+start], t))
470+
pack(args[i+start], t, cargs[i])
471+
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
472+
else: alloc(fntyp.sons[0].getSize.int)
473+
474+
libffi.call(cif, fn, retVal, cargs)
475+
476+
if retVal.isNil:
477+
result = emptyNode
478+
else:
479+
result = unpack(retVal, fntyp.sons[0], nil)
480+
result.info = info
481+
482+
if retVal != nil: dealloc retVal
483+
for i in 0 .. len-1:
484+
let t = args[i+start].typ
485+
args[i+start] = unpack(cargs[i], t, args[i+start])
486+
dealloc cargs[i]

compiler/nimrod.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ path:"$lib/packages/docutils"
1212

1313
define:booting
1414

15+
@if windows:
16+
cincludes: "$lib/wrappers/libffi/common"
17+
@end

compiler/vm.nim

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ import ast except getstr
1414

1515
import
1616
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
17-
parser, vmdeps, idents, trees, renderer
17+
parser, vmdeps, idents, trees, renderer, options
1818

1919
from semfold import leValueConv, ordinalValToString
2020

21+
when hasFFI:
22+
import evalffi
23+
2124
type
2225
PStackFrame* = ref TStackFrame
2326
TStackFrame* = object
@@ -608,24 +611,39 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
608611
let rc = instr.regC
609612
let isClosure = regs[rb].kind == nkPar
610613
let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym
611-
let newPc = compile(c, prc)
612-
#echo "new pc ", newPc, " calling: ", prc.name.s
613-
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
614-
newSeq(newFrame.slots, prc.position)
615-
if not isEmptyType(prc.typ.sons[0]):
616-
newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info)
617-
# pass every parameter by var (the language definition allows this):
618-
for i in 1 .. rc-1:
619-
newFrame.slots[i] = regs[rb+i]
620-
if isClosure:
621-
newFrame.slots[rc] = regs[rb].sons[1]
622-
# allocate the temporaries:
623-
for i in rc+ord(isClosure) .. <prc.position:
624-
newFrame.slots[i] = newNode(nkEmpty)
625-
tos = newFrame
626-
move(regs, newFrame.slots)
627-
# -1 for the following 'inc pc'
628-
pc = newPc-1
614+
if sfImportc in prc.flags:
615+
if allowFFI notin c.features:
616+
globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
617+
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep
618+
# 'regs' in a register:
619+
when hasFFI:
620+
let newValue = callForeignFunction(c.globals.sons[prc.position-1],
621+
prc.typ, tos.slots,
622+
rb+1, rc-1, c.debug[pc])
623+
if newValue.kind != nkEmpty:
624+
assert instr.opcode == opcIndCallAsgn
625+
regs[ra] = newValue
626+
else:
627+
globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
628+
else:
629+
let newPc = compile(c, prc)
630+
#echo "new pc ", newPc, " calling: ", prc.name.s
631+
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
632+
newSeq(newFrame.slots, prc.offset)
633+
if not isEmptyType(prc.typ.sons[0]):
634+
newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info)
635+
# pass every parameter by var (the language definition allows this):
636+
for i in 1 .. rc-1:
637+
newFrame.slots[i] = regs[rb+i]
638+
if isClosure:
639+
newFrame.slots[rc] = regs[rb].sons[1]
640+
# allocate the temporaries:
641+
for i in rc+ord(isClosure) .. <prc.offset:
642+
newFrame.slots[i] = newNode(nkEmpty)
643+
tos = newFrame
644+
move(regs, newFrame.slots)
645+
# -1 for the following 'inc pc'
646+
pc = newPc-1
629647
of opcTJmp:
630648
# jump Bx if A != 0
631649
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
@@ -1054,7 +1072,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
10541072
let start = genProc(c, sym)
10551073

10561074
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
1057-
let maxSlots = sym.position
1075+
let maxSlots = sym.offset
10581076
newSeq(tos.slots, maxSlots)
10591077
# setup arguments:
10601078
var L = n.safeLen

compiler/vmdef.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ type
175175
module*: PSym
176176
callsite*: PNode
177177
mode*: TEvalMode
178+
features*: TSandboxFlags
178179

179180
TPosition* = distinct int
180181

compiler/vmgen.nim

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212
import
1313
unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef,
14-
trees, intsets, rodread, magicsys
14+
trees, intsets, rodread, magicsys, options
15+
16+
when hasFFI:
17+
import evalffi
1518

1619
proc codeListing(c: PCtx, result: var string) =
1720
# first iteration: compute all necessary labels:
@@ -395,9 +398,14 @@ proc genReturn(c: PCtx; n: PNode) =
395398
proc genCall(c: PCtx; n: PNode; dest: var TDest) =
396399
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
397400
let x = c.getTempRange(n.len, slotTempUnknown)
398-
for i in 0.. <n.len:
401+
# varargs need 'opcSetType' for the FFI support:
402+
let fntyp = n.sons[0].typ
403+
for i in 0.. <n.len:
399404
var r: TRegister = x+i
400405
c.gen(n.sons[i], r)
406+
if i >= fntyp.len:
407+
internalAssert tfVarargs in fntyp.flags
408+
c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ))
401409
if dest < 0:
402410
c.gABC(n, opcIndCall, 0, x, n.len)
403411
else:
@@ -884,13 +892,26 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) =
884892
let lit = genLiteral(c, n)
885893
c.gABx(n, opc, dest, lit)
886894

895+
proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
896+
when hasFFI:
897+
if allowFFI in c.features:
898+
c.globals.add(importcSymbol(s))
899+
s.position = c.globals.len
900+
else:
901+
localError(info, errGenerated, "VM is not allowed to 'importc'")
902+
else:
903+
localError(info, errGenerated,
904+
"cannot 'importc' variable at compile time")
905+
887906
proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
888907
let s = n.sym
889908
if sfGlobal in s.flags:
890909
if dest < 0: dest = c.getTemp(s.typ)
891910
if s.position == 0:
892-
c.globals.add(s.ast)
893-
s.position = c.globals.len
911+
if sfImportc in s.flags: c.importcSym(n.info, s)
912+
else:
913+
c.globals.add(s.ast)
914+
s.position = c.globals.len
894915
# XXX var g = codeHere() ?
895916
c.gABx(n, opcLdGlobal, dest, s.position)
896917
else:
@@ -1003,9 +1024,11 @@ proc genVarSection(c: PCtx; n: PNode) =
10031024
let s = a.sons[0].sym
10041025
if sfGlobal in s.flags:
10051026
if s.position == 0:
1006-
let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast
1007-
c.globals.add(sa)
1008-
s.position = c.globals.len
1027+
if sfImportc in s.flags: c.importcSym(a.info, s)
1028+
else:
1029+
let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast
1030+
c.globals.add(sa)
1031+
s.position = c.globals.len
10091032
if a.sons[2].kind == nkEmpty:
10101033
when false:
10111034
withTemp(tmp, s.typ):
@@ -1103,6 +1126,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
11031126
of skVar, skForVar, skTemp, skLet, skParam, skResult:
11041127
genRdVar(c, n, dest)
11051128
of skProc, skConverter, skMacro, skMethod, skIterator:
1129+
if sfImportc in s.flags: c.importcSym(n.info, s)
11061130
genLit(c, n, dest)
11071131
of skConst:
11081132
gen(c, s.ast, dest)
@@ -1317,11 +1341,11 @@ proc genProc(c: PCtx; s: PSym): int =
13171341
c.patch(procStart)
13181342
c.gABC(body, opcEof, eofInstr.regA)
13191343
c.optimizeJumps(result)
1320-
s.position = c.prc.maxSlots
1344+
s.offset = c.prc.maxSlots
13211345
#if s.name.s == "innerProc":
13221346
# c.echoCode
13231347
# echo renderTree(body)
13241348
c.prc = oldPrc
13251349
else:
1326-
c.prc.maxSlots = s.position
1350+
c.prc.maxSlots = s.offset
13271351
result = x.intVal.int

config/nimrod.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ path="$lib/wrappers/readline"
2727
path="$lib/wrappers/sdl"
2828
path="$lib/wrappers/x11"
2929
path="$lib/wrappers/zip"
30+
path="$lib/wrappers/libffi"
3031
path="$lib/windows"
3132
path="$lib/posix"
3233
path="$lib/js"

lib/pure/strutils.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,8 +1020,8 @@ proc editDistance*(a, b: string): int {.noSideEffect,
10201020

10211021
# floating point formating:
10221022

1023-
proc c_sprintf(buf, frmt: CString) {.nodecl, importc: "sprintf", varargs,
1024-
noSideEffect.}
1023+
proc c_sprintf(buf, frmt: CString) {.header: "<stdio.h>", importc: "sprintf",
1024+
varargs, noSideEffect.}
10251025

10261026
type
10271027
TFloatFormat* = enum ## the different modes of floating point formating

lib/wrappers/libffi.nim renamed to lib/wrappers/libffi/libffi.nim

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,40 @@
2626

2727
{.deadCodeElim: on.}
2828

29-
when defined(windows):
30-
const libffidll* = "libffi.dll"
31-
elif defined(macosx):
32-
const libffidll* = "libffi.dylib"
33-
else:
34-
const libffidll* = "libffi.so"
29+
when defined(windows):
30+
# on Windows we don't use a DLL but instead embed libffi directly:
31+
{.pragma: mylib, header: r"ffi.h".}
32+
33+
{.compile: r"common\callproc.c".}
34+
{.compile: r"common\malloc_closure.c".}
35+
{.compile: r"common\raw_api.c".}
36+
when defined(vcc):
37+
#{.compile: "libffi_msvc\ffi.h".}
38+
#<ClInclude: "..\Modules\_ctypes\libffi_msvc\ffi_common.h".}
39+
#<ClInclude: "..\Modules\_ctypes\libffi_msvc\fficonfig.h".}
40+
#<ClInclude: "..\Modules\_ctypes\libffi_msvc\ffitarget.h".}
41+
{.compile: r"msvc\ffi.c".}
42+
{.compile: r"msvc\prep_cif.c".}
43+
{.compile: r"msvc\win32.c".}
44+
{.compile: r"msvc\types.c".}
45+
when defined(cpu64):
46+
{.compile: r"msvc\win64.asm".}
47+
else:
48+
{.compile: r"gcc\ffi.c".}
49+
{.compile: r"gcc\prep_cif.c".}
50+
{.compile: r"gcc\win32.c".}
51+
{.compile: r"gcc\types.c".}
52+
{.compile: r"gcc\closures.c".}
53+
when defined(cpu64):
54+
{.compile: r"gcc\ffi64.c".}
55+
{.compile: r"gcc\win64.S".}
56+
else:
57+
{.compile: r"gcc\win32.S".}
58+
59+
elif defined(macosx):
60+
{.pragma: mylib, dynlib: "libffi.dylib".}
61+
else:
62+
{.pragma: mylib, dynlib: "libffi.so".}
3563

3664
type
3765
TArg* = int
@@ -88,19 +116,19 @@ type
88116
elements*: ptr ptr TType
89117

90118
var
91-
type_void* {.importc: "ffi_type_void", dynlib: libffidll.}: TType
92-
type_uint8* {.importc: "ffi_type_uint8", dynlib: libffidll.}: TType
93-
type_sint8* {.importc: "ffi_type_sint8", dynlib: libffidll.}: TType
94-
type_uint16* {.importc: "ffi_type_uint16", dynlib: libffidll.}: TType
95-
type_sint16* {.importc: "ffi_type_sint16", dynlib: libffidll.}: TType
96-
type_uint32* {.importc: "ffi_type_uint32", dynlib: libffidll.}: TType
97-
type_sint32* {.importc: "ffi_type_sint32", dynlib: libffidll.}: TType
98-
type_uint64* {.importc: "ffi_type_uint64", dynlib: libffidll.}: TType
99-
type_sint64* {.importc: "ffi_type_sint64", dynlib: libffidll.}: TType
100-
type_float* {.importc: "ffi_type_float", dynlib: libffidll.}: TType
101-
type_double* {.importc: "ffi_type_double", dynlib: libffidll.}: TType
102-
type_pointer* {.importc: "ffi_type_pointer", dynlib: libffidll.}: TType
103-
type_longdouble* {.importc: "ffi_type_longdouble", dynlib: libffidll.}: TType
119+
type_void* {.importc: "ffi_type_void", mylib.}: TType
120+
type_uint8* {.importc: "ffi_type_uint8", mylib.}: TType
121+
type_sint8* {.importc: "ffi_type_sint8", mylib.}: TType
122+
type_uint16* {.importc: "ffi_type_uint16", mylib.}: TType
123+
type_sint16* {.importc: "ffi_type_sint16", mylib.}: TType
124+
type_uint32* {.importc: "ffi_type_uint32", mylib.}: TType
125+
type_sint32* {.importc: "ffi_type_sint32", mylib.}: TType
126+
type_uint64* {.importc: "ffi_type_uint64", mylib.}: TType
127+
type_sint64* {.importc: "ffi_type_sint64", mylib.}: TType
128+
type_float* {.importc: "ffi_type_float", mylib.}: TType
129+
type_double* {.importc: "ffi_type_double", mylib.}: TType
130+
type_pointer* {.importc: "ffi_type_pointer", mylib.}: TType
131+
type_longdouble* {.importc: "ffi_type_longdouble", mylib.}: TType
104132

105133
type
106134
Tstatus* {.size: sizeof(cint).} = enum
@@ -119,20 +147,18 @@ type
119147
sint*: TSArg
120148

121149
proc raw_call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
122-
avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call",
123-
dynlib: libffidll.}
150+
avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call", mylib.}
124151
proc ptrarray_to_raw*(cif: var Tcif; args: ptr pointer; raw: ptr TRaw) {.cdecl,
125-
importc: "ffi_ptrarray_to_raw", dynlib: libffidll.}
152+
importc: "ffi_ptrarray_to_raw", mylib.}
126153
proc raw_to_ptrarray*(cif: var Tcif; raw: ptr TRaw; args: ptr pointer) {.cdecl,
127-
importc: "ffi_raw_to_ptrarray", dynlib: libffidll.}
128-
proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size",
129-
dynlib: libffidll.}
154+
importc: "ffi_raw_to_ptrarray", mylib.}
155+
proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size", mylib.}
130156

131157
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
132158
atypes: ptr ptr TType): TStatus {.cdecl, importc: "ffi_prep_cif",
133-
dynlib: libffidll.}
159+
mylib.}
134160
proc call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
135-
avalue: ptr pointer) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
161+
avalue: ptr pointer) {.cdecl, importc: "ffi_call", mylib.}
136162

137163
# the same with an easier interface:
138164
type
@@ -141,9 +167,9 @@ type
141167

142168
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
143169
atypes: TParamList): TStatus {.cdecl, importc: "ffi_prep_cif",
144-
dynlib: libffidll.}
170+
mylib.}
145171
proc call*(cif: var Tcif; fn, rvalue: pointer;
146-
avalue: TArgList) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
172+
avalue: TArgList) {.cdecl, importc: "ffi_call", mylib.}
147173

148174
# Useful for eliminating compiler warnings
149175
##define FFI_FN(f) ((void (*)(void))f)

0 commit comments

Comments
 (0)