Merge branch 'vm2' of github.com:Araq/Nimrod into vm2
This commit is contained in:
commit
1081c104d0
78 changed files with 9241 additions and 595 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -13,3 +13,6 @@ path:"$lib/packages/docutils"
|
|||
define:booting
|
||||
import:testability
|
||||
|
||||
@if windows:
|
||||
cincludes: "$lib/wrappers/libffi/common"
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
568
compiler/vm.nim
568
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = ""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue