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

mir: lower cast with MIR pass #1411

Closed
wants to merge 2 commits into from
Closed
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
34 changes: 9 additions & 25 deletions compiler/backend/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1189,16 +1189,13 @@ proc genSomeCast(p: BProc, e: CgNode, d: var TLoc) =
of cnkCast, cnkConv, cnkHiddenConv: e.operand
of cnkCall: e[1]
else: unreachable()
# we use whatever C gives us. Except if we have a value-type, we need to go
# through its address:
# we use whatever C gives us
var a: TLoc
initLocExpr(p, src, a)
let etyp = skipTypes(e.typ, abstractRange)
let srcTyp = skipTypes(src.typ, abstractRange)
if etyp.kind in ValueTypes and lfIndirect notin a.flags:
putIntoDest(p, d, e, "(*($1*) ($2))" %
[getTypeDesc(p.module, e.typ), addrLoc(p.module, a)], a.storage)
elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
p.config.internalAssert(etyp.kind notin ValueTypes, e.info)
if etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
putIntoDest(p, d, e, "(($1) ($2))" %
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
else:
Expand All @@ -1216,26 +1213,13 @@ proc genSomeCast(p: BProc, e: CgNode, d: var TLoc) =
putIntoDest(p, d, e, "(($1) ($2))" %
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)

proc canon(types: TypeEnv, typ: PType): TypeId =
types.canonical(types[typ])

proc genCast(p: BProc, e: CgNode, d: var TLoc) =
const ValueTypes = {tyFloat..tyFloat64, tyTuple, tyObject, tyArray}
let
src = e.operand
destt = skipTypes(e.typ, abstractRange)
srct = skipTypes(src.typ, abstractRange)
if destt.kind in ValueTypes or srct.kind in ValueTypes:
# 'cast' and some float type involved? --> use a union.
inc(p.labels)
var lbl = p.labels.rope
var tmp: TLoc
tmp.r = "LOC$1.source" % [lbl]
linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
[getTypeDesc(p.module, src.typ), getTypeDesc(p.module, e.typ), lbl])
tmp.k = locExpr
tmp.lode = lodeTyp srct
tmp.storage = OnStack
tmp.flags = {}
expr(p, src, tmp)
putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage)
let x = e.operand
if canon(p.module.types, e.typ) == canon(p.module.types, x.typ):
expr(p, x, d) # no cast is necessary
else:
# I prefer the shorter cast version for pointer types -> generate less
# C code; plus it's the right thing to do for closures:
Expand Down
83 changes: 83 additions & 0 deletions compiler/mir/mirpasses.nim
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ proc overlapsConservative(tree: MirTree, a, b: Path, typA, typB: PType): bool =
# use path-based analysis:
result = overlaps(tree, a, b) != no

proc genSizeOf(bu: var MirBuilder, env: var MirEnv, typ: TypeId): Value =
let size = env.types.headerFor(typ, Lowered).size(env.types)
if size >= 0:
# known size -> use a literal value
literal(mnkIntLit, env.getOrIncl(size), env.types.sizeType)
else:
# some type with unknown size -> emit a sizeof call
bu.wrapTemp env.types.sizeType:
bu.buildMagicCall mSizeOf, env.types.sizeType:
bu.emitByVal typeLit(typ)

proc preventRvo(tree: MirTree, types: TypeEnv, changes: var Changeset) =
## Injects intermediate temporaries for assignments where the source is an
## RVO-using call rvalue and the destination potentially aliases with a
Expand Down Expand Up @@ -822,6 +833,77 @@ proc splitAssignments(tree: MirTree, changes: var Changeset) =
bu.subTree mnkMove:
bu.use tmp

proc lowerCast(tree: MirTree, graph: ModuleGraph, env: var MirEnv,
changes: var Changeset) =
## Lower cast operations between non-integer or non-pointer-like types into
## memory copies.

proc isRequiresCopy(types: TypeEnv, desc: HeaderId): bool {.inline.} =
var desc = desc
# XXX: skipping imported types is a workaround for ``nimCopyMem`` casting
# to ``size_t``, which would result in infinite recursion at run-
# time if turned into a ``nimCopyMem``
while types[desc].kind == tkImported:
desc = types.get(types[desc].elem).desc[Lowered]

types[desc].kind in {tkFloat, tkRecord, tkUnion, tkArray, tkImported}

proc apply(bu: var MirBuilder, tree: MirTree, dst, src: NodePosition,
env: var MirEnv, op: ProcedureId) {.nimcall.} =
let dstPtr = bu.wrapTemp PointerType:
bu.subTree mnkAddr, PointerType:
bu.emitFrom(tree, dst)
let srcPtr =
if tree[skipConversions(tree, OpValue src)].kind in LiteralDataNodes:
# cannot take the address of some literal value; use a temporary
let tmp = bu.wrapTemp tree[src].typ:
bu.subTree mnkCopy, tree[src].typ:
bu.emitFrom(tree, src)
bu.wrapTemp PointerType:
bu.subTree mnkAddr, PointerType:
bu.use tmp
else:
bu.wrapTemp PointerType:
bu.subTree mnkAddr, PointerType:
bu.emitFrom(tree, src)

let sizeOf = genSizeOf(bu, env, tree[dst].typ)

bu.subTree mnkVoid:
bu.buildCall op, VoidType:
bu.emitByVal dstPtr
bu.emitByVal srcPtr
bu.emitByVal sizeOf

for n in search(tree, {mnkCast}):
let
dst = env.types.get(tree[n].typ).desc[Lowered]
src = env.types.get(tree[n, 0].typ).desc[Lowered]

# ignore the cast if between the same types
if src != dst and (isRequiresCopy(env.types, dst) or
isRequiresCopy(env.types, src)):
let
asgn = tree.parent(n)
copy = env.procedures.add(graph.getCompilerProc("nimCopyMem"))

case tree[asgn].kind
of mnkAsgn, mnkInit:
# transform ``_1 = cast _2`` into:
# nimCopyMem(arg addr(_1), arg addr(_2), ...)
changes.replaceMulti(tree, asgn, bu):
apply(bu, tree, tree.child(asgn, 0), tree.child(n, 0), env, copy)

of mnkDef, mnkDefCursor:
# transform ``def _1 = cast _2`` into:
# def _1
# nimCopyMem(arg addr(_1), arg addr(_2), ...)
changes.replace(tree, n, MirNode(kind: mnkNone))
changes.insert(tree, tree.sibling(asgn), n, bu):
apply(bu, tree, tree.child(asgn, 0), tree.child(n, 0), env, copy)
else:
unreachable()

proc applyPasses*(body: var MirBody, prc: PSym, env: var MirEnv,
graph: ModuleGraph, target: TargetBackend) =
## Applies all applicable MIR passes to the body (`tree` and `source`) of
Expand Down Expand Up @@ -860,6 +942,7 @@ proc applyPasses*(body: var MirBody, prc: PSym, env: var MirEnv,
lowerChecks(body, graph, env, c)
injectStrPreparation(body.code, graph, env, c)
lowerCase(body.code, graph, env, c)
lowerCast(body.code, graph, env, c)

# instrument the body with profiler calls after all lowerings, but before
# optimization
Expand Down
Loading