diff options
author | Araq <rumpf_a@web.de> | 2013-12-24 01:30:48 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2013-12-24 01:30:48 +0100 |
commit | 1081c104d06c084e73230d0c7fc73b3824fb4f1d (patch) | |
tree | a1861108b9788f961023091d391be7968f187c50 /compiler | |
parent | c3b3339e779dfe19ed1891295a763aea47981131 (diff) | |
parent | feb9af48f1600ce814bc3b62765894ba503ec108 (diff) | |
download | Nim-1081c104d06c084e73230d0c7fc73b3824fb4f1d.tar.gz |
Merge branch 'vm2' of github.com:Araq/Nimrod into vm2
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/astalgo.nim | 2 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 19 | ||||
-rw-r--r-- | compiler/commands.nim | 6 | ||||
-rw-r--r-- | compiler/evalffi.nim | 80 | ||||
-rw-r--r-- | compiler/evaltempl.nim | 45 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 6 | ||||
-rw-r--r-- | compiler/lexer.nim | 10 | ||||
-rw-r--r-- | compiler/main.nim | 2 | ||||
-rw-r--r-- | compiler/msgs.nim | 4 | ||||
-rw-r--r-- | compiler/nimrod.nimrod.cfg | 3 | ||||
-rw-r--r-- | compiler/renderer.nim | 10 | ||||
-rw-r--r-- | compiler/sem.nim | 60 | ||||
-rw-r--r-- | compiler/semdata.nim | 2 | ||||
-rw-r--r-- | compiler/semexprs.nim | 15 | ||||
-rw-r--r-- | compiler/semfold.nim | 46 | ||||
-rw-r--r-- | compiler/semmagic.nim | 28 | ||||
-rw-r--r-- | compiler/semstmts.nim | 25 | ||||
-rw-r--r-- | compiler/transf.nim | 5 | ||||
-rw-r--r-- | compiler/vm.nim | 568 | ||||
-rw-r--r-- | compiler/vmdef.nim | 38 | ||||
-rw-r--r-- | compiler/vmdeps.nim | 59 | ||||
-rw-r--r-- | compiler/vmgen.nim | 318 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 10 |
24 files changed, 927 insertions, 437 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 0790df0c4..7199aa72d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -364,6 +364,7 @@ type nfSem # node has been checked for semantics nfDelegate # the call can use a delegator nfExprCall # this is an attempt to call a regular expression + nfIsRef # this node is a 'ref' node; used for the VM TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) @@ -792,7 +793,7 @@ const ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, - nfAllConst, nfDelegate} + nfAllConst, nfDelegate, nfIsRef} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 6c48dd00f..4f869cfca 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -216,7 +216,7 @@ proc makeYamlString*(s: string): PRope = const MaxLineLength = 64 result = nil var res = "\"" - for i in countup(0, len(s) - 1): + for i in countup(0, if s.isNil: -1 else: (len(s)-1)): if (i + 1) mod MaxLineLength == 0: add(res, '\"') add(res, "\n") diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index ac4bbb79f..d9e6d83d0 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -322,8 +322,20 @@ proc genComputedGoto(p: BProc; n: PNode) = gotoArray.appf("&&TMP$#, ", (id+i).toRope) gotoArray.appf("&&TMP$#};$n", (id+arraySize).toRope) line(p, cpsLocals, gotoArray) + + let topBlock = p.blocks.len-1 + let oldBody = p.blocks[topBlock].sections[cpsStmts] + p.blocks[topBlock].sections[cpsStmts] = nil + for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) + let tailB = p.blocks[topBlock].sections[cpsStmts] + + p.blocks[topBlock].sections[cpsStmts] = nil for j in 0 .. casePos-1: genStmts(p, n.sons[j]) + let tailA = p.blocks[topBlock].sections[cpsStmts] + + p.blocks[topBlock].sections[cpsStmts] = oldBody.con(tailA) + let caseStmt = n.sons[casePos] var a: TLoc initLocExpr(p, caseStmt.sons[0], a) @@ -340,8 +352,11 @@ proc genComputedGoto(p: BProc; n: PNode) = let val = getOrdValue(it.sons[j]) lineF(p, cpsStmts, "TMP$#:$n", intLiteral(val+id+1)) genStmts(p, it.lastSon) - for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) - for j in 0 .. casePos-1: genStmts(p, n.sons[j]) + #for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) # tailB + #for j in 0 .. casePos-1: genStmts(p, n.sons[j]) # tailA + app(p.s(cpsStmts), tailB) + app(p.s(cpsStmts), tailA) + var a: TLoc initLocExpr(p, caseStmt.sons[0], a) lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc) 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..3b8ce0505 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -9,7 +9,7 @@ ## This file implements the FFI part of the evaluator for Nimrod code. -import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs +import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os when defined(windows): const libcDll = "msvcrt.dll" @@ -20,7 +20,11 @@ type TDllCache = tables.TTable[string, TLibHandle] var gDllCache = initTable[string, TLibHandle]() - gExeHandle = LoadLib() + +when defined(windows): + var gExeHandle = loadLib(os.getAppFilename()) +else: + var gExeHandle = loadLib() proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer = result = cache[dll] @@ -28,15 +32,17 @@ proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer = var libs: seq[string] = @[] libCandidates(dll, libs) for c in libs: - result = LoadLib(c) + result = loadLib(c) if not result.isNil: break if result.isNil: - GlobalError(info, "cannot load: " & dll) + globalError(info, "cannot load: " & dll) cache[dll] = result const nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon +var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable + proc importcSymbol*(sym: PSym): PNode = let name = ropeToStr(sym.loc.r) @@ -47,10 +53,11 @@ proc importcSymbol*(sym: PSym): PNode = of "stdin": result.intVal = cast[TAddress](system.stdin) of "stdout": result.intVal = cast[TAddress](system.stdout) of "stderr": result.intVal = cast[TAddress](system.stderr) + of "vmErrnoWrapper": result.intVal = cast[TAddress](myerrno) else: let lib = sym.annex if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}: - GlobalError(sym.info, "dynlib needs to be a string lit for the REPL") + globalError(sym.info, "dynlib needs to be a string lit for the REPL") var theAddr: pointer if lib.isNil and not gExehandle.isNil: # first try this exe itself: @@ -58,10 +65,12 @@ proc importcSymbol*(sym: PSym): PNode = # then try libc: if theAddr.isNil: let dllhandle = gDllCache.getDll(libcDll, sym.info) - theAddr = dllhandle.checkedSymAddr(name) - else: - let dllhandle = gDllCache.getDll(lib.path.strVal, sym.info) - theAddr = dllhandle.checkedSymAddr(name) + theAddr = dllhandle.symAddr(name) + elif not lib.isNil: + let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll + else: lib.path.strVal, sym.info) + theAddr = dllhandle.symAddr(name) + if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s) result.intVal = cast[TAddress](theAddr) proc mapType(t: ast.PType): ptr libffi.TType = @@ -139,7 +148,7 @@ proc getField(n: PNode; position: int): PSym = else: internalError(n.info, "getField(record case branch)") of nkSym: if n.sym.position == position: result = n.sym - else: nil + else: discard proc packObject(x: PNode, typ: PType, res: pointer) = InternalAssert x.kind in {nkObjConstr, nkPar} @@ -192,7 +201,7 @@ proc pack(v: PNode, typ: PType, res: pointer) = of tyPointer, tyProc, tyCString, tyString: if v.kind == nkNilLit: # nothing to do since the memory is 0 initialized anyway - nil + discard elif v.kind == nkPtrLit: awr(pointer, cast[pointer](v.intVal)) elif v.kind in {nkStrLit..nkTripleStrLit}: @@ -202,7 +211,7 @@ proc pack(v: PNode, typ: PType, res: pointer) = of tyPtr, tyRef, tyVar: if v.kind == nkNilLit: # nothing to do since the memory is 0 initialized anyway - nil + discard elif v.kind == nkPtrLit: awr(pointer, cast[pointer](v.intVal)) else: @@ -220,7 +229,7 @@ proc pack(v: PNode, typ: PType, res: pointer) = of tyObject, tyTuple: packObject(v, typ, res) of tyNil: - nil + discard of tyDistinct, tyGenericInst: pack(v, typ.sons[0], res) else: @@ -241,7 +250,7 @@ proc unpackObjectAdd(x: pointer, n, result: PNode) = pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil) #echo "offset: ", n.sym.name.s, " ", n.sym.offset result.add pair - else: nil + else: discard proc unpackObject(x: pointer, typ: PType, n: PNode): PNode = # compute the field's offsets: @@ -441,3 +450,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/evaltempl.nim b/compiler/evaltempl.nim index 05be0e9d3..4bff9ae5e 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -16,9 +16,14 @@ import type TemplCtx {.pure, final.} = object owner, genSymOwner: PSym + instLines: bool # use the instantiation lines numbers mapping: TIdTable # every gensym'ed symbol needs to be mapped to some # new symbol +proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = + result = copyNode(a) + if ctx.instLines: result.info = b.info + proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = case templ.kind of nkSym: @@ -37,43 +42,17 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = x = copySym(s, false) x.owner = c.genSymOwner IdTablePut(c.mapping, s, x) - result.add newSymNode(x, templ.info) + result.add newSymNode(x, if c.instLines: actual.info else: templ.info) else: - result.add copyNode(templ) + result.add copyNode(c, templ, actual) of nkNone..nkIdent, nkType..nkNilLit: # atom - result.add copyNode(templ) + result.add copyNode(c, templ, actual) else: - var res = copyNode(templ) + var res = copyNode(c, templ, actual) for i in countup(0, sonsLen(templ) - 1): evalTemplateAux(templ.sons[i], actual, c, res) result.add res -when false: - proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode = - case templ.kind - of nkSym: - var s = templ.sym - if s.owner.id == c.owner.id: - if s.kind == skParam: - result = copyTree(actual.sons[s.position]) - else: - InternalAssert sfGenSym in s.flags - var x = PSym(IdTableGet(c.mapping, s)) - if x == nil: - x = copySym(s, false) - x.owner = c.genSymOwner - IdTablePut(c.mapping, s, x) - result = newSymNode(x, templ.info) - else: - result = copyNode(templ) - of nkNone..nkIdent, nkType..nkNilLit: # atom - result = copyNode(templ) - else: - result = copyNode(templ) - newSons(result, sonsLen(templ)) - for i in countup(0, sonsLen(templ) - 1): - result.sons[i] = evalTemplateAux(templ.sons[i], actual, c) - proc evalTemplateArgs(n: PNode, s: PSym): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar @@ -118,7 +97,9 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode = renderTree(result, {renderNoComments})) else: result = copyNode(body) - #evalTemplateAux(body, args, ctx, result) + ctx.instLines = body.kind notin {nkStmtList, nkStmtListExpr, + nkBlockStmt, nkBlockExpr} + if ctx.instLines: result.info = n.info for i in countup(0, safeLen(body) - 1): evalTemplateAux(body.sons[i], args, ctx, result) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 96eb3a5f4..dd48a0bc3 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -207,7 +207,9 @@ proc newCall(a, b: PSym): PNode = proc addHiddenParam(routine: PSym, param: PSym) = var params = routine.ast.sons[paramsPos] - param.position = params.len + # -1 is correct here as param.position is 0 based but we have at position 0 + # some nkEffect node: + param.position = params.len-1 addSon(params, newSymNode(param)) incl(routine.typ.flags, tfCapturesEnv) #echo "produced environment: ", param.id, " for ", routine.name.s @@ -549,6 +551,8 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = if x != nil: n.sons[i] = x proc liftLambdas*(fn: PSym, body: PNode): PNode = + # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs + # the transformation even when compiling to JS ... if body.kind == nkEmpty or gCmd == cmdCompileToJS: # ignore forward declaration: result = body diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 82bfa0ad4..eb9287dfe 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -41,11 +41,12 @@ type tkGeneric, tkIf, tkImport, tkIn, tkInclude, tkInterface, tkIs, tkIsnot, tkIterator, tkLambda, tkLet, - tkMacro, tkMethod, tkMixin, tkUsing, tkMod, tkNil, tkNot, tkNotin, + tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShared, tkShl, tkShr, tkStatic, tkTemplate, - tkTry, tkTuple, tkType, tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor, + tkTry, tkTuple, tkType, tkUsing, + tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor, tkYield, # end of keywords tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit, @@ -75,12 +76,13 @@ const "finally", "for", "from", "generic", "if", "import", "in", "include", "interface", "is", "isnot", "iterator", "lambda", "let", - "macro", "method", "mixin", "using", "mod", + "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", "shared", "shl", "shr", "static", "template", - "try", "tuple", "type", "var", "when", "while", "with", "without", "xor", + "try", "tuple", "type", "using", + "var", "when", "while", "with", "without", "xor", "yield", "tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit", "tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit", diff --git a/compiler/main.nim b/compiler/main.nim index 9ffe99454..b91b596b0 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -14,7 +14,7 @@ import os, condsyms, rodread, rodwrite, times, wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, jsgen, json, nversion, - platform, nimconf, importer, passaux, depends, evals, types, idgen, + platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists, pretty diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 895ba71f3..2a7d54d4e 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -700,11 +700,9 @@ type proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = template maybeTrace = - if defined(debug) or gVerbosity >= 3: + if defined(debug) or gVerbosity >= 3 or msg == errInternal: writeStackTrace() - if msg == errInternal: - writeStackTrace() # we always want a stack trace here if msg >= fatalMin and msg <= fatalMax: maybeTrace() quit(1) diff --git a/compiler/nimrod.nimrod.cfg b/compiler/nimrod.nimrod.cfg index 9fa1b8cba..b2ae97686 100644 --- a/compiler/nimrod.nimrod.cfg +++ b/compiler/nimrod.nimrod.cfg @@ -13,3 +13,6 @@ path:"$lib/packages/docutils" define:booting import:testability +@if windows: + cincludes: "$lib/wrappers/libffi/common" +@end diff --git a/compiler/renderer.nim b/compiler/renderer.nim index f6fb0f8c0..c8fe70e02 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -557,7 +557,7 @@ proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool = proc gstmts(g: var TSrcGen, n: PNode, c: TContext) = if n.kind == nkEmpty: return - if (n.kind == nkStmtList) or (n.kind == nkStmtListExpr): + if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: indentNL(g) for i in countup(0, sonsLen(n) - 1): optNL(g) @@ -1069,7 +1069,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n.sons[1]) - of nkStmtList, nkStmtListExpr: gstmts(g, n, emptyContext) + of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext) of nkIfStmt: putWithSpace(g, tkIf, "if") gif(g, n) @@ -1246,8 +1246,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkBracketLe, "[") gcomma(g, n) put(g, tkBracketRi, "]") + of nkMetaNode: + put(g, tkParLe, "(META|") + gsub(g, n.sons[0]) + put(g, tkParRi, ")") else: - #nkNone, nkMetaNode, nkExplicitTypeListCall: + #nkNone, nkExplicitTypeListCall: InternalError(n.info, "rnimsyn.gsub(" & $n.kind & ')') proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = diff --git a/compiler/sem.nim b/compiler/sem.nim index 3ace623bc..ed3c0e045 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -14,7 +14,7 @@ import wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, magicsys, parser, nversion, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, - semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting, + semthreads, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2 # implementation @@ -43,7 +43,7 @@ proc activate(c: PContext, n: PNode) proc semQuoteAst(c: PContext, n: PNode): PNode proc finishMethod(c: PContext, s: PSym) -proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode +proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode proc typeMismatch(n: PNode, formal, actual: PType) = if formal.kind != tyError and actual.kind != tyError: @@ -63,7 +63,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = if result == nil: typeMismatch(arg, formal, arg.typ) # error correction: - result = copyNode(arg) + result = copyTree(arg) result.typ = formal var CommonTypeBegin = PType(kind: tyExpr) @@ -169,25 +169,26 @@ when false: result = newSymNode(symFromType(t, info), info) result.typ = makeTypeDesc(c, t) -proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext = - result = newEvalContext(c.module, mode) - result.getType = proc (n: PNode): PNode = - result = tryExpr(c, n) - if result == nil: - result = newSymNode(errorSym(c, n)) - elif result.typ == nil: - result = newSymNode(getSysSym"void") - else: - result.typ = makeTypeDesc(c, result.typ) +when false: + proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext = + result = newEvalContext(c.module, mode) + result.getType = proc (n: PNode): PNode = + result = tryExpr(c, n) + if result == nil: + result = newSymNode(errorSym(c, n)) + elif result.typ == nil: + result = newSymNode(getSysSym"void") + else: + result.typ = makeTypeDesc(c, result.typ) - result.handleIsOperator = proc (n: PNode): PNode = - result = IsOpImpl(c, n) + result.handleIsOperator = proc (n: PNode): PNode = + result = IsOpImpl(c, n) -proc evalConstExpr(c: PContext, module: PSym, e: PNode): PNode = - result = evalConstExprAux(c.createEvalContext(emConst), module, nil, e) + proc evalConstExpr(c: PContext, module: PSym, e: PNode): PNode = + result = evalConstExprAux(c.createEvalContext(emConst), module, nil, e) -proc evalStaticExpr(c: PContext, module: PSym, e: PNode, prc: PSym): PNode = - result = evalConstExprAux(c.createEvalContext(emStatic), module, prc, e) + proc evalStaticExpr(c: PContext, module: PSym, e: PNode, prc: PSym): PNode = + result = evalConstExprAux(c.createEvalContext(emStatic), module, prc, e) proc semConstExpr(c: PContext, n: PNode): PNode = var e = semExprWithType(c, n) @@ -196,7 +197,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode = return n result = getConstExpr(c.module, e) if result == nil: - result = evalConstExpr(c, c.module, e) + result = evalConstExpr(c.module, e) if result == nil or result.kind == nkEmpty: if e.info != n.info: pushInfoContext(n.info) @@ -206,6 +207,19 @@ proc semConstExpr(c: PContext, n: PNode): PNode = LocalError(e.info, errConstExprExpected) # error correction: result = e + else: + # recompute the types as 'eval' isn't guaranteed to construct types nor + # that the types are sound: + result = semExprWithType(c, result) + #result = fitNode(c, e.typ, result) inlined with special case: + let arg = result + result = indexTypesMatch(c, e.typ, arg.typ, arg) + if result == nil: + result = arg + # for 'tcnstseq' we support [] to become 'seq' + if e.typ.skipTypes(abstractInst).kind == tySequence and + arg.typ.skipTypes(abstractInst).kind == tyArrayConstr: + arg.typ = e.typ include hlo, seminst, semcall @@ -243,10 +257,10 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, if sym == c.p.owner: GlobalError(n.info, errRecursiveDependencyX, sym.name.s) - if c.evalContext == nil: - c.evalContext = c.createEvalContext(emStatic) + #if c.evalContext == nil: + # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.evalContext, n, nOrig, sym) + result = evalMacroCall(c.module, n, nOrig, sym) if semCheck: result = semAfterMacroCall(c, result, sym) proc forceBool(c: PContext, n: PNode): PNode = diff --git a/compiler/semdata.nim b/compiler/semdata.nim index d02359d4c..650a399f7 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -13,7 +13,7 @@ import strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab, wordrecg, ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, - magicsys, nversion, nimsets, parser, times, passes, rodread, evals + magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef type TOptionEntry* = object of lists.TListEntry # entries to put on a diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 310aabc32..c45b83095 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -300,11 +300,11 @@ proc semOf(c: PContext, n: PNode): PNode = result = n proc isOpImpl(c: PContext, n: PNode): PNode = - InternalAssert n.sonsLen == 3 and - n[1].kind == nkSym and n[1].sym.kind == skType and + internalAssert n.sonsLen == 3 and + n[1].typ != nil and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].sym.typ.skipTypes({tyTypeDesc}) + let t1 = n[1].typ.skipTypes({tyTypeDesc}) if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize @@ -640,18 +640,18 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = call.add(a) #echo "NOW evaluating at compile time: ", call.renderTree if sfCompileTime in callee.flags: - result = evalStaticExpr(c, c.module, call, c.p.owner) + result = evalStaticExpr(c.module, call, c.p.owner) if result.isNil: LocalError(n.info, errCannotInterpretNodeX, renderTree(call)) else: - result = evalConstExpr(c, c.module, call) + result = evalConstExpr(c.module, call) if result.isNil: result = n #if result != n: # echo "SUCCESS evaluated at compile time: ", call.renderTree proc semStaticExpr(c: PContext, n: PNode): PNode = let a = semExpr(c, n.sons[0]) - result = evalStaticExpr(c, c.module, a, c.p.owner) + result = evalStaticExpr(c.module, a, c.p.owner) if result.isNil: LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) result = emptyNode @@ -780,6 +780,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = #semLazyOpAux(c, n) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) if result != nil: result = afterCallActions(c, result, nOrig, flags) + else: result = errorNode(c, n) proc buildStringify(c: PContext, arg: PNode): PNode = if arg.typ != nil and @@ -1839,7 +1840,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # don't have to check the symbol for semantics here again! result = semSym(c, n, n.sym, flags) of nkEmpty, nkNone, nkCommentStmt: - nil + discard of nkNilLit: result.typ = getSysType(tyNil) of nkIntLit: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index ca06ea1b6..fb1816f9c 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -229,6 +229,33 @@ discard """ mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 """ +proc evalIs(n, a: PNode): PNode = + internalAssert a.kind == nkSym and a.sym.kind == skType + internalAssert n.sonsLen == 3 and + n[2].kind in {nkStrLit..nkTripleStrLit, nkType} + + let t1 = a.sym.typ + + if n[2].kind in {nkStrLit..nkTripleStrLit}: + case n[2].strVal.normalize + of "closure": + let t = skipTypes(t1, abstractRange) + result = newIntNode(nkIntLit, ord(t.kind == tyProc and + t.callConv == ccClosure and + tfIterator notin t.flags)) + of "iterator": + let t = skipTypes(t1, abstractRange) + result = newIntNode(nkIntLit, ord(t.kind == tyProc and + t.callConv == ccClosure and + tfIterator in t.flags)) + else: + # XXX semexprs.isOpImpl is slightly different and requires a context. yay. + let t2 = n[2].typ + var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1) + else: sameType(t1, t2) + result = newIntNode(nkIntLit, ord(match)) + result.typ = n.typ + proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = # b and c may be nil result = nil @@ -372,7 +399,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym: - nil + discard of mRand: result = newIntNodeT(math.random(a.getInt.int), n) else: InternalError(a.info, "evalOp(" & $m & ')') @@ -446,8 +473,6 @@ proc magicCall(m: PSym, n: PNode): PNode = if sonsLen(n) > 3: c = getConstExpr(m, n.sons[3]) if c == nil: return - else: - b = nil result = evalOp(s.magic, n, a, b, c) proc getAppType(n: PNode): PNode = @@ -485,7 +510,7 @@ proc foldConv*(n, a: PNode; check = false): PNode = result = a result.typ = n.typ of tyOpenArray, tyVarargs, tyProc: - nil + discard else: result = a result.typ = n.typ @@ -523,7 +548,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = nil else: LocalError(n.info, errIndexOutOfBounds) - else: nil + else: discard proc foldFieldAccess(m: PSym, n: PNode): PNode = # a real field access; proc calls have already been transformed @@ -592,7 +617,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result.typ = s.typ.sons[0] else: result = newSymNodeTypeDesc(s, n.info) - else: nil + else: discard of nkCharLit..nkNilLit: result = copyNode(n) of nkIfExpr: @@ -604,7 +629,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode = try: case s.magic of mNone: - return # XXX: if it has no sideEffect, it should be evaluated + # If it has no sideEffect, it should be evaluated. But not here. + return of mSizeOf: var a = n.sons[1] if computeSize(a.typ) < 0: @@ -644,6 +670,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) of mConStrStr: result = foldConStrStr(m, n) + of mIs: + let a = getConstExpr(m, n[1]) + if a != nil and a.kind == nkSym and a.sym.kind == skType: + result = evalIs(n, a) else: result = magicCall(m, n) except EOverflow: @@ -727,4 +757,4 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of nkBracketExpr: result = foldArrayAccess(m, n) of nkDotExpr: result = foldFieldAccess(m, n) else: - nil + discard diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 88567b10a..aab4c82f5 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -32,15 +32,29 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode = result.add(filename) result.add(line) + +proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = + let typ = operand.skipTypes({tyTypeDesc}) + case trait.sym.name.s.normalize + of "name": + result = newStrNode(nkStrLit, typ.typeToString(preferName)) + result.typ = newType(tyString, context) + result.info = trait.info + of "arity": + result = newIntNode(nkIntLit, typ.n.len-1) + result.typ = newType(tyInt, context) + result.info = trait.info + else: + internalAssert false + proc semTypeTraits(c: PContext, n: PNode): PNode = checkMinSonsLen(n, 2) - internalAssert n.sons[1].kind == nkSym - let typArg = n.sons[1].sym - if typArg.kind == skType or - (typArg.kind == skParam and typArg.typ.sonsLen > 0): - # This is either a type known to sem or a typedesc - # param to a regular proc (again, known at instantiation) - result = evalTypeTrait(n[0], n[1], GetCurrOwner()) + let t = n.sons[1].typ + internalAssert t != nil + if t.kind == tyTypeDesc and t.len == 0: + result = n + elif not containsGenericType(t): + result = evalTypeTrait(n[0], t, GetCurrOwner()) else: # a typedesc variable, pass unmodified to evals result = n diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a1805fdec..6f0cc3c8b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -425,7 +425,9 @@ proc semConst(c: PContext, n: PNode): PNode = def = fitRemoveHiddenConv(c, typ, def) else: typ = def.typ - if typ == nil: continue + if typ == nil: + LocalError(a.sons[2].info, errConstExprExpected) + continue if not typeAllowed(typ, skConst): LocalError(a.info, errXisNoType, typeToString(typ)) continue @@ -1159,20 +1161,25 @@ proc setLine(n: PNode, info: TLineInfo) = proc semPragmaBlock(c: PContext, n: PNode): PNode = let pragmaList = n.sons[0] pragma(c, nil, pragmaList, exprPragmas) - result = semStmt(c, n.sons[1]) + result = semExpr(c, n.sons[1]) for i in 0 .. <pragmaList.len: if whichPragma(pragmaList.sons[i]) == wLine: setLine(result, pragmaList.sons[i].info) proc semStaticStmt(c: PContext, n: PNode): PNode = let a = semStmt(c, n.sons[0]) - result = evalStaticExpr(c, c.module, a, c.p.owner) - if result.isNil: - LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) - result = emptyNode - elif result.kind == nkEmpty: - result = newNodeI(nkDiscardStmt, n.info, 1) - result.sons[0] = emptyNode + n.sons[0] = a + evalStaticStmt(c.module, a, c.p.owner) + result = newNodeI(nkDiscardStmt, n.info, 1) + result.sons[0] = emptyNode + when false: + result = evalStaticStmt(c.module, a, c.p.owner) + if result.isNil: + LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) + result = emptyNode + elif result.kind == nkEmpty: + result = newNodeI(nkDiscardStmt, n.info, 1) + result.sons[0] = emptyNode proc usesResult(n: PNode): bool = # nkStmtList(expr) properly propagates the void context, diff --git a/compiler/transf.nim b/compiler/transf.nim index 206c21c3d..77642a3b8 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -735,12 +735,9 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = if nfTransf in n.flags or prc.kind in {skTemplate}: result = n else: - #when useEffectSystem: trackProc(prc, n) var c = openTransf(module, "") result = processTransf(c, n, prc) - if prc.kind != skMacro: - # XXX no closures yet for macros: - result = liftLambdas(prc, result) + result = liftLambdas(prc, result) if prc.kind == skIterator and prc.typ.callConv == ccClosure: result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) diff --git a/compiler/vm.nim b/compiler/vm.nim index 7705746de..709baf7b2 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -10,11 +10,17 @@ ## This file implements the new evaluation engine for Nimrod code. ## An instruction is 1-2 int32s in memory, it is a register based VM. +import ast except getstr + import - strutils, ast, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, - parser, vmdeps, idents + strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, + parser, vmdeps, idents, trees, renderer, options from semfold import leValueConv, ordinalValToString +from evaltempl import evalTemplate + +when hasFFI: + import evalffi type PStackFrame* = ref TStackFrame @@ -46,9 +52,9 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int) = proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: TMsgKind, arg = "") = - MsgWriteln("stack trace: (most recent call last)") + msgWriteln("stack trace: (most recent call last)") stackTraceAux(c, tos, pc) - LocalError(c.debug[pc], msg, arg) + localError(c.debug[pc], msg, arg) proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX, @@ -57,15 +63,25 @@ proc bailOut(c: PCtx; tos: PStackFrame) = when not defined(nimComputedGoto): {.pragma: computedGoto.} -template inc(pc: ptr TInstr, diff = 1) = - inc cast[TAddress](pc), TInstr.sizeof * diff - proc myreset(n: PNode) = when defined(system.reset): var oldInfo = n.info reset(n[]) n.info = oldInfo +proc skipMeta(n: PNode): PNode = (if n.kind != nkMetaNode: n else: n.sons[0]) + +proc setMeta(n, child: PNode) = + assert n.kind == nkMetaNode + let child = child.skipMeta + if n.sons.isNil: n.sons = @[child] + else: n.sons[0] = child + +proc uast(n: PNode): PNode {.inline.} = + # "underlying ast" + assert n.kind == nkMetaNode + n.sons[0] + template ensureKind(k: expr) {.immediate, dirty.} = if regs[ra].kind != k: myreset(regs[ra]) @@ -96,23 +112,53 @@ template decodeBx(k: expr) {.immediate, dirty.} = template move(a, b: expr) = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler -proc asgnRef(x, y: PNode) = - myreset(x) - x.kind = y.kind +proc moveConst(x, y: PNode) = + if x.kind != y.kind: + myreset(x) + x.kind = y.kind x.typ = y.typ case x.kind of nkCharLit..nkInt64Lit: x.intVal = y.intVal of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal - of nkStrLit..nkTripleStrLit: x.strVal = y.strVal + of nkStrLit..nkTripleStrLit: move(x.strVal, y.strVal) of nkIdent: x.ident = y.ident of nkSym: x.sym = y.sym + of nkMetaNode: + if x.sons.isNil: x.sons = @[y.sons[0]] + else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: move(x.sons, y.sons) +# this seems to be the best way to model the reference semantics +# of PNimrodNode: +template asgnRef(x, y: expr) = moveConst(x, y) + +proc copyValue(src: PNode): PNode = + if src == nil or nfIsRef in src.flags: + return src + result = newNode(src.kind) + result.info = src.info + result.typ = src.typ + result.flags = src.flags * PersistentNodeFlags + when defined(useNodeIds): + if result.id == nodeIdToDebug: + echo "COMES FROM ", src.id + case src.Kind + of nkCharLit..nkUInt64Lit: result.intVal = src.intVal + of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkSym: result.sym = src.sym + of nkIdent: result.ident = src.ident + of nkStrLit..nkTripleStrLit: result.strVal = src.strVal + else: + newSeq(result.sons, sonsLen(src)) + for i in countup(0, sonsLen(src) - 1): + result.sons[i] = copyValue(src.sons[i]) + proc asgnComplex(x, y: PNode) = - myreset(x) - x.kind = y.kind + if x.kind != y.kind: + myreset(x) + x.kind = y.kind x.typ = y.typ case x.kind of nkCharLit..nkInt64Lit: x.intVal = y.intVal @@ -120,13 +166,16 @@ proc asgnComplex(x, y: PNode) = of nkStrLit..nkTripleStrLit: x.strVal = y.strVal of nkIdent: x.ident = y.ident of nkSym: x.sym = y.sym + of nkMetaNode: + if x.sons.isNil: x.sons = @[y.sons[0]] + else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: - let y = y.copyTree + let y = y.copyValue for i in countup(0, sonsLen(y) - 1): addSon(x, y.sons[i]) template getstr(a: expr): expr = - (if a.kind == nkStrLit: a.strVal else: $chr(int(a.intVal))) + (if a.kind in {nkStrLit..nkTripleStrLit}: a.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = if f.safePoints.isNil: f.safePoints = @[] @@ -231,16 +280,23 @@ proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) #c.echoCode -proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = +proc regsContents(regs: TNodeSeq) = + for i in 0.. <regs.len: + echo "Register ", i + #debug regs[i] + +proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = var pc = start var tos = tos var regs: TNodeSeq # alias to tos.slots for performance move(regs, tos.slots) + #echo "NEW RUN ------------------------" while true: - {.computedGoto.} + #{.computedGoto.} let instr = c.code[pc] let ra = instr.regA #echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra + #message(c.debug[pc], warnUser, "gah") case instr.opcode of opcEof: return regs[ra] of opcRet: @@ -248,12 +304,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = pc = tos.comesFrom tos = tos.next let retVal = regs[0] - if tos.isNil: return retVal + if tos.isNil: + #echo "RET ", retVal.rendertree + return retVal move(regs, tos.slots) assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn} if c.code[pc].opcode == opcIndCallAsgn: regs[c.code[pc].regA] = retVal + #echo "RET2 ", retVal.rendertree, " ", c.code[pc].regA of opcYldYoid: assert false of opcYldVal: assert false of opcAsgnInt: @@ -270,31 +329,45 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = of opcAsgnRef: asgnRef(regs[ra], regs[instr.regB]) of opcWrGlobalRef: - asgnRef(c.globals[instr.regBx-wordExcess-1], regs[ra]) + asgnRef(c.globals.sons[instr.regBx-wordExcess-1], regs[ra]) of opcWrGlobal: asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra]) of opcLdArr: # a = b[c] let rb = instr.regB let rc = instr.regC - let idx = regs[rc].intVal + let idx = regs[rc].intVal.int # XXX what if the array is not 0-based? -> codegen should insert a sub - regs[ra] = regs[rb].sons[idx.int] + assert regs[rb].kind != nkMetaNode + let src = regs[rb] + if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: + asgnComplex(regs[ra], src.sons[idx]) + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdStrIdx: decodeBC(nkIntLit) - let idx = regs[rc].intVal - regs[ra].intVal = regs[rb].strVal[idx.int].ord + let idx = regs[rc].intVal.int + if idx <=% regs[rb].strVal.len: + regs[ra].intVal = regs[rb].strVal[idx].ord + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArr: # a[b] = c let rb = instr.regB let rc = instr.regC - let idx = regs[rb].intVal - asgnComplex(regs[ra].sons[idx.int], regs[rc]) + let idx = regs[rb].intVal.int + if idx <% regs[ra].len: + asgnComplex(regs[ra].sons[idx], regs[rc]) + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArrRef: let rb = instr.regB let rc = instr.regC - let idx = regs[rb].intVal - asgnRef(regs[ra].sons[idx.int], regs[rc]) + let idx = regs[rb].intVal.int + if idx <% regs[ra].len: + asgnRef(regs[ra].sons[idx], regs[rc]) + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdObj: # a = b.c let rb = instr.regB @@ -306,6 +379,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = # a.b = c let rb = instr.regB let rc = instr.regC + #if regs[ra].isNil or regs[ra].sons.isNil or rb >= len(regs[ra]): + # debug regs[ra] + # debug regs[rc] + # echo "RB ", rb + # internalError(c.debug[pc], "argl") asgnComplex(regs[ra].sons[rb], regs[rc]) of opcWrObjRef: let rb = instr.regB @@ -314,7 +392,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = of opcWrStrIdx: decodeBC(nkStrLit) let idx = regs[rb].intVal.int - regs[ra].strVal[idx] = chr(regs[rc].intVal) + if idx <% regs[ra].strVal.len: + regs[ra].strVal[idx] = chr(regs[rc].intVal) + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) of opcAddr: decodeB(nkRefTy) if regs[ra].len == 0: regs[ra].add regs[rb] @@ -325,6 +406,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = if regs[rb].kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) assert regs[rb].kind == nkRefTy + # XXX this is not correct regs[ra] = regs[rb].sons[0] of opcAddInt: decodeBC(nkIntLit) @@ -341,8 +423,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = of opcLenSeq: decodeBImm(nkIntLit) #assert regs[rb].kind == nkBracket - # also used by mNLen - regs[ra].intVal = regs[rb].len - imm + # also used by mNLen: + regs[ra].intVal = regs[rb].skipMeta.len - imm of opcLenStr: decodeBImm(nkIntLit) assert regs[rb].kind == nkStrLit @@ -350,6 +432,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = of opcIncl: decodeB(nkCurly) if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb])) + of opcInclRange: + decodeBC(nkCurly) + var r = newNode(nkRange) + r.add regs[rb] + r.add regs[rc] + addSon(regs[ra], r.copyTree) of opcExcl: decodeB(nkCurly) var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ) @@ -440,6 +528,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = regs[ra].intVal = ord((regs[rb].kind == nkNilLit and regs[rc].kind == nkNilLit) or regs[rb].sons == regs[rc].sons) + of opcEqNimrodNode: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].skipMeta == regs[rc].skipMeta) of opcXor: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) @@ -461,24 +552,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = regs[ra].intVal = not regs[rb].intVal of opcEqStr: decodeBC(nkIntLit) - regs[ra].intVal = Ord(regs[rb].strVal == regs[rc].strVal) + regs[ra].intVal = ord(regs[rb].strVal == regs[rc].strVal) of opcLeStr: decodeBC(nkIntLit) - regs[ra].intVal = Ord(regs[rb].strVal <= regs[rc].strVal) + regs[ra].intVal = ord(regs[rb].strVal <= regs[rc].strVal) of opcLtStr: decodeBC(nkIntLit) - regs[ra].intVal = Ord(regs[rb].strVal < regs[rc].strVal) + regs[ra].intVal = ord(regs[rb].strVal < regs[rc].strVal) of opcLeSet: decodeBC(nkIntLit) - regs[ra].intVal = Ord(containsSets(regs[rb], regs[rc])) + regs[ra].intVal = ord(containsSets(regs[rb], regs[rc])) of opcEqSet: decodeBC(nkIntLit) - regs[ra].intVal = Ord(equalSets(regs[rb], regs[rc])) + regs[ra].intVal = ord(equalSets(regs[rb], regs[rc])) of opcLtSet: decodeBC(nkIntLit) let a = regs[rb] let b = regs[rc] - regs[ra].intVal = Ord(containsSets(a, b) and not equalSets(a, b)) + regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) of opcMulSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons) @@ -508,12 +599,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = of opcEcho: let rb = instr.regB for i in ra..ra+rb-1: - if regs[i].kind != nkStrLit: debug regs[i] + #if regs[i].kind != nkStrLit: debug regs[i] write(stdout, regs[i].strVal) writeln(stdout, "") of opcContainsSet: decodeBC(nkIntLit) - regs[ra].intVal = Ord(inSet(regs[rb], regs[rc])) + regs[ra].intVal = ord(inSet(regs[rb], regs[rc])) of opcSubStr: decodeBC(nkStrLit) inc pc @@ -533,22 +624,55 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB let rc = instr.regC - let prc = regs[rb].sym - let newPc = compile(c, prc) - 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] - # allocate the temporaries: - for i in rc .. <prc.position: - newFrame.slots[i] = newNode(nkEmpty) - tos = newFrame - move(regs, newFrame.slots) - # -1 for the following 'inc pc' - pc = newPc-1 + let isClosure = regs[rb].kind == nkPar + 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 prcValue = c.globals.sons[prc.position-1] + if prcValue.kind == nkEmpty: + globalError(c.debug[pc], errGenerated, "canot run " & prc.name.s) + let newValue = callForeignFunction(prcValue, prc.typ, tos.slots, + rb+1, rc-1, c.debug[pc]) + if newValue.kind != nkEmpty: + assert instr.opcode == opcIndCallAsgn + asgnRef(regs[ra], newValue) + else: + globalError(c.debug[pc], errGenerated, "VM not built with FFI support") + elif prc.kind != skTemplate: + 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]) or prc.kind == skMacro: + 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 + else: + # for 'getAst' support we need to support template expansion here: + let genSymOwner = if tos.next != nil and tos.next.prc != nil: + tos.next.prc + else: + c.module + var macroCall = newNodeI(nkCall, c.debug[pc]) + macroCall.add(newSymNode(prc)) + for i in 1 .. rc-1: macroCall.add(regs[rb+i].skipMeta) + let a = evalTemplate(macroCall, prc, genSymOwner) + ensureKind(nkMetaNode) + setMeta(regs[ra], a) of opcTJmp: # jump Bx if A != 0 let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' @@ -564,18 +688,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' inc pc, rbx of opcBranch: - # we know the next instruction is a 'jmp': + # we know the next instruction is a 'fjmp': let branch = c.constants[instr.regBx-wordExcess] var cond = false for j in countup(0, sonsLen(branch) - 2): if overlap(regs[ra], branch.sons[j]): cond = true break - assert c.code[pc+1].opcode == opcJmp + assert c.code[pc+1].opcode == opcFJmp inc pc # we skip this instruction so that the final 'inc(pc)' skips # the following jump - if cond: + if not cond: let instr2 = c.code[pc] let rbx = instr2.regBx - wordExcess - 1 # -1 for the following 'inc pc' inc pc, rbx @@ -608,6 +732,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = of opcNew: let typ = c.types[instr.regBx - wordExcess] regs[ra] = getNullValue(typ, regs[ra].info) + regs[ra].flags.incl nfIsRef of opcNewSeq: let typ = c.types[instr.regBx - wordExcess] inc pc @@ -629,7 +754,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = let typ = c.types[instr.regBx - wordExcess] regs[ra] = getNullValue(typ, c.debug[pc]) of opcLdConst: - regs[ra] = c.constants.sons[instr.regBx - wordExcess] + let rb = instr.regBx - wordExcess + if regs[ra].isNil: + regs[ra] = copyTree(c.constants.sons[rb]) + else: + moveConst(regs[ra], c.constants.sons[rb]) of opcAsgnConst: let rb = instr.regBx - wordExcess if regs[ra].isNil: @@ -637,67 +766,110 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = else: asgnComplex(regs[ra], c.constants.sons[rb]) of opcLdGlobal: - let rb = instr.regBx - wordExcess + let rb = instr.regBx - wordExcess - 1 if regs[ra].isNil: regs[ra] = copyTree(c.globals.sons[rb]) else: asgnComplex(regs[ra], c.globals.sons[rb]) - of opcRepr, opcSetLenStr, opcSetLenSeq, - opcSwap, opcIsNil, opcOf, - opcCast, opcQuit, opcReset: + of opcRepr: + decodeB(nkStrLit) + regs[ra].strVal = renderTree(regs[rb].skipMeta, {renderNoComments}) + of opcQuit: + if c.mode in {emRepl, emStaticExpr, emStaticStmt}: + Message(c.debug[pc], hintQuitCalled) + quit(int(getOrdValue(regs[ra]))) + else: + return nil + of opcSetLenStr: + decodeB(nkStrLit) + regs[ra].strVal.setLen(regs[rb].getOrdValue.int) + of opcOf: + decodeBC(nkIntLit) + let typ = c.types[regs[rc].intVal.int] + regs[ra].intVal = ord(inheritanceDiff(regs[rb].typ, typ) >= 0) + of opcIs: + decodeBC(nkIntLit) + let t1 = regs[rb].typ.skipTypes({tyTypeDesc}) + let t2 = c.types[regs[rc].intVal.int] + let match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1) + else: sameType(t1, t2) + regs[ra].intVal = ord(match) + of opcSetLenSeq: + decodeB(nkBracket) + let newLen = regs[rb].getOrdValue.int + setLen(regs[ra].sons, newLen) + of opcSwap, opcReset: internalError(c.debug[pc], "too implement") + of opcIsNil: + decodeB(nkIntLit) + regs[ra].intVal = ord(regs[rb].skipMeta.kind == nkNilLit) of opcNBindSym: - # trivial implementation: - let rb = instr.regB - regs[ra] = regs[rb].sons[1] + decodeBx(nkMetaNode) + setMeta(regs[ra], copyTree(c.constants.sons[rbx])) of opcNChild: - let rb = instr.regB - let rc = instr.regC - regs[ra] = regs[rb].sons[regs[rc].intVal.int] + decodeBC(nkMetaNode) + if regs[rb].kind != nkMetaNode: + internalError(c.debug[pc], "no MetaNode") + let idx = regs[rc].intVal.int + let src = regs[rb].uast + if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: + setMeta(regs[ra], src.sons[idx]) + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNSetChild: - let rb = instr.regB - let rc = instr.regC - regs[ra].sons[regs[rb].intVal.int] = regs[rc] + decodeBC(nkMetaNode) + let idx = regs[rb].intVal.int + var dest = regs[ra].uast + if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len: + dest.sons[idx] = regs[rc].uast + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNAdd: - declBC() - regs[rb].add(regs[rb]) - regs[ra] = regs[rb] + decodeBC(nkMetaNode) + var u = regs[rb].uast + u.add(regs[rc].uast) + setMeta(regs[ra], u) of opcNAddMultiple: - declBC() + decodeBC(nkMetaNode) let x = regs[rc] + var u = regs[rb].uast # XXX can be optimized: - for i in 0.. <x.len: regs[rb].add(x.sons[i]) - regs[ra] = regs[rb] + for i in 0.. <x.len: u.add(x.sons[i].skipMeta) + setMeta(regs[ra], u) of opcNKind: decodeB(nkIntLit) - regs[ra].intVal = ord(regs[rb].kind) + regs[ra].intVal = ord(regs[rb].uast.kind) of opcNIntVal: decodeB(nkIntLit) - let a = regs[rb] + let a = regs[rb].uast case a.kind of nkCharLit..nkInt64Lit: regs[ra].intVal = a.intVal else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal") of opcNFloatVal: decodeB(nkFloatLit) - let a = regs[rb] + let a = regs[rb].uast case a.kind of nkFloatLit..nkFloat64Lit: regs[ra].floatVal = a.floatVal else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") of opcNSymbol: - let rb = instr.regB - if regs[rb].kind != nkSym: + decodeB(nkSym) + let a = regs[rb].uast + if a.kind == nkSym: + regs[ra].sym = a.sym + else: stackTrace(c, tos, pc, errFieldXNotFound, "symbol") - regs[ra] = regs[rb] of opcNIdent: - let rb = instr.regB - if regs[rb].kind != nkIdent: + decodeB(nkIdent) + let a = regs[rb].uast + if a.kind == nkIdent: + regs[ra].ident = a.ident + else: stackTrace(c, tos, pc, errFieldXNotFound, "ident") - regs[ra] = regs[rb] of opcNGetType: - InternalError(c.debug[pc], "unknown opcode " & $instr.opcode) + InternalError(c.debug[pc], "unknown opcode " & $instr.opcode) of opcNStrVal: decodeB(nkStrLit) - let a = regs[rb] + let a = regs[rb].uast case a.kind of nkStrLit..nkTripleStrLit: regs[ra].strVal = a.strVal else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") @@ -714,25 +886,26 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = of opcNHint: Message(c.debug[pc], hintUser, regs[ra].strVal) of opcParseExprToAst: - let rb = instr.regB + decodeB(nkMetaNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? let ast = parseString(regs[rb].strVal, c.debug[pc].toFilename, c.debug[pc].line.int) if sonsLen(ast) != 1: GlobalError(c.debug[pc], errExprExpected, "multiple statements") - regs[ra] = ast.sons[0] + setMeta(regs[ra], ast.sons[0]) of opcParseStmtToAst: - let rb = instr.regB + decodeB(nkMetaNode) let ast = parseString(regs[rb].strVal, c.debug[pc].toFilename, c.debug[pc].line.int) - regs[ra] = ast + setMeta(regs[ra], ast) of opcCallSite: - if c.callsite != nil: regs[ra] = c.callsite + ensureKind(nkMetaNode) + if c.callsite != nil: setMeta(regs[ra], c.callsite) else: stackTrace(c, tos, pc, errFieldXNotFound, "callsite") of opcNLineInfo: - let rb = instr.regB + decodeB(nkStrLit) let n = regs[rb] - regs[ra] = newStrNode(nkStrLit, n.info.toFileLineCol) + regs[ra].strVal = n.info.toFileLineCol regs[ra].info = c.debug[pc] of opcEqIdent: decodeBC(nkIntLit) @@ -741,16 +914,16 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = else: regs[ra].intVal = 0 of opcStrToIdent: - let rb = instr.regB + decodeB(nkIdent) if regs[rb].kind notin {nkStrLit..nkTripleStrLit}: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") else: - regs[ra] = newNodeI(nkIdent, c.debug[pc]) + regs[ra].info = c.debug[pc] regs[ra].ident = getIdent(regs[rb].strVal) of opcIdentToStr: - let rb = instr.regB + decodeB(nkStrLit) let a = regs[rb] - regs[ra] = newNodeI(nkStrLit, c.debug[pc]) + regs[ra].info = c.debug[pc] if a.kind == nkSym: regs[ra].strVal = a.sym.name.s elif a.kind == nkIdent: @@ -767,84 +940,117 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame) = stackTrace(c, tos, pc, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ "unknown type" , "unknown type"]) - of opcNSetIntVal: + of opcCast: let rb = instr.regB - if regs[ra].kind in {nkCharLit..nkInt64Lit} and + inc pc + let typ = c.types[c.code[pc].regBx - wordExcess] + when hasFFI: + let dest = fficast(regs[rb], typ) + asgnRef(regs[ra], dest) + else: + globalError(c.debug[pc], "cannot evaluate cast") + of opcNSetIntVal: + decodeB(nkMetaNode) + var dest = regs[ra].uast + if dest.kind in {nkCharLit..nkInt64Lit} and regs[rb].kind in {nkCharLit..nkInt64Lit}: - regs[ra].intVal = regs[rb].intVal - else: + dest.intVal = regs[rb].intVal + else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal") of opcNSetFloatVal: - let rb = instr.regB - if regs[ra].kind in {nkFloatLit..nkFloat64Lit} and + decodeB(nkMetaNode) + var dest = regs[ra].uast + if dest.kind in {nkFloatLit..nkFloat64Lit} and regs[rb].kind in {nkFloatLit..nkFloat64Lit}: - regs[ra].floatVal = regs[rb].floatVal + dest.floatVal = regs[rb].floatVal else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") of opcNSetSymbol: - let rb = instr.regB - if regs[ra].kind == nkSym and regs[rb].kind == nkSym: - regs[ra].sym = regs[rb].sym + decodeB(nkMetaNode) + var dest = regs[ra].uast + if dest.kind == nkSym and regs[rb].kind == nkSym: + dest.sym = regs[rb].sym else: stackTrace(c, tos, pc, errFieldXNotFound, "symbol") of opcNSetIdent: - let rb = instr.regB - if regs[ra].kind == nkIdent and regs[rb].kind == nkIdent: - regs[ra].ident = regs[rb].ident + decodeB(nkMetaNode) + var dest = regs[ra].uast + if dest.kind == nkIdent and regs[rb].kind == nkIdent: + dest.ident = regs[rb].ident else: stackTrace(c, tos, pc, errFieldXNotFound, "ident") of opcNSetType: - let b = regs[instr.regB] + decodeB(nkMetaNode) + let b = regs[rb].skipMeta InternalAssert b.kind == nkSym and b.sym.kind == skType - regs[ra].typ = b.sym.typ + regs[ra].uast.typ = b.sym.typ of opcNSetStrVal: - let rb = instr.regB - if regs[ra].kind in {nkStrLit..nkTripleStrLit} and + decodeB(nkMetaNode) + var dest = regs[ra].uast + if dest.kind in {nkStrLit..nkTripleStrLit} and regs[rb].kind in {nkStrLit..nkTripleStrLit}: - regs[ra].strVal = regs[rb].strVal + dest.strVal = regs[rb].strVal else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") of opcNNewNimNode: - let rb = instr.regB - let rc = instr.regC + decodeBC(nkMetaNode) var k = regs[rb].intVal - if k < 0 or k > ord(high(TNodeKind)): + if k < 0 or k > ord(high(TNodeKind)) or k == ord(nkMetaNode): internalError(c.debug[pc], - "request to create a NimNode with invalid kind") - regs[ra] = newNodeI(TNodeKind(int(k)), - if regs[rc].kind == nkNilLit: c.debug[pc] else: regs[rc].info) + "request to create a NimNode of invalid kind") + let cc = regs[rc].skipMeta + setMeta(regs[ra], newNodeI(TNodeKind(int(k)), + if cc.kind == nkNilLit: c.debug[pc] else: cc.info)) + regs[ra].sons[0].flags.incl nfIsRef of opcNCopyNimNode: - let rb = instr.regB - regs[ra] = copyNode(regs[rb]) + decodeB(nkMetaNode) + setMeta(regs[ra], copyNode(regs[rb])) of opcNCopyNimTree: - let rb = instr.regB - regs[ra] = copyTree(regs[rb]) + decodeB(nkMetaNode) + setMeta(regs[ra], copyTree(regs[rb])) of opcNDel: - let rb = instr.regB - let rc = instr.regC + decodeBC(nkMetaNode) + let bb = regs[rb].intVal.int for i in countup(0, regs[rc].intVal.int-1): - delSon(regs[ra], regs[rb].intVal.int) + delSon(regs[ra].uast, bb) of opcGenSym: - let k = regs[instr.regB].intVal - let b = regs[instr.regC] - let name = if b.strVal.len == 0: ":tmp" else: b.strVal + decodeBC(nkMetaNode) + let k = regs[rb].intVal + let name = if regs[rc].strVal.len == 0: ":tmp" else: regs[rc].strVal if k < 0 or k > ord(high(TSymKind)): internalError(c.debug[pc], "request to create symbol of invalid kind") - regs[ra] = newSymNode(newSym(k.TSymKind, name.getIdent, c.module, - c.debug[pc])) - incl(regs[ra].sym.flags, sfGenSym) + var sym = newSym(k.TSymKind, name.getIdent, c.module, c.debug[pc]) + incl(sym.flags, sfGenSym) + setMeta(regs[ra], newSymNode(sym)) of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation decodeB(nkStrLit) let typ = regs[rb].sym.typ.skipTypes({tyTypeDesc}) regs[ra].strVal = typ.typeToString(preferExported) + of opcGlobalOnce: + let rb = instr.regBx + if c.globals.sons[rb - wordExcess - 1].kind != nkEmpty: + # skip initialization instructions: + while true: + inc pc + if c.code[pc].opcode in {opcWrGlobal, opcWrGlobalRef} and + c.code[pc].regBx == rb: + break + of opcGlobalAlias: + let rb = instr.regBx - wordExcess - 1 + regs[ra] = c.globals.sons[rb] inc pc -proc execute(c: PCtx, start: int) = +proc fixType(result, n: PNode) {.inline.} = + # XXX do it deeply for complex values + #if result.typ.isNil: result.typ = n.typ + +proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) - rawExecute(c, start, tos) + for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) + result = rawExecute(c, start, tos) proc evalStmt*(c: PCtx, n: PNode) = let start = genStmt(c, n) @@ -857,12 +1063,30 @@ proc evalExpr*(c: PCtx, n: PNode): PNode = let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) + if not result.isNil: + result = result.skipMeta + fixType(result, n) + +# for now we share the 'globals' environment. XXX Coming soon: An API for +# storing&loading the 'globals' environment to get what a component system +# requires. +var + globalCtx: PCtx + +proc setupGlobalCtx(module: PSym) = + if globalCtx.isNil: globalCtx = newCtx(module) + else: refresh(globalCtx, module) proc myOpen(module: PSym): PPassContext = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowFFI, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) - result = newCtx(module) + + # XXX produce a new 'globals' environment here: + setupGlobalCtx(module) + result = globalCtx + when hasFFI: + globalCtx.features = {allowFFI, allowCast} var oldErrorCount: int @@ -875,50 +1099,70 @@ proc myProcess(c: PPassContext, n: PNode): PNode = result = n oldErrorCount = msgs.gErrorCounter -const vmPass* = makePass(myOpen, nil, myProcess, myProcess) +const evalPass* = makePass(myOpen, nil, myProcess, myProcess) -proc evalConstExprAux(module, prc: PSym, e: PNode, mode: TEvalMode): PNode = - var p = newCtx(module) - var s = newStackFrame() - s.call = e - s.prc = prc - pushStackFrame(p, s) - result = tryEval(p, e) - if result != nil and result.kind == nkExceptBranch: result = nil - popStackFrame(p) +proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode = + setupGlobalCtx(module) + var c = globalCtx + c.mode = mode + let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) + assert c.code[start].opcode != opcEof + var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil) + newSeq(tos.slots, c.prc.maxSlots) + for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) + result = rawExecute(c, start, tos) + fixType(result, n) proc evalConstExpr*(module: PSym, e: PNode): PNode = result = evalConstExprAux(module, nil, e, emConst) -proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode = - result = evalConstExprAux(module, prc, e, emStatic) +proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode = + result = evalConstExprAux(module, prc, e, emStaticExpr) + +proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = + discard evalConstExprAux(module, prc, e, emStaticStmt) proc setupMacroParam(x: PNode): PNode = result = x if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] + let y = result + y.flags.incl nfIsRef + result = newNode(nkMetaNode) + result.add y + result.typ = x.typ -proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode = +var evalMacroCounter: int + +proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # XXX GlobalError() is ugly here, but I don't know a better solution for now - inc(evalTemplateCounter) - if evalTemplateCounter > 100: + inc(evalMacroCounter) + if evalMacroCounter > 100: GlobalError(n.info, errTemplateInstantiationTooNested) + setupGlobalCtx(module) + var c = globalCtx c.callsite = nOrig - let body = optBody(c, sym) - let start = genStmt(c, body) + let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) - newSeq(tos.slots, c.prc.maxSlots) + let maxSlots = sym.offset + newSeq(tos.slots, maxSlots) # setup arguments: var L = n.safeLen if L == 0: L = 1 - InternalAssert tos.slots.len >= L + # This is wrong for tests/reject/tind1.nim where the passed 'else' part + # doesn't end up in the parameter: + #InternalAssert tos.slots.len >= L # return value: tos.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) # setup parameters: - for i in 1 .. < L: tos.slots[i] = setupMacroParam(n.sons[i]) - rawExecute(c, start, tos) - result = tos.slots[0] + for i in 1 .. < min(tos.slots.len, L): + tos.slots[i] = setupMacroParam(n.sons[i]) + # temporary storage: + for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) + result = rawExecute(c, start, tos) if cyclicTree(result): GlobalError(n.info, errCyclicTree) - dec(evalTemplateCounter) + dec(evalMacroCounter) + if result != nil: + result = result.skipMeta c.callsite = nil diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index d4b3d891d..b4b787798 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -51,16 +51,16 @@ type opcLenSeq, opcLenStr, - opcIncl, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt, + opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt, opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt, opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, - opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcXor, + opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, - opcSwap, opcIsNil, opcOf, + opcSwap, opcIsNil, opcOf, opcIs, opcSubStr, opcConv, opcCast, opcQuit, opcReset, opcAddStrCh, @@ -101,7 +101,6 @@ type opcRaise, opcNChild, opcNSetChild, - opcNBindSym, # opcodes for the AST manipulation following opcCallSite, opcNewStr, @@ -120,8 +119,11 @@ type opcAsgnConst, # dest = copy(constants[Bx]) opcLdGlobal, # dest = globals[Bx] opcLdImmInt, # dest = immediate value + opcNBindSym, opcWrGlobal, opcWrGlobalRef, + opcGlobalAlias, # load an alias to a global into a register + opcGlobalOnce, # used to introduce an assignment to a global once opcSetType, # dest.typ = types[Bx] opcTypeTrait @@ -129,6 +131,21 @@ type label*: PSym fixups*: seq[TPosition] + TEvalMode* = enum ## reason for evaluation + emRepl, ## evaluate because in REPL mode + emConst, ## evaluate for 'const' according to spec + emOptimize, ## evaluate for optimization purposes (same as + ## emConst?) + emStaticExpr, ## evaluate for enforced compile time eval + ## ('static' context) + emStaticStmt ## 'static' as an expression + + TSandboxFlag* = enum ## what the evaluation engine should allow + allowCast, ## allow unsafe language feature: 'cast' + allowFFI, ## allow the FFI + allowInfiniteLoops ## allow endless loops + TSandboxFlags* = set[TSandboxFlag] + TSlotKind* = enum # We try to re-use slots in a smart way to # minimize allocations; however the VM supports arbitrary # temporary slot usage. This is required for the parameter @@ -146,6 +163,8 @@ type blocks*: seq[TBlock] # blocks; temp data structure slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] maxSlots*: int + globals*: array[TRegister, int] # hack: to support passing globals byref + # we map a slot persistently to a global PCtx* = ref TCtx TCtx* = object of passes.TPassContext # code gen context @@ -160,17 +179,22 @@ type prc*: PProc module*: PSym callsite*: PNode + mode*: TEvalMode + features*: TSandboxFlags TPosition* = distinct int PEvalContext* = PCtx - proc newCtx*(module: PSym): PCtx = PCtx(code: @[], debug: @[], - globals: newNode(nkStmtList), constants: newNode(nkStmtList), types: @[], + globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module) +proc refresh*(c: PCtx, module: PSym) = + c.module = module + c.prc = PProc(blocks: @[]) + const firstABxInstr* = opcTJmp largeInstrs* = { # instructions which use 2 int32s instead of 1: @@ -183,3 +207,5 @@ template regA*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 8'u3 template regB*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 16'u32 and 0xff'u32) template regC*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 24'u32) template regBx*(x: TInstr): int {.immediate.} = (x.uint32 shr 16'u32).int + +template jmpDiff*(x: TInstr): int {.immediate.} = regBx(x) - wordExcess diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 2a40276d1..07100897b 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -32,62 +32,5 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = appendToModule(module, newNode(nkIncludeStmt, info, @[ newStrNode(nkStrLit, filename)])) except EIO: - result = "" LocalError(info, errCannotOpenFile, file) - -when false: - proc opExpandToAst*(c: PEvalContext, original: PNode): PNode = - var - n = original.copyTree - macroCall = n.sons[1] - expandedSym = macroCall.sons[0].sym - - for i in countup(1, macroCall.sonsLen - 1): - macroCall.sons[i] = evalAux(c, macroCall.sons[i], {}) - - case expandedSym.kind - of skTemplate: - let genSymOwner = if c.tos != nil and c.tos.prc != nil: - c.tos.prc - else: - c.module - result = evalTemplate(macroCall, expandedSym, genSymOwner) - of skMacro: - # At this point macroCall.sons[0] is nkSym node. - # To be completely compatible with normal macro invocation, - # we want to replace it with nkIdent node featuring - # the original unmangled macro name. - macroCall.sons[0] = newIdentNode(expandedSym.name, expandedSym.info) - result = evalMacroCall(c, macroCall, original, expandedSym) - else: - InternalError(macroCall.info, - "ExpandToAst: expanded symbol is no macro or template") - result = emptyNode - - proc opIs*(n: PNode): PNode = - InternalAssert n.sonsLen == 3 and - n[1].kind == nkSym and n[1].sym.kind == skType and - n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - - let t1 = n[1].sym.typ - - if n[2].kind in {nkStrLit..nkTripleStrLit}: - case n[2].strVal.normalize - of "closure": - let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator notin t.flags)) - of "iterator": - let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator in t.flags)) - else: - let t2 = n[2].typ - var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1) - else: sameType(t1, t2) - result = newIntNode(nkIntLit, ord(match)) - - result.typ = n.typ - + result = "" diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 84d82e117..ab120f008 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -11,19 +11,22 @@ import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, - trees, intsets, rodread, magicsys + trees, intsets, rodread, magicsys, options -proc codeListing(c: PCtx, result: var string) = +when hasFFI: + import evalffi + +proc codeListing(c: PCtx, result: var string, start=0) = # first iteration: compute all necessary labels: var jumpTargets = initIntSet() - for i in 0.. < c.code.len: + for i in start.. < c.code.len: let x = c.code[i] if x.opcode in relativeJumps: jumpTargets.incl(i+x.regBx-wordExcess) # for debugging purposes - var i = 0 + var i = start while i < c.code.len: if i in jumpTargets: result.addf("L$1:\n", i) let x = c.code[i] @@ -45,9 +48,9 @@ proc codeListing(c: PCtx, result: var string) = result.add("\n") inc i -proc echoCode*(c: PCtx) = +proc echoCode*(c: PCtx, start=0) {.deprecated.} = var buf = "" - codeListing(c, buf) + codeListing(c, buf, start) echo buf proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = @@ -112,8 +115,10 @@ const proc getTemp(c: PCtx; typ: PType): TRegister = let c = c.prc - # we prefer the same slot kind here for efficiency: - let k = typ.getSlotKind + # we prefer the same slot kind here for efficiency. Unfortunately for + # discardable return types we may not know the desired type. This can happen + # for e.g. mNAdd[Multiple]: + let k = if typ.isNil: slotTempComplex else: typ.getSlotKind for i in 0 .. c.maxSlots-1: if c.slots[i].kind == k and not c.slots[i].inUse: c.slots[i].inUse = true @@ -129,6 +134,21 @@ proc getTemp(c: PCtx; typ: PType): TRegister = c.slots[c.maxSlots] = (inUse: true, kind: k) inc c.maxSlots +proc getGlobalSlot(c: PCtx; n: PNode; s: PSym): TRegister = + let p = c.prc + for i in 0 .. p.maxSlots-1: + if p.globals[i] == s.id: return TRegister(i) + + result = TRegister(p.maxSlots) + p.slots[p.maxSlots] = (inUse: true, kind: slotFixedVar) + p.globals[p.maxSlots] = s.id + inc p.maxSlots + # XXX this is still not correct! We need to load the global in a proc init + # section, otherwise control flow could lead to a usage before it's been + # loaded. + c.gABx(n, opcGlobalAlias, result, s.position) + # XXX add some internal asserts here + proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false @@ -179,13 +199,16 @@ proc gen(c: PCtx; n: PNode; dest: TRegister) = proc gen(c: PCtx; n: PNode) = var tmp: TDest = -1 gen(c, n, tmp) - InternalAssert tmp < 0 + #if n.typ.isEmptyType: InternalAssert tmp < 0 proc genx(c: PCtx; n: PNode): TRegister = var tmp: TDest = -1 gen(c, n, tmp) result = TRegister(tmp) +proc clearDest(n: PNode; dest: var TDest) {.inline.} = + if isEmptyType(n.typ): dest = -1 + proc isNotOpr(n: PNode): bool = n.kind in nkCallKinds and n.sons[0].kind == nkSym and n.sons[0].sym.magic == mNot @@ -224,6 +247,7 @@ proc genWhile(c: PCtx; n: PNode) = proc genBlock(c: PCtx; n: PNode; dest: var TDest) = withBlock(n.sons[0].sym): c.gen(n.sons[1], dest) + clearDest(n, dest) proc genBreak(c: PCtx; n: PNode) = let L1 = c.xjmp(n, opcJmp) @@ -268,6 +292,7 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) = else: c.gen(it.sons[0], dest) for endPos in endings: c.patch(endPos) + clearDest(n, dest) proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, a @@ -275,9 +300,9 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, b # L1: if dest < 0: dest = getTemp(c, n.typ) - c.gen(n.sons[0], dest) - let L1 = c.xjmp(n, opc) c.gen(n.sons[1], dest) + let L1 = c.xjmp(n, opc, dest) + c.gen(n.sons[2], dest) c.patch(L1) proc rawGenLiteral(c: PCtx; n: PNode): int = @@ -296,7 +321,8 @@ proc sameConstant*(a, b: PNode): bool = of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal - of nkEmpty, nkNilLit, nkType: result = true + of nkType: result = a.typ == b.typ + of nkEmpty, nkNilLit: result = true else: if sonsLen(a) == sonsLen(b): for i in countup(0, sonsLen(a) - 1): @@ -309,6 +335,11 @@ proc genLiteral(c: PCtx; n: PNode): int = if sameConstant(c.constants[i], n): return i result = rawGenLiteral(c, n) +proc unused(n: PNode; x: TDest) {.inline.} = + if x >= 0: + #debug(n) + InternalError(n.info, "not unused") + proc genCase(c: PCtx; n: PNode; dest: var TDest) = # if (!expr1) goto L1; # thenPart @@ -320,7 +351,10 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = # L2: # elsePart # Lend: - if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) + if not isEmptyType(n.typ): + if dest < 0: dest = getTemp(c, n.typ) + else: + unused(n, dest) var endings: seq[TPosition] = @[] withTemp(tmp, n.sons[0].typ): c.gen(n.sons[0], tmp) @@ -340,6 +374,7 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = endings.add(c.xjmp(it.lastSon, opcJmp, 0)) c.patch(elsePos) for endPos in endings: c.patch(endPos) + clearDest(n, dest) proc genType(c: PCtx; typ: PType): int = for i, t in c.types: @@ -379,6 +414,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = if fin.kind == nkFinally: c.gen(fin.sons[0], dest) c.gABx(fin, opcFinallyEnd, 0, 0) + clearDest(n, dest) proc genRaise(c: PCtx; n: PNode) = let dest = genx(c, n.sons[0]) @@ -393,14 +429,20 @@ 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: c.gABC(n, opcIndCallAsgn, dest, x, n.len) c.freeTempRange(x, n.len) + clearDest(n, dest) proc genNew(c: PCtx; n: PNode) = let dest = c.genx(n.sons[1]) @@ -463,7 +505,7 @@ proc genBinaryStmt(c: PCtx; n: PNode; opc: TOpcode) = c.freeTemp(tmp) proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) = - let tmp = c.genx(n.sons[2]) + let tmp = c.genx(n.sons[1]) c.gABC(n, opc, tmp, 0, 0) c.freeTemp(tmp) @@ -493,9 +535,6 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = else: genBinaryABC(c, n, dest, opc) -proc unused(n: PNode; x: TDest) {.inline.} = - if x >= 0: InternalError(n.info, "not unused") - proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = let tmp = c.genx(arg) c.gABx(n, opcSetType, tmp, genType(c, arg.typ)) @@ -508,7 +547,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) = let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) c.genSetType(n.sons[1], tmp) - c.gABC(n, opc, dest, tmp) + c.gABC(n, opcCard, dest, tmp) c.freeTemp(tmp) proc genMagic(c: PCtx; n: PNode; dest: var TDest) = @@ -659,12 +698,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = unused(n, dest) var d = c.genx(n.sons[1]) c.gABC(n, opcReset, d) - of mOf: + of mOf, mIs: if dest < 0: dest = c.getTemp(n.typ) var tmp = c.genx(n.sons[1]) - c.gABC(n, opcOf, dest, tmp) - c.gABx(n, opcOf, 0, c.genType(n.sons[2].typ.skipTypes(abstractPtrs))) + var idx = c.getTemp(getSysType(tyInt)) + var typ = n.sons[2].typ + if m == mOf: typ = typ.skipTypes(abstractPtrs) + c.gABx(n, opcLdImmInt, idx, c.genType(typ)) + c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx) c.freeTemp(tmp) + c.freeTemp(idx) of mSizeOf: GlobalError(n.info, errCannotInterpretNodeX, renderTree(n)) of mHigh: @@ -696,16 +739,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = genUnaryABC(c, n, dest, opcParseExprToAst) of mParseStmtToAst: genUnaryABC(c, n, dest, opcParseStmtToAst) - of mExpandToAst: - InternalError(n.info, "cannot generate code for: " & $m) of mTypeTrait: let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) - c.gABx(n, opcSetType, tmp, c.genType(n.sons[1])) + c.gABx(n, opcSetType, tmp, c.genType(n.sons[1].typ)) c.gABC(n, opcTypeTrait, dest, tmp) c.freeTemp(tmp) - of mIs: - InternalError(n.info, "cannot generate code for: " & $m) of mSlurp: genUnaryABC(c, n, dest, opcSlurp) of mStaticExec: genBinaryABC(c, n, dest, opcGorge) of mNLen: genUnaryABI(c, n, dest, opcLenSeq) @@ -750,11 +789,17 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode) of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode) of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree) - of mNBindSym: genUnaryABC(c, n, dest, opcNBindSym) + of mNBindSym: + if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}: + let idx = c.genLiteral(n[1]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABx(n, opcNBindSym, dest, idx) + else: + internalError(n.info, "invalid bindSym usage") of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) of mIdentToStr: genUnaryABC(c, n, dest, opcIdentToStr) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) - of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqRef) + of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) of mNLineInfo: genUnaryABC(c, n, dest, opcNLineInfo) of mNHint: unused(n, dest) @@ -771,6 +816,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64: c.genCall(n, dest) + of mExpandToAst: + if n.len != 2: + globalError(n.info, errGenerated, "expandToAst requires 1 argument") + let arg = n.sons[1] + if arg.kind in nkCallKinds: + #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}: + # "ExpandToAst: expanded symbol is no macro or template" + c.genCall(arg, dest) + else: + globalError(n.info, "expandToAst requires a call expression") else: # mGCref, mGCunref, InternalError(n.info, "cannot generate code for: " & $m) @@ -823,7 +878,7 @@ proc whichAsgnOpc(n: PNode): TOpcode = opcAsgnStr of tyFloat..tyFloat128: opcAsgnFloat - of tyRef, tyNil: + of tyRef, tyNil, tyVar: opcAsgnRef else: opcAsgnComplex @@ -839,6 +894,8 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = gABC(c, ri, whichAsgnOpc(ri), dest, tmp) c.freeTemp(tmp) +template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar + proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: @@ -850,15 +907,17 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = else: c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp) c.freeTemp(tmp) - of nkDotExpr: - let dest = c.genx(le.sons[0]) - let idx = c.genx(le.sons[1]) + of nkDotExpr, nkCheckedFieldExpr: + # XXX field checks here + let left = if le.kind == nkDotExpr: le else: le.sons[0] + let dest = c.genx(left.sons[0]) + let idx = c.genx(left.sons[1]) let tmp = c.genx(ri) - c.gABC(le, whichAsgnOpc(le, opcWrObj), dest, idx, tmp) + c.gABC(left, whichAsgnOpc(left, opcWrObj), dest, idx, tmp) c.freeTemp(tmp) of nkSym: let s = le.sym - if sfGlobal in s.flags: + if s.isGlobal: withTemp(tmp, le.typ): gen(c, ri, tmp) c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position) @@ -878,15 +937,52 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) = let lit = genLiteral(c, n) c.gABx(n, opc, dest, lit) +proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = + var n = newNode(nkType) + n.typ = t + genLit(c, n, dest) + +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 cannotEval(n: PNode) {.noinline.} = + globalError(n.info, errGenerated, "cannot evaluate at compile time: " & + n.renderTree) + +proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = + c.globals.add(emptyNode.copyNode) + s.position = c.globals.len + # This is rather hard to support, due to the laziness of the VM code + # generator. See tests/compile/tmacro2 for why this is necesary: + # var decls{.compileTime.}: seq[PNimrodNode] = @[] + c.gABx(n, opcGlobalOnce, 0, s.position) + let tmp = c.genx(s.ast) + c.gABx(n, whichAsgnOpc(n, opcWrGlobal), tmp, s.position) + c.freeTemp(tmp) + 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.isGlobal: + if sfCompileTime in s.flags or c.mode == emRepl: + discard + else: + cannotEval(n) if s.position == 0: - c.globals.add(s.ast) - s.position = c.globals.len - # XXX var g = codeHere() ? - c.gABx(n, opcLdGlobal, dest, s.position) + if sfImportc in s.flags: c.importcSym(n.info, s) + else: genGlobalInit(c, n, s) + if dest < 0: + dest = c.getGlobalSlot(n, s) + #c.gABx(n, opcAliasGlobal, dest, s.position) + else: + c.gABx(n, opcLdGlobal, dest, s.position) else: if s.position > 0 or (s.position == 0 and s.kind in {skParam, skResult}): if dest < 0: @@ -895,7 +991,9 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = # we need to generate an assignment: genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp) else: - InternalError(n.info, s.name.s & " " & $s.position) + # see tests/t99bott for an example that triggers it: + cannotEval(n) + #InternalError(n.info, s.name.s & " " & $s.position) proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let a = c.genx(n.sons[0]) @@ -908,6 +1006,10 @@ proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = proc genObjAccess(c: PCtx; n: PNode; dest: var TDest) = genAccess(c, n, dest, opcLdObj) +proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest) = + # XXX implement field checks! + genAccess(c, n.sons[0], dest, opcLdObj) + proc genArrAccess(c: PCtx; n: PNode; dest: var TDest) = if n.sons[0].typ.skipTypes(abstractVarRange).kind in {tyString, tyCString}: genAccess(c, n, dest, opcLdStrIdx) @@ -938,8 +1040,15 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = of tyFloat..tyFloat128: result = newNodeIt(nkFloatLit, info, t) of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, - tyStmt, tyTypeDesc, tyProc, tyRef: + tyStmt, tyTypeDesc, tyRef: result = newNodeIT(nkNilLit, info, t) + of tyProc: + if t.callConv != ccClosure: + result = newNodeIT(nkNilLit, info, t) + else: + result = newNodeIT(nkPar, info, t) + result.add(newNodeIT(nkNilLit, info, t)) + result.add(newNodeIT(nkNilLit, info, t)) of tyObject: result = newNodeIT(nkPar, info, t) getNullValueAux(t.n, result) @@ -984,11 +1093,14 @@ proc genVarSection(c: PCtx; n: PNode) = c.freeTemp(tmp) elif a.sons[0].kind == nkSym: let s = a.sons[0].sym - if sfGlobal in s.flags: + if s.isGlobal: 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 + # "Once support" is unnecessary here if a.sons[2].kind == nkEmpty: when false: withTemp(tmp, s.typ): @@ -1016,23 +1128,31 @@ proc genVarSection(c: PCtx; n: PNode) = proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) - let intType = getSysType(tyInt) - var tmp = getTemp(c, intType) - c.gABx(n, opcLdNull, tmp, c.genType(intType)) - for x in n: - let a = c.genx(x) - c.gABC(n, opcWrArr, dest, a, tmp) - c.gABI(n, opcAddImmInt, tmp, tmp, 1) - c.freeTemp(a) - c.freeTemp(tmp) + if n.len > 0: + let intType = getSysType(tyInt) + var tmp = getTemp(c, intType) + c.gABx(n, opcLdNull, tmp, c.genType(intType)) + for x in n: + let a = c.genx(x) + c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) + c.gABI(n, opcAddImmInt, tmp, tmp, 1) + c.freeTemp(a) + c.freeTemp(tmp) proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) for x in n: - let a = c.genx(x) - c.gABC(n, opcIncl, dest, a) - c.freeTemp(a) + if x.kind == nkRange: + let a = c.genx(x.sons[0]) + let b = c.genx(x.sons[1]) + c.gABC(n, opcInclRange, dest, a, b) + c.freeTemp(b) + c.freeTemp(a) + else: + let a = c.genx(x) + c.gABC(n, opcIncl, dest, a) + c.freeTemp(a) proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) @@ -1054,7 +1174,8 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) - var idx = getTemp(c, getSysType(tyInt)) + c.gABx(n, opcLdNull, dest, c.genType(n.typ)) + # XXX x = (x.old, 22) produces wrong code ... stupid self assignments for i in 0.. <n.len: let it = n.sons[i] if it.kind == nkExprColonExpr: @@ -1065,10 +1186,8 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = c.freeTemp(idx) else: let tmp = c.genx(it) - c.gABx(it, opcLdImmInt, idx, i) - c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, idx, tmp) + c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) c.freeTemp(tmp) - c.freeTemp(idx) proc genProc*(c: PCtx; s: PSym): int @@ -1079,7 +1198,9 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) = case s.kind of skVar, skForVar, skTemp, skLet, skParam, skResult: genRdVar(c, n, dest) - of skProc, skConverter, skMacro, skMethod, skIterator: + of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator: + # 'skTemplate' is only allowed for 'getAst' support: + if sfImportc in s.flags: c.importcSym(n.info, s) genLit(c, n, dest) of skConst: gen(c, s.ast, dest) @@ -1096,6 +1217,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) = InternalError(n.info, "too large offset! cannot generate code for: " & s.name.s) dest = s.position + of skType: + genTypeLit(c, s.typ, dest) else: InternalError(n.info, "cannot generate code for: " & s.name.s) of nkCallKinds: @@ -1109,11 +1232,15 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) = c.gABx(n, opcLdImmInt, dest, n.intVal.int) else: genLit(c, n, dest) - of nkUIntLit..nkNilLit: genLit(c, n, dest) + of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) + of nkNilLit: + if not n.typ.isEmptyType: genLit(c, n, dest) + else: unused(n, dest) of nkAsgn, nkFastAsgn: unused(n, dest) genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn) of nkDotExpr: genObjAccess(c, n, dest) + of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest) of nkBracketExpr: genArrAccess(c, n, dest) of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref) of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr) @@ -1176,6 +1303,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) = of nkCurly: genSetConstr(c, n, dest) of nkObjConstr: genObjConstr(c, n, dest) of nkPar, nkClosure: genTupleConstr(c, n, dest) + of nkCast: + if allowCast in c.features: + genConv(c, n, n.sons[1], dest, opcCast) + else: + localError(n.info, errGenerated, "VM is not allowed to 'cast'") else: InternalError n.info, "too implement " & $n.kind @@ -1193,14 +1325,16 @@ proc genStmt*(c: PCtx; n: PNode): int = var d: TDest = -1 c.gen(n, d) c.gABC(n, opcEof) - InternalAssert d < 0 + if d >= 0: internalError(n.info, "some destination set") -proc genExpr*(c: PCtx; n: PNode): int = +proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = c.removeLastEof result = c.code.len var d: TDest = -1 c.gen(n, d) - InternalAssert d >= 0 + if d < 0: + if requiresValue: internalError(n.info, "no destination set") + d = 0 c.gABC(n, opcEof, d) proc genParams(c: PCtx; params: PNode) = @@ -1225,12 +1359,11 @@ proc optimizeJumps(c: PCtx; start: int) = case opc of opcTJmp, opcFJmp: var reg = c.code[i].regA - var d = i + c.code[i].regBx - var iters = maxIterations - while iters > 0: + var d = i + c.code[i].jmpDiff + for iters in countdown(maxIterations, 0): case c.code[d].opcode of opcJmp: - d = d + c.code[d].regBx + d = d + c.code[d].jmpDiff of opcTJmp, opcFJmp: if c.code[d].regA != reg: break # tjmp x, 23 @@ -1238,28 +1371,40 @@ proc optimizeJumps(c: PCtx; start: int) = # tjmp x, 12 # -- we know 'x' is true, and so can jump to 12+13: if c.code[d].opcode == opc: - d = d + c.code[d].regBx + d = d + c.code[d].jmpDiff else: # tjmp x, 23 # fjmp x, 22 # We know 'x' is true so skip to the next instruction: d = d + 1 else: break - dec iters - c.finalJumpTarget(i, d - i) + if d != i + c.code[i].jmpDiff: + c.finalJumpTarget(i, d - i) of opcJmp: - var d = i + c.code[i].regBx + var d = i + c.code[i].jmpDiff var iters = maxIterations while c.code[d].opcode == opcJmp and iters > 0: - d = d + c.code[d].regBx + d = d + c.code[d].jmpDiff dec iters - c.finalJumpTarget(i, d - i) + if c.code[d].opcode == opcRet: + # optimize 'jmp to ret' to 'ret' here + c.code[i] = c.code[d] + elif d != i + c.code[i].jmpDiff: + c.finalJumpTarget(i, d - i) else: discard proc genProc(c: PCtx; s: PSym): int = let x = s.ast.sons[optimizedCodePos] if x.kind == nkEmpty: - c.removeLastEof + #if s.name.s == "outterMacro" or s.name.s == "innerProc": + # echo "GENERATING CODE FOR ", s.name.s + let last = c.code.len-1 + var eofInstr: TInstr + if last >= 0 and c.code[last].opcode == opcEof: + eofInstr = c.code[last] + c.code.setLen(last) + c.debug.setLen(last) + #c.removeLastEof result = c.code.len+1 # skip the jump instruction s.ast.sons[optimizedCodePos] = newIntNode(nkIntLit, result) # thanks to the jmp we can add top level statements easily and also nest @@ -1271,13 +1416,22 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = p # iterate over the parameters and allocate space for them: genParams(c, s.typ.n) + if tfCapturesEnv in s.typ.flags: + #let env = s.ast.sons[paramsPos].lastSon.sym + #assert env.position == 2 + c.prc.slots[c.prc.maxSlots] = (inUse: true, kind: slotFixedLet) + inc c.prc.maxSlots gen(c, body) # generate final 'return' statement: c.gABC(body, opcRet) c.patch(procStart) - c.gABC(body, opcEof) - s.position = c.prc.maxSlots + c.gABC(body, opcEof, eofInstr.regA) + c.optimizeJumps(result) + s.offset = c.prc.maxSlots + #if s.name.s == "importImpl_forward" or s.name.s == "importImpl": + # c.echoCode(result) + # echo renderTree(body) c.prc = oldPrc - #c.echoCode else: + c.prc.maxSlots = s.offset result = x.intVal.int diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 39b19646e..5f0e5be94 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -28,9 +28,9 @@ type wElif, wElse, wEnd, wEnum, wExcept, wExport, wFinally, wFor, wFrom, wGeneric, wIf, wImport, wIn, wInclude, wInterface, wIs, wIsnot, wIterator, wLambda, wLet, - wMacro, wMethod, wMixin, wUsing, wMod, wNil, + wMacro, wMethod, wMixin, wMod, wNil, wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn, - wShared, wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wVar, + wShared, wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, wWhen, wWhile, wWith, wWithout, wXor, wYield, wColon, wColonColon, wEquals, wDot, wDotDot, @@ -95,7 +95,7 @@ const cppNimSharedKeywords* = { wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport, - wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing } + wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing} specialWords*: array[low(TSpecialWord)..high(TSpecialWord), string] = ["", @@ -107,11 +107,11 @@ const "finally", "for", "from", "generic", "if", "import", "in", "include", "interface", "is", "isnot", "iterator", "lambda", "let", - "macro", "method", "mixin", "using", "mod", "nil", "not", "notin", + "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", "shared", "shl", "shr", "static", - "template", "try", "tuple", "type", "var", + "template", "try", "tuple", "type", "using", "var", "when", "while", "with", "without", "xor", "yield", |