cbuilder: most of ccgstmts (#24420)

This adapts most of ccgstmts to cbuilder, missing C++ exceptions and
`genCaseGeneric`/`genIfForCaseUntil`. Rebased version of #24399 which
was split into some other PRs and since has had conflicts with #24416.
This commit is contained in:
metagn
2024-11-09 08:47:35 +03:00
committed by GitHub
parent b3c1fbaf13
commit 8091d76306
4 changed files with 278 additions and 166 deletions

View File

@@ -55,6 +55,9 @@ template addCast(builder: var Builder, typ: Snippet, valueBody: typed) =
proc cAddr(value: Snippet): Snippet =
"&" & value
proc cLabelAddr(value: TLabel): Snippet =
"&&" & value
proc cDeref(value: Snippet): Snippet =
"(*" & value & ")"

View File

@@ -257,21 +257,26 @@ proc addLabel(builder: var Builder, name: TLabel) =
proc addReturn(builder: var Builder) =
builder.add("return;\n")
proc addReturn(builder: var Builder, value: string) =
proc addReturn(builder: var Builder, value: Snippet) =
builder.add("return ")
builder.add(value)
builder.add(";\n")
template addGoto(builder: var Builder, label: TLabel) =
proc addGoto(builder: var Builder, label: TLabel) =
builder.add("goto ")
builder.add(label)
builder.add(";\n")
template addIncr(builder: var Builder, val: Snippet) =
proc addComputedGoto(builder: var Builder, value: Snippet) =
builder.add("goto *")
builder.add(value)
builder.add(";\n")
proc addIncr(builder: var Builder, val: Snippet) =
builder.add(val)
builder.add("++;\n")
template addDecr(builder: var Builder, val: Snippet) =
proc addDecr(builder: var Builder, val: Snippet) =
builder.add(val)
builder.add("--;\n")

View File

@@ -24,12 +24,12 @@ proc registerTraverseProc(p: BProc, v: PSym) =
traverseProc = genTraverseProcForGlobal(p.module, v, v.info)
if traverseProc.len != 0 and not p.hcrOn:
if sfThread in v.flags:
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
else:
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
p.module.preInitProc.procSec(cpsInit).add("\n\t")
let fnName = cgsymValue(p.module,
if sfThread in v.flags: "nimRegisterThreadLocalMarker"
else: "nimRegisterGlobalMarker")
p.module.preInitProc.procSec(cpsInit).addCallStmt(fnName, traverseProc)
p.module.preInitProc.procSec(cpsInit).add("\n")
proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
if n.kind == nkEmpty:
@@ -134,11 +134,14 @@ proc genVarTuple(p: BProc, n: PNode) =
assignLocalVar(p, vn)
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
var field = initLoc(locExpr, vn, tup.storage)
if t.kind == tyTuple:
field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)]
else:
if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
let rtup = rdLoc(tup)
let fieldName =
if t.kind == tyTuple:
"Field" & $i
else:
if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
mangleRecFieldName(p.module, t.n[i].sym)
field.snippet = dotField(rtup, fieldName)
putLocIntoDest(p, v.loc, field)
if forHcr or isGlobalInBlock:
hcrGlobals.add((loc: v.loc, tp: "NULL"))
@@ -153,10 +156,20 @@ proc genVarTuple(p: BProc, n: PNode) =
# insert the registration of the globals for the different parts of the tuple at the
# start of the current scope (after they have been iterated) and init a boolean to
# check if any of them is newly introduced and the initializing code has to be ran
lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond])
p.s(cpsLocals).addVar(kind = Local,
name = hcrCond,
typ = "NIM_BOOL",
initializer = "NIM_FALSE")
for curr in hcrGlobals:
lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N",
[hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
let rc = rdLoc(curr.loc)
p.s(cpsLocals).addInPlaceOp(BitOr, "NIM_BOOL",
hcrCond,
cCall("hcrRegisterGlobal",
getModuleDllPath(p.module, n[0].sym),
'"' & curr.loc.snippet & '"',
cSizeof(rc),
curr.tp,
cCast("void**", cAddr(curr.loc.snippet))))
proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
@@ -174,9 +187,9 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
a.flags.incl(lfEnforceDeref)
expr(p, ri, a)
proc assignLabel(b: var TBlock; result: var Builder) {.inline.} =
proc assignLabel(b: var TBlock; result: var TLabel) {.inline.} =
b.label = "LA" & b.id.rope
result.add b.label
result = b.label
proc startSimpleBlock(p: BProc, scope: out ScopeBuilder): int {.discardable, inline.} =
startBlockWith(p):
@@ -208,9 +221,9 @@ proc genState(p: BProc, n: PNode) =
let n0 = n[0]
if n0.kind == nkIntLit:
let idx = n[0].intVal
linefmt(p, cpsStmts, "STATE$1: ;$n", [idx])
p.s(cpsStmts).addLabel("STATE" & $idx)
elif n0.kind == nkStrLit:
linefmt(p, cpsStmts, "$1: ;$n", [n0.strVal])
p.s(cpsStmts).addLabel(n0.strVal)
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# Called by return and break stmts.
@@ -224,7 +237,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
if p.config.exc == excSetjmp:
# Pop safe points generated by try
if not tryStmt.inExcept:
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popSafePoint"))
# Pop this try-stmt of the list of nested trys
# so we don't infinite recurse on it in the next step.
@@ -246,7 +259,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# except-blocks we are in
if noSafePoints notin p.flags:
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later
@@ -255,21 +268,22 @@ proc genGotoState(p: BProc, n: PNode) =
# case 0: goto STATE0;
# ...
var a: TLoc = initLocExpr(p, n[0])
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
p.flags.incl beforeRetNeeded
lineF(p, cpsStmts, "case -1:$n", [])
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlockLen)
lineF(p, cpsStmts, " goto BeforeRet_;$n", [])
var statesCounter = lastOrd(p.config, n[0].typ)
if n.len >= 2 and n[1].kind == nkIntLit:
statesCounter = getInt(n[1])
let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
else: rope"STATE"
for i in 0i64..toInt64(statesCounter):
lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
lineF(p, cpsStmts, "}$n", [])
let ra = rdLoc(a)
p.s(cpsStmts).addSwitchStmt(ra):
p.flags.incl beforeRetNeeded
p.s(cpsStmts).addSingleSwitchCase(cIntValue(-1)):
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlockLen)
p.s(cpsStmts).addGoto("BeforeRet_")
var statesCounter = lastOrd(p.config, n[0].typ)
if n.len >= 2 and n[1].kind == nkIntLit:
statesCounter = getInt(n[1])
let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
else: rope"STATE"
for i in 0i64..toInt64(statesCounter):
p.s(cpsStmts).addSingleSwitchCase(cIntValue(i)):
p.s(cpsStmts).addGoto(prefix & $i)
proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc
@@ -277,17 +291,27 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
if n[0].kind == nkClosure:
a = initLocExpr(p, n[0][1])
d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
let ra = a.rdLoc
d.snippet = cOp(LessThan,
subscript(
cCast(ptrType("NI"), ra),
cIntValue(1)),
cIntValue(0))
else:
a = initLocExpr(p, n[0])
let ra = a.rdLoc
# the environment is guaranteed to contain the 'state' field at offset 1:
d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
d.snippet = cOp(LessThan,
subscript(
cCast(ptrType("NI"), dotField(ra, "ClE_0")),
cIntValue(1)),
cIntValue(0))
proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
localError(p.config, value.info, "'goto' target must be a literal value")
else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
p.s(cpsStmts).addGoto("NIMSTATE_" & $value.intVal)
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder)
@@ -416,8 +440,13 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
if forHcr and targetProc.blocks.len > 3 and v.owner.kind == skModule:
# put it in the locals section - mainly because of loops which
# use the var in a call to resetLoc() in the statements section
lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n",
[v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
let rv = rdLoc(v.loc)
p.s(cpsLocals).addCallStmt("hcrRegisterGlobal",
getModuleDllPath(p.module, v),
'"' & v.loc.snippet & '"',
cSizeof(rv),
traverseProc,
cCast("void**", cAddr(v.loc.snippet)))
# nothing special left to do later on - let's avoid closing and reopening blocks
forHcr = false
@@ -503,18 +532,18 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
a = initLocExprSingleUse(p, it[0])
lelse = getLabel(p)
inc(p.labels)
lineF(p, cpsStmts, "if (!$1) goto $2;$n",
[rdLoc(a), lelse])
let ra = rdLoc(a)
p.s(cpsStmts).addSingleIfStmt(cOp(Not, ra)):
p.s(cpsStmts).addGoto(lelse)
if p.module.compileToCpp:
# avoid "jump to label crosses initialization" error:
p.s(cpsStmts).add "{"
expr(p, it[1], d)
p.s(cpsStmts).add "}"
p.s(cpsStmts).addScope():
expr(p, it[1], d)
else:
expr(p, it[1], d)
endSimpleBlock(p, scope)
if n.len > 1:
lineF(p, cpsStmts, "goto $1;$n", [lend])
p.s(cpsStmts).addGoto(lend)
fixLabel(p, lelse)
elif it.len == 1:
var scope: ScopeBuilder
@@ -536,8 +565,12 @@ proc genReturnStmt(p: BProc, t: PNode) =
# If we're in a finally block, and we came here by exception
# consume it before we return.
var safePoint = p.finallySafePoints[^1]
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", [safePoint])
lineF(p, cpsStmts, "goto BeforeRet_;$n", [])
p.s(cpsStmts).addSingleIfStmt(
cOp(NotEqual,
dotField(safePoint, "status"),
cIntValue(0))):
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
p.s(cpsStmts).addGoto("BeforeRet_")
proc genGotoForCase(p: BProc; caseStmt: PNode) =
for i in 1..<caseStmt.len:
@@ -549,7 +582,7 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) =
localError(p.config, it.info, "range notation not available for computed goto")
return
let val = getOrdValue(it[j])
lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
p.s(cpsStmts).addLabel("NIMSTATE_" & $val)
genStmts(p, it.lastSon)
endSimpleBlock(p, scope)
@@ -594,19 +627,24 @@ proc genComputedGoto(p: BProc; n: PNode) =
var id = p.labels+1
inc p.labels, arraySize+1
let tmp = "TMP$1_" % [id.rope]
var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope]
for i in 1..arraySize-1:
gotoArray.addf("&&TMP$#_, ", [rope(id+i)])
gotoArray.addf("&&TMP$#_};$n", [rope(id+arraySize)])
line(p, cpsLocals, gotoArray)
p.s(cpsStmts).addArrayVarWithInitializer(kind = Global,
name = tmp,
elementType = "void*",
len = arraySize):
var labelsInit: StructInitializer
p.s(cpsStmts).addStructInitializer(labelsInit, kind = siArray):
for i in 1..arraySize:
p.s(cpsStmts).addField(labelsInit, ""):
p.s(cpsStmts).add(cLabelAddr("TMP" & $(id+i) & "_"))
for j in 0..<casePos:
genStmts(p, n[j])
let caseStmt = n[casePos]
var a: TLoc = initLocExpr(p, caseStmt[0])
let ra = a.rdLoc
# first goto:
lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
p.s(cpsStmts).addComputedGoto(subscript(tmp, ra))
for i in 1..<caseStmt.len:
var scope: ScopeBuilder
@@ -619,7 +657,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
let val = getOrdValue(it[j])
let lit = cIntLiteral(toInt64(val)+id+1)
lineF(p, cpsStmts, "TMP$#_:$n", [lit])
p.s(cpsStmts).addLabel("TMP" & lit & "_")
genStmts(p, it.lastSon)
@@ -643,7 +681,8 @@ proc genComputedGoto(p: BProc; n: PNode) =
genStmts(p, it)
var a: TLoc = initLocExpr(p, caseStmt[0])
lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
let ra = a.rdLoc
p.s(cpsStmts).addComputedGoto(subscript(tmp, ra))
endSimpleBlock(p, scope)
for j in casePos+1..<n.len:
@@ -674,14 +713,16 @@ proc genWhileStmt(p: BProc, t: PNode) =
p.blocks[p.breakIdx].isLoop = true
a = initLocExpr(p, t[0])
if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
appcg(p, cpsStmts, ";$n", [])
let ra = a.rdLoc
var label: TLabel = ""
assignLabel(p.blocks[p.breakIdx], label)
p.s(cpsStmts).addSingleIfStmt(cOp(Not, ra)):
p.s(cpsStmts).addGoto(label)
genStmts(p, loopBody)
if optProfiler in p.options:
# invoke at loop body exit:
linefmt(p, cpsStmts, "#nimProfile();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimProfile"))
endBlockWith(p):
finishWhileStmt(p.s(cpsStmts), stmt)
@@ -763,31 +804,33 @@ proc genBreakStmt(p: BProc, t: PNode) =
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
genLineDir(p, t)
lineF(p, cpsStmts, "goto $1;$n", [p.blocks[idx].label])
p.s(cpsStmts).addGoto(p.blocks[idx].label)
proc raiseExit(p: BProc) =
assert p.config.exc == excGoto
if nimErrorFlagDisabled notin p.flags:
p.flags.incl nimErrorFlagAccessed
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;$n", [])
else:
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
[p.nestedTryStmts[^1].label])
p.s(cpsStmts).addSingleIfStmt(cUnlikely(cDeref("nimErr_"))):
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
p.s(cpsStmts).addGoto("BeforeRet_")
else:
p.s(cpsStmts).addGoto("LA" & $p.nestedTryStmts[^1].label & "_")
proc raiseExitCleanup(p: BProc, destroy: string) =
assert p.config.exc == excGoto
if nimErrorFlagDisabled notin p.flags:
p.flags.incl nimErrorFlagAccessed
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy])
else:
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n",
[p.nestedTryStmts[^1].label, destroy])
p.s(cpsStmts).addSingleIfStmt(cUnlikely(cDeref("nimErr_"))):
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(destroy)
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
p.s(cpsStmts).addGoto("BeforeRet_")
else:
p.s(cpsStmts).addGoto("LA" & $p.nestedTryStmts[^1].label & "_")
proc finallyActions(p: BProc) =
if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
@@ -803,12 +846,11 @@ proc raiseInstr(p: BProc; result: var Builder) =
if L == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
result.add ropecg(p.module, "goto BeforeRet_;$n", [])
result.addGoto("BeforeRet_")
else:
# raise inside an 'except' must go to the finally block,
# raise outside an 'except' block must go to the 'except' list.
result.add ropecg(p.module, "goto LA$1_;$n",
[p.nestedTryStmts[L-1].label])
result.addGoto("LA" & $p.nestedTryStmts[L-1].label & "_")
# + ord(p.nestedTryStmts[L-1].inExcept)])
proc genRaiseStmt(p: BProc, t: PNode) =
@@ -830,16 +872,22 @@ proc genRaiseStmt(p: BProc, t: PNode) =
if isImportedException(typ, p.config):
lineF(p, cpsStmts, "throw $1;$n", [e])
else:
lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n",
[e, makeCString(typ.sym.name.s),
makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s),
quotedFilename(p.config, t.info), toLinenumber(t.info)])
let eName = makeCString(typ.sym.name.s)
let pName = makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s)
let fName = quotedFilename(p.config, t.info)
let ln = toLinenumber(t.info)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "raiseExceptionEx"),
cCast(ptrType(cgsymValue(p.module, "Exception")), e),
eName,
pName,
fName,
cIntValue(ln))
if optOwnedRefs in p.config.globalOptions:
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [e])
p.s(cpsStmts).addAssignment(e, "NIM_NIL")
else:
finallyActions(p)
genLineDir(p, t)
linefmt(p, cpsStmts, "#reraiseException();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "reraiseException"))
raiseInstr(p, p.s(cpsStmts))
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
@@ -861,10 +909,10 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
for i in 1..until:
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
lineF(p, cpsStmts, "LA$1_: ;$n", [rope(labId + i)])
p.s(cpsStmts).addLabel("LA" & $(labId + i) & "_")
if t[i].kind == nkOfBranch:
exprBlock(p, t[i][^1], d)
lineF(p, cpsStmts, "goto $1;$n", [lend])
p.s(cpsStmts).addGoto(lend)
else:
exprBlock(p, t[i][0], d)
result = lend
@@ -872,35 +920,37 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
rangeFormat, eqFormat: FormatStr,
until: int, a: TLoc): TLabel =
# XXX doesn't work with cbuilder
# generate a C-if statement for a Nim case statement
var res: TLabel
var labId = p.labels
for i in 1..until:
inc(p.labels)
let lab = "LA" & $p.labels & "_"
if t[i].kind == nkOfBranch: # else statement
genCaseGenericBranch(p, t[i], a, rangeFormat, eqFormat,
"LA" & rope(p.labels) & "_")
genCaseGenericBranch(p, t[i], a, rangeFormat, eqFormat, lab)
else:
lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
p.s(cpsStmts).addGoto(lab)
if until < t.len-1:
inc(p.labels)
var gotoTarget = p.labels
lineF(p, cpsStmts, "goto LA$1_;$n", [rope(gotoTarget)])
var gotoTarget = "LA" & $p.labels & "_"
p.s(cpsStmts).addGoto(gotoTarget)
res = genCaseSecondPass(p, t, d, labId, until)
lineF(p, cpsStmts, "LA$1_: ;$n", [rope(gotoTarget)])
p.s(cpsStmts).addLabel(gotoTarget)
else:
res = genCaseSecondPass(p, t, d, labId, until)
res
template genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
rangeFormat, eqFormat: FormatStr) =
# XXX doesn't work with cbuilder
var a: TLoc = initLocExpr(p, t[0])
var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, t.len-1, a)
fixLabel(p, lend)
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
stringKind: TTypeKind,
branches: var openArray[Rope]) =
branches: var openArray[Builder]) =
var x: TLoc
for i in 0..<b.len - 1:
assert(b[i].kind != nkRange)
@@ -912,12 +962,13 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
of nkNilLit: j = 0
else:
assert false, "invalid string case branch node kind"
if stringKind == tyCstring:
appcg(p.module, branches[j], "if (#eqCstrings($1, $2)) goto $3;$n",
[rdLoc(e), rdLoc(x), labl])
else:
appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
[rdLoc(e), rdLoc(x), labl])
let re = rdLoc(e)
let rx = rdLoc(x)
branches[j].addSingleIfStmtWithCond():
let eqName = if stringKind == tyCstring: "eqCstrings" else: "eqStrings"
branches[j].addCall(cgsymValue(p.module, eqName), re, rx)
do:
branches[j].addGoto(labl)
proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
# count how many constant strings there are in the case:
@@ -926,7 +977,7 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
if t[i].kind == nkOfBranch: inc(strings, t[i].len - 1)
if strings > stringCaseThreshold:
var bitMask = math.nextPowerOfTwo(strings) - 1
var branches: seq[Rope]
var branches: seq[Builder]
newSeq(branches, bitMask + 1)
var a: TLoc = initLocExpr(p, t[0]) # first pass: generate ifs+goto:
var labId = p.labels
@@ -939,20 +990,21 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
# else statement: nothing to do yet
# but we reserved a label, which we use later
discard
if stringKind == tyCstring:
linefmt(p, cpsStmts, "switch (#hashCstring($1) & $2) {$n",
[rdLoc(a), bitMask])
else:
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
[rdLoc(a), bitMask])
for j in 0..high(branches):
if branches[j] != "":
let lit = cIntLiteral(j)
lineF(p, cpsStmts, "case $1: $n$2break;$n",
[lit, branches[j]])
lineF(p, cpsStmts, "}$n", []) # else statement:
let fnName = if stringKind == tyCstring: "hashCstring" else: "hashString"
let ra = rdLoc(a)
p.s(cpsStmts).addSwitchStmt(
cOp(BitAnd, "NI",
cCall(cgsymValue(p.module, fnName), ra),
cIntValue(bitMask))):
for j in 0..high(branches):
if branches[j].buf.len != 0:
let lit = cIntLiteral(j)
p.s(cpsStmts).addSingleSwitchCase(lit):
p.s(cpsStmts).add(extract(branches[j]))
p.s(cpsStmts).addBreak()
# else statement:
if t[^1].kind != nkOfBranch:
lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
p.s(cpsStmts).addGoto("LA" & rope(p.labels) & "_")
# third pass: generate statements
var lend = genCaseSecondPass(p, t, d, labId, t.len-1)
fixLabel(p, lend)
@@ -1064,9 +1116,12 @@ proc genRestoreFrameAfterException(p: BProc) =
if optStackTrace in p.module.config.options:
if hasCurFramePointer notin p.flags:
p.flags.incl hasCurFramePointer
p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", []))
p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", []))
linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n", [])
p.procSec(cpsLocals).add('\t')
p.procSec(cpsLocals).addVar(kind = Local, name = "_nimCurFrame", typ = ptrType("TFrame"))
p.procSec(cpsInit).add('\t')
p.procSec(cpsInit).addAssignmentWithValue("_nimCurFrame"):
p.procSec(cpsInit).addCall(cgsymValue(p.module, "getFrame"))
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "setFrame"), "_nimCurFrame")
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
#[ code to generate:
@@ -1387,7 +1442,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
else:
startBlockWith(p):
scope = initScope(p.s(cpsStmts))
linefmt(p, cpsStmts, "LA$1_:;$n", [lab])
p.s(cpsStmts).addLabel("LA" & $lab & "_")
p.nestedTryStmts[^1].inExcept = true
var i = 1
@@ -1412,33 +1467,46 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
isScope = true
innerScope = initScope(p.s(cpsStmts))
# we handled the exception, remember this:
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
p.s(cpsStmts).addAssignment(cDeref("nimErr_"), "NIM_FALSE")
expr(p, t[i][0], d)
else:
if not innerIsIf:
innerIsIf = true
innerIfStmt = initIfStmt(p.s(cpsStmts))
var orExpr = newRopeAppender()
var orExpr: Snippet = ""
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
if orExpr.len != 0: orExpr.add("||")
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
var excVal = cCall(cgsymValue(p.module, "nimBorrowCurrentException"))
let member =
if p.module.compileToCpp:
derefField(excVal, "m_type")
else:
dotField(derefField(excVal, "Sup"), "m_type")
var branch: Snippet = ""
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(t[i][j].typ)
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
[memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
branch = cCall(cgsymValue(p.module, "isObjDisplayCheck"),
member,
checkFor,
$genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config))))
else:
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
branch = cCall(cgsymValue(p.module, "isObj"),
member,
checkFor)
if orExpr.len == 0:
orExpr = branch
else:
orExpr = cOp(Or, orExpr, branch)
startBlockWith(p):
initElifBranch(p.s(cpsStmts), innerIfStmt, orExpr)
# we handled the exception, remember this:
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
p.s(cpsStmts).addAssignment(cDeref("nimErr_"), "NIM_FALSE")
expr(p, t[i][^1], d)
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
linefmt(p, cpsStmts, "LA$1_:;$n", [nextExcept])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
p.s(cpsStmts).addLabel("LA" & $nextExcept & "_")
endBlockWith(p):
if isScope:
finishScope(p.s(cpsStmts), innerScope)
@@ -1465,8 +1533,9 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
genStmts(p, t[i][0])
else:
# pretend we did handle the error for the safe execution of the 'finally' section:
p.procSec(cpsLocals).add(ropecg(p.module, "NIM_BOOL oldNimErrFin$1_;$n", [lab]))
linefmt(p, cpsStmts, "oldNimErrFin$1_ = *nimErr_; *nimErr_ = NIM_FALSE;$n", [lab])
p.procSec(cpsLocals).addVar(kind = Local, name = "oldNimErrFin" & $lab & "_", typ = "NIM_BOOL")
p.s(cpsStmts).addAssignment("oldNimErrFin" & $lab & "_", cDeref("nimErr_"))
p.s(cpsStmts).addAssignment(cDeref("nimErr_"), "NIM_FALSE")
genStmts(p, t[i][0])
# this is correct for all these cases:
# 1. finally is run during ordinary control flow
@@ -1474,7 +1543,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
# error back to nil.
# 3. finally is run for exception handling code without any 'except'
# handler present or only handlers that did not match.
linefmt(p, cpsStmts, "*nimErr_ = oldNimErrFin$1_;$n", [lab])
p.s(cpsStmts).addAssignment(cDeref("nimErr_"), "oldNimErrFin" & $lab & "_")
endSimpleBlock(p, finallyScope)
raiseExit(p)
if hasExcept: inc p.withinTryWithExcept
@@ -1522,20 +1591,24 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
var nonQuirkyIf = default(IfBuilder)
if not quirkyExceptions:
safePoint = getTempName(p.module)
linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint])
linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", [safePoint])
p.s(cpsLocals).addVar(name = safePoint, typ = cgsymValue(p.module, "TSafePoint"))
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "pushSafePoint"), cAddr(safePoint))
if isDefined(p.config, "nimStdSetjmp"):
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"):
p.s(cpsStmts).addCall("setjmp", dotField(safePoint, "context"))
elif isDefined(p.config, "nimSigSetjmp"):
linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint])
p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"):
p.s(cpsStmts).addCall("sigsetjmp", dotField(safePoint, "context"), cIntValue(0))
elif isDefined(p.config, "nimBuiltinSetjmp"):
linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint])
p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"):
p.s(cpsStmts).addCall("__builtin_setjmp", dotField(safePoint, "context"))
elif isDefined(p.config, "nimRawSetjmp"):
if isDefined(p.config, "mswindows"):
if isDefined(p.config, "vcc") or isDefined(p.config, "clangcl"):
# For the vcc compiler, use `setjmp()` with one argument.
# See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-170
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"):
p.s(cpsStmts).addCall("setjmp", dotField(safePoint, "context"))
else:
# The Windows `_setjmp()` takes two arguments, with the second being an
# undocumented buffer used by the SEH mechanism for stack unwinding.
@@ -1543,11 +1616,14 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
# prone to stack corruption during unwinding, so we disable that by setting
# it to NULL.
# More details: https://github.com/status-im/nimbus-eth2/issues/3121
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint])
p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"):
p.s(cpsStmts).addCall("_setjmp", dotField(safePoint, "context"), cIntValue(0))
else:
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"):
p.s(cpsStmts).addCall("_setjmp", dotField(safePoint, "context"))
else:
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"):
p.s(cpsStmts).addCall("setjmp", dotField(safePoint, "context"))
nonQuirkyIf = initIfStmt(p.s(cpsStmts))
initElifBranch(p.s(cpsStmts), nonQuirkyIf, removeSinglePar(
cOp(Equal, dotField(safePoint, "status"), cIntValue(0))))
@@ -1558,11 +1634,11 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
var quirkyScope = default(ScopeBuilder)
var isScope = false
if not quirkyExceptions:
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popSafePoint"))
finishBranch(p.s(cpsStmts), nonQuirkyIf)
startBlockWith(p):
initElseBranch(p.s(cpsStmts), nonQuirkyIf)
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popSafePoint"))
genRestoreFrameAfterException(p)
elif 1 < t.len and t[1].kind == nkExceptBranch:
startBlockWith(p):
@@ -1589,27 +1665,40 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
else:
scope = initScope(p.s(cpsStmts))
if not quirkyExceptions:
linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint])
p.s(cpsStmts).addFieldAssignment(safePoint, "status", cIntValue(0))
expr(p, t[i][0], d)
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
endBlockWith(p):
if exceptIfInited:
finishBranch(p.s(cpsStmts), exceptIf)
else:
finishScope(p.s(cpsStmts), scope)
else:
var orExpr = newRopeAppender()
var orExpr: Snippet = ""
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
if orExpr.len != 0: orExpr.add("||")
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
var excVal = cCall(cgsymValue(p.module, "nimBorrowCurrentException"))
let member =
if p.module.compileToCpp:
derefField(excVal, "m_type")
else:
dotField(derefField(excVal, "Sup"), "m_type")
var branch: Snippet = ""
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(t[i][j].typ)
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
[memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
branch = cCall(cgsymValue(p.module, "isObjDisplayCheck"),
member,
checkFor,
$genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config))))
else:
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
branch = cCall(cgsymValue(p.module, "isObj"),
member,
checkFor)
if orExpr.len == 0:
orExpr = branch
else:
orExpr = cOp(Or, orExpr, branch)
if not exceptIfInited:
exceptIf = initIfStmt(p.s(cpsStmts))
@@ -1617,9 +1706,9 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
startBlockWith(p):
initElifBranch(p.s(cpsStmts), exceptIf, orExpr)
if not quirkyExceptions:
linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint])
p.s(cpsStmts).addFieldAssignment(safePoint, "status", cIntValue(0))
expr(p, t[i][^1], d)
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
endBlockWith(p):
finishBranch(p.s(cpsStmts), exceptIf)
inc(i)
@@ -1645,11 +1734,19 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
# re-raise the unhandled one but instead keep the old one (it was
# not popped either):
if not quirkyExceptions and getCompilerProc(p.module.g.graph, "nimLeaveFinally") != nil:
linefmt(p, cpsStmts, "if ($1.status != 0) #nimLeaveFinally();$n", [safePoint])
p.s(cpsStmts).addSingleIfStmt(
cOp(NotEqual,
dotField(safePoint, "status"),
cIntValue(0))):
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimLeaveFinally"))
endSimpleBlock(p, finallyScope)
discard pop(p.finallySafePoints)
if not quirkyExceptions:
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
p.s(cpsStmts).addSingleIfStmt(
cOp(NotEqual,
dotField(safePoint, "status"),
cIntValue(0))):
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "reraiseException"))
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
var res = ""
@@ -1766,13 +1863,17 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
assert t.kind == tyObject
discard genTypeInfoV1(p.module, t, a.lode.info)
if not containsOrIncl(p.module.declaredThings, field.id):
appcg(p.module, cfsVars, "extern $1",
[discriminatorTableDecl(p.module, t, field)])
p.module.s[cfsVars].addDeclWithVisibility(Extern):
discriminatorTableDecl(p.module, t, field, p.module.s[cfsVars])
let lit = cIntLiteral(toInt64(lengthOrd(p.config, field.typ))+1)
lineCg(p, cpsStmts,
"#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
lit])
let ra = rdLoc(a)
let rtmp = rdLoc(tmp)
let dn = discriminatorTableName(p.module, t, field)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "FieldDiscriminantCheck"),
cCast("NI", cCast("NU", ra)),
cCast("NI", cCast("NU", rtmp)),
dn,
lit)
if p.config.exc == excGoto:
raiseExit(p)

View File

@@ -1329,10 +1329,13 @@ proc discriminatorTableName(m: BModule; objtype: PType, d: PSym): Rope =
proc rope(arg: Int128): Rope = rope($arg)
proc discriminatorTableDecl(m: BModule; objtype: PType, d: PSym): Rope =
proc discriminatorTableDecl(m: BModule; objtype: PType, d: PSym, result: var Builder) =
cgsym(m, "TNimNode")
var tmp = discriminatorTableName(m, objtype, d)
result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
result.addArrayVar(kind = Local,
name = tmp,
elementType = ptrType("TNimNode"),
len = toInt(lengthOrd(m.config, d.typ)) + 1)
proc genTNimNodeArray(m: BModule; name: Rope, size: int) =
if m.hcrOn: