Skip to content

Commit

Permalink
use cbuilder for seq type generation (#24202)
Browse files Browse the repository at this point in the history
`addSimpleStruct` is just so the compiler doesn't use so much extra
computation on analyzing the `typ` parameter for `addStruct`, which
doesn't change anything for `seq` types. We could probably still get
away with using `addStruct` instead, or making `addStruct` accept `nil`
as the `typ` argument but this would be even more computation.

There were a lot of hidden issues with `addStruct` being a template &
template argument substitution, so most of the behavior is moved into
`startStruct`/`finishStruct` procs.

This is turning out to be a lot of code for just a couple of changed
lines, we might have to split `cbuilder` into multiple modules.
  • Loading branch information
metagn authored Oct 3, 2024
1 parent d6a71a1 commit 89978b4
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 40 deletions.
109 changes: 81 additions & 28 deletions compiler/cbuilder.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ type
template newBuilder(s: string): Builder =
s

proc addField(obj: var Builder; name, typ: Snippet) =
proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
obj.add('\t')
obj.add(typ)
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")

proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
## for fields based on an `skField` symbol
obj.add('\t')
if field.alignment > 0:
obj.add("NIM_ALIGN(")
Expand All @@ -32,6 +37,13 @@ proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bo
obj.add(initializer)
obj.add(";\n")

type
BaseClassKind = enum
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
StructBuilderInfo = object
baseKind: BaseClassKind
preFieldsLen: int

proc structOrUnion(t: PType): Snippet =
let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: "union"
Expand All @@ -40,47 +52,80 @@ proc structOrUnion(t: PType): Snippet =
proc ptrType(t: Snippet): Snippet =
t & "*"

template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
if tfPacked in typ.flags:
proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone)
obj.add("struct ")
obj.add(name)
if baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
result.preFieldsLen = obj.len
if result.baseKind == bcSupField:
obj.addField(name = "Sup", typ = baseType)

proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.len:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
obj.add("};\n")

template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
## for independent structs, not directly based on a Nim type
let info = startSimpleStruct(obj, m, name, baseType)
body
finishSimpleStruct(obj, m, info)

proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone)
if tfPacked in t.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add(structOrUnion(typ))
obj.add(structOrUnion(t))
obj.add(" __attribute__((__packed__))")
else:
obj.add("#pragma pack(push, 1)\n")
obj.add(structOrUnion(typ))
obj.add(structOrUnion(t))
else:
obj.add(structOrUnion(typ))
obj.add(structOrUnion(t))
obj.add(" ")
obj.add(name)
type BaseClassKind = enum
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
var baseKind = bcNone
if typ.kind == tyObject:
if typ.baseClass == nil:
if lacksMTypeField(typ):
baseKind = bcNone
if t.kind == tyObject:
if t.baseClass == nil:
if lacksMTypeField(t):
result.baseKind = bcNone
elif optTinyRtti in m.config.globalOptions:
baseKind = bcNoneTinyRtti
result.baseKind = bcNoneTinyRtti
else:
baseKind = bcNoneRtti
result.baseKind = bcNoneRtti
elif m.compileToCpp:
baseKind = bcCppInherit
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
elif baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
baseKind = bcSupField
if baseKind == bcCppInherit:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
let currLen = obj.len
case baseKind
result.preFieldsLen = obj.len
case result.baseKind
of bcNone:
# rest of the options add a field or don't need it due to inheritance,
# we need to add the dummy field for uncheckedarray ahead of time
# so that it remains trailing
if typ.itemId notin m.g.graph.memberProcsPerType and
typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
if t.itemId notin m.g.graph.memberProcsPerType and
t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
# only consists of flexible array field, add *initial* dummy field
obj.addField(name = "dummy", typ = "char")
of bcCppInherit: discard
Expand All @@ -90,13 +135,21 @@ template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseT
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
of bcSupField:
obj.addField(name = "Sup", typ = baseType)
body
if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:

proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.len and
t.itemId notin m.g.graph.memberProcsPerType:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
obj.add("};\n")
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
result.add("#pragma pack(pop)\n")
if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
obj.add("#pragma pack(pop)\n")

template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
## for structs built directly from a Nim type
let info = startStruct(obj, m, typ, name, baseType)
body
finishStruct(obj, m, typ, info)

template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
## adds a field with a `struct { ... }` type, building it according to `body`
Expand Down
20 changes: 8 additions & 12 deletions compiler/ccgtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -967,18 +967,14 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
m.typeCache[sig] = result & seqStar(m)
if not isImportedType(t):
if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
const
cppSeq = "struct $2 : #TGenericSeq {$n"
cSeq = "struct $2 {$n" &
" #TGenericSeq Sup;$n"
if m.compileToCpp:
appcg(m, m.s[cfsSeqTypes],
cppSeq & " $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
else:
appcg(m, m.s[cfsSeqTypes],
cSeq & " $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
var struct = newBuilder("")
let baseType = cgsymValue(m, "TGenericSeq")
struct.addSimpleStruct(m, name = result, baseType = baseType):
struct.addField(
name = "data",
typ = getTypeDescAux(m, t.elementType, check, kind),
isFlexArray = true)
m.s[cfsSeqTypes].add struct
else:
result = rope("TGenericSeq")
result.add(seqStar(m))
Expand Down

0 comments on commit 89978b4

Please sign in to comment.