From 07628b0dec43dd417b6b536eff1c0f0a5c539b6d Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 13 Oct 2024 20:56:17 +0300 Subject: [PATCH] use cbuilder for most braced initializers (#24259) `StructInitializer` is now used for most braced initializers in the C generation, mostly in `genBracedInit`, `getNullValueAux`, `getDefaultValue`. The exceptions are: * the default case branch initializer for objects uses C99 designated initializers with field names, which are not implemented for `StructInitializer` yet (`siNamedStruct`) * the uses in `ccgliterals` are untouched so all of ccgliterals can be done separately and in 1 go There is one case where `genBracedInit` does not use cbuilder, which is the global literal variable for openarrays. The reason for this is simply that variables with C array type are not implemented, which I thought would be best to leave out of this PR. For the simplicity of the implementation, code in `getNullValueAuxT` that reset the initializer back to its initial state if the `Sup` field did not have any fields itself, is now disabled. This was so the compiler does not generate `{}` for the Sup field, i.e. `{{}}`, but every call to `getNullValueAuxT` still generates `{}` if the struct doesn't have any fields, so I don't know if it really breaks anything. The case where the Sup field doesn't have any fields but the struct does also would have generated `{{}, field}`. Worst case, we can implement either the "resetting" or just disable the generation of the `Sup` field if there are no fields total. But a better fix might be to always generate `{0}` if the struct has no fields, in line with the `char dummy` field that gets added for all objects with no fields. This doesn't seem necessary for now but might be for the NIFC output, in which case we can probably keep the logic contained inside cbuilder (if no fields generated for `siOrderedStruct`/`siNamedStruct`, we add a `0` for the `dummy` field). This would stipulate that all uses of struct initializers are exhaustive for every field in structs. --- compiler/cbuilderdecls.nim | 46 ++++-- compiler/cbuilderexprs.nim | 6 + compiler/ccgexprs.nim | 327 ++++++++++++++++++++++--------------- compiler/ccgliterals.nim | 2 +- compiler/ccgstmts.nim | 2 +- compiler/ccgtypes.nim | 1 + 6 files changed, 234 insertions(+), 150 deletions(-) diff --git a/compiler/cbuilderdecls.nim b/compiler/cbuilderdecls.nim index 4185c71826..e3c1c31075 100644 --- a/compiler/cbuilderdecls.nim +++ b/compiler/cbuilderdecls.nim @@ -63,41 +63,55 @@ template addTypedef(builder: var Builder, name: string, typeBody: typed) = builder.add(name) builder.add(";\n") -type StructInitializer = object - ## context for building struct initializers, i.e. `{ field1, field2 }` - # XXX use in genBracedInit - orderCompliant: bool - ## if true, fields will not be named, instead values are placed in order - needsComma: bool +type + StructInitializerKind = enum + siOrderedStruct ## struct constructor, but without named fields on C + siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer + siArray ## array constructor + siWrapper ## wrapper for a single field, generates it verbatim -proc initStructInitializer(builder: var Builder, orderCompliant: bool): StructInitializer = + StructInitializer = object + ## context for building struct initializers, i.e. `{ field1, field2 }` + kind: StructInitializerKind + ## if true, fields will not be named, instead values are placed in order + needsComma: bool + +proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer = ## starts building a struct initializer, `orderCompliant = true` means ## built fields must be ordered correctly - doAssert orderCompliant, "named struct constructors unimplemented" - result = StructInitializer(orderCompliant: true, needsComma: false) - builder.add("{ ") + assert kind != siNamedStruct, "named struct constructors unimplemented" + result = StructInitializer(kind: kind, needsComma: false) + if kind != siWrapper: + builder.add("{") template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) = ## adds a field to a struct initializer, with the value built in `valueBody` if constr.needsComma: + assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields" builder.add(", ") else: constr.needsComma = true - if constr.orderCompliant: + case constr.kind + of siArray, siWrapper: # no name, can just add value valueBody - else: - doAssert false, "named struct constructors unimplemented" + of siOrderedStruct: + # no name, can just add value on C + assert name.len != 0, "name has to be given for struct initializer field" + valueBody + of siNamedStruct: + assert false, "named struct constructors unimplemented" proc finishStructInitializer(builder: var Builder, constr: StructInitializer) = ## finishes building a struct initializer - builder.add(" }") + if constr.kind != siWrapper: + builder.add("}") -template addStructInitializer(builder: var Builder, constr: out StructInitializer, orderCompliant: bool, body: typed) = +template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) = ## builds a struct initializer, i.e. `{ field1, field2 }` ## a `var StructInitializer` must be declared and passed as a parameter so ## that it can be used with `addField` - constr = builder.initStructInitializer(orderCompliant) + constr = builder.initStructInitializer(kind) body builder.finishStructInitializer(constr) diff --git a/compiler/cbuilderexprs.nim b/compiler/cbuilderexprs.nim index 16caaa0215..f70405be7a 100644 --- a/compiler/cbuilderexprs.nim +++ b/compiler/cbuilderexprs.nim @@ -15,5 +15,11 @@ const proc procPtrType(conv: TCallingConvention, rettype: Snippet, name: string): Snippet = CallingConvToStr[conv] & "_PTR(" & rettype & ", " & name & ")" +proc cCast(typ, value: Snippet): Snippet = + "((" & typ & ") " & value & ")" + +proc cAddr(value: Snippet): Snippet = + "&" & value + proc bitOr(a, b: Snippet): Snippet = a & " | " & b diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a344816ba8..21ecc9688b 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -13,7 +13,7 @@ when defined(nimCompilerStacktraceHints): import std/stackframes proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, - result: var Rope; count: var int; + result: var Builder; init: var StructInitializer; isConst: bool, info: TLineInfo) # -------------------------- constant expressions ------------------------ @@ -3193,7 +3193,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkMixinStmt, nkBindStmt: discard else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") -proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = +proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder) = var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc}) case t.kind of tyBool: result.add rope"NIM_FALSE" @@ -3204,38 +3204,56 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = result.add rope"NIM_NIL" of tyString, tySequence: if optSeqDestructors in p.config.globalOptions: - result.add "{0, NIM_NIL}" + var seqInit: StructInitializer + result.addStructInitializer(seqInit, kind = siOrderedStruct): + result.addField(seqInit, name = "len"): + result.add("0") + result.addField(seqInit, name = "p"): + result.add("NIM_NIL") else: result.add "NIM_NIL" of tyProc: if t.callConv != ccClosure: result.add "NIM_NIL" else: - result.add "{NIM_NIL, NIM_NIL}" + var closureInit: StructInitializer + result.addStructInitializer(closureInit, kind = siOrderedStruct): + result.addField(closureInit, name = "ClP_0"): + result.add("NIM_NIL") + result.addField(closureInit, name = "ClE_0"): + result.add("NIM_NIL") of tyObject: - var count = 0 - result.add "{" - getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info) - result.add "}" + var objInit: StructInitializer + result.addStructInitializer(objInit, kind = siOrderedStruct): + getNullValueAuxT(p, t, t, t.n, nil, result, objInit, true, info) of tyTuple: - result.add "{" - if p.vccAndC and t.isEmptyTupleType: - result.add "0" - for i, a in t.ikids: - if i > 0: result.add ", " - getDefaultValue(p, a, info, result) - result.add "}" + var tupleInit: StructInitializer + result.addStructInitializer(tupleInit, kind = siOrderedStruct): + if p.vccAndC and t.isEmptyTupleType: + result.addField(tupleInit, name = "dummy"): + result.add "0" + for i, a in t.ikids: + result.addField(tupleInit, name = "Field" & $i): + getDefaultValue(p, a, info, result) of tyArray: - result.add "{" - for i in 0.. 0: result.add ", " - getDefaultValue(p, t.elementType, info, result) - result.add "}" + var arrInit: StructInitializer + result.addStructInitializer(arrInit, kind = siArray): + for i in 0.. 0: res.add ", " + getNullValueAux(p, t, obj[0], constOrNil, result, init, isConst, info) var branch = Zero if constOrNil != nil: ## find kind value, default is zero if not specified @@ -3269,140 +3285,177 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, break let selectedBranch = caseObjDefaultBranch(obj, branch) - res.add "{" - var countB = 0 + # XXX siNamedStruct needs to be implemented to replace `res` here + var res = "{" + var branchInit: StructInitializer let b = lastSon(obj[selectedBranch]) # designated initilization is the only way to init non first element of unions # branches are allowed to have no members (b.len == 0), in this case they don't need initializer if b.kind == nkRecList and not isEmptyCaseObjectBranch(b): - res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" - getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) - res.add "}" + res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = " + res.addStructInitializer(branchInit, kind = siOrderedStruct): + getNullValueAux(p, t, b, constOrNil, res, branchInit, isConst, info) elif b.kind == nkSym: res.add "." & mangleRecFieldName(p.module, b.sym) & " = " - getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) + res.addStructInitializer(branchInit, kind = siWrapper): + getNullValueAux(p, t, b, constOrNil, res, branchInit, isConst, info) else: return - result.add res - result.add "}" + result.addField(init, name = ""): + # XXX figure out name for the union, see use of `addAnonUnion` + result.add res + result.add "}" of nkSym: - if count > 0: result.add ", " - inc count let field = obj.sym - if constOrNil != nil: - for i in 1.. 0: result.add ",\n" - if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, it[0].typ, result) - else: genBracedInit(p, it, isConst, it.typ, result) - result.add("}\n") - -proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) = - result.add "{" - if p.vccAndC and n.len == 0: - result.add "0" - for i in 0.. 0: result.add ",\n" - if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, tup[i], result) - else: genBracedInit(p, it, isConst, tup[i], result) - result.add("}\n") - -proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) = - var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] - let base = t.skipTypes(abstractInst)[0] - if n.len > 0: - # array part needs extra curlies: - data.add(", {") +proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Builder) = + var arrInit: StructInitializer + result.addStructInitializer(arrInit, kind = siArray): + if p.vccAndC and n.len == 0 and n.typ.kind == tyArray: + result.addField(arrInit, name = ""): + getDefaultValue(p, n.typ.elementType, n.info, result) for i in 0.. 0: data.addf(",$n", []) - genBracedInit(p, n[i], isConst, base, data) - data.add("}") - data.add("}") + let it = n[i] + var ind, val: PNode + if it.kind == nkExprColonExpr: + ind = it[0] + val = it[1] + else: + ind = it + val = it + result.addField(arrInit, name = ""): + genBracedInit(p, val, isConst, ind.typ, result) +proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Builder) = + var tupleInit: StructInitializer + result.addStructInitializer(tupleInit, kind = siOrderedStruct): + if p.vccAndC and n.len == 0: + result.addField(tupleInit, name = "dummy"): + result.add("0") + for i in 0.. 0: + def.addField(structInit, name = "data"): + var arrInit: StructInitializer + def.addStructInitializer(arrInit, kind = siArray): + for i in 0.. 0: - data.add(", {") - for i in 0.. 0: data.addf(",$n", []) - genBracedInit(p, n[i], isConst, base, data) - data.add("}") - let payload = getTempName(p.module) - appcg(p.module, cfsStrData, - "static $5 struct {$n" & - " NI cap; $1 data[$2];$n" & - "} $3 = {$2 | NIM_STRLIT_FLAG$4};$n", [ - getTypeDesc(p.module, base), n.len, payload, data, - if isConst: "const" else: ""]) - result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] -proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope) = + var def = newBuilder("") + def.addVarWithTypeAndInitializer( + if isConst: AlwaysConst else: Global, + name = payload): + def.addSimpleStruct(p.module, name = "", baseType = ""): + def.addField(name = "cap", typ = "NI") + def.addArrayField(name = "data", elementType = getTypeDesc(p.module, base), len = n.len) + do: + var structInit: StructInitializer + def.addStructInitializer(structInit, kind = siOrderedStruct): + def.addField(structInit, name = "cap"): + def.add(bitOr(rope(n.len), "NIM_STRLIT_FLAG")) + if n.len > 0: + def.addField(structInit, name = "data"): + var arrInit: StructInitializer + def.addStructInitializer(arrInit, kind = siArray): + for i in 0..