From 801fbe7e948ee786ceeb497ecad97607a21a663c Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 3 Dec 2019 11:58:56 +0100 Subject: [PATCH] better support for PROGMEM like annotations for lets/vars; fixes #12216 --- compiler/ccgexprs.nim | 75 ++++++++++++++++++++------------------ compiler/ccgliterals.nim | 31 +++++++++------- compiler/ccgmerge.nim | 2 +- compiler/ccgstmts.nim | 38 +++++++++++++------ compiler/cgen.nim | 18 +++++++-- compiler/cgendata.nim | 2 +- tests/ccgbugs/tprogmem.nim | 11 ++++++ 7 files changed, 109 insertions(+), 68 deletions(-) create mode 100644 tests/ccgbugs/tprogmem.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a95086c716294..6f274c0ec75c3 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1313,7 +1313,6 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = genTypeInfo(p.module, seqtype, e.info), a.rdLoc])) gcUsage(p.config, e) -proc genConstExpr(p: BProc, n: PNode): Rope proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: let t = n.typ @@ -1324,7 +1323,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = # expression not found in the cache: inc(p.module.labels) p.module.s[cfsData].addf("NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)]) + [getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true)]) result = true else: result = false @@ -2340,7 +2339,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = inc(p.module.labels) var tmp = "CNSTCLOSURE" & rope(p.module.labels) p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) + [getTypeDesc(p.module, n.typ), tmp, genBracedInit(p, n, isConst = true)]) putIntoDest(p, d, n, tmp, OnStatic) else: var tmp, a, b: TLoc @@ -2484,7 +2483,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = # expression not found in the cache: inc(p.module.labels) p.module.s[cfsData].addf("NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) + [getTypeDesc(p.module, t), tmp, genBracedInit(p, n, isConst = true)]) if d.k == locNone: fillLoc(d, locData, n, tmp, OnStatic) @@ -2697,9 +2696,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkBreakState: genBreakState(p, n, d) else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") -proc genNamedConstExpr(p: BProc, n: PNode): Rope = - if n.kind == nkExprColonExpr: result = genConstExpr(p, n[1]) - else: result = genConstExpr(p, n) +proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope = + if n.kind == nkExprColonExpr: result = genBracedInit(p, n[1], isConst) + else: result = genBracedInit(p, n, isConst) proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc}) @@ -2739,15 +2738,16 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = globalError(p.config, info, "cannot create null element for: " & $t.kind) proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, - result: var Rope; count: var int) = + result: var Rope; count: var int; + isConst: bool) = case obj.kind of nkRecList: for it in obj.sons: - getNullValueAux(p, t, it, cons, result, count) + getNullValueAux(p, t, it, cons, result, count, isConst) of nkRecCase: - getNullValueAux(p, t, obj[0], cons, result, count) + getNullValueAux(p, t, obj[0], cons, result, count, isConst) for i in 1.. 0: result.add ", " inc count @@ -2755,10 +2755,10 @@ proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, for i in 1.. 0: - result.add(genNamedConstExpr(p, n[^1])) + result.add(genNamedConstExpr(p, n[^1], isConst)) result.addf("}$n", []) -proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = +proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope = var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] if n.len > 0: # array part needs extra curlies: data.add(", {") for i in 0.. 0: data.addf(",$n", []) - data.add genConstExpr(p, n[i]) + data.add genBracedInit(p, n[i], isConst) data.add("}") data.add("}") @@ -2816,35 +2817,37 @@ proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = let base = t.skipTypes(abstractInst)[0] appcg(p.module, cfsData, - "NIM_CONST struct {$n" & + "$5 struct {$n" & " #TGenericSeq Sup;$n" & " $1 data[$2];$n" & "} $3 = $4;$n", [ - getTypeDesc(p.module, base), n.len, result, data]) + getTypeDesc(p.module, base), n.len, result, data, + if isConst: "NIM_CONST" else: ""]) result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] -proc genConstSeqV2(p: BProc, n: PNode, t: PType): Rope = +proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = var data = rope"{" for i in 0.. 0: data.addf(",$n", []) - data.add genConstExpr(p, n[i]) + data.add genBracedInit(p, n[i], isConst) data.add("}") let payload = getTempName(p.module) let base = t.skipTypes(abstractInst)[0] appcg(p.module, cfsData, - "static const struct {$n" & + "static $5 struct {$n" & " NI cap; void* allocator; $1 data[$2];$n" & "} $3 = {$2, NIM_NIL, $4};$n", [ - getTypeDesc(p.module, base), n.len, payload, data]) + getTypeDesc(p.module, base), n.len, payload, data, + if isConst: "const" else: ""]) result = "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] -proc genConstExpr(p: BProc, n: PNode): Rope = +proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: - result = genConstExpr(p, n[1]) + result = genBracedInit(p, n[1], isConst) of nkCurly: var cs: TBitSet toBitSet(p.config, n, cs) @@ -2853,9 +2856,9 @@ proc genConstExpr(p: BProc, n: PNode): Rope = var t = skipTypes(n.typ, abstractInstOwned) if t.kind == tySequence: if optSeqDestructors in p.config.globalOptions: - result = genConstSeqV2(p, n, n.typ) + result = genConstSeqV2(p, n, n.typ, isConst) else: - result = genConstSeq(p, n, n.typ) + result = genConstSeq(p, n, n.typ, isConst) elif t.kind == tyProc and t.callConv == ccClosure and n.len > 1 and n[1].kind == nkNilLit: # Conversion: nimcall -> closure. @@ -2872,12 +2875,12 @@ proc genConstExpr(p: BProc, n: PNode): Rope = initLocExpr(p, n[0], d) result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, t, clHalfWithEnv), rdLoc(d)] else: - result = genConstSimpleList(p, n) + result = genConstSimpleList(p, n, isConst) of nkObjConstr: - result = genConstObjConstr(p, n) + result = genConstObjConstr(p, n, isConst) of nkStrLit..nkTripleStrLit: if optSeqDestructors in p.config.globalOptions: - result = genStringLiteralV2Const(p.module, n) + result = genStringLiteralV2Const(p.module, n, isConst) else: var d: TLoc initLocExpr(p, n, d) diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index a016bdcaa8a5a..b513485087326 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -53,29 +53,31 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope = # ------ Version 2: destructor based strings and seqs ----------------------- -proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope) = - m.s[cfsData].addf("static const struct {$n" & +proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) = + m.s[cfsData].addf("static $4 struct {$n" & " NI cap; void* allocator; NIM_CHAR data[$2+1];$n" & "} $1 = { $2, NIM_NIL, $3 };$n", - [result, rope(s.len), makeCString(s)]) + [result, rope(s.len), makeCString(s), + rope(if isConst: "const" else: "")]) -proc genStringLiteralV2(m: BModule; n: PNode): Rope = +proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: let pureLit = getTempName(m) - genStringLiteralDataOnlyV2(m, n.strVal, pureLit) + genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) result = getTempName(m) discard cgsym(m, "NimStrPayload") discard cgsym(m, "NimStringV2") # string literal not found in the cache: - m.s[cfsData].addf("static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", - [result, rope(n.strVal.len), pureLit]) + m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [result, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")]) else: result = getTempName(m) - m.s[cfsData].addf("static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", - [result, rope(n.strVal.len), m.tmpBase & rope(id)]) + m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [result, rope(n.strVal.len), m.tmpBase & rope(id), + rope(if isConst: "const" else: "")]) -proc genStringLiteralV2Const(m: BModule; n: PNode): Rope = +proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) var pureLit: Rope if id == m.labels: @@ -83,19 +85,20 @@ proc genStringLiteralV2Const(m: BModule; n: PNode): Rope = discard cgsym(m, "NimStrPayload") discard cgsym(m, "NimStringV2") # string literal not found in the cache: - genStringLiteralDataOnlyV2(m, n.strVal, pureLit) + genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) else: pureLit = m.tmpBase & rope(id) result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit] # ------ Version selector --------------------------------------------------- -proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = +proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo; + isConst: bool): Rope = case detectStrVersion(m) of 0, 1: result = genStringLiteralDataOnlyV1(m, s) of 2: result = getTempName(m) - genStringLiteralDataOnlyV2(m, s, result) + genStringLiteralDataOnlyV2(m, s, result, isConst) else: localError(m.config, info, "cannot determine how to produce code for string literal") @@ -105,6 +108,6 @@ proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = proc genStringLiteral(m: BModule; n: PNode): Rope = case detectStrVersion(m) of 0, 1: result = genStringLiteralV1(m, n) - of 2: result = genStringLiteralV2(m, n) + of 2: result = genStringLiteralV2(m, n, isConst = true) else: localError(m.config, n.info, "cannot determine how to produce code for string literal") diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 2d7e0bda7406f..2be34aace8d0a 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -28,8 +28,8 @@ const cfsFieldInfo: "NIM_merge_FIELD_INFO", cfsTypeInfo: "NIM_merge_TYPE_INFO", cfsProcHeaders: "NIM_merge_PROC_HEADERS", - cfsVars: "NIM_merge_VARS", cfsData: "NIM_merge_DATA", + cfsVars: "NIM_merge_VARS", cfsProcs: "NIM_merge_PROCS", cfsInitProc: "NIM_merge_INIT_PROC", cfsDatInitProc: "NIM_merge_DATINIT_PROC", diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 130c410119791..6b25d0721ff98 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -92,7 +92,7 @@ proc genVarTuple(p: BProc, n: PNode) = if sfCompileTime in v.flags: continue var traverseProc: Rope if sfGlobal in v.flags: - assignGlobalVar(p, vn) + assignGlobalVar(p, vn, nil) genObjectInit(p, cpsInit, v.typ, v.loc, true) traverseProc = getTraverseProc(p, v) if traverseProc != nil and not p.hcrOn: @@ -270,6 +270,18 @@ proc genGotoVar(p: BProc; value: PNode) = else: lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) +proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope + +proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope = + if lfDynamicLib in v.loc.flags or sfThread in v.flags: + result = nil + elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value) and + not containsGarbageCollectedRef(v.typ): + #echo "New code produced for ", v.name.s, " ", p.config $ value.info + result = genBracedInit(p, value, isConst = false) + else: + result = nil + proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = if sfGoto in v.flags: # translate 'var state {.goto.} = X' into 'goto LX': @@ -277,6 +289,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = return var targetProc = p var traverseProc: Rope + let valueAsRope = potentialValueInit(p, v, value) if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and value.kind == nkEmpty and @@ -285,22 +298,23 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = if sfPure in v.flags: # v.owner.kind != skModule: targetProc = p.module.preInitProc - assignGlobalVar(targetProc, vn) + assignGlobalVar(targetProc, vn, valueAsRope) # XXX: be careful here. # Global variables should not be zeromem-ed within loops # (see bug #20). # That's why we are doing the construction inside the preInitProc. # genObjectInit relies on the C runtime's guarantees that # global variables will be initialized to zero. - var loc = v.loc - - # When the native TLS is unavailable, a global thread-local variable needs - # one more layer of indirection in order to access the TLS block. - # Only do this for complex types that may need a call to `objectInit` - if sfThread in v.flags and emulatedThreadVars(p.config) and - isComplexValueType(v.typ): - initLocExprSingleUse(p.module.preInitProc, vn, loc) - genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, true) + if valueAsRope == nil: + var loc = v.loc + + # When the native TLS is unavailable, a global thread-local variable needs + # one more layer of indirection in order to access the TLS block. + # Only do this for complex types that may need a call to `objectInit` + if sfThread in v.flags and emulatedThreadVars(p.config) and + isComplexValueType(v.typ): + initLocExprSingleUse(p.module.preInitProc, vn, loc) + genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, true) # Alternative construction using default constructor (which may zeromem): # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) if sfExportc in v.flags and p.module.g.generatedHeader != nil: @@ -358,7 +372,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N", [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) startBlock(targetProc) - if value.kind != nkEmpty: + if value.kind != nkEmpty and valueAsRope == nil: genLineDir(targetProc, vn) loadInto(targetProc, vn, value, v.loc) if forHcr: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8313638059509..d9029d1ced028 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -509,7 +509,7 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool = # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var) # and s.loc.k == locGlobalVar # loc isn't always initialized when this proc is used -proc assignGlobalVar(p: BProc, n: PNode) = +proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = let s = n.sym if s.loc.k == locNone: fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap) @@ -521,12 +521,16 @@ proc assignGlobalVar(p: BProc, n: PNode) = varInDynamicLib(q, s) else: s.loc.r = mangleDynLibProc(s) + if value != nil: + internalError(p.config, n.info, ".dynlib variables cannot have a value") return useHeader(p.module, s) if lfNoDecl in s.loc.flags: return if not containsOrIncl(p.module.declaredThings, s.id): if sfThread in s.flags: declareThreadVar(p.module, s, sfImportc in s.flags) + if value != nil: + internalError(p.config, n.info, ".threadvar variables cannot have a value") else: var decl: Rope = nil var td = getTypeDesc(p.module, s.loc.t) @@ -539,9 +543,15 @@ proc assignGlobalVar(p: BProc, n: PNode) = if p.hcrOn: decl.add("*") if sfRegister in s.flags: decl.add(" register") if sfVolatile in s.flags: decl.add(" volatile") - decl.addf(" $1;$n", [s.loc.r]) + if value != nil: + decl.addf(" $1 = $2;$n", [s.loc.r, value]) + else: + decl.addf(" $1;$n", [s.loc.r]) else: - decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) + if value != nil: + decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value]) + else: + decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) p.module.s[cfsVars].add(decl) if p.withinLoop > 0: # fixes tests/run/tzeroarray: @@ -1152,7 +1162,7 @@ proc requestConstImpl(p: BProc, sym: PSym) = if q != nil and not containsOrIncl(q.declaredThings, sym.id): assert q.initProc.module == q q.s[cfsData].addf("NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(q, sym.typ), sym.loc.r, genConstExpr(q.initProc, sym.ast)]) + [getTypeDesc(q, sym.typ), sym.loc.r, genBracedInit(q.initProc, sym.ast, isConst = true)]) # declare header: if q != m and not containsOrIncl(m.declaredThings, sym.id): assert(sym.loc.r != nil) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 9d128017ed663..9255e82a5ff50 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -27,8 +27,8 @@ type cfsFieldInfo, # section for field information cfsTypeInfo, # section for type information cfsProcHeaders, # section for C procs prototypes - cfsVars, # section for C variable declarations cfsData, # section for C constant data + cfsVars, # section for C variable declarations cfsProcs, # section for C procs that are not inline cfsInitProc, # section for the C init proc cfsDatInitProc, # section for the C datInit proc diff --git a/tests/ccgbugs/tprogmem.nim b/tests/ccgbugs/tprogmem.nim new file mode 100644 index 0000000000000..884ca158abcbc --- /dev/null +++ b/tests/ccgbugs/tprogmem.nim @@ -0,0 +1,11 @@ +discard """ + output: "5" + cmd: r"nim c --hints:on $options -d:release $file" + ccodecheck: "'/*PROGMEM*/ myLetVariable = {'" + target: "C" +""" + +var myLetVariable {.exportc, codegenDecl: "$# /*PROGMEM*/ $#".} = [1, 2, 3] + +myLetVariable[0] = 5 +echo myLetVariable[0]