diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2023-07-23 13:39:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-23 13:39:58 +0200 |
commit | be1844541c87a132ca076d8a8f741bec01825ba1 (patch) | |
tree | 3579a23d29d0dae95d765e7e3e24f888690132bc | |
parent | 62869a5c68e4dd91e00ee77b039f0175482ef4fa (diff) | |
download | Nim-be1844541c87a132ca076d8a8f741bec01825ba1.tar.gz |
implemented 'push quirky' switch for fine grained control over the ex… (#22318)
* implemented 'push quirky' switch for fine grained control over the exception handling overhead * documentation
-rw-r--r-- | changelogs/changelog_2_0_0_details.md | 3 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 7 | ||||
-rw-r--r-- | compiler/cgen.nim | 10 | ||||
-rw-r--r-- | compiler/cgendata.nim | 18 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/options.nim | 1 | ||||
-rw-r--r-- | compiler/pragmas.nim | 7 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 1 | ||||
-rw-r--r-- | doc/manual_experimental.md | 81 | ||||
-rw-r--r-- | doc/nimc.md | 4 |
10 files changed, 102 insertions, 31 deletions
diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md index 8f9e7afd0..e3895639a 100644 --- a/changelogs/changelog_2_0_0_details.md +++ b/changelogs/changelog_2_0_0_details.md @@ -458,6 +458,9 @@ - `=wasMoved` can now be overridden by users. +- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code + generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care. + - Tuple unpacking for variables is now treated as syntax sugar that directly expands into multiple assignments. Along with this, tuple unpacking for variables can now be nested. diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f536a82c1..319738981 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -315,7 +315,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var targetProc = p var valueAsRope = "" potentialValueInit(p, v, value, valueAsRope) - if sfGlobal in v.flags: + if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and value.kind == nkEmpty and v.loc.flags * {lfHeader, lfNoDecl} != {}: @@ -1050,7 +1050,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = expr(p, t[0], d) endBlock(p) - # First pass: handle Nim based exceptions: + # First pass: handle Nim based exceptions: lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1]) genRestoreFrameAfterException(p) # an unhandled exception happened! @@ -1308,7 +1308,8 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" 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)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", + [memberName, 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]) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0450625fc..ed149ed0e 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -619,7 +619,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 genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = +proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = let s = n.sym if s.constraint.isNil: if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: @@ -640,7 +640,7 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = else: decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) -proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) +proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) = let s = vn.sym @@ -701,7 +701,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = decl.addf(" $1 = $2;$n", [s.loc.r, value]) else: decl.addf(" $1;$n", [s.loc.r]) - + p.module.s[cfsVars].add(decl) if p.withinLoop > 0 and value == "": # fixes tests/run/tzeroarray: @@ -1134,7 +1134,7 @@ proc getProcTypeCast(m: BModule, prc: PSym): Rope = proc genProcBody(p: BProc; procBody: PNode) = genStmts(p, procBody) # modifies p.locals, p.init, etc. - if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}: + if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}: p.flags.incl nimErrorFlagDeclared p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", [])) p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", [])) @@ -1178,7 +1178,7 @@ proc genProcAux*(m: BModule, prc: PSym) = initLocalVar(p, res, immediateAsgn=false) returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) elif sfConstructor in prc.flags: - fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) + fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) else: fillResult(p.config, resNode, prc.typ) assignParam(p, res, prc.typ[0]) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index e1309e0fd..4d15cf131 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -193,15 +193,15 @@ proc initBlock*(): TBlock = result.sections[i] = newRopeAppender() proc newProc*(prc: PSym, module: BModule): BProc = - new(result) - result.prc = prc - result.module = module - result.options = if prc != nil: prc.options - else: module.config.options - result.blocks = @[initBlock()] - result.nestedTryStmts = @[] - result.finallySafePoints = @[] - result.sigConflicts = initCountTable[string]() + result = BProc( + prc: prc, + module: module, + options: if prc != nil: prc.options + else: module.config.options, + blocks: @[initBlock()], + sigConflicts: initCountTable[string]()) + if optQuirky in result.options: + result.flags = {nimErrorFlagDisabled} proc newModuleList*(g: ModuleGraph): BModuleList = BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](), diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 12634248c..c68050449 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -156,3 +156,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasChecksums") defineSymbol("nimHasSendable") defineSymbol("nimAllowNonVarDestructor") + defineSymbol("nimHasQuirky") diff --git a/compiler/options.nim b/compiler/options.nim index d3cf71d4f..8286a575d 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -49,6 +49,7 @@ type # please make sure we have under 32 options optSinkInference # 'sink T' inference optCursorInference optImportHidden + optQuirky TOptions* = set[TOption] TGlobalOption* = enum diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 9e4a0052d..0d95f596c 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -34,7 +34,7 @@ const wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, - wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual} + wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, @@ -405,6 +405,7 @@ proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} = of wImplicitStatic: {optImplicitStatic} of wPatterns, wTrMacros: {optTrMacros} of wSinkInference: {optSinkInference} + of wQuirky: {optQuirky} else: {} proc processExperimental(c: PContext; n: PNode) = @@ -1273,12 +1274,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragmaProposition(c, it) of wEnsures: pragmaEnsures(c, it) - of wEnforceNoRaises: + of wEnforceNoRaises, wQuirky: sym.flags.incl sfNeverRaises of wSystemRaisesDefect: sym.flags.incl sfSystemRaisesDefect of wVirtual: - processVirtual(c, it, sym) + processVirtual(c, it, sym) else: invalidPragma(c, it) elif comesFromPush and whichKeyword(ident) != wInvalid: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 21b097075..f784f0a75 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -89,6 +89,7 @@ type wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wSystemRaisesDefect = "systemRaisesDefect", wRedefine = "redefine", wCallsite = "callsite", + wQuirky = "quirky", wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 54354f92b..602ca46a5 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2009,6 +2009,65 @@ The field is within a `case` section of an `object`. is solid and it is expected that eventually this mode becomes the default in later versions. +Quirky routines +=============== + +The default code generation strategy of exceptions under the ARC/ORC model is the so called +`--exceptions:goto` implementation. This implementation inserts a check after every call that +can potentially raise an exception. A typical instruction sequence for this on +for a x86 64 bit machine looks like: + + ``` + cmp DWORD PTR [rbx], 0 + je .L1 + ``` + +This is a memory fetch followed by jump. (An ideal implementation would +use the carry flag and a single instruction like ``jc .L1``.) + +This overhead might not be desired and depending on the sematics of the routine may not be required +either. +So it can be disabled via a `.quirky` annotation: + + ```nim + proc wontRaise(x: int) {.quirky.} = + if x != 0: + # because of `quirky` this will continue even if `write` raised an IO exception: + write x + wontRaise(x-1) + + wontRaise 10 + + ``` + +If the used exception model is not `--exceptions:goto` then the `quirky` pragma has no effect and is +ignored. + +The `quirky` pragma can also be be pushed in order to affect a group of routines and whether +the compiler supports the pragma can be checked with `defined(nimHasQuirky)`: + + ```nim + when defined(nimHasQuirky): + {.push quirky: on.} + + proc doRaise() = raise newException(ValueError, "") + + proc f(): string = "abc" + + proc q(cond: bool) = + if cond: + doRaise() + echo f() + + q(true) + + when defined(nimHasQuirky): + {.pop.} + ``` + +**Warning**: The `quirky` pragma only affects code generation, no check for validity is performed! + + Threading under ARC/ORC ======================= @@ -2141,13 +2200,13 @@ Here's an example of how to use the virtual pragma: ```nim proc newCpp*[T](): ptr T {.importcpp: "new '*0()".} -type +type Foo = object of RootObj FooPtr = ptr Foo Boo = object of Foo BooPtr = ptr Boo -proc salute(self: FooPtr) {.virtual.} = +proc salute(self: FooPtr) {.virtual.} = echo "hello foo" proc salute(self: BooPtr) {.virtual.} = @@ -2177,13 +2236,13 @@ The return type can be referred to as `-> '0`, but this is optional and often no #include <iostream> class CppPrinter { public: - + virtual void printConst(char* message) const { std::cout << "Const Message: " << message << std::endl; } virtual void printConstRef(char* message, const int& flag) const { std::cout << "Const Ref Message: " << message << std::endl; - } + } }; """.} @@ -2194,7 +2253,7 @@ type proc printConst(self: CppPrinter; message:cstring) {.importcpp.} CppPrinter().printConst(message) -# override is optional. +# override is optional. proc printConst(self: NimPrinter; message: cstring) {.virtual: "$1('2 #2) const override".} = echo "NimPrinter: " & $message @@ -2224,10 +2283,10 @@ proc makeFoo(x: int32): Foo {.constructor.} = ``` -It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. +It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. Notice, inside the body of the constructor one has access to `this` which is of the type `ptr Foo`. No `result` variable is available. -Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints. +Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints. For example: @@ -2242,11 +2301,11 @@ struct CppClass { this->x = inX; this->y = inY; } - //CppClass() = default; + //CppClass() = default; }; """.} -type +type CppClass* {.importcpp, inheritable.} = object x: int32 y: int32 @@ -2256,11 +2315,11 @@ proc makeNimClass(x: int32): NimClass {.constructor:"NimClass('1 #1) : CppClass( this.x = x # Optional: define the default constructor explicitly -proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} = +proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} = this.x = 1 ``` -In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor. +In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor. Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized. diff --git a/doc/nimc.md b/doc/nimc.md index 7c42c7c1b..9c6ea7033 100644 --- a/doc/nimc.md +++ b/doc/nimc.md @@ -481,9 +481,13 @@ They are: 5. nl_types. No headers for this. 6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used. + DLL generation ============== +**Note**: The same rules apply to `lib*.so` shared object files on UNIX. For better +readability only the DLL version is decribed here. + Nim supports the generation of DLLs. However, there must be only one instance of the GC per process/address space. This instance is contained in ``nimrtl.dll``. This means that every generated Nim DLL depends |