Skip to content

Commit

Permalink
vm: move ExpandToAst logic into own instruction
Browse files Browse the repository at this point in the history
The logic for template expansion as needed by the `mExpandToAst` magic
(used by `quote`) is now implemented as a separate instruction instead
of as part of `IndCallAsgn`. This makes the role of template expansion
less confusing ("why are templates 'called' inside the VM?", "weren't
they expanded already?") and also allows for a different instruction
argument make-up. The latter is required by both a future overhaul
of macro expansion and the refactored VM.

User-facing behaviour stays the same
  • Loading branch information
zerbina committed Apr 17, 2022
1 parent afc56f9 commit 9d8abbd
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 23 deletions.
48 changes: 29 additions & 19 deletions compiler/vm/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,34 @@ proc rawExecute(c: var TCtx, pc: var int, tos: var StackFrameIndex): TFullReg =
ast[i] = a.sym.ast[i]
ast[bodyPos] = transformBody(c.graph, c.idgen, a.sym, cache=true)
ast.copyTree()
of opcExpandToAst:
decodeBC(rkNode)

let
callExprAst = c.constants[regs[rb].intVal.int]
prc = callExprAst[0].sym
prevFrame = c.sframes[tos].next

assert callExprAst.kind in nkCallKinds
assert prc.kind == skTemplate

let genSymOwner = if prevFrame > 0 and c.sframes[prevFrame].prc != nil:
c.sframes[prevFrame].prc
else:
c.module
var templCall = newNodeI(nkCall, c.debug[pc])
templCall.add(newSymNode(prc))
for i in 1..rc-1:
let node = regs[rb+i].regToNode
node.info = c.debug[pc]
templCall.add(node)

var a = evalTemplate(templCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen)
if a.kind == nkStmtList and a.len == 1: # flatten if a single statement
a = a[0]
a.recSetFlagIsRef
regs[ra].node = a

of opcSymOwner:
decodeB(rkNode)
let a = regs[rb].node
Expand Down Expand Up @@ -1370,7 +1398,7 @@ proc rawExecute(c: var TCtx, pc: var int, tos: var StackFrameIndex): TFullReg =
currentException: c.currentExceptionA,
currentLineInfo: c.debug[pc]))

elif prc.kind != skTemplate:
else:
let newPc = compile(c, prc)
# tricky: a recursion is also a jump back, so we use the same
# logic as for loops:
Expand All @@ -1387,24 +1415,6 @@ proc rawExecute(c: var TCtx, pc: var int, tos: var StackFrameIndex): TFullReg =
pushFrame(newFrame)
# -1 for the following 'inc pc'
pc = newPc-1
else:
let prevFrame = c.sframes[tos].next
# for 'getAst' support we need to support template expansion here:
let genSymOwner = if prevFrame > 0 and c.sframes[prevFrame].prc != nil:
c.sframes[prevFrame].prc
else:
c.module
var macroCall = newNodeI(nkCall, c.debug[pc])
macroCall.add(newSymNode(prc))
for i in 1..rc-1:
let node = regs[rb+i].regToNode
node.info = c.debug[pc]
macroCall.add(node)
var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen)
if a.kind == nkStmtList and a.len == 1: a = a[0]
a.recSetFlagIsRef
ensureKind(rkNode)
regs[ra].node = a
of opcTJmp:
# jump Bx if A != 0
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
Expand Down
4 changes: 3 additions & 1 deletion compiler/vm/vm_enums.nim
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ type
opcEqIdent,
opcStrToIdent,
opcGetImpl,
opcGetImplTransf
opcGetImplTransf,

opcExpandToAst,

opcEcho,
opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
Expand Down
27 changes: 24 additions & 3 deletions compiler/vm/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,26 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) =
#if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}:
# "ExpandToAst: expanded symbol is no macro or template"
if dest < 0: dest = c.getTemp(n.typ)
c.genCall(arg, dest)

if arg[0].sym.kind == skTemplate:
let x = c.getTempRange(arg.len, slotTempUnknown)

# Pass the whole call expression as the first value. Since the node
# might have a nil type, we can't use `genLit`. Instead load the
# constant index and make the instruction do the lookup itself
c.gABx(n, opcLdImmInt, TRegister(x), c.genLiteral(arg))

# Generate the arguments
for i in 1..<arg.len:
var d = TDest(x+i)
c.gen(arg[i], d, {gfIsParam})

c.gABC(arg, opcExpandToAst, dest, x, arg.len)
c.freeTempRange(x, arg.len)
else:
# macros are still invoked via the opcIndCall mechanism
c.genCall(arg, dest)

# do not call clearDest(n, dest) here as getAst has a meta-type as such
# produces a value
else:
Expand Down Expand Up @@ -2081,13 +2100,15 @@ proc gen(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of skVar, skForVar, skTemp, skLet, skParam, skResult:
genRdVar(c, n, dest, flags)

of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator:
# 'skTemplate' is only allowed for 'getAst' support:
of skProc, skFunc, skConverter, skMacro, skMethod, skIterator:
if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure:
fail(n.info, rsemVmNoClosureIterators, sym = s)
if procIsCallback(c, s): discard
elif importcCond(c, s): fail(n.info, rsemVmCannotImportc, sym = s)
genLit(c, n, dest)
of skTemplate:
# template symbols can be used as arguments in a `getAst` expression
genLit(c, n, dest)
of skConst:
let constVal = if s.ast != nil: s.ast else: s.typ.n
gen(c, constVal, dest)
Expand Down

0 comments on commit 9d8abbd

Please sign in to comment.