diff options
author | metagn <metagngn@gmail.com> | 2024-09-27 12:12:39 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-27 11:12:39 +0200 |
commit | 2cdc0e913fc6d7b3486b907de502faa508820c42 (patch) | |
tree | 04e79e8d0f9c6237e45cfe9e2b18e2628d934480 | |
parent | dc3ffb6a71f36e3cfd1fc001d128a86b46c88e7a (diff) | |
download | Nim-2cdc0e913fc6d7b3486b907de502faa508820c42.tar.gz |
use cbuilder for tuple/object generation (#24145)
based on #24127 Needs some tweaks to replace the other `struct` type generations, e.g. seqs, maybe by exposing `BaseTypeKind` as a parameter. C++ and codegenDecl etc seem like they are going to need attention. Also `Builder` should really be `distinct string` that one has to call `extract` on, but for this to be optimal in the current codegen, we would need something like: ```nim template buildInto(s: var string, builderName: untyped, body) = template `builderName`: untyped = Builder(s) body buildInto(result, builder): builder.add ... ``` but this could be a separate PR since it might not work with the compiler. The possibly-not-optimal alternative is to do: ```nim template build(builderName: untyped, body): string = var `builderName` = Builder("") body extract(`builderName`) result = build(builder): builder.add ... ``` where the compiler maybe copies the built string but shouldn't. --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
-rw-r--r-- | compiler/cbuilder.nim | 121 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 134 | ||||
-rw-r--r-- | compiler/cgen.nim | 1 |
3 files changed, 153 insertions, 103 deletions
diff --git a/compiler/cbuilder.nim b/compiler/cbuilder.nim new file mode 100644 index 000000000..6fc0bd881 --- /dev/null +++ b/compiler/cbuilder.nim @@ -0,0 +1,121 @@ +type + Snippet = string + Builder = string + +template newBuilder(s: string): Builder = + s + +proc addField(obj: var Builder; name, typ: Snippet) = + obj.add('\t') + obj.add(typ) + obj.add(" ") + obj.add(name) + obj.add(";\n") + +proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) = + obj.add('\t') + if field.alignment > 0: + obj.add("NIM_ALIGN(") + obj.addInt(field.alignment) + obj.add(") ") + obj.add(typ) + if sfNoalias in field.flags: + obj.add(" NIM_NOALIAS") + obj.add(" ") + obj.add(name) + if isFlexArray: + obj.add("[SEQ_DECL_SIZE]") + if field.bitsize != 0: + obj.add(":") + obj.addInt(field.bitsize) + if initializer.len != 0: + obj.add(initializer) + obj.add(";\n") + +proc structOrUnion(t: PType): Snippet = + let t = t.skipTypes({tyAlias, tySink}) + if tfUnion in t.flags: "union" + else: "struct" + +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: + if hasAttribute in CC[m.config.cCompiler].props: + obj.add(structOrUnion(typ)) + obj.add(" __attribute__((__packed__))") + else: + obj.add("#pragma pack(push, 1)\n") + obj.add(structOrUnion(typ)) + else: + obj.add(structOrUnion(typ)) + 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 + elif optTinyRtti in m.config.globalOptions: + baseKind = bcNoneTinyRtti + else: + baseKind = bcNoneRtti + elif m.compileToCpp: + baseKind = bcCppInherit + else: + baseKind = bcSupField + if baseKind == bcCppInherit: + obj.add(" : public ") + obj.add(baseType) + obj.add(" ") + obj.add("{\n") + let currLen = obj.len + case 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: + # only consists of flexible array field, add *initial* dummy field + obj.addField(name = "dummy", typ = "char") + of bcCppInherit: discard + of bcNoneRtti: + obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType"))) + of bcNoneTinyRtti: + 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: + # 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") + +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` + obj.add('\t') + if tfPacked in parentTyp.flags: + if hasAttribute in CC[m.config.cCompiler].props: + obj.add("struct __attribute__((__packed__)) {\n") + else: + obj.add("#pragma pack(push, 1)\nstruct {") + else: + obj.add("struct {\n") + body + obj.add("} ") + obj.add(fieldName) + obj.add(";\n") + if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props: + result.add("#pragma pack(pop)\n") + +template addAnonUnion(obj: var Builder; body: typed) = + obj.add "union{\n" + body + obj.add("};\n") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 5c3e82346..5b5218a3e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -376,11 +376,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope = result = getSimpleTypeDesc(m, typ) if result == "": result = cacheGetType(m.typeCache, sig) -proc structOrUnion(t: PType): Rope = - let t = t.skipTypes({tyAlias, tySink}) - if tfUnion in t.flags: "union" - else: "struct" - proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) = if m.compileToCpp: m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename] @@ -698,7 +693,7 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool) proc genRecordFieldsAux(m: BModule; n: PNode, rectype: PType, - check: var IntSet; result: var Rope; unionPrefix = "") = + check: var IntSet; result: var Builder; unionPrefix = "") = case n.kind of nkRecList: for i in 0..<n.len: @@ -715,64 +710,51 @@ proc genRecordFieldsAux(m: BModule; n: PNode, let k = lastSon(n[i]) if k.kind != nkSym: let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i - var a = newRopeAppender() + var a = newBuilder("") genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".") - if a != "": - if tfPacked notin rectype.flags: - unionBody.add("struct {") - else: - if hasAttribute in CC[m.config.cCompiler].props: - unionBody.add("struct __attribute__((__packed__)){") - else: - unionBody.addf("#pragma pack(push, 1)$nstruct{", []) - unionBody.add(a) - unionBody.addf("} $1;$n", [structName]) - if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props: - unionBody.addf("#pragma pack(pop)$n", []) + if a.len != 0: + unionBody.addFieldWithStructType(m, rectype, structName): + unionBody.add(a) else: genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix) else: internalError(m.config, "genRecordFieldsAux(record case branch)") - if unionBody != "": - result.addf("union{\n$1};$n", [unionBody]) + if unionBody.len != 0: + result.addAnonUnion: + result.add(unionBody) of nkSym: let field = n.sym if field.typ.kind == tyVoid: return #assert(field.ast == nil) let sname = mangleRecFieldName(m, field) fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown) - if field.alignment > 0: - result.addf "NIM_ALIGN($1) ", [rope(field.alignment)] # for importcpp'ed objects, we only need to set field.loc, but don't # have to recurse via 'getTypeDescAux'. And not doing so prevents problems # with heavily templatized C++ code: if not isImportedCppType(rectype): - let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: "" - let fieldType = field.loc.lode.typ.skipTypes(abstractInst) + var typ: Rope = "" + var isFlexArray = false + var initializer = "" if fieldType.kind == tyUncheckedArray: - result.addf("\t$1 $2[SEQ_DECL_SIZE];$n", - [getTypeDescAux(m, fieldType.elemType, check, dkField), sname]) + typ = getTypeDescAux(m, fieldType.elemType, check, dkField) + isFlexArray = true elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. - result.addf("\t$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, dkField), sname, noAlias]) - elif field.bitsize != 0: - result.addf("\t$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, rope($field.bitsize), noAlias]) + typ = getTypeDescWeak(m, field.loc.t, check, dkField) else: + typ = getTypeDescAux(m, field.loc.t, check, dkField) # don't use fieldType here because we need the # tyGenericInst for C++ template support let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags) if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)): var didGenTemp = false - var initializer = genCppInitializer(m, nil, fieldType, didGenTemp) - result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer]) - else: - result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias]) + initializer = genCppInitializer(m, nil, fieldType, didGenTemp) + result.addField(field, sname, typ, isFlexArray, initializer) else: internalError(m.config, n.info, "genRecordFieldsAux()") proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false) -proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = - result = newRopeAppender() +proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) = genRecordFieldsAux(m, typ.n, typ, check, result) if typ.itemId in m.g.graph.memberProcsPerType: let procs = m.g.graph.memberProcsPerType[typ.itemId] @@ -794,88 +776,34 @@ proc fillObjectFields*(m: BModule; typ: PType) = # sometimes generic objects are not consistently merged. We patch over # this fact here. var check = initIntSet() - discard getRecordFields(m, typ, check) + var ignored = newBuilder("") + addRecordFields(ignored, m, typ, check) proc mangleDynLibProc(sym: PSym): Rope -proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope, - check: var IntSet, hasField:var bool): Rope = - result = "" - if typ.kind == tyObject: - if typ.baseClass == nil: - if lacksMTypeField(typ): - appcg(m, result, " {$n", []) - else: - if optTinyRtti in m.config.globalOptions: - appcg(m, result, " {$n#TNimTypeV2* m_type;$n", []) - else: - appcg(m, result, " {$n#TNimType* m_type;$n", []) - hasField = true - elif m.compileToCpp: - appcg(m, result, " : public $1 {$n", [baseType]) - if typ.isException and m.config.exc == excCpp: - when false: - appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions - if typ.sym.magic == mException: - # Add cleanup destructor to Exception base class - appcg(m, result, "~$1();$n", [name]) - # define it out of the class body and into the procs section so we don't have to - # artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR) - appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name]) - hasField = true - else: - appcg(m, result, " {$n $1 Sup;$n", [baseType]) - hasField = true - else: - result.addf(" {$n", [name]) - proc getRecordDesc(m: BModule; typ: PType, name: Rope, check: var IntSet): Rope = # declare the record: - var hasField = false - var structOrUnion: string - if tfPacked in typ.flags: - if hasAttribute in CC[m.config.cCompiler].props: - structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))" - else: - structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ) - else: - structOrUnion = structOrUnion(typ) var baseType: string = "" if typ.baseClass != nil: baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField) if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: - result = structOrUnion & " " & name - result.add(getRecordDescAux(m, typ, name, baseType, check, hasField)) - let desc = getRecordFields(m, typ, check) - if not hasField and typ.itemId notin m.g.graph.memberProcsPerType: - if desc == "": - result.add("\tchar dummy;\n") - elif typ.n.len == 1 and typ.n[0].kind == nkSym: - let field = typ.n[0].sym - let fieldType = field.typ.skipTypes(abstractInst) - if fieldType.kind == tyUncheckedArray: - result.add("\tchar dummy;\n") - result.add(desc) - else: - result.add(desc) - result.add("};\L") + result = newBuilder("") + result.addStruct(m, typ, name, baseType): + result.addRecordFields(m, typ, check) else: - let desc = getRecordFields(m, typ, check) + var desc = newBuilder("") + desc.addRecordFields(m, typ, check) result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType]) - if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: - result.add "#pragma pack(pop)\L" proc getTupleDesc(m: BModule; typ: PType, name: Rope, check: var IntSet): Rope = - result = "$1 $2 {$n" % [structOrUnion(typ), name] - var desc: Rope = "" - for i, a in typ.ikids: - desc.addf("$1 Field$2;$n", - [getTypeDescAux(m, a, check, dkField), rope(i)]) - if desc == "": result.add("char dummy;\L") - else: result.add(desc) - result.add("};\L") + result = newBuilder("") + result.addStruct(m, typ, name, ""): + for i, a in typ.ikids: + result.addField( + name = "Field" & $i, + typ = getTypeDescAux(m, a, check, dkField)) proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool = # A helper proc for handling cppimport patterns, involving numeric diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8a6bd96ec..091f5c842 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -373,6 +373,7 @@ proc dataField(p: BProc): Rope = proc genProcPrototype(m: BModule, sym: PSym) +include cbuilder include ccgliterals include ccgtypes |