diff options
-rw-r--r-- | compiler/commands.nim | 6 | ||||
-rw-r--r-- | compiler/evalffi.nim | 43 | ||||
-rw-r--r-- | compiler/nimrod.cfg | 3 | ||||
-rw-r--r-- | compiler/vm.nim | 58 | ||||
-rw-r--r-- | compiler/vmdef.nim | 1 | ||||
-rw-r--r-- | compiler/vmgen.nim | 42 | ||||
-rw-r--r-- | config/nimrod.cfg | 1 | ||||
-rw-r--r-- | lib/pure/strutils.nim | 4 | ||||
-rw-r--r-- | lib/wrappers/libffi/libffi.nim (renamed from lib/wrappers/libffi.nim) | 84 |
9 files changed, 179 insertions, 63 deletions
diff --git a/compiler/commands.nim b/compiler/commands.nim index d3266930b..a02728dac 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -398,13 +398,13 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg) of "cincludes": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cIncludes.add arg + if pass in {passCmd2, passPP}: cIncludes.add arg.processPath of "clibdir": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLibs.add arg + if pass in {passCmd2, passPP}: cLibs.add arg.processPath of "clib": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLinkedLibs.add arg + if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath of "header": headerFile = arg incl(gGlobalOptions, optGenIndex) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 21a131996..92b79f9b6 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -441,3 +441,46 @@ proc callForeignFunction*(call: PNode): PNode = for i in 1 .. call.len-1: call.sons[i] = unpack(args[i-1], typ.sons[i], call[i]) 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] diff --git a/compiler/nimrod.cfg b/compiler/nimrod.cfg index ac8f732f1..0affe86d9 100644 --- a/compiler/nimrod.cfg +++ b/compiler/nimrod.cfg @@ -12,3 +12,6 @@ path:"$lib/packages/docutils" define:booting +@if windows: + cincludes: "$lib/wrappers/libffi/common" +@end diff --git a/compiler/vm.nim b/compiler/vm.nim index 1596e4220..019397bbc 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -14,10 +14,13 @@ import ast except getstr import 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 +when hasFFI: + import evalffi + type PStackFrame* = ref TStackFrame TStackFrame* = object @@ -608,24 +611,39 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = let rc = instr.regC let isClosure = regs[rb].kind == nkPar let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym - let newPc = compile(c, prc) - #echo "new pc ", newPc, " calling: ", prc.name.s - var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) - newSeq(newFrame.slots, prc.position) - if not isEmptyType(prc.typ.sons[0]): - newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) - # pass every parameter by var (the language definition allows this): - for i in 1 .. rc-1: - newFrame.slots[i] = regs[rb+i] - if isClosure: - newFrame.slots[rc] = regs[rb].sons[1] - # allocate the temporaries: - for i in rc+ord(isClosure) .. <prc.position: - newFrame.slots[i] = newNode(nkEmpty) - tos = newFrame - move(regs, newFrame.slots) - # -1 for the following 'inc pc' - pc = newPc-1 + 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) + #echo "new pc ", newPc, " calling: ", prc.name.s + var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) + newSeq(newFrame.slots, prc.offset) + if not isEmptyType(prc.typ.sons[0]): + newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) + # pass every parameter by var (the language definition allows this): + for i in 1 .. rc-1: + newFrame.slots[i] = regs[rb+i] + if isClosure: + newFrame.slots[rc] = regs[rb].sons[1] + # allocate the temporaries: + for i in rc+ord(isClosure) .. <prc.offset: + newFrame.slots[i] = newNode(nkEmpty) + tos = newFrame + move(regs, newFrame.slots) + # -1 for the following 'inc pc' + pc = newPc-1 of opcTJmp: # jump Bx if A != 0 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 = let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) - let maxSlots = sym.position + let maxSlots = sym.offset newSeq(tos.slots, maxSlots) # setup arguments: var L = n.safeLen diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 16ba1ed8e..15ea9767b 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -175,6 +175,7 @@ type module*: PSym callsite*: PNode mode*: TEvalMode + features*: TSandboxFlags TPosition* = distinct int diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 332184e0d..46edad2cd 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -11,7 +11,10 @@ import 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) = # 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) = if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) let x = c.getTempRange(n.len, slotTempUnknown) - for i in 0.. <n.len: + # varargs need 'opcSetType' for the FFI support: + let fntyp = n.sons[0].typ + for i in 0.. <n.len: var r: TRegister = x+i 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: c.gABC(n, opcIndCall, 0, x, n.len) else: @@ -884,13 +892,26 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) = let lit = genLiteral(c, n) 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) = let s = n.sym if sfGlobal in s.flags: if dest < 0: dest = c.getTemp(s.typ) if s.position == 0: - c.globals.add(s.ast) - s.position = c.globals.len + if sfImportc in s.flags: c.importcSym(n.info, s) + else: + c.globals.add(s.ast) + s.position = c.globals.len # XXX var g = codeHere() ? c.gABx(n, opcLdGlobal, dest, s.position) else: @@ -1003,9 +1024,11 @@ proc genVarSection(c: PCtx; n: PNode) = let s = a.sons[0].sym if sfGlobal in s.flags: if s.position == 0: - let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast - c.globals.add(sa) - s.position = c.globals.len + 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 + c.globals.add(sa) + s.position = c.globals.len if a.sons[2].kind == nkEmpty: when false: withTemp(tmp, s.typ): @@ -1103,6 +1126,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) = of skVar, skForVar, skTemp, skLet, skParam, skResult: genRdVar(c, n, dest) of skProc, skConverter, skMacro, skMethod, skIterator: + if sfImportc in s.flags: c.importcSym(n.info, s) genLit(c, n, dest) of skConst: gen(c, s.ast, dest) @@ -1317,11 +1341,11 @@ proc genProc(c: PCtx; s: PSym): int = c.patch(procStart) c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) - s.position = c.prc.maxSlots + s.offset = c.prc.maxSlots #if s.name.s == "innerProc": # c.echoCode # echo renderTree(body) c.prc = oldPrc else: - c.prc.maxSlots = s.position + c.prc.maxSlots = s.offset result = x.intVal.int diff --git a/config/nimrod.cfg b/config/nimrod.cfg index 77cc742b2..fbbf8efbb 100644 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -27,6 +27,7 @@ path="$lib/wrappers/readline" path="$lib/wrappers/sdl" path="$lib/wrappers/x11" path="$lib/wrappers/zip" +path="$lib/wrappers/libffi" path="$lib/windows" path="$lib/posix" path="$lib/js" diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 98d1f16d2..79525aa18 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1020,8 +1020,8 @@ proc editDistance*(a, b: string): int {.noSideEffect, # floating point formating: -proc c_sprintf(buf, frmt: CString) {.nodecl, importc: "sprintf", varargs, - noSideEffect.} +proc c_sprintf(buf, frmt: CString) {.header: "<stdio.h>", importc: "sprintf", + varargs, noSideEffect.} type TFloatFormat* = enum ## the different modes of floating point formating diff --git a/lib/wrappers/libffi.nim b/lib/wrappers/libffi/libffi.nim index 514ce024f..5ce9cc2e2 100644 --- a/lib/wrappers/libffi.nim +++ b/lib/wrappers/libffi/libffi.nim @@ -26,12 +26,40 @@ {.deadCodeElim: on.} -when defined(windows): - const libffidll* = "libffi.dll" -elif defined(macosx): - const libffidll* = "libffi.dylib" -else: - const libffidll* = "libffi.so" +when defined(windows): + # 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): + {.pragma: mylib, dynlib: "libffi.dylib".} +else: + {.pragma: mylib, dynlib: "libffi.so".} type TArg* = int @@ -88,19 +116,19 @@ type elements*: ptr ptr TType var - type_void* {.importc: "ffi_type_void", dynlib: libffidll.}: TType - type_uint8* {.importc: "ffi_type_uint8", dynlib: libffidll.}: TType - type_sint8* {.importc: "ffi_type_sint8", dynlib: libffidll.}: TType - type_uint16* {.importc: "ffi_type_uint16", dynlib: libffidll.}: TType - type_sint16* {.importc: "ffi_type_sint16", dynlib: libffidll.}: TType - type_uint32* {.importc: "ffi_type_uint32", dynlib: libffidll.}: TType - type_sint32* {.importc: "ffi_type_sint32", dynlib: libffidll.}: TType - type_uint64* {.importc: "ffi_type_uint64", dynlib: libffidll.}: TType - type_sint64* {.importc: "ffi_type_sint64", dynlib: libffidll.}: TType - type_float* {.importc: "ffi_type_float", dynlib: libffidll.}: TType - type_double* {.importc: "ffi_type_double", dynlib: libffidll.}: TType - type_pointer* {.importc: "ffi_type_pointer", dynlib: libffidll.}: TType - type_longdouble* {.importc: "ffi_type_longdouble", dynlib: libffidll.}: TType + type_void* {.importc: "ffi_type_void", mylib.}: TType + type_uint8* {.importc: "ffi_type_uint8", mylib.}: TType + type_sint8* {.importc: "ffi_type_sint8", mylib.}: TType + type_uint16* {.importc: "ffi_type_uint16", mylib.}: TType + type_sint16* {.importc: "ffi_type_sint16", mylib.}: TType + type_uint32* {.importc: "ffi_type_uint32", mylib.}: TType + type_sint32* {.importc: "ffi_type_sint32", mylib.}: TType + type_uint64* {.importc: "ffi_type_uint64", mylib.}: TType + type_sint64* {.importc: "ffi_type_sint64", mylib.}: TType + type_float* {.importc: "ffi_type_float", mylib.}: TType + type_double* {.importc: "ffi_type_double", mylib.}: TType + type_pointer* {.importc: "ffi_type_pointer", mylib.}: TType + type_longdouble* {.importc: "ffi_type_longdouble", mylib.}: TType type Tstatus* {.size: sizeof(cint).} = enum @@ -119,20 +147,18 @@ type sint*: TSArg proc raw_call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer; - avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call", - dynlib: libffidll.} + avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call", mylib.} 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, - importc: "ffi_raw_to_ptrarray", dynlib: libffidll.} -proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size", - dynlib: libffidll.} + importc: "ffi_raw_to_ptrarray", mylib.} +proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size", mylib.} proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType; atypes: ptr ptr TType): TStatus {.cdecl, importc: "ffi_prep_cif", - dynlib: libffidll.} + mylib.} 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: type @@ -141,9 +167,9 @@ type proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType; atypes: TParamList): TStatus {.cdecl, importc: "ffi_prep_cif", - dynlib: libffidll.} + mylib.} 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 ##define FFI_FN(f) ((void (*)(void))f) |