next steps for FFI support

This commit is contained in:
Araq 2013-11-25 13:04:11 +01:00
commit 9035d15ed2
9 changed files with 179 additions and 63 deletions

View file

@ -398,13 +398,13 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg) if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg)
of "cincludes": of "cincludes":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: cIncludes.add arg if pass in {passCmd2, passPP}: cIncludes.add arg.processPath
of "clibdir": of "clibdir":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: cLibs.add arg if pass in {passCmd2, passPP}: cLibs.add arg.processPath
of "clib": of "clib":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: cLinkedLibs.add arg if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath
of "header": of "header":
headerFile = arg headerFile = arg
incl(gGlobalOptions, optGenIndex) incl(gGlobalOptions, optGenIndex)

View file

@ -441,3 +441,46 @@ proc callForeignFunction*(call: PNode): PNode =
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(args[i-1], typ.sons[i], call[i])
dealloc args[i-1] dealloc args[i-1]
proc callForeignFunction*(fn: PNode, fntyp: PType,
args: var TNodeSeq, start, len: int,
info: TLineInfo): PNode =
internalAssert fn.kind == nkPtrLit
var cif: TCif
var sig: TParamList
for i in 0..len-1:
var aTyp = args[i+start].typ
if aTyp.isNil:
internalAssert i+1 < fntyp.len
aTyp = fntyp.sons[i+1]
args[i+start].typ = aTyp
sig[i] = mapType(aTyp)
if sig[i].isNil: globalError(info, "cannot map FFI type")
if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len),
mapType(fntyp.sons[0]), sig) != OK:
globalError(info, "error in FFI call")
var cargs: TArgList
let fn = cast[pointer](fn.intVal)
for i in 0 .. len-1:
let t = args[i+start].typ
cargs[i] = alloc0(packSize(args[i+start], t))
pack(args[i+start], t, cargs[i])
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
else: alloc(fntyp.sons[0].getSize.int)
libffi.call(cif, fn, retVal, cargs)
if retVal.isNil:
result = emptyNode
else:
result = unpack(retVal, fntyp.sons[0], nil)
result.info = info
if retVal != nil: dealloc retVal
for i in 0 .. len-1:
let t = args[i+start].typ
args[i+start] = unpack(cargs[i], t, args[i+start])
dealloc cargs[i]

View file

@ -12,3 +12,6 @@ path:"$lib/packages/docutils"
define:booting define:booting
@if windows:
cincludes: "$lib/wrappers/libffi/common"
@end

View file

@ -14,10 +14,13 @@ import ast except getstr
import import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
parser, vmdeps, idents, trees, renderer parser, vmdeps, idents, trees, renderer, options
from semfold import leValueConv, ordinalValToString from semfold import leValueConv, ordinalValToString
when hasFFI:
import evalffi
type type
PStackFrame* = ref TStackFrame PStackFrame* = ref TStackFrame
TStackFrame* = object TStackFrame* = object
@ -608,10 +611,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
let rc = instr.regC let rc = instr.regC
let isClosure = regs[rb].kind == nkPar let isClosure = regs[rb].kind == nkPar
let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym
if sfImportc in prc.flags:
if allowFFI notin c.features:
globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep
# 'regs' in a register:
when hasFFI:
let newValue = callForeignFunction(c.globals.sons[prc.position-1],
prc.typ, tos.slots,
rb+1, rc-1, c.debug[pc])
if newValue.kind != nkEmpty:
assert instr.opcode == opcIndCallAsgn
regs[ra] = newValue
else:
globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
else:
let newPc = compile(c, prc) let newPc = compile(c, prc)
#echo "new pc ", newPc, " calling: ", prc.name.s #echo "new pc ", newPc, " calling: ", prc.name.s
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
newSeq(newFrame.slots, prc.position) newSeq(newFrame.slots, prc.offset)
if not isEmptyType(prc.typ.sons[0]): if not isEmptyType(prc.typ.sons[0]):
newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info)
# pass every parameter by var (the language definition allows this): # pass every parameter by var (the language definition allows this):
@ -620,7 +638,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
if isClosure: if isClosure:
newFrame.slots[rc] = regs[rb].sons[1] newFrame.slots[rc] = regs[rb].sons[1]
# allocate the temporaries: # allocate the temporaries:
for i in rc+ord(isClosure) .. <prc.position: for i in rc+ord(isClosure) .. <prc.offset:
newFrame.slots[i] = newNode(nkEmpty) newFrame.slots[i] = newNode(nkEmpty)
tos = newFrame tos = newFrame
move(regs, newFrame.slots) move(regs, newFrame.slots)
@ -1054,7 +1072,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
let start = genProc(c, sym) let start = genProc(c, sym)
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
let maxSlots = sym.position let maxSlots = sym.offset
newSeq(tos.slots, maxSlots) newSeq(tos.slots, maxSlots)
# setup arguments: # setup arguments:
var L = n.safeLen var L = n.safeLen

View file

@ -175,6 +175,7 @@ type
module*: PSym module*: PSym
callsite*: PNode callsite*: PNode
mode*: TEvalMode mode*: TEvalMode
features*: TSandboxFlags
TPosition* = distinct int TPosition* = distinct int

View file

@ -11,7 +11,10 @@
import import
unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef,
trees, intsets, rodread, magicsys trees, intsets, rodread, magicsys, options
when hasFFI:
import evalffi
proc codeListing(c: PCtx, result: var string) = proc codeListing(c: PCtx, result: var string) =
# first iteration: compute all necessary labels: # first iteration: compute all necessary labels:
@ -395,9 +398,14 @@ proc genReturn(c: PCtx; n: PNode) =
proc genCall(c: PCtx; n: PNode; dest: var TDest) = proc genCall(c: PCtx; n: PNode; dest: var TDest) =
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
let x = c.getTempRange(n.len, slotTempUnknown) let x = c.getTempRange(n.len, slotTempUnknown)
# varargs need 'opcSetType' for the FFI support:
let fntyp = n.sons[0].typ
for i in 0.. <n.len: for i in 0.. <n.len:
var r: TRegister = x+i var r: TRegister = x+i
c.gen(n.sons[i], r) c.gen(n.sons[i], r)
if i >= fntyp.len:
internalAssert tfVarargs in fntyp.flags
c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ))
if dest < 0: if dest < 0:
c.gABC(n, opcIndCall, 0, x, n.len) c.gABC(n, opcIndCall, 0, x, n.len)
else: else:
@ -884,11 +892,24 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) =
let lit = genLiteral(c, n) let lit = genLiteral(c, n)
c.gABx(n, opc, dest, lit) c.gABx(n, opc, dest, lit)
proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
when hasFFI:
if allowFFI in c.features:
c.globals.add(importcSymbol(s))
s.position = c.globals.len
else:
localError(info, errGenerated, "VM is not allowed to 'importc'")
else:
localError(info, errGenerated,
"cannot 'importc' variable at compile time")
proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
let s = n.sym let s = n.sym
if sfGlobal in s.flags: if sfGlobal in s.flags:
if dest < 0: dest = c.getTemp(s.typ) if dest < 0: dest = c.getTemp(s.typ)
if s.position == 0: if s.position == 0:
if sfImportc in s.flags: c.importcSym(n.info, s)
else:
c.globals.add(s.ast) c.globals.add(s.ast)
s.position = c.globals.len s.position = c.globals.len
# XXX var g = codeHere() ? # XXX var g = codeHere() ?
@ -1003,6 +1024,8 @@ proc genVarSection(c: PCtx; n: PNode) =
let s = a.sons[0].sym let s = a.sons[0].sym
if sfGlobal in s.flags: if sfGlobal in s.flags:
if s.position == 0: if s.position == 0:
if sfImportc in s.flags: c.importcSym(a.info, s)
else:
let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast
c.globals.add(sa) c.globals.add(sa)
s.position = c.globals.len s.position = c.globals.len
@ -1103,6 +1126,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
of skVar, skForVar, skTemp, skLet, skParam, skResult: of skVar, skForVar, skTemp, skLet, skParam, skResult:
genRdVar(c, n, dest) genRdVar(c, n, dest)
of skProc, skConverter, skMacro, skMethod, skIterator: of skProc, skConverter, skMacro, skMethod, skIterator:
if sfImportc in s.flags: c.importcSym(n.info, s)
genLit(c, n, dest) genLit(c, n, dest)
of skConst: of skConst:
gen(c, s.ast, dest) gen(c, s.ast, dest)
@ -1317,11 +1341,11 @@ proc genProc(c: PCtx; s: PSym): int =
c.patch(procStart) c.patch(procStart)
c.gABC(body, opcEof, eofInstr.regA) c.gABC(body, opcEof, eofInstr.regA)
c.optimizeJumps(result) c.optimizeJumps(result)
s.position = c.prc.maxSlots s.offset = c.prc.maxSlots
#if s.name.s == "innerProc": #if s.name.s == "innerProc":
# c.echoCode # c.echoCode
# echo renderTree(body) # echo renderTree(body)
c.prc = oldPrc c.prc = oldPrc
else: else:
c.prc.maxSlots = s.position c.prc.maxSlots = s.offset
result = x.intVal.int result = x.intVal.int

View file

@ -27,6 +27,7 @@ path="$lib/wrappers/readline"
path="$lib/wrappers/sdl" path="$lib/wrappers/sdl"
path="$lib/wrappers/x11" path="$lib/wrappers/x11"
path="$lib/wrappers/zip" path="$lib/wrappers/zip"
path="$lib/wrappers/libffi"
path="$lib/windows" path="$lib/windows"
path="$lib/posix" path="$lib/posix"
path="$lib/js" path="$lib/js"

View file

@ -1020,8 +1020,8 @@ proc editDistance*(a, b: string): int {.noSideEffect,
# floating point formating: # floating point formating:
proc c_sprintf(buf, frmt: CString) {.nodecl, importc: "sprintf", varargs, proc c_sprintf(buf, frmt: CString) {.header: "<stdio.h>", importc: "sprintf",
noSideEffect.} varargs, noSideEffect.}
type type
TFloatFormat* = enum ## the different modes of floating point formating TFloatFormat* = enum ## the different modes of floating point formating

View file

@ -27,11 +27,39 @@
{.deadCodeElim: on.} {.deadCodeElim: on.}
when defined(windows): when defined(windows):
const libffidll* = "libffi.dll" # on Windows we don't use a DLL but instead embed libffi directly:
{.pragma: mylib, header: r"ffi.h".}
{.compile: r"common\callproc.c".}
{.compile: r"common\malloc_closure.c".}
{.compile: r"common\raw_api.c".}
when defined(vcc):
#{.compile: "libffi_msvc\ffi.h".}
#<ClInclude: "..\Modules\_ctypes\libffi_msvc\ffi_common.h".}
#<ClInclude: "..\Modules\_ctypes\libffi_msvc\fficonfig.h".}
#<ClInclude: "..\Modules\_ctypes\libffi_msvc\ffitarget.h".}
{.compile: r"msvc\ffi.c".}
{.compile: r"msvc\prep_cif.c".}
{.compile: r"msvc\win32.c".}
{.compile: r"msvc\types.c".}
when defined(cpu64):
{.compile: r"msvc\win64.asm".}
else:
{.compile: r"gcc\ffi.c".}
{.compile: r"gcc\prep_cif.c".}
{.compile: r"gcc\win32.c".}
{.compile: r"gcc\types.c".}
{.compile: r"gcc\closures.c".}
when defined(cpu64):
{.compile: r"gcc\ffi64.c".}
{.compile: r"gcc\win64.S".}
else:
{.compile: r"gcc\win32.S".}
elif defined(macosx): elif defined(macosx):
const libffidll* = "libffi.dylib" {.pragma: mylib, dynlib: "libffi.dylib".}
else: else:
const libffidll* = "libffi.so" {.pragma: mylib, dynlib: "libffi.so".}
type type
TArg* = int TArg* = int
@ -88,19 +116,19 @@ type
elements*: ptr ptr TType elements*: ptr ptr TType
var var
type_void* {.importc: "ffi_type_void", dynlib: libffidll.}: TType type_void* {.importc: "ffi_type_void", mylib.}: TType
type_uint8* {.importc: "ffi_type_uint8", dynlib: libffidll.}: TType type_uint8* {.importc: "ffi_type_uint8", mylib.}: TType
type_sint8* {.importc: "ffi_type_sint8", dynlib: libffidll.}: TType type_sint8* {.importc: "ffi_type_sint8", mylib.}: TType
type_uint16* {.importc: "ffi_type_uint16", dynlib: libffidll.}: TType type_uint16* {.importc: "ffi_type_uint16", mylib.}: TType
type_sint16* {.importc: "ffi_type_sint16", dynlib: libffidll.}: TType type_sint16* {.importc: "ffi_type_sint16", mylib.}: TType
type_uint32* {.importc: "ffi_type_uint32", dynlib: libffidll.}: TType type_uint32* {.importc: "ffi_type_uint32", mylib.}: TType
type_sint32* {.importc: "ffi_type_sint32", dynlib: libffidll.}: TType type_sint32* {.importc: "ffi_type_sint32", mylib.}: TType
type_uint64* {.importc: "ffi_type_uint64", dynlib: libffidll.}: TType type_uint64* {.importc: "ffi_type_uint64", mylib.}: TType
type_sint64* {.importc: "ffi_type_sint64", dynlib: libffidll.}: TType type_sint64* {.importc: "ffi_type_sint64", mylib.}: TType
type_float* {.importc: "ffi_type_float", dynlib: libffidll.}: TType type_float* {.importc: "ffi_type_float", mylib.}: TType
type_double* {.importc: "ffi_type_double", dynlib: libffidll.}: TType type_double* {.importc: "ffi_type_double", mylib.}: TType
type_pointer* {.importc: "ffi_type_pointer", dynlib: libffidll.}: TType type_pointer* {.importc: "ffi_type_pointer", mylib.}: TType
type_longdouble* {.importc: "ffi_type_longdouble", dynlib: libffidll.}: TType type_longdouble* {.importc: "ffi_type_longdouble", mylib.}: TType
type type
Tstatus* {.size: sizeof(cint).} = enum Tstatus* {.size: sizeof(cint).} = enum
@ -119,20 +147,18 @@ type
sint*: TSArg sint*: TSArg
proc raw_call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer; proc raw_call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call", avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call", mylib.}
dynlib: libffidll.}
proc ptrarray_to_raw*(cif: var Tcif; args: ptr pointer; raw: ptr TRaw) {.cdecl, proc ptrarray_to_raw*(cif: var Tcif; args: ptr pointer; raw: ptr TRaw) {.cdecl,
importc: "ffi_ptrarray_to_raw", dynlib: libffidll.} importc: "ffi_ptrarray_to_raw", mylib.}
proc raw_to_ptrarray*(cif: var Tcif; raw: ptr TRaw; args: ptr pointer) {.cdecl, proc raw_to_ptrarray*(cif: var Tcif; raw: ptr TRaw; args: ptr pointer) {.cdecl,
importc: "ffi_raw_to_ptrarray", dynlib: libffidll.} importc: "ffi_raw_to_ptrarray", mylib.}
proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size", proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size", mylib.}
dynlib: libffidll.}
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType; proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
atypes: ptr ptr TType): TStatus {.cdecl, importc: "ffi_prep_cif", atypes: ptr ptr TType): TStatus {.cdecl, importc: "ffi_prep_cif",
dynlib: libffidll.} mylib.}
proc call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer; proc call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
avalue: ptr pointer) {.cdecl, importc: "ffi_call", dynlib: libffidll.} avalue: ptr pointer) {.cdecl, importc: "ffi_call", mylib.}
# the same with an easier interface: # the same with an easier interface:
type type
@ -141,9 +167,9 @@ type
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType; proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
atypes: TParamList): TStatus {.cdecl, importc: "ffi_prep_cif", atypes: TParamList): TStatus {.cdecl, importc: "ffi_prep_cif",
dynlib: libffidll.} mylib.}
proc call*(cif: var Tcif; fn, rvalue: pointer; proc call*(cif: var Tcif; fn, rvalue: pointer;
avalue: TArgList) {.cdecl, importc: "ffi_call", dynlib: libffidll.} avalue: TArgList) {.cdecl, importc: "ffi_call", mylib.}
# Useful for eliminating compiler warnings # Useful for eliminating compiler warnings
##define FFI_FN(f) ((void (*)(void))f) ##define FFI_FN(f) ((void (*)(void))f)