Merge branch 'vm2' of github.com:Araq/Nimrod into vm2

This commit is contained in:
Araq 2013-12-24 01:30:48 +01:00
commit 1081c104d0
78 changed files with 9241 additions and 595 deletions

View file

@ -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

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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]

View file

@ -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)

View file

@ -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

View file

@ -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",

View file

@ -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

View file

@ -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)

View file

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

View file

@ -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 =

View file

@ -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 =

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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 = ""

View file

@ -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

View file

@ -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",