# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # included from cgen.nim const RangeExpandLimit = 256 # do not generate ranges # over 'RangeExpandLimit' elements stringCaseThreshold = 8 # above X strings a hash-switch for strings is generated proc registerTraverseProc(p: BProc, v: PSym) = var traverseProc = "" if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and optOwnedRefs notin p.config.globalOptions and containsManagedMemory(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) traverseProc = genTraverseProcForGlobal(p.module, v, v.info) if traverseProc.len != 0 and not p.hcrOn: 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: result = false elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc: if n[0].kind == nkSym and sfConstructor in n[0].sym.flags: result = true elif isInvalidReturnType(conf, n[0].typ, true): # var v = f() # is transformed into: var v; f(addr v) # where 'f' **does not** initialize the result! result = false else: result = true elif isInvalidReturnType(conf, n.typ, false): result = false else: result = true proc inExceptBlockLen(p: BProc): int = result = 0 for x in p.nestedTryStmts: if x.inExcept: result.inc proc startBlockInside(p: BProc): int {.discardable.} = inc(p.labels) result = p.blocks.len p.blocks.add initBlock() p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16 template startBlockWith(p: BProc, body: typed): int = body startBlockInside(p) proc blockBody(b: var TBlock; result: var Builder) = result.add extract(b.sections[cpsLocals]) if b.frameLen > 0: result.addInPlaceOp(Add, NimInt, dotField("FR_", "len"), cIntValue(b.frameLen.int)) result.add(extract(b.sections[cpsInit])) result.add(extract(b.sections[cpsStmts])) if b.frameLen > 0: result.addInPlaceOp(Sub, NimInt, dotField("FR_", "len"), cIntValue(b.frameLen.int)) proc endBlockInside(p: BProc) = let topBlock = p.blocks.len-1 # the block is merged into the parent block p.blocks[topBlock].blockBody(p.blocks[topBlock-1].sections[cpsStmts]) setLen(p.blocks, topBlock) proc endBlockOutside(p: BProc, label: TLabel) = if label.len != 0: let topBlock = p.blocks.len - 1 p.blocks[topBlock].sections[cpsStmts].addLabel(label) template endBlockWith(p: BProc, body: typed) = let label = p.blocks[p.blocks.len - 1].label endBlockInside(p) body endBlockOutside(p, label) proc genVarTuple(p: BProc, n: PNode) = if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") # if we have a something that's been captured, use the lowering instead: for i in 0.. 2 # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block) forHcr = forHcr and not isGlobalInBlock var hcrIf = default(IfBuilder) if forHcr: startBlockWith(p): # check with the boolean if the initializing code for the tuple should be ran hcrIf = initIfStmt(p.s(cpsStmts)) initElifBranch(p.s(cpsStmts), hcrIf, hcrCond) genLineDir(p, n) var tup = initLocExpr(p, n[^1]) var t = tup.t.skipTypes(abstractInst) for i in 0..= 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 d = initLoc(locExpr, n, OnUnknown) if n[0].kind == nkClosure: a = initLocExpr(p, n[0][1]) let ra = a.rdLoc d.snippet = cOp(LessThan, subscript( cCast(ptrType(NimInt), 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 = cOp(LessThan, subscript( cCast(ptrType(NimInt), 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: p.s(cpsStmts).addGoto("NIMSTATE_" & $value.intVal) proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder) proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Builder) = if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn: discard "nothing to do" elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ): #echo "New code produced for ", v.name.s, " ", p.config $ value.info genBracedInit(p, value, isConst = false, v.typ, result) proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): Snippet = var res = newBuilder("") var argBuilder = default(CallBuilder) # not init, only building params let typ = skipTypes(call[0].typ, abstractInst) assert(typ.kind == tyProc) for i in 1.. 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 let rv = rdLoc(v.loc) p.s(cpsLocals).addCallStmt("hcrRegisterGlobal", getModuleDllPath(p.module, v), '"' & v.loc.snippet & '"', cSizeof(rv), traverseProc, cCast(ptrType(CPointer), cAddr(v.loc.snippet))) # nothing special left to do later on - let's avoid closing and reopening blocks forHcr = false # we close and reopen the global if (nim_hcr_do_init_) blocks in the main Init function # for the module so we can have globals and top-level code be interleaved and still # be able to re-run it but without the top level code - just the init of globals var hcrInit = default(IfBuilder) if forHcr: startBlockWith(targetProc): hcrInit = initIfStmt(p.s(cpsStmts)) initElifBranch(p.s(cpsStmts), hcrInit, cCall("hcrRegisterGlobal", getModuleDllPath(p.module, v), '"' & v.loc.snippet & '"', cSizeof(rdLoc(v.loc)), traverseProc, cCast(ptrType(CPointer), cAddr(v.loc.snippet)))) if value.kind != nkEmpty and valueAsRope.len == 0: genLineDir(targetProc, vn) if not isCppCtorCall: backendEnsureMutable v loadInto(targetProc, vn, value, v.locImpl) if forHcr: endBlockWith(targetProc): finishBranch(p.s(cpsStmts), hcrInit) finishIfStmt(p.s(cpsStmts), hcrInit) proc genSingleVar(p: BProc, a: PNode) = let v = a[0].sym if sfCompileTime in v.flags: # fix issue #12640 # {.global, compileTime.} pragma in proc if sfGlobal in v.flags and p.prc != nil and p.prc.kind == skProc: discard else: return genSingleVar(p, v, a[0], a[2]) proc genClosureVar(p: BProc, a: PNode) = var immediateAsgn = a[2].kind != nkEmpty var v: TLoc = initLocExpr(p, a[0]) genLineDir(p, a) if immediateAsgn: loadInto(p, a[0], a[2], v) elif sfNoInit notin a[0][1].sym.flags: constructLoc(p, v) proc genVarStmt(p: BProc, n: PNode) = for it in n: case it.kind of nkCommentStmt: discard of nkIdentDefs: # can be a lifted var nowadays ... if it[0].kind == nkSym: genSingleVar(p, it) else: genClosureVar(p, it) of nkSym: genSingleVar(p, it.sym, newSymNode(it.sym), it.sym.astdef) else: genVarTuple(p, it) proc genIf(p: BProc, n: PNode, d: var TLoc) = # # { if (!expr1) goto L1; # thenPart } # goto LEnd # L1: # { if (!expr2) goto L2; # thenPart2 } # goto LEnd # L2: # { elsePart } # Lend: var a: TLoc lelse: TLabel if not isEmptyType(n.typ) and d.k == locNone: d = getTemp(p, n.typ) genLineDir(p, n) let lend = getLabel(p) for it in n.sons: # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(n.typ): d.k = locNone if it.len == 2: var scope: ScopeBuilder startSimpleBlock(p, scope) a = initLocExprSingleUse(p, it[0]) lelse = getLabel(p) inc(p.labels) 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).addScope(): expr(p, it[1], d) else: expr(p, it[1], d) endSimpleBlock(p, scope) if n.len > 1: p.s(cpsStmts).addGoto(lend) fixLabel(p, lelse) elif it.len == 1: var scope: ScopeBuilder startSimpleBlock(p, scope) expr(p, it[0], d) endSimpleBlock(p, scope) else: internalError(p.config, n.info, "genIf()") if n.len > 1: fixLabel(p, lend) proc genReturnStmt(p: BProc, t: PNode) = if nfPreventCg in t.flags: return p.flags.incl beforeRetNeeded genLineDir(p, t) if (t[0].kind != nkEmpty): genStmts(p, t[0]) blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, howManyExcepts = p.inExceptBlockLen, isReturnStmt = true) if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags: # If we're in a finally block, and we came here by exception # consume it before we return. var safePoint = p.finallySafePoints[^1] 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.. 10_000: localError(p.config, it.info, "case statement has too many cases for computed goto"); return arraySize = toInt(aSize) if firstOrd(p.config, it[0].typ) != 0: localError(p.config, it.info, "case statement has to start at 0 for computed goto"); return if casePos < 0: localError(p.config, n.info, "no case statement found for computed goto"); return var id = p.labels+1 inc p.labels, arraySize+1 let tmp = "TMP$1_" % [id.rope] p.s(cpsStmts).addArrayVarWithInitializer(kind = Global, name = tmp, elementType = CPointer, 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..= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: internalError(p.config, t.info, "no loop to break") p.blocks[idx].label = "LA" & p.blocks[idx].id.rope blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) 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 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 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: # if the current try stmt have a finally block, # we must execute it before reraising let finallyBlock = p.nestedTryStmts[^1].fin if finallyBlock != nil: genSimpleBlock(p, finallyBlock[0]) proc raiseInstr(p: BProc; result: var Builder) = if p.config.exc == excGoto: let L = p.nestedTryStmts.len if L == 0: p.flags.incl beforeRetNeeded # easy case, simply goto 'ret': 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.addGoto("LA" & $p.nestedTryStmts[L-1].label & "_") # + ord(p.nestedTryStmts[L-1].inExcept)]) proc genRaiseStmt(p: BProc, t: PNode) = if t[0].kind != nkEmpty: var a: TLoc = initLocExprSingleUse(p, t[0]) finallyActions(p) var e = rdLoc(a) discard getTypeDesc(p.module, t[0].typ) var typ = skipTypes(t[0].typ, abstractPtrs) case p.config.exc of excCpp: blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen) of excGoto: blockLeaveActions(p, howManyTrys = 0, howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0)) else: discard genLineDir(p, t) if isImportedException(typ, p.config): lineF(p, cpsStmts, "throw $1;$n", [e]) else: 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: p.s(cpsStmts).addAssignment(e, NimNil) else: finallyActions(p) genLineDir(p, t) p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "reraiseException")) raiseInstr(p, p.s(cpsStmts)) template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, rangeFormat, eqFormat: untyped) = var x, y: TLoc for i in 0.. stringCaseThreshold: var bitMask = math.nextPowerOfTwo(strings) - 1 var branches: seq[Builder] newSeq(branches, bitMask + 1) var a: TLoc = initLocExpr(p, t[0]) # first pass: generate ifs+goto: var labId = p.labels for i in 1.. RangeExpandLimit: return true proc ifSwitchSplitPoint(p: BProc, n: PNode): int = result = 0 for i in 1.. 0: lend = genIfForCaseUntil(p, n, d, splitPoint, a): p.s(cpsStmts).addSingleIfStmt(cOp(And, cOp(GreaterEqual, ra, rb), cOp(LessEqual, ra, rc))): p.s(cpsStmts).addGoto(rlabel) do: p.s(cpsStmts).addSingleIfStmt( removeSinglePar(cOp(Equal, ra, rb))): p.s(cpsStmts).addGoto(rlabel) # generate switch part (might be empty): if splitPoint+1 < n.len: let rca = rdCharLoc(a) p.s(cpsStmts).addSwitchStmt(rca): var hasDefault = false for i in splitPoint+1..") if not isEmptyType(t.typ) and d.k == locNone: d = getTemp(p, t.typ) genLineDir(p, t) inc(p.labels, 2) let etmp = p.labels #init on locals, fixes #23306 lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp]) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) if t.kind == nkHiddenTryStmt: lineCg(p, cpsStmts, "try {$n", []) expr(p, t[0], d) lineCg(p, cpsStmts, "}$n", []) else: startBlockWith(p): p.s(cpsStmts).add("try {\n") expr(p, t[0], d) endBlockWith(p): p.s(cpsStmts).add("}\n") # First pass: handle Nim based exceptions: lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1]) genRestoreFrameAfterException(p) # an unhandled exception happened! lineCg(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp]) p.nestedTryStmts[^1].inExcept = true var hasImportedCppExceptions = false var i = 1 var ifStmt = default(IfBuilder) var hasIf = false var hasElse = false while (i < t.len) and (t[i].kind == nkExceptBranch): # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone if t[i].len == 1: hasImportedCppExceptions = true hasElse = true # general except section: var scope = default(ScopeBuilder) startBlockWith(p): if hasIf: initElseBranch(p.s(cpsStmts), ifStmt) else: scope = initScope(p.s(cpsStmts)) # we handled the error: expr(p, t[i][0], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlockWith(p): if hasIf: finishBranch(p.s(cpsStmts), ifStmt) else: finishScope(p.s(cpsStmts), scope) else: var orExpr = newRopeAppender() var exvar = PNode(nil) for j in 0..$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) if orExpr.len != 0: if not hasIf: hasIf = true ifStmt = initIfStmt(p.s(cpsStmts)) startBlockWith(p): initElifBranch(p.s(cpsStmts), ifStmt, orExpr) if exvar != nil: fillLocalName(p, exvar.sym) backendEnsureMutable exvar.sym fillLoc(exvar.sym.locImpl, locTemp, exvar, OnStack) linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ), rdLoc(exvar.sym.loc), rope(etmp+1)]) # we handled the error: linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp]) expr(p, t[i][^1], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlockWith(p): finishBranch(p.s(cpsStmts), ifStmt) inc(i) if hasIf and not hasElse: p.s(cpsStmts).addElseBranch(ifStmt): p.s(cpsStmts).add("throw;\n") if hasIf: finishIfStmt(p.s(cpsStmts), ifStmt) linefmt(p, cpsStmts, "}$n", []) # Second pass: handle C++ based exceptions: template genExceptBranchBody(body: PNode) {.dirty.} = genRestoreFrameAfterException(p) #linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp]) expr(p, body, d) var catchAllPresent = false incl p.flags, noSafePoints # mark as not needing 'popCurrentException' if hasImportedCppExceptions: for i in 1.. 0 and t[^1].kind == nkFinally: if not catchAllPresent: startBlockWith(p): p.s(cpsStmts).add("catch (...) {\n") genRestoreFrameAfterException(p) linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp]) endBlockWith(p): p.s(cpsStmts).add("}\n") var scope: ScopeBuilder startSimpleBlock(p, scope) genStmts(p, t[^1][0]) linefmt(p, cpsStmts, "if (T$1_) std::rethrow_exception(T$1_);$n", [etmp]) endSimpleBlock(p, scope) proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) = # There are two versions we generate, depending on whether we # catch C++ exceptions, imported via .importcpp or not. The # code can be easier if there are no imported C++ exceptions # to deal with. # code to generate: # # try # { # myDiv(4, 9); # } catch (NimExceptionType1&) { # body # } catch (NimExceptionType2&) { # finallyPart() # raise; # } # catch(...) { # general_handler_body # } # finallyPart(); template genExceptBranchBody(body: PNode) {.dirty.} = genRestoreFrameAfterException(p) expr(p, body, d) if not isEmptyType(t.typ) and d.k == locNone: d = getTemp(p, t.typ) genLineDir(p, t) cgsym(p.module, "popCurrentExceptionEx") let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) startBlockWith(p): p.s(cpsStmts).add("try {\n") expr(p, t[0], d) endBlockWith(p): p.s(cpsStmts).add("}\n") var catchAllPresent = false p.nestedTryStmts[^1].inExcept = true for i in 1..") else: p.flags.incl noSafePoints genLineDir(p, t) cgsym(p.module, "Exception") var safePoint: Rope = "" var nonQuirkyIf = default(IfBuilder) if not quirkyExceptions: safePoint = getTempName(p.module) 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"): p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"): p.s(cpsStmts).addCall("setjmp", dotField(safePoint, "context")) elif isDefined(p.config, "nimSigSetjmp"): p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"): p.s(cpsStmts).addCall("sigsetjmp", dotField(safePoint, "context"), cIntValue(0)) elif isDefined(p.config, "nimBuiltinSetjmp"): 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 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. # Mingw-w64 has been trying to get it right for years, but it's still # 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 p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"): p.s(cpsStmts).addCall("_setjmp", dotField(safePoint, "context"), cIntValue(0)) else: p.s(cpsStmts).addFieldAssignmentWithValue(safePoint, "status"): p.s(cpsStmts).addCall("_setjmp", dotField(safePoint, "context")) else: 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)))) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural)) expr(p, t[0], d) var quirkyIf = default(IfBuilder) var quirkyScope = default(ScopeBuilder) var isScope = false if not quirkyExceptions: p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popSafePoint")) finishBranch(p.s(cpsStmts), nonQuirkyIf) startBlockWith(p): initElseBranch(p.s(cpsStmts), nonQuirkyIf) p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popSafePoint")) genRestoreFrameAfterException(p) elif 1 < t.len and t[1].kind == nkExceptBranch: startBlockWith(p): quirkyIf = initIfStmt(p.s(cpsStmts)) initElifBranch(p.s(cpsStmts), quirkyIf, cCall(cgsymValue(p.module, "nimBorrowCurrentException"))) else: isScope = true startBlockWith(p): quirkyScope = initScope(p.s(cpsStmts)) p.nestedTryStmts[^1].inExcept = true var i = 1 var exceptIf = default(IfBuilder) var exceptIfInited = false while (i < t.len) and (t[i].kind == nkExceptBranch): # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone if t[i].len == 1: # general except section: var scope = default(ScopeBuilder) startBlockWith(p): if exceptIfInited: initElseBranch(p.s(cpsStmts), exceptIf) else: scope = initScope(p.s(cpsStmts)) if not quirkyExceptions: p.s(cpsStmts).addFieldAssignment(safePoint, "status", cIntValue(0)) expr(p, t[i][0], d) 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: Snippet = "" for j in 0..= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}: let sec = n[0].strVal if sec.startsWith("/*TYPESECTION*/"): result = cfsForwardTypes # TODO WORKAROUND elif sec.startsWith("/*VARSECTION*/"): result = cfsVars elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders proc genEmit(p: BProc, t: PNode) = var s = newRopeAppender() genAsmOrEmitStmt(p, t[1], false, s) if p.prc == nil: # top level emit pragma? let section = determineSection(t[1]) genCLineDir(p.module.s[section], t.info, p.config) p.module.s[section].add(s) else: genLineDir(p, t) line(p, cpsStmts, s) proc genPragma(p: BProc, n: PNode) = for i in 0..