From 88a441ea8ea25f7a298a1279e0fb89274e2c544d Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 2 Jan 2013 01:17:53 +0100 Subject: [PATCH] next steps for FFI at compile time --- compiler/evalffi.nim | 42 +++-- compiler/evals.nim | 23 ++- compiler/main.nim | 1 - lib/system.nim | 312 +++++++++++++++++---------------- lib/wrappers/sdl/sdl_image.nim | 18 +- todo.txt | 1 + 6 files changed, 215 insertions(+), 182 deletions(-) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 8f708b76ca..b55f2baad5 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -279,6 +279,13 @@ proc unpackArray(x: pointer, typ: PType, n: PNode): PNode = for i in 0 .. < result.len: 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 = template aw(k, v, field: expr) {.immediate, dirty.} = if n.isNil: @@ -287,7 +294,8 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode = else: # check we have the right field: 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") 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 tyChar: awi(nkIntLit, rd(char, x).ord) of tyInt: awi(nkIntLit, rd(int, x)) - of tyInt8: awi(nkIntLit, rd(int8, x)) - of tyInt16: awi(nkIntLit, rd(int16, x)) - of tyInt32: awi(nkIntLit, rd(int32, x)) - of tyInt64: awi(nkIntLit, rd(int64, x)) - of tyUInt: awi(nkIntLit, rd(uint, x).biggestInt) - of tyUInt8: awi(nkIntLit, rd(uint8, x).biggestInt) - of tyUInt16: awi(nkIntLit, rd(uint16, x).biggestInt) - of tyUInt32: awi(nkIntLit, rd(uint32, x).biggestInt) - of tyUInt64: awi(nkIntLit, rd(uint64, x).biggestInt) + of tyInt8: awi(nkInt8Lit, rd(int8, x)) + of tyInt16: awi(nkInt16Lit, rd(int16, x)) + of tyInt32: awi(nkInt32Lit, rd(int32, x)) + of tyInt64: awi(nkInt64Lit, rd(int64, x)) + of tyUInt: awi(nkUIntLit, rd(uint, x).biggestInt) + of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).biggestInt) + of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).biggestInt) + of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).biggestInt) + of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).biggestInt) of tyEnum: case typ.getSize of 1: awi(nkIntLit, rd(uint8, x).biggestInt) @@ -327,8 +335,8 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode = else: GlobalError(n.info, "cannot map value from FFI (tyEnum, tySet)") of tyFloat: awf(nkFloatLit, rd(float, x)) - of tyFloat32: awf(nkFloatLit, rd(float32, x)) - of tyFloat64: awf(nkFloatLit, rd(float64, x)) + of tyFloat32: awf(nkFloat32Lit, rd(float32, x)) + of tyFloat64: awf(nkFloat64Lit, rd(float64, x)) of tyPointer, tyProc: let p = rd(pointer, x) if p.isNil: @@ -339,7 +347,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode = let p = rd(pointer, x) if p.isNil: setNil() - elif n != nil and n.kind == nkPtrLit: + elif n == nil or n.kind == nkPtrLit: awi(nkPtrLit, cast[TAddress](p)) elif n != nil and n.len == 1: 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? 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 = InternalAssert call.sons[0].kind == nkPtrLit diff --git a/compiler/evals.nim b/compiler/evals.nim index f0a09fff37..5623f79558 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -77,6 +77,7 @@ proc newEvalContext*(module: PSym, filename: string, new(result) result.module = module result.mode = mode + result.features = {allowFFI} initIdNodeTable(result.globals) proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = @@ -90,7 +91,7 @@ proc popStackFrame*(c: PEvalContext) {.inline.} = proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode -proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = +proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = result = newNodeI(nkExceptBranch, info) # creating a nkExceptBranch without sons # means that it could not be evaluated @@ -351,7 +352,7 @@ proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode = result = copyTree(result) else: when hasFFI: - if sfImportc in s.flags: + if sfImportc in s.flags and allowFFI in c.features: result = importcSymbol(s) IdNodeTablePut(c.globals, s, 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) 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) newCall.sons[0] = evalGlobalVar(c, prc.sym, {}) for i in 1 .. = 1: stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ)) else: - stackTrace(c, n, errCannotInterpretNodeX, renderTree(n)) + stackTrace(c, result, errCannotInterpretNodeX, renderTree(n)) proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode = 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 = var c = newEvalContext(module, filename, emRepl) + c.features = {allowCast, allowFFI, allowInfiniteLoops} pushStackFrame(c, newStackFrame()) result = c diff --git a/compiler/main.nim b/compiler/main.nim index a65660380d..93def6f09f 100755 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -153,7 +153,6 @@ proc CommandCompileToEcmaScript = proc CommandInteractive = msgs.gErrorMax = high(int) # do not stop after first error - incl(gGlobalOptions, optSafeCode) #setTarget(osNimrodVM, cpuNimrodVM) initDefines() DefineSymbol("nimrodvm") diff --git a/lib/system.nim b/lib/system.nim index cbca90a3bc..8106df14a1 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1547,41 +1547,42 @@ when false: # ----------------- GC interface --------------------------------------------- -proc GC_disable*() {.rtl, inl.} - ## 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 - ## the mark and sweep phase with `GC_disableMarkAndSweep`. +when not defined(nimrodVM): + proc GC_disable*() {.rtl, inl.} + ## 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 + ## the mark and sweep phase with `GC_disableMarkAndSweep`. -proc GC_enable*() {.rtl, inl.} - ## enables the GC again. + proc GC_enable*() {.rtl, inl.} + ## enables the GC again. -proc GC_fullCollect*() {.rtl.} - ## forces a full garbage collection pass. - ## Ordinary code does not need to call this (and should not). + proc GC_fullCollect*() {.rtl.} + ## forces a full garbage collection pass. + ## Ordinary code does not need to call this (and should not). -type - TGC_Strategy* = enum ## the strategy the GC should use for the application - gcThroughput, ## optimize for throughput - gcResponsiveness, ## optimize for responsiveness (default) - gcOptimizeTime, ## optimize for speed - gcOptimizeSpace ## optimize for memory footprint + type + TGC_Strategy* = enum ## the strategy the GC should use for the application + gcThroughput, ## optimize for throughput + gcResponsiveness, ## optimize for responsiveness (default) + gcOptimizeTime, ## optimize for speed + gcOptimizeSpace ## optimize for memory footprint -proc GC_setStrategy*(strategy: TGC_Strategy) {.rtl, deprecated.} - ## tells the GC the desired strategy for the application. - ## **Deprecated** since version 0.8.14. This has always been a nop. + proc GC_setStrategy*(strategy: TGC_Strategy) {.rtl, deprecated.} + ## tells the GC the desired strategy for the application. + ## **Deprecated** since version 0.8.14. This has always been a nop. -proc GC_enableMarkAndSweep*() {.rtl.} -proc GC_disableMarkAndSweep*() {.rtl.} - ## the current implementation uses a reference counting garbage collector - ## with a seldomly run mark and sweep phase to free cycles. The mark and - ## sweep phase may take a long time and is not needed if the application - ## does not create cycles. Thus the mark and sweep phase can be deactivated - ## and activated separately from the rest of the GC. + proc GC_enableMarkAndSweep*() {.rtl.} + proc GC_disableMarkAndSweep*() {.rtl.} + ## the current implementation uses a reference counting garbage collector + ## with a seldomly run mark and sweep phase to free cycles. The mark and + ## sweep phase may take a long time and is not needed if the application + ## does not create cycles. Thus the mark and sweep phase can be deactivated + ## and activated separately from the rest of the GC. -proc GC_getStatistics*(): string {.rtl.} - ## returns an informative string about the GC's activity. This may be useful - ## for tweaking. - + proc GC_getStatistics*(): string {.rtl.} + ## returns an informative string about the GC's activity. This may be useful + ## for tweaking. + proc GC_ref*[T](x: ref T) {.magic: "GCref".} proc GC_ref*[T](x: seq[T]) {.magic: "GCref".} proc GC_ref*(x: string) {.magic: "GCref".} @@ -1707,28 +1708,29 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".} ## get type information for `x`. Ordinary code should not use this, but ## 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.} - proc initGC() - when not defined(boehmgc) and not defined(useMalloc): - proc initAllocator() {.inline.} + when not defined(NimrodVM): + proc initGC() + when not defined(boehmgc) and not defined(useMalloc): + proc initAllocator() {.inline.} - proc initStackBottom() {.inline, compilerproc.} = - # WARNING: This is very fragile! An array size of 8 does not work on my - # Linux 64bit system. -- That's because the stack direction is the other - # way round. - when defined(setStackBottom): - var locals {.volatile.}: pointer - locals = addr(locals) - setStackBottom(locals) + proc initStackBottom() {.inline, compilerproc.} = + # WARNING: This is very fragile! An array size of 8 does not work on my + # Linux 64bit system. -- That's because the stack direction is the other + # way round. + when defined(setStackBottom): + var locals {.volatile.}: pointer + locals = addr(locals) + setStackBottom(locals) - var - strDesc: TNimType + var + strDesc: TNimType - strDesc.size = sizeof(string) - strDesc.kind = tyString - strDesc.flags = {ntfAcyclic} + strDesc.size = sizeof(string) + strDesc.kind = tyString + strDesc.flags = {ntfAcyclic} include "system/ansi_c" @@ -1736,22 +1738,23 @@ when not defined(EcmaScript) and not defined(NimrodVM): result = int(c_strcmp(x, y)) const pccHack = if defined(pcc): "_" else: "" # Hack for PCC - when defined(windows): - # work-around C's sucking abstraction: - # BUGFIX: stdin and stdout should be binary files! - proc setmode(handle, mode: int) {.importc: pccHack & "setmode", - header: "".} - proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno", - header: "".} - var - O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int + when not defined(NimrodVM): + when defined(windows): + # work-around C's sucking abstraction: + # BUGFIX: stdin and stdout should be binary files! + proc setmode(handle, mode: int) {.importc: pccHack & "setmode", + header: "".} + proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno", + header: "".} + var + O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int - # we use binary mode in Windows: - setmode(fileno(c_stdin), O_BINARY) - setmode(fileno(c_stdout), O_BINARY) - - when defined(endb): - proc endbStep() + # we use binary mode in Windows: + setmode(fileno(c_stdin), O_BINARY) + setmode(fileno(c_stdout), O_BINARY) + + when defined(endb): + proc endbStep() # ----------------- IO Part ------------------------------------------------ type @@ -1957,13 +1960,14 @@ when not defined(EcmaScript) and not defined(NimrodVM): inc(i) dealloc(a) - proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.} - ## atomic increment of `memLoc`. Returns the value after the operation. - - proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable.} - ## atomic decrement of `memLoc`. Returns the value after the operation. + when not defined(NimrodVM): + proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.} + ## atomic increment of `memLoc`. Returns the value after the operation. + + proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable.} + ## atomic decrement of `memLoc`. Returns the value after the operation. - include "system/atomics" + include "system/atomics" type PSafePoint = ptr TSafePoint @@ -1979,71 +1983,76 @@ when not defined(EcmaScript) and not defined(NimrodVM): when hasThreadSupport: include "system/syslocks" include "system/threads" - elif not defined(nogc): + elif not defined(nogc) and not defined(NimrodVM): when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom() initGC() - proc setControlCHook*(hook: proc () {.noconv.}) - ## allows you to override the behaviour of your application when CTRL+C - ## is pressed. Only one such hook is supported. - - proc writeStackTrace*() {.tags: [FWriteIO].} - ## writes the current stack trace to ``stderr``. This is only works - ## for debug builds. - when hostOS != "standalone": - proc getStackTrace*(): string - ## gets the current stack trace. This only works for debug builds. - - proc getStackTrace*(e: ref E_Base): string - ## gets the stack trace associated with `e`, which is the stack that - ## lead to the ``raise`` statement. This only works for debug builds. + when not defined(NimrodVM): + proc setControlCHook*(hook: proc () {.noconv.}) + ## allows you to override the behaviour of your application when CTRL+C + ## is pressed. Only one such hook is supported. - {.push stack_trace: off, profiler:off.} - when hostOS == "standalone": - include "system/embedded" - else: - include "system/excpt" - - # we cannot compile this with stack tracing on - # as it would recurse endlessly! - include "system/arithm" - {.pop.} # stack trace - {.pop.} # stack trace - - when hostOS != "standalone": include "system/dyncalls" - include "system/sets" + proc writeStackTrace*() {.tags: [FWriteIO].} + ## writes the current stack trace to ``stderr``. This is only works + ## for debug builds. + when hostOS != "standalone": + proc getStackTrace*(): string + ## gets the current stack trace. This only works for debug builds. - const - GenericSeqSize = (2 * sizeof(int)) - - proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int = - sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") - var d: int - var a = cast[TAddress](aa) - case n.typ.size - of 1: d = ze(cast[ptr int8](a +% n.offset)[]) - of 2: d = ze(cast[ptr int16](a +% n.offset)[]) - of 4: d = int(cast[ptr int32](a +% n.offset)[]) - else: sysAssert(false, "getDiscriminant: invalid n.typ.size") - return d - - proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode = - var discr = getDiscriminant(aa, n) - if discr <% n.len: - result = n.sons[discr] - if result == nil: result = n.sons[n.len] - # n.sons[n.len] contains the ``else`` part (but may be nil) + proc getStackTrace*(e: ref E_Base): string + ## gets the stack trace associated with `e`, which is the stack that + ## lead to the ``raise`` statement. This only works for debug builds. + + {.push stack_trace: off, profiler:off.} + when hostOS == "standalone": + include "system/embedded" else: - result = n.sons[n.len] + include "system/excpt" + + # we cannot compile this with stack tracing on + # as it would recurse endlessly! + include "system/arithm" + {.pop.} # stack trace + {.pop.} # stack trace + + when hostOS != "standalone" and not defined(NimrodVM): + include "system/dyncalls" + when not defined(NimrodVM): + include "system/sets" - include "system/mmdisp" - {.push stack_trace: off, profiler:off.} - when hostOS != "standalone": include "system/sysstr" - {.pop.} + const + GenericSeqSize = (2 * sizeof(int)) + + proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int = + sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") + var d: int + var a = cast[TAddress](aa) + case n.typ.size + of 1: d = ze(cast[ptr int8](a +% n.offset)[]) + of 2: d = ze(cast[ptr int16](a +% n.offset)[]) + of 4: d = int(cast[ptr int32](a +% n.offset)[]) + else: sysAssert(false, "getDiscriminant: invalid n.typ.size") + return d - include "system/sysio" - when hasThreadSupport: - include "system/channels" + proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode = + var discr = getDiscriminant(aa, n) + if discr <% n.len: + result = n.sons[discr] + if result == nil: result = n.sons[n.len] + # n.sons[n.len] contains the ``else`` part (but may be nil) + else: + result = n.sons[n.len] + + include "system/mmdisp" + {.push stack_trace: off, profiler:off.} + when hostOS != "standalone": include "system/sysstr" + {.pop.} + + include "system/sysio" + when hasThreadSupport: + include "system/channels" + else: + include "system/sysio" iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} = ## 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)) while f.readLine(res): yield TaintedString(res) - when hostOS != "standalone": + when hostOS != "standalone" and not defined(NimrodVM): include "system/assign" include "system/repr" @@ -2083,42 +2092,43 @@ when not defined(EcmaScript) and not defined(NimrodVM): excHandler.raiseAction = action {.push stack_trace: off, profiler:off.} - when defined(endb): + when defined(endb) and not defined(NimrodVM): include "system/debugger" when defined(profiler) or defined(memProfiler): include "system/profiler" {.pop.} # stacktrace - 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 - ## optimizer. - - proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} - ## can be used to mark a condition to be unlikely. This is a hint for the - ## optimizer. + when not defined(NimrodVM): + 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 + ## optimizer. - proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = - ## retrieves the raw proc pointer of the closure `x`. This is - ## useful for interfacing closures with C. - {.emit: """ - `result` = `x`.ClPrc; - """.} + proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} + ## can be used to mark a condition to be unlikely. This is a hint for the + ## optimizer. + + proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = + ## retrieves the raw proc pointer of the closure `x`. This is + ## useful for interfacing closures with C. + {.emit: """ + `result` = `x`.ClPrc; + """.} - proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} = - ## retrieves the raw environment pointer of the closure `x`. This is - ## useful for interfacing closures with C. - {.emit: """ - `result` = `x`.ClEnv; - """.} + proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} = + ## retrieves the raw environment pointer of the closure `x`. This is + ## useful for interfacing closures with C. + {.emit: """ + `result` = `x`.ClEnv; + """.} - proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} = - ## can be used to determine if a first class iterator has finished. - {.emit: """ - `result` = *((NI*) `x`.ClEnv) < 0; - """.} + proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} = + ## can be used to determine if a first class iterator has finished. + {.emit: """ + `result` = *((NI*) `x`.ClEnv) < 0; + """.} -elif defined(ecmaScript) or defined(NimrodVM): +elif defined(ecmaScript): # Stubs: proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil diff --git a/lib/wrappers/sdl/sdl_image.nim b/lib/wrappers/sdl/sdl_image.nim index 7df9aedd4e..16e41070b8 100755 --- a/lib/wrappers/sdl/sdl_image.nim +++ b/lib/wrappers/sdl/sdl_image.nim @@ -128,19 +128,19 @@ # #****************************************************************************** -import +import sdl -when defined(windows): - const +when defined(windows): + const ImageLibName = "SDL_Image.dll" -elif defined(macosx): - const +elif defined(macosx): + const ImageLibName = "libSDL_image-1.2.0.dylib" -else: - const - ImageLibName = "libSDL_image.so" -const +else: + const + ImageLibName = "libSDL_image(.so|-1.2.so.0)" +const IMAGE_MAJOR_VERSION* = 1 IMAGE_MINOR_VERSION* = 2 IMAGE_PATCHLEVEL* = 5 diff --git a/todo.txt b/todo.txt index caecc9b709..67f4e617f4 100755 --- a/todo.txt +++ b/todo.txt @@ -27,6 +27,7 @@ version 0.9.X - optimize genericAssign in the code generator - better support for macros that rewrite procs - macros need access to types and symbols (partially implemented) +- result = result shr 8 for the "system()" wrapper - rethink the syntax/grammar: * parser is not strict enough with newlines