some progress on the VM
This commit is contained in:
parent
c260b22fbc
commit
e8fc470310
103 changed files with 294 additions and 11 deletions
151
compiler/main/nimbatch.nim
Normal file
151
compiler/main/nimbatch.nim
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# This file implements the new "batch compiler" that can translate Nimrod
|
||||||
|
# code into POSIX shell scripts or Windows ``.bat`` files. Only a small
|
||||||
|
# subset of Nimrod can be translated. This compiler also acts as a new
|
||||||
|
# "hacking the Nimrod compiler" tutorial.
|
||||||
|
|
||||||
|
|
||||||
|
## ==============================
|
||||||
|
## Let's hack the Nimrod compiler
|
||||||
|
## ==============================
|
||||||
|
|
||||||
|
## Starting with version 0.9.6 the compiler's internals have been cleaned up
|
||||||
|
## quite a bit. However, still lot's of weird modules need to be imported
|
||||||
|
## before we can start:
|
||||||
|
|
||||||
|
import
|
||||||
|
llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
|
||||||
|
os, condsyms, rodread, rodwrite, times,
|
||||||
|
wordrecg, sem, semdata, idents, passes, docgen, extccomp,
|
||||||
|
cgen, jsgen, json, nversion,
|
||||||
|
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
|
||||||
|
tables, docgen2, service, parser, modules
|
||||||
|
|
||||||
|
proc `=~`(s: PSym; name: string): bool =
|
||||||
|
let a = name.split('.')
|
||||||
|
var s = s
|
||||||
|
for i in countdown(a.high, a.low):
|
||||||
|
if s == nil or not identEq(s.name, a[i]): return false
|
||||||
|
s = s.owner
|
||||||
|
return true
|
||||||
|
|
||||||
|
type
|
||||||
|
ShellTarget = enum
|
||||||
|
targetBatch, targetSh
|
||||||
|
Context = ref object
|
||||||
|
target: ShellTarget
|
||||||
|
code: string
|
||||||
|
|
||||||
|
proc `=~`(n: PNode; name: string): bool =
|
||||||
|
result = n.kind == nkSym and match(n.sym, name)
|
||||||
|
|
||||||
|
proc gen(c: Context; n: PNode) =
|
||||||
|
template `~`(x): expr = op =~ x
|
||||||
|
template `|`(a, b): expr = (if c.target == targetBatch: a else: b)
|
||||||
|
template add(x) = c.code.add(x)
|
||||||
|
template addf(x, y) = c.code.addf(x, y)
|
||||||
|
|
||||||
|
case n.kind
|
||||||
|
of nkIfStmt, nkIfExpr:
|
||||||
|
|
||||||
|
of nkForStmt:
|
||||||
|
|
||||||
|
of nkCallKinds:
|
||||||
|
let op = n[0]
|
||||||
|
if ~"stdlib.os.changeDir":
|
||||||
|
addf("" | "",
|
||||||
|
elif ~"stdlib.os.createDir":
|
||||||
|
addf("mkdir $#",
|
||||||
|
|
||||||
|
proc semanticPasses() =
|
||||||
|
registerPass verbosePass
|
||||||
|
registerPass semPass
|
||||||
|
|
||||||
|
proc commandCompileToSH =
|
||||||
|
#incl(gGlobalOptions, optSafeCode)
|
||||||
|
setTarget(osJS, cpuJS)
|
||||||
|
#initDefines()
|
||||||
|
defineSymbol("nimrod") # 'nimrod' is always defined
|
||||||
|
defineSymbol("batch")
|
||||||
|
semanticPasses()
|
||||||
|
registerPass(JSgenPass)
|
||||||
|
compileProject()
|
||||||
|
|
||||||
|
proc wantMainModule =
|
||||||
|
if gProjectFull.len == 0:
|
||||||
|
if optMainModule.len == 0:
|
||||||
|
fatal(gCmdLineInfo, errCommandExpectsFilename)
|
||||||
|
else:
|
||||||
|
gProjectName = optMainModule
|
||||||
|
gProjectFull = gProjectPath / gProjectName
|
||||||
|
|
||||||
|
gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
|
||||||
|
|
||||||
|
proc requireMainModuleOption =
|
||||||
|
if optMainModule.len == 0:
|
||||||
|
fatal(gCmdLineInfo, errMainModuleMustBeSpecified)
|
||||||
|
else:
|
||||||
|
gProjectName = optMainModule
|
||||||
|
gProjectFull = gProjectPath / gProjectName
|
||||||
|
|
||||||
|
gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
|
||||||
|
|
||||||
|
proc mainCommand* =
|
||||||
|
# In "nimrod serve" scenario, each command must reset the registered passes
|
||||||
|
clearPasses()
|
||||||
|
gLastCmdTime = epochTime()
|
||||||
|
appendStr(searchPaths, options.libpath)
|
||||||
|
if gProjectFull.len != 0:
|
||||||
|
# current path is always looked first for modules
|
||||||
|
prependStr(searchPaths, gProjectPath)
|
||||||
|
setId(100)
|
||||||
|
passes.gIncludeFile = includeModule
|
||||||
|
passes.gImportModule = importModule
|
||||||
|
case command.normalize
|
||||||
|
of "c", "cc", "compile", "compiletoc":
|
||||||
|
# compile means compileToC currently
|
||||||
|
gCmd = cmdCompileToC
|
||||||
|
wantMainModule()
|
||||||
|
commandCompileToC()
|
||||||
|
of "cpp", "compiletocpp":
|
||||||
|
extccomp.cExt = ".cpp"
|
||||||
|
gCmd = cmdCompileToCpp
|
||||||
|
if cCompiler == ccGcc: setCC("gcc")
|
||||||
|
wantMainModule()
|
||||||
|
defineSymbol("cpp")
|
||||||
|
commandCompileToC()
|
||||||
|
of "objc", "compiletooc":
|
||||||
|
extccomp.cExt = ".m"
|
||||||
|
gCmd = cmdCompileToOC
|
||||||
|
wantMainModule()
|
||||||
|
defineSymbol("objc")
|
||||||
|
commandCompileToC()
|
||||||
|
of "run":
|
||||||
|
gCmd = cmdRun
|
||||||
|
wantMainModule()
|
||||||
|
when hasTinyCBackend:
|
||||||
|
extccomp.setCC("tcc")
|
||||||
|
commandCompileToC()
|
||||||
|
else:
|
||||||
|
rawMessage(errInvalidCommandX, command)
|
||||||
|
of "js", "compiletojs":
|
||||||
|
gCmd = cmdCompileToJS
|
||||||
|
wantMainModule()
|
||||||
|
commandCompileToJS()
|
||||||
|
else:
|
||||||
|
rawMessage(errInvalidCommandX, command)
|
||||||
|
|
||||||
|
if (msgs.gErrorCounter == 0 and
|
||||||
|
gCmd notin {cmdInterpret, cmdRun, cmdDump} and
|
||||||
|
gVerbosity > 0):
|
||||||
|
rawMessage(hintSuccessX, [$gLinesCompiled,
|
||||||
|
formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
|
||||||
|
formatSize(getTotalMem())])
|
||||||
|
|
@ -5,7 +5,25 @@ mainModule:"nimrod.nim"
|
||||||
# gc:markAndSweep
|
# gc:markAndSweep
|
||||||
|
|
||||||
hint[XDeclaredButNotUsed]:off
|
hint[XDeclaredButNotUsed]:off
|
||||||
path:"llvm"
|
path:"ast"
|
||||||
|
path:"caas"
|
||||||
|
path:"cgen"
|
||||||
|
path:"common"
|
||||||
|
path:"docgen"
|
||||||
|
path:"jsgen"
|
||||||
|
path:"keywords"
|
||||||
|
path:"lexer"
|
||||||
|
path:"main"
|
||||||
|
path:"options"
|
||||||
|
path:"parsers"
|
||||||
|
path:"passes"
|
||||||
|
path:"renderers"
|
||||||
|
path:"rodfiles"
|
||||||
|
path:"sem"
|
||||||
|
path:"symtab"
|
||||||
|
path:"transformations"
|
||||||
|
path:"vm"
|
||||||
|
|
||||||
path:"$projectPath/.."
|
path:"$projectPath/.."
|
||||||
|
|
||||||
path:"$lib/packages/docutils"
|
path:"$lib/packages/docutils"
|
||||||
|
|
|
||||||
|
|
@ -277,8 +277,7 @@ when noTimeMachine:
|
||||||
##
|
##
|
||||||
## The whole proc is optional and will ignore all kinds of errors. The only
|
## The whole proc is optional and will ignore all kinds of errors. The only
|
||||||
## way to be sure that it works is to call ``tmutil isexcluded path``.
|
## way to be sure that it works is to call ``tmutil isexcluded path``.
|
||||||
if alreadyExcludedDirs.contains(dir): return
|
if alreadyExcludedDirs.containsOrIncl(dir): return
|
||||||
alreadyExcludedDirs.incl(dir)
|
|
||||||
try:
|
try:
|
||||||
var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
|
var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
|
||||||
discard p.waitForExit
|
discard p.waitForExit
|
||||||
|
|
@ -44,6 +44,7 @@ type
|
||||||
# parameters come first
|
# parameters come first
|
||||||
next: PStackFrame # for stacking
|
next: PStackFrame # for stacking
|
||||||
comesFrom: int
|
comesFrom: int
|
||||||
|
firstArg, argsLen, resultPos: Byte # used when frame is passed to callback
|
||||||
safePoints: seq[int] # used for exception handling
|
safePoints: seq[int] # used for exception handling
|
||||||
# XXX 'break' should perform cleanup actions
|
# XXX 'break' should perform cleanup actions
|
||||||
# What does the C backend do for it?
|
# What does the C backend do for it?
|
||||||
|
|
@ -141,6 +142,51 @@ template createStr(x) =
|
||||||
template createSet(x) =
|
template createSet(x) =
|
||||||
x.node = newNode(nkCurly)
|
x.node = newNode(nkCurly)
|
||||||
|
|
||||||
|
proc argsLen*(f: VmFrame): Natural =
|
||||||
|
## returns the number of arguments that this frame contains. Normally you
|
||||||
|
## should already know this number due to nimrod's static typing.
|
||||||
|
result = Natural(cast[PStackFrame](f).argsLen)
|
||||||
|
|
||||||
|
proc getInt*(f: VmFrame; index: Natural): BiggestInt =
|
||||||
|
## returns the value of the index'th slot in the frame. The slot has to be
|
||||||
|
## of the type: int|int16|...
|
||||||
|
let f = cast[PStackFrame](f)
|
||||||
|
result = f.slots[index + f.firstArg].intVal
|
||||||
|
|
||||||
|
proc getFloat*(f: VmFrame; index: Natural): BiggestFloat =
|
||||||
|
## returns the value of the index'th slot in the frame. The slot has to be
|
||||||
|
## of the type: float|float32|float64 or a distinct type of float or a
|
||||||
|
## float range.
|
||||||
|
let f = cast[PStackFrame](f)
|
||||||
|
result = f.slots[index + f.firstArg].floatVal
|
||||||
|
|
||||||
|
proc getString*(f: VmFrame; index: Natural): string =
|
||||||
|
## returns the value of the index'th slot in the frame. The slot has to be
|
||||||
|
## of the type string.
|
||||||
|
let f = cast[PStackFrame](f)
|
||||||
|
result = f.slots[index + f.firstArg].n.strVal
|
||||||
|
|
||||||
|
template rawSetResult(a, k, field) {.immediate, dirty.} =
|
||||||
|
if a.kind != k:
|
||||||
|
myreset a
|
||||||
|
a.kind = k
|
||||||
|
a.field = x
|
||||||
|
|
||||||
|
proc setResultInt*(f: VmFrame; x: BiggestInt) =
|
||||||
|
## sets the result value of the wrapped proc.
|
||||||
|
let f = cast[PStackFrame](f)
|
||||||
|
rawSetResult(f.slots[f.firstArg-1], rkInt, intVal)
|
||||||
|
|
||||||
|
proc setResultFloat*(f: VmFrame; x: BiggestFloat) =
|
||||||
|
## sets the result value of the wrapped proc.
|
||||||
|
let f = cast[PStackFrame](f)
|
||||||
|
rawSetResult(f.slots[f.firstArg-1], rkFloat, floatVal)
|
||||||
|
|
||||||
|
proc setResultString*(f: VmFrame; x: string) =
|
||||||
|
## sets the result value of the wrapped proc.
|
||||||
|
let f = cast[PStackFrame](f)
|
||||||
|
rawSetResult(f.slots[f.firstArg-1], rkStr, n.strVal)
|
||||||
|
|
||||||
proc moveConst(x: var TFullReg, y: TFullReg) =
|
proc moveConst(x: var TFullReg, y: TFullReg) =
|
||||||
if x.kind != y.kind:
|
if x.kind != y.kind:
|
||||||
myreset(x)
|
myreset(x)
|
||||||
|
|
@ -791,7 +837,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
let bb = regs[rb].node
|
let bb = regs[rb].node
|
||||||
let isClosure = bb.kind == nkPar
|
let isClosure = bb.kind == nkPar
|
||||||
let prc = if not isClosure: bb.sym else: bb.sons[0].sym
|
let prc = if not isClosure: bb.sym else: bb.sons[0].sym
|
||||||
if sfImportc in prc.flags:
|
if prc.offset < 0:
|
||||||
|
tos.firstArg = rb
|
||||||
|
tos.argsLen = rc
|
||||||
|
tos.resultPos = ra
|
||||||
|
c.vmProcs[-prc.offset - 1].prc(tos)
|
||||||
|
elif sfImportc in prc.flags:
|
||||||
if allowFFI notin c.features:
|
if allowFFI notin c.features:
|
||||||
globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
|
globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
|
||||||
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep
|
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
## This module contains the type definitions for the new evaluation engine.
|
## This module contains the type definitions for the new evaluation engine.
|
||||||
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
||||||
|
|
||||||
import ast, passes, msgs, intsets
|
import ast, passes, msgs, intsets, strtabs
|
||||||
|
|
||||||
const
|
const
|
||||||
byteExcess* = 128 # we use excess-K for immediates
|
byteExcess* = 128 # we use excess-K for immediates
|
||||||
|
|
@ -171,6 +171,12 @@ type
|
||||||
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
|
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
|
||||||
maxSlots*: int
|
maxSlots*: int
|
||||||
|
|
||||||
|
VmFrame* = distinct pointer
|
||||||
|
VmProc* = proc (f: VmFrame) {.closure.}
|
||||||
|
|
||||||
|
VmSym* = object
|
||||||
|
name*: string
|
||||||
|
prc*: VmProc
|
||||||
PCtx* = ref TCtx
|
PCtx* = ref TCtx
|
||||||
TCtx* = object of passes.TPassContext # code gen context
|
TCtx* = object of passes.TPassContext # code gen context
|
||||||
code*: seq[TInstr]
|
code*: seq[TInstr]
|
||||||
|
|
@ -188,6 +194,8 @@ type
|
||||||
features*: TSandboxFlags
|
features*: TSandboxFlags
|
||||||
traceActive*: bool
|
traceActive*: bool
|
||||||
loopIterations*: int
|
loopIterations*: int
|
||||||
|
vmProcs*: seq[VmSym]
|
||||||
|
marked*: PStringTable
|
||||||
|
|
||||||
TPosition* = distinct int
|
TPosition* = distinct int
|
||||||
|
|
||||||
|
|
@ -196,7 +204,8 @@ type
|
||||||
proc newCtx*(module: PSym): PCtx =
|
proc newCtx*(module: PSym): PCtx =
|
||||||
PCtx(code: @[], debug: @[],
|
PCtx(code: @[], debug: @[],
|
||||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||||
prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations)
|
prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations,
|
||||||
|
vmProcs: @[], marked: nil)
|
||||||
|
|
||||||
proc refresh*(c: PCtx, module: PSym) =
|
proc refresh*(c: PCtx, module: PSym) =
|
||||||
c.module = module
|
c.module = module
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue