Skip to content

Commit

Permalink
mir: add a dedicated IR for types (#1382)
Browse files Browse the repository at this point in the history
## Summary

* implement a dedicated type IR for the MIR and back-end phase
* use the new IR in parts of the C code generator
* use a new name mangling scheme for types that doesn't rely on
  signature hashes

## Details

The idea behind introducing a dedicated type IR for the MIR and back-
end phase is to:
* have a simpler and more regular IR during lowering and code
  generation
* decouple type lowering from code generation 
* make `PType` solely used by `sem` and `transf`

The key ideas for the type IR are that:
* same ID (`HeaderId`) means same type; there are no duplicates like
  with `PType`
* transformations and code generation have easy access to the various
  stages of a type's representation (including the `PType`)
* lineage to `PType`s is kept track of

The current design likely covers more ground than it ultimately needs
to, but this was a deliberate choice in order to ease the transition to
the new IR.

### Translation

The lowering and or fixes performed by `mirtypes` during the
translation of types intends to exactly match what was previously
implemented in `ccgtypes`.

So that the IR is well-tested, all `PType`s entering `mirgen` are
translated to the IR representation.

### Code generation

Most of the existing is still oblivious to the new type IR, with only
the type emission in the C code generation. Some analysis that was
previously performed on `PType`s is ported to operate on MIR type
descriptions.

The `mangling` module implements a semi-stable, reversible name
mangling for types. It's necessary since not all MIR types have an
originating-from `PType` (such as the internal payload type for `seq`s
and `string`s), meaning that the `sighash`-based mangling cannot be
used there.

### AST

Associating fields with names in the C code generator is now done via
the new type IR, meaning that the `locId` field on `TSym` is obsolete;
it's removed.

### Tests

* multiple `ccodecheck` test are adjusted to `int` now translating to a
  sized integer instead of `NI`
* due to usage of the to-MIR-type-translation,
  `tempty_typed_expressions_issues.nim` now also fails for JS

---------

Co-authored-by: Saem Ghani <[email protected]>
  • Loading branch information
zerbina and saem committed Jul 24, 2024
1 parent f993076 commit a0d524b
Show file tree
Hide file tree
Showing 17 changed files with 1,921 additions and 764 deletions.
2 changes: 1 addition & 1 deletion compiler/ast/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ template transitionSymKindCommon*(k: TSymKind) =
s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
options: obj.options, position: obj.position, offset: obj.offset,
extname: obj.extname, extFlags: obj.extFlags, locId: obj.locId,
extname: obj.extname, extFlags: obj.extFlags,
annex: obj.annex, constraint: obj.constraint)
when defined(nimsuggest):
s.allUsages = obj.allUsages
Expand Down
2 changes: 0 additions & 2 deletions compiler/ast/ast_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1723,8 +1723,6 @@ type
## generated name is to be used
extFlags*: ExternalFlags ## additional flags that are relevant to code
## generation
locId*: uint32 ## associates the symbol with a loc in the C code
## generator. 0 means unset.
annex*: LibId ## additional fields (seldom used, so we use a
## reference to another object to save space)
constraint*: PNode ## additional constraints like 'lit|result'; also
Expand Down
29 changes: 29 additions & 0 deletions compiler/ast/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1557,3 +1557,32 @@ proc classifyBackendView*(t: PType): BackendViewKind =
tyGenericParam, tyForward, tyBuiltInTypeClass, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyAnything, tyFromExpr:
unreachable()

proc isPassByRef*(conf: ConfigRef; s: PSym, retType: PType): bool =
var pt = skipTypes(s.typ, typedescInst)
assert skResult != s.kind

if tfByRef in pt.flags: return true
elif tfByCopy in pt.flags: return false
case pt.kind
of tyObject:
if s.typ.sym != nil and sfForward in s.typ.sym.flags:
# forwarded objects are *always* passed by pointers for consistency!
result = true
elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
result = true # requested anyway
elif (tfFinal in pt.flags) and (pt[0] == nil):
result = false # no need, because no subtyping possible
else:
result = true # ordinary objects are always passed by reference,
# otherwise casting doesn't work
of tyTuple:
result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
else:
result = false

# first parameter and return type is 'lent T'? --> use pass by pointer if
# not already a pointer-like type
if s.position == 0 and retType != nil and retType.kind == tyLent:
result = pt.kind notin {tyVar, tyOpenArray, tyVarargs, tyRef, tyPtr,
tyPointer}
17 changes: 9 additions & 8 deletions compiler/backend/ccgcalls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ proc fixupCall(p: BProc, le, ri: CgNode, d: var TLoc,
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ[0] != nil:
if isInvalidReturnType(p.config, typ[0]):
if isInvalidReturnType(p.module, typ[0]):
if params != "": pl.add(~", ")
# the destination is guaranteed to be either a temporary or an lvalue
# that can be modified in-place
Expand All @@ -53,7 +53,7 @@ proc fixupCall(p: BProc, le, ri: CgNode, d: var TLoc,
# procedure
if d.k == locNone:
getTemp(p, typ[0], d)
pl.add(addrLoc(p.config, d))
pl.add(addrLoc(p.module, d))
pl.add(~");$n")
line(p, cpsStmts, pl)
exitCall(p, ri)
Expand Down Expand Up @@ -105,6 +105,7 @@ proc genOpenArraySlice(p: BProc; q: CgNode; formalType, destType: PType): (Rope,
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
lengthExpr)
of tyString, tySequence:
requestFullDesc(p.module, a.t)
let atyp = skipTypes(a.t, abstractInst)
if atyp.kind in {tyVar}:
result = ("((*$1).p != NIM_NIL ? ($4*)(*$1)$3+$2 : NIM_NIL)" %
Expand Down Expand Up @@ -139,12 +140,12 @@ proc genArg(p: BProc, n: CgNode, param: PSym; call: CgNode): Rope =
result = "$1.Field0, $1.Field1" % [rdLoc(a)]
else:
result = "$1, $1Len_0" % [rdLoc(a)]
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
elif ccgIntroducedPtr(p.module, param, call[0].typ[0]):
initLocExpr(p, n, a)
if n.kind in cnkLiterals + {cnkNilLit}:
result = addrLoc(p.config, literalsNeedsTmp(p, a))
result = addrLoc(p.module, literalsNeedsTmp(p, a))
else:
result = addrLoc(p.config, a)
result = addrLoc(p.module, a)
else:
initLocExprSingleUse(p, n, a)
result = rdLoc(a)
Expand Down Expand Up @@ -206,9 +207,9 @@ proc genClosureCall(p: BProc, le, ri: CgNode, d: var TLoc) =
else:
lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])

let rawProc = getClosureType(p.module, typ, clHalf)
let rawProc = getClosureType(p.module, ri[0].typ, clHalf)
if typ[0] != nil:
if isInvalidReturnType(p.config, typ[0]):
if isInvalidReturnType(p.module, typ[0]):
if numArgs(ri) > 0: pl.add(~", ")
# the destination is guaranteed to be either a temporary or an lvalue
# that can be modified in-place
Expand All @@ -217,7 +218,7 @@ proc genClosureCall(p: BProc, le, ri: CgNode, d: var TLoc) =
# procedure
if d.k == locNone:
getTemp(p, typ[0], d)
pl.add(addrLoc(p.config, d))
pl.add(addrLoc(p.module, d))
genCallPattern()
exitCall(p, ri)
else:
Expand Down
72 changes: 31 additions & 41 deletions compiler/backend/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n",
[rdLoc(d), a.rdLoc])
of tySequence, tyString:
requestFullDesc(p.module, a.t)
linefmt(p, cpsStmts, "$1.Field0 = ($2.p != NIM_NIL ? $2$3 : NIM_NIL); $1.Field1 = $4;$n",
[rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)])
of tyArray:
Expand All @@ -140,7 +141,7 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
proc genAssignment(p: BProc, dest, src: TLoc) =
# This function replaces all other methods for generating
# the assignment operation in C.
case mapType(p.config, dest.t)
case mapType(p.module, dest.t)
of ctChar, ctBool, ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
ctFloat, ctFloat32, ctFloat64,
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
Expand All @@ -166,7 +167,7 @@ proc genAssignment(p: BProc, dest, src: TLoc) =
#writeStackTrace()
#echo p.currLineInfo, " requesting"
linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
[addrLoc(p.config, dest), getSize(p.config, dest.t),
[addrLoc(p.module, dest), getSize(p.config, dest.t),
makeCString(toFullPath(p.config, p.currLineInfo)),
p.currLineInfo.safeLineNm])

Expand All @@ -176,25 +177,25 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
var tmp: TLoc
getTemp(p, a.t, tmp)
genAssignment(p, tmp, a)
addrLoc(p.config, tmp)
addrLoc(p.module, tmp)
else:
addrLoc(p.config, a)
addrLoc(p.module, a)

var ty = skipTypes(dest.t, abstractVarRange + {tyStatic})
case ty.kind
of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
# XXX optimize this
linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
[addrLoc(p.config, dest), addrLocOrTemp(src),
[addrLoc(p.module, dest), addrLocOrTemp(src),
genTypeInfoV1(p.module, dest.t, dest.lode.info)])
of tySequence, tyString:
linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
[addrLoc(p.config, dest), addrLocOrTemp(src),
[addrLoc(p.module, dest), addrLocOrTemp(src),
genTypeInfoV1(p.module, dest.t, dest.lode.info)])
of tyOpenArray, tyVarargs:
linefmt(p, cpsStmts,
"#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
[addrLoc(p.config, dest), addrLocOrTemp(src),
[addrLoc(p.module, dest), addrLocOrTemp(src),
genTypeInfoV1(p.module, dest.t, dest.lode.info)])
of tySet:
if mapSetType(p.config, ty) == ctArray:
Expand Down Expand Up @@ -396,7 +397,7 @@ proc unaryArith(p: BProc, e, x: CgNode, d: var TLoc, op: TMagic) =
proc genDeref(p: BProc, e: CgNode, d: var TLoc) =
let
src = e.operand
mt = mapType(p.config, src.typ)
mt = mapType(p.module, src.typ)
if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags:
# XXX the amount of hacks for C's arrays is incredible, maybe we should
# simply wrap them in a struct? --> Losing auto vectorization then?
Expand Down Expand Up @@ -438,14 +439,14 @@ proc genDeref(p: BProc, e: CgNode, d: var TLoc) =
putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage)

proc genAddr(p: BProc, e: CgNode, d: var TLoc) =
if mapType(p.config, e.operand.typ) == ctArray:
if mapType(p.module, e.operand.typ) == ctArray:
expr(p, e.operand, d)
else:
var a: TLoc
initLoc(a, locNone, e.operand, OnUnknown)
a.flags.incl lfWantLvalue
expr(p, e.operand, a)
putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
putIntoDest(p, d, e, addrLoc(p.module, a), a.storage)

template inheritLocation(d: var TLoc, a: TLoc) =
if d.k == locNone: d.storage = a.storage
Expand All @@ -469,33 +470,22 @@ proc genTupleElem(p: BProc, e: CgNode, d: var TLoc) =
r.addf(".Field$1", [rope(e[1].intVal)])
putIntoDest(p, d, e, r, a.storage)

proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
resTyp: ptr PType = nil): PSym =
var ty = ty
assert r != ""
while ty != nil:
ty = ty.skipTypes(skipPtrs)
assert ty.kind == tyObject
result = lookupInRecord(ty.n, field.name)
if result != nil:
if resTyp != nil: resTyp[] = ty
break
r.add(".Sup")
ty = ty[0]
if result == nil: internalError(p.config, field.info, "genCheckedRecordField")
proc handleSupAccess(types: TypeEnv, ty: PType; field: PSym; r: var Rope) =
let depth = computeDepth(types, types.headerFor(types[ty], Canonical),
field.position.int32)
for _ in 0..<depth:
r.add ".Sup"

proc genRecordField(p: BProc, e: CgNode, d: var TLoc) =
var a: TLoc
genRecordFieldAux(p, e, d, a)
var r = rdLoc(a)
var f = e[1].field
let f = e[1].field
let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
p.config.internalAssert(ty.kind == tyObject, e[0].info)
if true:
var rtyp: PType
let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
ensureObjectFields(p.module, field, rtyp)
r.addf(".$1", [p.fieldName(field)])
handleSupAccess(p.module.types, ty, f, r)
r.addf(".$1", [p.fieldName(ty, f)])
putIntoDest(p, d, e, r, a.storage)

proc genUncheckedArrayElem(p: BProc, n, x, y: CgNode, d: var TLoc) =
Expand Down Expand Up @@ -574,6 +564,7 @@ proc genSeqElem(p: BProc, n, x, y: CgNode, d: var TLoc) =
var a, b: TLoc
initLocExpr(p, x, a)
initLocExpr(p, y, b)
requestFullDesc(p.module, a.t)
var ty = skipTypes(a.t, abstractVarRange)
if ty.kind in {tyRef, tyPtr}:
ty = skipTypes(ty.lastSon, abstractVarRange)
Expand Down Expand Up @@ -742,8 +733,7 @@ proc specializeInitObjectN(p: BProc, accessor: Rope, n: PNode, typ: PType) =
p.config.internalAssert(n[0].kind == nkSym, n.info,
"specializeInitObjectN")
let disc = n[0].sym
ensureObjectFields(p.module, disc, typ)
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, p.fieldName(disc)])
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, p.fieldName(typ, disc)])
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
Expand All @@ -757,8 +747,7 @@ proc specializeInitObjectN(p: BProc, accessor: Rope, n: PNode, typ: PType) =
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
ensureObjectFields(p.module, field, typ)
specializeInitObject(p, "$1.$2" % [accessor, p.fieldName(field)],
specializeInitObject(p, "$1.$2" % [accessor, p.fieldName(typ, field)],
field.typ, n.info)
else: internalError(p.config, n.info, "specializeInitObjectN()")

Expand Down Expand Up @@ -854,10 +843,9 @@ proc genObjConstr(p: BProc, e: CgNode, d: var TLoc) =
for it in e.items:
var tmp2: TLoc
tmp2.r = r
let field = lookupFieldAgain(p, ty, it[0].field, tmp2.r)
ensureObjectFields(p.module, field, ty)
handleSupAccess(p.module.types, ty, it[0].field, tmp2.r)
tmp2.r.add(".")
tmp2.r.add(p.fieldName(field))
tmp2.r.add(p.fieldName(ty, it[0].field))
tmp2.k = d.k
tmp2.storage = d.storage
tmp2.lode = it[1]
Expand Down Expand Up @@ -1209,7 +1197,7 @@ proc genSomeCast(p: BProc, e: CgNode, d: var TLoc) =
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.config, a)], a.storage)
[getTypeDesc(p.module, e.typ), addrLoc(p.module, a)], a.storage)
elif 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)
Expand Down Expand Up @@ -1284,6 +1272,7 @@ proc genDestroy(p: BProc; n: CgNode) =
let t = arg.typ.skipTypes(abstractInst)
case t.kind
of tyString:
requestFullDesc(p.module, arg.typ)
var a: TLoc
initLocExpr(p, arg, a)
if optThreads in p.config.globalOptions:
Expand All @@ -1295,6 +1284,7 @@ proc genDestroy(p: BProc; n: CgNode) =
" #dealloc($1.p);$n" &
"}$n", [rdLoc(a)])
of tySequence:
requestFullDesc(p.module, arg.typ)
var a: TLoc
initLocExpr(p, arg, a)
linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
Expand Down Expand Up @@ -1377,7 +1367,7 @@ proc genMagicExpr(p: BProc, e: CgNode, d: var TLoc, op: TMagic) =
let member =
if dotExpr.kind == cnkTupleAccess:
"Field" & rope(dotExpr[1].intVal)
else: p.fieldName(dotExpr[1].field)
else: p.fieldName(dotExpr[0].typ, dotExpr[1].field)
putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
of mChr: genSomeCast(p, e, d)
of mOrd: genOrd(p, e, d)
Expand Down Expand Up @@ -1566,14 +1556,14 @@ proc downConv(p: BProc, n: CgNode, d: var TLoc) =
if lfWantLvalue in d.flags:
putIntoDest(p, d, n,
"(($1*) ($2))" % [getTypeDesc(p.module, n.typ),
addrLoc(p.config, a)], a.storage)
addrLoc(p.module, a)], a.storage)
d.flags.incl lfIndirect
else:
putIntoDest(p, d, n,
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
else:
putIntoDest(p, d, n, "(*($1*) ($2))" %
[getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage)
[getTypeDesc(p.module, dest), addrLoc(p.module, a)], a.storage)

proc upConv(p: BProc, n: CgNode, d: var TLoc) =
## Generates and emits the code for the ``cnkObjUpConv`` (conversion to
Expand All @@ -1591,7 +1581,7 @@ proc upConv(p: BProc, n: CgNode, d: var TLoc) =
# expression and then cast the pointer:
putIntoDest(p, d, n,
"(($1*) ($2))" % [getTypeDesc(p.module, n.typ),
addrLoc(p.config, a)],
addrLoc(p.module, a)],
a.storage)
# an indirection is used:
d.flags.incl lfIndirect
Expand Down
9 changes: 5 additions & 4 deletions compiler/backend/ccgstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ proc genExcept(p: BProc, n: CgNode) =
# setup the handler frame:
var tmp: TLoc
getTemp(p, p.module.g.graph.getCompilerProc("ExceptionFrame").typ, tmp)
lineCg(p, cpsStmts, "#nimCatchException($1);$n", [addrLoc(p.config, tmp)])
lineCg(p, cpsStmts, "#nimCatchException($1);$n", [addrLoc(p.module, tmp)])

proc genAsmOrEmitStmt(p: BProc, t: CgNode, isAsmStmt=false): Rope =
var res = ""
Expand All @@ -317,9 +317,10 @@ proc genAsmOrEmitStmt(p: BProc, t: CgNode, isAsmStmt=false): Rope =
let sym = it.astLit.sym
# special support for raw field symbols
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
p.config.internalAssert(sym.locId != 0, it.info):
"field's surrounding type not setup"
res.add(p.fieldName(sym))
# make sure the owner was generated, so that the field's mangled name
# is available
discard getTypeDesc(p.module, sym.owner.typ)
res.add(p.fieldName(sym.owner.typ, sym))
of cnkLocal:
# make sure the C type description is available:
discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs))
Expand Down
1 change: 0 additions & 1 deletion compiler/backend/ccgthreadvars.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ proc declareThreadVar*(m: BModule, id: GlobalId, isExtern: bool) =
proc generateThreadLocalStorage(m: BModule) =
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
finishTypeDescriptions(m)
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])

proc generateThreadVarsSize(m: BModule) =
Expand Down
Loading

0 comments on commit a0d524b

Please sign in to comment.