Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vmjit: remove expression-related workaround #1262

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions compiler/backend/cgirgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func disable(cl: var TranslateCl) {.inline.} =
proc stmtToIr(tree: MirBody, env: MirEnv, cl: var TranslateCl,
cr: var TreeCursor, stmts: var seq[CgNode])
proc scopeToIr(tree: MirBody, env: MirEnv, cl: var TranslateCl,
cr: var TreeCursor, stmts: var seq[CgNode], allowExpr=false)
cr: var TreeCursor, stmts: var seq[CgNode])

proc handleSpecialConv(c: ConfigRef, n: CgNode, info: TLineInfo,
dest: PType): CgNode =
Expand Down Expand Up @@ -1050,12 +1050,8 @@ proc genDefFor(sym: sink CgNode): CgNode =
unreachable()

proc scopeToIr(tree: MirBody, env: MirEnv, cl: var TranslateCl,
cr: var TreeCursor, stmts: var seq[CgNode],
allowExpr = false) =
cr: var TreeCursor, stmts: var seq[CgNode]) =
let
ends =
if allowExpr: {mnkEnd} + Atoms
else: {mnkEnd}
prev = cl.defs.len
prevInUnscoped = cl.inUnscoped
start = stmts.len
Expand All @@ -1064,7 +1060,7 @@ proc scopeToIr(tree: MirBody, env: MirEnv, cl: var TranslateCl,
cl.inUnscoped = false

# translate all statements:
while cr.hasNext(tree) and tree[cr].kind notin ends:
while cr.hasNext(tree) and tree[cr].kind != mnkEnd:
stmtToIr(tree, env, cl, cr, stmts)

if cr.hasNext(tree) and tree[cr].kind == mnkEnd:
Expand All @@ -1085,7 +1081,7 @@ proc tb(tree: MirBody, env: MirEnv, cl: var TranslateCl,
## Translate `tree` to the corresponding ``CgNode`` representation.
var cr = TreeCursor(pos: start.uint32)
var stmts: seq[CgNode]
scopeToIr(tree, env, cl, cr, stmts, allowExpr=true)
scopeToIr(tree, env, cl, cr, stmts)
if cl.raiseExits.len > 0:
# there's unhandled exceptional control-flow
patchResume(cl.raiseExits, 0)
Expand All @@ -1094,11 +1090,6 @@ proc tb(tree: MirBody, env: MirEnv, cl: var TranslateCl,
if cl.returnLabel.isSome:
join unknownLineInfo, cl.returnLabel.get()

if cr.hasNext(tree):
# the tree must be an expression; the last node is required to be an atom
let x = atomToIr(tree, cl, cr)
stmts.add x

# XXX: the list of statements is still wrapped in a node for now, but
# this needs to change once all code generators use the new CGIR
result = newStmt(cnkStmtList, unknownLineInfo)
Expand Down
40 changes: 30 additions & 10 deletions compiler/mir/mirgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2050,16 +2050,6 @@ proc generateCode*(graph: ModuleGraph, env: var MirEnv,
if n.typ.isEmptyType:
withFront c.builder:
gen(c, n)
elif n.typ.kind == tyTypeDesc:
# FIXME: this shouldn't happen, but type expressions are sometimes
# evaluated with the VM, such as a ``typeof(T.x)`` appearing as
# a field type within a generic object definition. While it makes
# sense to allow evaluating type expression with the VM, in simple
# situtations like the example above, it's simpler, faster, and more
# intuitive to either evaluate them directly when analying the type
# expression or during ``semfold``
c.builder.useSource(c.sp, n)
c.use genTypeExpr(c, n)
else:
c.builder.useSource(c.sp, n)
# XXX: restructure the ``mirgen`` API to use a dedicated procedure for
Expand Down Expand Up @@ -2166,6 +2156,36 @@ proc generateCode*(graph: ModuleGraph, env: var MirEnv, owner: PSym,
let (code, locals) = finish(move c.builder, default(Store[LocalId, Local]))
MirBody(locals: locals, source: move c.sp.map, code: code)

proc exprToMir*(graph: ModuleGraph, env: var MirEnv,
config: TranslationConfig, e: PNode): MirBody =
## Only meant to be used by `vmjit <#vmjit>`_. Produces a MIR body for a
## standalone expression. The result of the expression is assigned to the
## special local with ID 0.
var c = TCtx(context: skUnknown, graph: graph, config: config)
c.sp.active = (e, c.sp.map.add(e))
swap(c.env, env)

let res = c.addLocal(Local(typ: e.typ)) # the result variable
c.scope:
c.buildStmt mnkDef:
c.use toValue(mnkLocal, res, e.typ)
if e.typ.kind == tyTypeDesc:
# FIXME: this shouldn't happen, but type expressions are sometimes
# evaluated with the VM, such as a ``typeof(T.x)`` appearing as
# a field type within a generic object definition. While it
# makes sense to allow evaluating type expression with the VM,
# in simple situtations like the example above, it's simpler,
# faster, and more intuitive to either evaluate them directly
# when analyzing the type expression, or during ``semfold``
c.use genTypeExpr(c, e)
else:
c.genAsgnSource(e, {dfOwns, dfEmpty})

swap(c.env, env)

let (code, locals) = finish(move c.builder, default(Store[LocalId, Local]))
MirBody(locals: locals, source: move c.sp.map, code: code)

proc constDataToMir*(env: var MirEnv, n: PNode): MirTree =
## Translates the construction expression AST `n` representing some
## constant data to its corresponding MIR representation.
Expand Down
15 changes: 1 addition & 14 deletions compiler/mir/mirpasses.nim
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,6 @@ proc eliminateTemporaries(tree: MirTree, changes: var Changeset) =
## call(arg a.b.c)
var ct = initCountTable[uint32]()

proc isDangerous(tree: MirTree, n: NodePosition): bool =
# HACK: this is a tremendous hack to detect whether `n` is part of a
# loose expression, which are currently required by expression
# support for ``vmjit``. Remove as soon as no longer needed
var i = int n
while i < tree.len and tree[i].kind notin StmtNodes:
inc i

result = i >= tree.len

# first pass: gather all single-use temporaries that are created from
# lvalues and are eligible for elimination.
var i = NodePosition 0
Expand All @@ -240,10 +230,7 @@ proc eliminateTemporaries(tree: MirTree, changes: var Changeset) =
# looking for names
let id = tree[i].local
if hasKey(ct, id.uint32):
if isDangerous(tree, i):
ct.del(id.uint32)
else:
ct.inc(id.uint32)
ct.inc(id.uint32)

inc i
of mnkDeref, mnkDerefView:
Expand Down
37 changes: 4 additions & 33 deletions compiler/vm/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3198,45 +3198,16 @@ proc genStmt*(c: var TCtx; body: sink Body): Result[int, VmGenDiag] =
c.prc = initProc(c, nil, body)
let n = c.prc.body.code

var d: TDest = -1
try:
let eh = genSetEh(c, n.info)
c.gen(n, d)
c.gen(n)
c.patchSetEh(eh)
except VmGenError as e:
return typeof(result).err(move e.diag)

c.config.internalAssert(d < 0, n.info, "VM problem: dest register is set")
result = typeof(result).ok(c.prc.regInfo.len)

proc genExpr*(c: var TCtx; body: sink Body): Result[int, VmGenDiag] =
## Generates and emits the code for a standalone expression.
c.prc = initProc(c, nil, body)
let n = c.prc.body.code

var d: TDest = -1
try:
let eh = genSetEh(c, n.info)
if n.kind == cnkStmtList:
# special case the expression here so that ``gen`` doesn't have to
for i in 0..<n.len-1:
c.gen(n[i])

c.gen(n[^1], d)
else:
c.gen(n, d)

c.patchSetEh(eh)
except VmGenError as e:
return typeof(result).err(move e.diag)

# the destination register not being set likely indicate that `n` is not an
# expression
c.config.internalAssert(d != noDest, n.info):
"VM problem: dest register is not set"
# standalone expressions are treated as nullary procedures that
# directly return the value
c.gABC(n, opcRet, d)
if not isEmptyType(c.prc.body[resultId].typ):
# the body has a result, emit a return
c.gABC(n, opcRet, c.prc[resultId].reg)

result = typeof(result).ok(c.prc.regInfo.len)

Expand Down
68 changes: 23 additions & 45 deletions compiler/vm/vmjit.nim
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import
datatables,
mirbodies,
mirbridge,
mirconstr,
mirenv,
mirgen,
mirpasses,
Expand All @@ -44,9 +43,6 @@ import
compiler/sem/[
transf
],
compiler/utils/[
containers
],
compiler/vm/[
identpatterns,
vmaux,
Expand All @@ -61,6 +57,11 @@ import
results
]

# XXX: temporary imports for expression support
from compiler/ast/ast import newTreeIT
from compiler/sem/semdata import makeVarType
from compiler/sem/parampatterns import isAssignable, TAssignableResult

export VmGenResult

type
Expand Down Expand Up @@ -148,17 +149,19 @@ proc generateMirCode(c: var TCtx, env: var MirEnv, n: PNode;
isStmt = false): MirBody =
## Generates the initial MIR code for a standalone statement/expression.
if isStmt:
# we want statements wrapped in a scope, hence generating a proper
# fragment
result = generateCode(c.graph, env, c.module, selectOptions(c), n)
else:
var bu: MirBuilder
# add an empty local so that the result slot is occupied:
discard bu.addLocal(Local())
# XXX: ^^ this is a hack, and yet another reason to remove expression
# support from the JIT
generateCode(c.graph, env, selectOptions(c), n, bu, result.source)
(result.code, result.locals) = finish(bu, default(Store[LocalId, Local]))
var n = n
# optimization: wrap the expression in a hidden address if it's an lvalue
# expression. This eliminates the unnecessary copy that would be created
# otherwise
if isAssignable(nil, n, isUnsafeAddr=true) in {arLocalLValue, arLValue,
arLentValue}:
n = newTreeIT(nkHiddenAddr, n.info,
makeVarType(c.module, n.typ, c.idgen, tyLent),
n)

result = exprToMir(c.graph, env, selectOptions(c), n)

proc generateIR(c: var TCtx, env: MirEnv, body: sink MirBody): Body =
backends.generateIR(c.graph, c.idgen, env, c.module, body)
Expand Down Expand Up @@ -194,15 +197,14 @@ proc applyPasses(c: var TCtx, env: var MirEnv, prc: PSym, body: var MirBody) =
if restore:
prc.options.incl optProfiler

proc genStmt*(jit: var JitState, c: var TCtx; n: PNode): VmGenResult =
## Generates and emits code for the standalone top-level statement `n`.
proc gen(jit: var JitState, c: var TCtx, n: PNode, isStmt: bool): VmGenResult =
preCheck(jit.gen.env, n)
c.removeLastEof()

let cp = checkpoint(jit.gen.env)

# `n` is expected to have been put through ``transf`` already
var mirBody = generateMirCode(c, jit.gen.env, n, isStmt = true)
var mirBody = generateMirCode(c, jit.gen.env, n, isStmt)
applyPasses(c, jit.gen.env, c.module, mirBody)
for _ in discover(jit.gen.env, cp):
discard "nothing to register"
Expand All @@ -222,37 +224,13 @@ proc genStmt*(jit: var JitState, c: var TCtx; n: PNode): VmGenResult =

result = VmGenResult.ok: (start: start, regCount: r.get)

proc genStmt*(jit: var JitState, c: var TCtx, n: PNode): VmGenResult =
## Generates and emits code for the standalone top-level statement `n`.
gen(jit, c, n, isStmt = true)

proc genExpr*(jit: var JitState, c: var TCtx, n: PNode): VmGenResult =
## Generates and emits code for the standalone expression `n`
preCheck(jit.gen.env, n)
c.removeLastEof()

# XXX: the way standalone expressions are currently handled is going to
# be a problem as soon as proper MIR passes need to be run (which
# all expect statements). Ideally, dedicated support for
# expressions would be removed from the JIT.

let cp = checkpoint(jit.gen.env)

var mirBody = generateMirCode(c, jit.gen.env, n)
applyPasses(c, jit.gen.env, c.module, mirBody)
for _ in discover(jit.gen.env, cp):
discard "nothing to register"

let
body = generateIR(c, jit.gen.env, mirBody)
start = c.code.len

# generate the bytecode:
let r = runCodeGen(c, jit.gen, body): genExpr(jit.gen, body)

if unlikely(r.isErr):
rewind(jit.gen.env, cp)
return VmGenResult.err(r.takeErr)

updateEnvironment(c, jit.gen.env, cp)

result = VmGenResult.ok: (start: start, regCount: r.get)
gen(jit, c, n, isStmt = false)

proc genProc(jit: var JitState, c: var TCtx, s: PSym): VmGenResult =
let body =
Expand Down
Loading