mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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]](),
|
||||
|
||||
@@ -156,3 +156,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasChecksums")
|
||||
defineSymbol("nimHasSendable")
|
||||
defineSymbol("nimAllowNonVarDestructor")
|
||||
defineSymbol("nimHasQuirky")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user