Skip to content

Commit

Permalink
better support for PROGMEM like annotations for lets/vars; fixes #12216
Browse files Browse the repository at this point in the history
  • Loading branch information
Araq committed Dec 3, 2019
1 parent eed3288 commit 801fbe7
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 68 deletions.
75 changes: 39 additions & 36 deletions compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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})
Expand Down Expand Up @@ -2739,112 +2738,116 @@ 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..<obj.len:
getNullValueAux(p, t, lastSon(obj[i]), cons, result, count)
getNullValueAux(p, t, lastSon(obj[i]), cons, result, count, isConst)
of nkSym:
if count > 0: result.add ", "
inc count
let field = obj.sym
for i in 1..<cons.len:
if cons[i].kind == nkExprColonExpr:
if cons[i][0].sym.name.id == field.name.id:
result.add genConstExpr(p, cons[i][1])
result.add genBracedInit(p, cons[i][1], isConst)
return
elif i == field.position:
result.add genConstExpr(p, cons[i])
result.add genBracedInit(p, cons[i], isConst)
return
# not found, produce default value:
result.add getDefaultValue(p, field.typ, cons.info)
else:
localError(p.config, cons.info, "cannot create null element for: " & $obj)

proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode,
result: var Rope; count: var int) =
result: var Rope; count: var int;
isConst: bool) =
var base = t[0]
let oldRes = result
if not p.module.compileToCpp: result.add "{"
let oldcount = count
if base != nil:
base = skipTypes(base, skipPtrs)
getNullValueAuxT(p, orig, base, base.n, cons, result, count)
getNullValueAuxT(p, orig, base, base.n, cons, result, count, isConst)
elif not isObjLackingTypeField(t) and not p.module.compileToCpp:
result.addf("$1", [genTypeInfo(p.module, orig, obj.info)])
inc count
getNullValueAux(p, t, obj, cons, result, count)
getNullValueAux(p, t, obj, cons, result, count, isConst)
# do not emit '{}' as that is not valid C:
if oldcount == count: result = oldRes
elif not p.module.compileToCpp: result.add "}"

proc genConstObjConstr(p: BProc; n: PNode): Rope =
proc genConstObjConstr(p: BProc; n: PNode; isConst: bool): Rope =
result = nil
let t = n.typ.skipTypes(abstractInstOwned)
var count = 0
#if not isObjLackingTypeField(t) and not p.module.compileToCpp:
# result.addf("{$1}", [genTypeInfo(p.module, t)])
# inc count
getNullValueAuxT(p, t, t, t.n, n, result, count)
getNullValueAuxT(p, t, t, t.n, n, result, count, isConst)
if p.module.compileToCpp:
result = "{$1}$n" % [result]

proc genConstSimpleList(p: BProc, n: PNode): Rope =
proc genConstSimpleList(p: BProc, n: PNode; isConst: bool): Rope =
result = rope("{")
for i in 0..<n.len - 1:
result.addf("$1,$n", [genNamedConstExpr(p, n[i])])
result.addf("$1,$n", [genNamedConstExpr(p, n[i], isConst)])
if n.len > 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..<n.len:
if i > 0: data.addf(",$n", [])
data.add genConstExpr(p, n[i])
data.add genBracedInit(p, n[i], isConst)
data.add("}")
data.add("}")

result = getTempName(p.module)
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..<n.len:
if i > 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)
Expand All @@ -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.
Expand All @@ -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)
Expand Down
31 changes: 17 additions & 14 deletions compiler/ccgliterals.nim
Original file line number Diff line number Diff line change
Expand Up @@ -53,49 +53,52 @@ 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:
pureLit = getTempName(m)
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")

Expand All @@ -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")
2 changes: 1 addition & 1 deletion compiler/ccgmerge.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
38 changes: 26 additions & 12 deletions compiler/ccgstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -270,13 +270,26 @@ 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':
genGotoVar(p, value)
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
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit 801fbe7

Please sign in to comment.