diff --git a/skully/backend.nim b/skully/backend.nim index fcae2c06..e2be3ce9 100644 --- a/skully/backend.nim +++ b/skully/backend.nim @@ -308,6 +308,49 @@ proc translateProcType(c; env: TypeEnv, id: TypeId, ignoreClosure: bool, bu) = if desc.callConv(env) == ccClosure and not ignoreClosure: bu.add typeRef(c, env, PointerType) +proc computeSizeAlign(c; env: TypeEnv, typ: Typeid, + withTag = true): (int64, int16) = + ## Computes the size and alignment of an embedded record/union. + # TODO: this shouldn't be our responsiblity! The NimSkull compiler has to + # set the size and alignment for embedded records + let desc = env.headerFor(typ, Lowered) + var size = 0'i64 + var align = 0'i16 + case desc.kind + of tkTaggedUnion: + if withTag: + let tag = env.headerFor(env[env.lookupField(typ, 0)].typ, Lowered) + align = tag.align + size = tag.size(env) + + for _, it in env.fields(desc, 1): + if env.isEmbedded(it.typ): + # an anonymous record; its fields need to be considered too + let (s, a) = computeSizeAlign(c, env, it.typ) + align = max(align, a) + size = max(size, s) + else: + let sub = env.headerFor(it.typ, Canonical) + align = max(align, sub.align) + size = max(size, sub.size(env)) + of tkRecord: + for _, it in env.fields(desc, 0): + # align the offset, then add the size + if env.isEmbedded(it.typ): + let (s, a) = computeSizeAlign(c, env, it.typ) + size = align(size, a) + size += s + align = max(align, a) + else: + let sub = env.headerFor(it.typ, Canonical) + size = align(size, sub.align) + size += sub.size(env) + align = max(align, sub.align) + else: + unreachable() + # don't add any padding at the end + result = (size, align) + proc embedTaggedUnion(c; env: TypeEnv, id: TypeId, bu) = privateAccess(RecField) # required for accessing the field offsets @@ -317,29 +360,16 @@ proc embedTaggedUnion(c; env: TypeEnv, id: TypeId, bu) = let desc = env.headerFor(id, Lowered) tagField = env.lookupField(id, 0) - - # compute the alignment for the union: - # TODO: this needs to be upstreamed into NimSkull - var alignment = 0'i16 - for _, it in env.fields(desc, 1): - let sub = env.headerFor(it.typ, Canonical) - if env.isEmbedded(it.typ): - # an anonymous record; its fields need to be considered too - for _, inner in env.fields(sub): - alignment = max(alignment, env.headerFor(inner.typ, Canonical).align) - else: - alignment = max(alignment, sub.align) - - let offset = align(env[tagField].offset.uint32 + desc.size(env).uint32, - uint32 alignment) - ## the offset of the union within the embedding record + tagDesc = env.headerFor(env[tagField].typ, Lowered) + (size, alignment) = computeSizeAlign(c, env, id, withTag=false) + # the size and alignment of the union (without the tag) + offset = align(env[tagField].offset.uint32 + tagDesc.size(env).uint32, + uint32 alignment) + ## the offset of the union within the embedding record let inner = block: - # XXX: size and alignment are currently ignored, as it's simpler this way - # and no pass inspects these values anyhow (at least at the time of - # writing) var bu = initBuilder(Union) - bu.add node(Immediate, 0) + bu.add node(Immediate, uint32 size) bu.add node(Immediate, uint32 alignment) for _, it in env.fields(desc, 1): if env.isEmbedded(it.typ): @@ -347,12 +377,13 @@ proc embedTaggedUnion(c; env: TypeEnv, id: TypeId, bu) = # IL record types must not be empty; add a dummy field bu.add node(UInt, 1) else: + let (s, a) = computeSizeAlign(c, env, it.typ) # emit the record type for the embedded type, but make sure to # correct the field offsets; they need to be relative to the start # of the union, not to the start of the embedding record var bu2 = initBuilder(Record) - bu2.add node(Immediate, 0) - bu2.add node(Immediate, 0) + bu2.add node(Immediate, s.uint32) + bu2.add node(Immediate, a.uint32) for _, recf in env.fields(env.headerFor(it.typ, Lowered)): bu2.subTree Field: bu2.add node(Immediate, recf.offset.uint32 - offset) @@ -638,6 +669,8 @@ proc genConst(c; env; tree; n; dest: Expr, stmts) = putIntoDest node(FloatVal, c.lit.pack(env.getFloat(tree[n].number))) of mnkProcVal: putIntoDest node(ProcVal, c.registerProc(tree[n].prc)) + of mnkNilLit: + putIntoDest node(Nil) of mnkArrayConstr: for i, it in tree.args(n): let nDest = makeExpr tree[it].typ: @@ -965,7 +998,8 @@ proc translateValue(c; env: MirEnv, tree: MirTree, n: NodePosition, emit(c, env, tree, tree.child(n, 0), diff, b, bu) else: if env.types.headerFor(b, Lowered).kind in PointerLike: - # there are no pointer types in the IL; nothing to do + # all MIR pointer types map to the same IL pointer type; + # nothing special to do recurse(tree.child(n, 0), wantValue) else: assert not wantValue @@ -1012,11 +1046,14 @@ proc getTypeInfoV2(c; env: var MirEnv, typ: TypeId): Expr = proc genDefault(c; env: var MirEnv; dest: Expr, typ: TypeId, bu) = let typ = env.types.canonical(typ) case env.types.headerFor(typ, Lowered).kind - of tkBool, tkChar, tkInt, tkUInt, tkRef, tkPtr, tkVar, tkLent, tkPointer: + of tkBool, tkChar, tkInt, tkUInt: genAsgn(dest, makeExpr(@[node(IntVal, c.lit.pack(0))], typ), bu) of tkFloat: genAsgn(dest, makeExpr(@[node(FloatVal, c.lit.pack(0.0))], typ), bu) + of tkRef, tkPtr, tkVar, tkLent, tkPointer, tkCstring, tkProc: + genAsgn(dest, makeExpr(@[node(Nil)], typ), bu) else: + # TODO: for seqs, closures, and openarrays, set the fields directly let size = env.types.headerFor(typ, Lowered).size(env.types) bu.subTree Clear: takeAddr(dest, bu) @@ -1114,10 +1151,10 @@ proc genNumericConv(c; env; val: Expr, to: TypeId, bu) = bu.add typeRef(c, env, val.typ) bu.use val - template sizedConv(widen, narrow: NodeKind) = + template sizedConv(narrow, widen: NodeKind) = if dst.size(env.types) < src.size(env.types): convOp narrow - elif dst.size(env.types) > dst.size(env.types): + elif dst.size(env.types) > src.size(env.types): convOp widen else: # nothing to do @@ -1149,7 +1186,7 @@ proc genNumericConv(c; env; val: Expr, to: TypeId, bu) = of ints, uints: convOp Conv of tkFloat: - sizedConv Promote, Demote + sizedConv Demote, Promote else: unreachable() of tkPtr, tkPointer, tkProc, tkCstring: @@ -1393,7 +1430,7 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) = of mEqProc: let typ = env.types.canonical(tree[tree.argument(n, 0)].typ) if env.types.headerFor(typ, Lowered).kind == tkProc: - # simple integer equality suffices + # simple pointer equality suffices wrapAsgn Eq: bu.add typeRef(c, env, typ) value(tree.argument(n, 0)) @@ -1404,7 +1441,7 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) = # TODO: use a proper short-circuiting ``and``, not a ``bitand`` wrapAsgn BitAnd: bu.add typeRef(c, env, PointerType) - wrapAsgn Eq: + bu.subTree Eq: bu.add typeRef(c, env, PointerType) bu.subTree Copy: bu.subTree Field: @@ -1414,7 +1451,7 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) = bu.subTree Field: lvalue tree.argument(n, 1) bu.add node(Immediate, 0) - wrapAsgn Eq: + bu.subTree Eq: bu.add typeRef(c, env, PointerType) bu.subTree Copy: bu.subTree Field: @@ -1474,10 +1511,7 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) = bu.add typeRef(c, env, tree[n].typ) value(tree.argument(n, 0)) value(tree.argument(n, 1)) - of mOrd: - wrapAsgn: - value(tree.argument(n, 0)) - of mChr: + of mOrd, mChr: # it's a conversion, nothing more let input = c.gen(env, tree, NodePosition(tree.argument(n, 0)), true) wrapAsgn: @@ -1563,14 +1597,14 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) = bu.subTree Call: bu.add compilerProc(c, env, "cmpStrings") value(tree.argument(n, 0)) - bu.add node(IntVal, 0) + bu.add node(IntVal, 0) of mLtStr: wrapAsgn Lt: bu.add typeRef(c, env, env.types.sizeType) bu.subTree Call: bu.add compilerProc(c, env, "cmpStrings") value(tree.argument(n, 0)) - bu.add node(IntVal, 0) + bu.add node(IntVal, 0) of mFinished: # the status field is stored at the start of the env object, load it and # test whether the value is < 0 @@ -1787,7 +1821,7 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) = bu.subTree Copy: bu.subTree Field: lvalue(tree.argument(n, 0)) - bu.add node(Immediate, 0) + bu.add node(Immediate, 1) bu.add node(Nil) bu.goto then bu.goto els @@ -1887,7 +1921,7 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) = bu.subTree Eq: bu.add typeRef(c, env, e.typ) bu.use e - bu.add node(IntVal, c.lit.pack(0)) + bu.add node(Nil) bu.goto els bu.goto then stmts.join then @@ -2061,7 +2095,7 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) = bu.subTree Field: lvalue callee bu.add node(Immediate, 1) - bu.add node(IntVal, c.lit.pack(0)) + bu.add node(Nil) bu.goto els bu.goto then stmts.join then @@ -2267,7 +2301,7 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) = bu.subTree Field: bu.useLvalue dest bu.add node(Immediate, 0) - bu.add node(IntVal, c.lit.pack(0)) + bu.add node(Nil) stmts.addStmt Asgn: bu.subTree Field: bu.useLvalue dest @@ -2335,8 +2369,14 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) = c.genSetElem(env, tree, it, tree[n].typ, bu) c.genIncl(env, dest, e, stmts) of mnkCast: - let dst = env.types.canonical(tree[n].typ) - let src = env.types.canonical(tree[n, 0].typ) + proc canon(env: TypeEnv, id: TypeId): TypeId = + ## Skips to the non-imported canonical type. + result = env.canonical(id) + if env.headerFor(result, Lowered).kind == tkImported: + result = canon(env, env.headerFor(result, Lowered).elem) + + let dst = env.types.canon(tree[n].typ) + let src = env.types.canon(tree[n, 0].typ) let tdst = c.genType(env.types, dst) let tsrc = c.genType(env.types, src) if tdst == tsrc: @@ -2352,9 +2392,9 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) = takeAddr tree.child(n, 0) bu.add node(IntVal, c.lit.pack(size)) else: - template isPointer(id: TypeId): bool = + template isInt(id: TypeId): bool = env.types.headerFor(id, Lowered).kind in - {tkPointer, tkProc, tkRef, tkPtr, tkCstring} + {tkInt, tkUInt, tkBool, tkChar} let a = env.types.headerFor(dst, Lowered).size(env.types) let b = env.types.headerFor(src, Lowered).size(env.types) @@ -2367,29 +2407,29 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) = value tree.child(n, 0) else: let op = if a < b: Trunc else: Zext - # pointer values cannot be truncated/extended directly -- the operation - # has to happen on a uint - if isPointer(src): - wrapAsgn op: - bu.add typeRef(c, env, dst) - bu.add typeRef(c, env, env.types.usizeType) + var val = c.gen(env, tree, tree.child(n, 0), true) + # non-integer values cannot be truncated/extended directly; the value + # has to be bitcast into an int first + if not isInt(src): + val = makeExpr env.types.usizeType: bu.subTree Reinterp: bu.add typeRef(c, env, env.types.usizeType) bu.add typeRef(c, env, src) - value tree.child(n, 0) - elif isPointer(dst): + bu.use val + + if isInt(dst): + wrapAsgn op: + bu.add typeRef(c, env, dst) + bu.add typeRef(c, env, val.typ) + bu.use val + else: wrapAsgn Reinterp: bu.add typeRef(c, env, dst) bu.add typeRef(c, env, env.types.usizeType) bu.subTree op: bu.add typeRef(c, env, env.types.usizeType) - bu.add typeRef(c, env, src) - value tree.child(n, 0) - else: - wrapAsgn op: - bu.add typeRef(c, env, dst) - bu.add typeRef(c, env, dst) - value tree.child(n, 0) + bu.add typeRef(c, env, val.typ) + bu.use val else: unreachable() @@ -2583,7 +2623,7 @@ proc translateStmt(env: var MirEnv, tree; n; stmts; c) = of mnkContinue: guardActive() stmts.addStmt Raise: - bu.add node(IntVal, 0) + bu.add node(Nil) c.genExit(tree, tree.child(n, 0), bu) c.prc.active = false of mnkRaise: @@ -2591,7 +2631,7 @@ proc translateStmt(env: var MirEnv, tree; n; stmts; c) = # NimSkull exceptions are managed separately; there's nothing to pass # along to ``Raise`` stmts.addStmt Raise: - bu.add node(IntVal, 0) + bu.add node(Nil) c.genExit(tree, tree.child(n, 0), bu) c.prc.active = false of mnkEmit, mnkAsm: