.noalias annotation; frontend support (#15419)
* .noalias annotation; frontend support * added .noalias support to Nim; implements https://github.com/nim-lang/RFCs/issues/204
This commit is contained in:
parent
453167a41e
commit
50b6f6996a
11 changed files with 53 additions and 12 deletions
|
|
@ -195,7 +195,7 @@
|
||||||
is
|
is
|
||||||
cool!
|
cool!
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
- Add `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname.
|
- Add `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname.
|
||||||
|
|
||||||
|
|
@ -283,6 +283,9 @@ proc mydiv(a, b): int {.raises: [].} =
|
||||||
an experimental feature. In other words, you don't have to write pragma
|
an experimental feature. In other words, you don't have to write pragma
|
||||||
`{.experimental: "forLoopMacros".}` if you want to use them.
|
`{.experimental: "forLoopMacros".}` if you want to use them.
|
||||||
|
|
||||||
|
- Added a ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased
|
||||||
|
performance this keyword can enable.
|
||||||
|
|
||||||
|
|
||||||
## Compiler changes
|
## Compiler changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ type
|
||||||
TNodeKinds* = set[TNodeKind]
|
TNodeKinds* = set[TNodeKind]
|
||||||
|
|
||||||
type
|
type
|
||||||
TSymFlag* = enum # 43 flags!
|
TSymFlag* = enum # 46 flags!
|
||||||
sfUsed, # read access of sym (for warnings) or simply used
|
sfUsed, # read access of sym (for warnings) or simply used
|
||||||
sfExported, # symbol is exported from module
|
sfExported, # symbol is exported from module
|
||||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||||
|
|
@ -293,7 +293,8 @@ type
|
||||||
sfNeverRaises # proc can never raise an exception, not even OverflowDefect
|
sfNeverRaises # proc can never raise an exception, not even OverflowDefect
|
||||||
# or out-of-memory
|
# or out-of-memory
|
||||||
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
|
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
|
||||||
sfSingleUsedTemp # For temporaries that we know will only be used once
|
sfSingleUsedTemp # For temporaries that we know will only be used once
|
||||||
|
sfNoalias # 'noalias' annotation, means C's 'restrict'
|
||||||
|
|
||||||
TSymFlags* = set[TSymFlag]
|
TSymFlags* = set[TSymFlag]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -474,6 +474,8 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
|
||||||
else:
|
else:
|
||||||
params.add(getTypeDescAux(m, param.typ, check, skParam))
|
params.add(getTypeDescAux(m, param.typ, check, skParam))
|
||||||
params.add(~" ")
|
params.add(~" ")
|
||||||
|
if sfNoalias in param.flags:
|
||||||
|
params.add(~"NIM_NOALIAS ")
|
||||||
params.add(param.loc.r)
|
params.add(param.loc.r)
|
||||||
# declare the len field for open arrays:
|
# declare the len field for open arrays:
|
||||||
var arr = param.typ
|
var arr = param.typ
|
||||||
|
|
|
||||||
|
|
@ -503,6 +503,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||||
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
|
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
|
||||||
# decl.add(" GC_GUARD")
|
# decl.add(" GC_GUARD")
|
||||||
if sfVolatile in s.flags: result.add(" volatile")
|
if sfVolatile in s.flags: result.add(" volatile")
|
||||||
|
if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
|
||||||
result.add(" ")
|
result.add(" ")
|
||||||
result.add(s.loc.r)
|
result.add(s.loc.r)
|
||||||
else:
|
else:
|
||||||
|
|
@ -563,6 +564,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
|
||||||
if p.hcrOn: decl.add("*")
|
if p.hcrOn: decl.add("*")
|
||||||
if sfRegister in s.flags: decl.add(" register")
|
if sfRegister in s.flags: decl.add(" register")
|
||||||
if sfVolatile in s.flags: decl.add(" volatile")
|
if sfVolatile in s.flags: decl.add(" volatile")
|
||||||
|
if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
|
||||||
if value != nil:
|
if value != nil:
|
||||||
decl.addf(" $1 = $2;$n", [s.loc.r, value])
|
decl.addf(" $1 = $2;$n", [s.loc.r, value])
|
||||||
else:
|
else:
|
||||||
|
|
@ -1261,6 +1263,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
|
||||||
if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
|
if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
|
||||||
if sfRegister in sym.flags: m.s[cfsVars].add(" register")
|
if sfRegister in sym.flags: m.s[cfsVars].add(" register")
|
||||||
if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
|
if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
|
||||||
|
if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
|
||||||
m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
|
m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
|
||||||
if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
|
if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
|
||||||
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
|
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
|
||||||
|
|
|
||||||
|
|
@ -65,14 +65,15 @@ const
|
||||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||||
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||||
fieldPragmas* = declPragmas + {
|
fieldPragmas* = declPragmas + {
|
||||||
wGuard, wBitsize, wCursor, wRequiresInit} - {wExportNims, wNodecl} # why exclude these?
|
wGuard, wBitsize, wCursor, wRequiresInit, wNoalias} - {wExportNims, wNodecl} # why exclude these?
|
||||||
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
||||||
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
||||||
wNoInit, wCompileTime, wGlobal,
|
wNoInit, wCompileTime, wGlobal,
|
||||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor}
|
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias}
|
||||||
constPragmas* = declPragmas + {wHeader, wMagic,
|
constPragmas* = declPragmas + {wHeader, wMagic,
|
||||||
wGensym, wInject,
|
wGensym, wInject,
|
||||||
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
||||||
|
paramPragmas* = {wNoalias, wInject, wGensym}
|
||||||
letPragmas* = varPragmas
|
letPragmas* = varPragmas
|
||||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
|
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
|
||||||
wThread, wRaises, wLocks, wTags, wGcSafe,
|
wThread, wRaises, wLocks, wTags, wGcSafe,
|
||||||
|
|
@ -864,6 +865,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wRegister:
|
of wRegister:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
incl(sym.flags, sfRegister)
|
incl(sym.flags, sfRegister)
|
||||||
|
of wNoalias:
|
||||||
|
noVal(c, it)
|
||||||
|
incl(sym.flags, sfNoalias)
|
||||||
of wThreadVar:
|
of wThreadVar:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
incl(sym.flags, {sfThread, sfGlobal})
|
incl(sym.flags, {sfThread, sfGlobal})
|
||||||
|
|
|
||||||
|
|
@ -1264,6 +1264,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
|
|
||||||
for j in 0..<a.len-2:
|
for j in 0..<a.len-2:
|
||||||
var arg = newSymG(skParam, if a[j].kind == nkPragmaExpr: a[j][0] else: a[j], c)
|
var arg = newSymG(skParam, if a[j].kind == nkPragmaExpr: a[j][0] else: a[j], c)
|
||||||
|
if a[j].kind == nkPragmaExpr:
|
||||||
|
pragma(c, arg, a[j][1], paramPragmas)
|
||||||
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
||||||
let param = strTableGet(c.signatures, arg.name)
|
let param = strTableGet(c.signatures, arg.name)
|
||||||
if param != nil: typ = param.typ
|
if param != nil: typ = param.typ
|
||||||
|
|
@ -2056,7 +2058,7 @@ proc semGenericConstraints(c: PContext, x: PType): PType =
|
||||||
|
|
||||||
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
|
|
||||||
template addSym(result: PNode, s: PSym): untyped =
|
template addSym(result: PNode, s: PSym): untyped =
|
||||||
if father != nil: addSonSkipIntLit(father, s.typ)
|
if father != nil: addSonSkipIntLit(father, s.typ)
|
||||||
if sfGenSym notin s.flags: addDecl(c, s)
|
if sfGenSym notin s.flags: addDecl(c, s)
|
||||||
result.add newSymNode(s)
|
result.add newSymNode(s)
|
||||||
|
|
@ -2066,8 +2068,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
illFormedAst(n, c.config)
|
illFormedAst(n, c.config)
|
||||||
return
|
return
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
var a = n[i]
|
var a = n[i]
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkSym: result.addSym(a.sym)
|
of nkSym: result.addSym(a.sym)
|
||||||
of nkIdentDefs:
|
of nkIdentDefs:
|
||||||
var def = a[^1]
|
var def = a[^1]
|
||||||
|
|
@ -2128,7 +2130,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
|
|
||||||
if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
|
if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
|
||||||
if def.kind != nkEmpty: s.ast = def
|
if def.kind != nkEmpty: s.ast = def
|
||||||
s.position = result.len
|
s.position = result.len
|
||||||
result.addSym(s)
|
result.addSym(s)
|
||||||
else:
|
else:
|
||||||
illFormedAst(n, c.config)
|
illFormedAst(n, c.config)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ type
|
||||||
wColon, wColonColon, wEquals, wDot, wDotDot,
|
wColon, wColonColon, wEquals, wDot, wDotDot,
|
||||||
wStar, wMinus,
|
wStar, wMinus,
|
||||||
wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks,
|
wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks,
|
||||||
wIntDefine, wStrDefine, wBoolDefine, wCursor,
|
wIntDefine, wStrDefine, wBoolDefine, wCursor, wNoalias,
|
||||||
|
|
||||||
wImmediate, wConstructor, wDestructor, wDelegator, wOverride,
|
wImmediate, wConstructor, wDestructor, wDelegator, wOverride,
|
||||||
wImportCpp, wImportObjC,
|
wImportCpp, wImportObjC,
|
||||||
|
|
@ -124,7 +124,7 @@ const
|
||||||
":", "::", "=", ".", "..",
|
":", "::", "=", ".", "..",
|
||||||
"*", "-",
|
"*", "-",
|
||||||
"magic", "thread", "final", "profiler", "memtracker", "objchecks",
|
"magic", "thread", "final", "profiler", "memtracker", "objchecks",
|
||||||
"intdefine", "strdefine", "booldefine", "cursor",
|
"intdefine", "strdefine", "booldefine", "cursor", "noalias",
|
||||||
|
|
||||||
"immediate", "constructor", "destructor", "delegator", "override",
|
"immediate", "constructor", "destructor", "delegator", "override",
|
||||||
"importcpp", "importobjc",
|
"importcpp", "importobjc",
|
||||||
|
|
|
||||||
|
|
@ -1764,6 +1764,21 @@ via ``.noSideEffect``. The rules 3 and 4 can also be approximated by a different
|
||||||
can only passed to a parameter of a ``.noSideEffect`` proc.
|
can only passed to a parameter of a ``.noSideEffect`` proc.
|
||||||
|
|
||||||
|
|
||||||
|
Noalias annotation
|
||||||
|
==================
|
||||||
|
|
||||||
|
Since version 1.4 of the Nim compiler, there is a ``.noalias`` annotation for variables
|
||||||
|
and parameters. It is mapped directly to C/C++'s ``restrict`` keyword and means that
|
||||||
|
the underlying pointer is pointing to a unique location in memory, no other aliases to
|
||||||
|
this location exist. It is *unchecked* that this alias restriction is followed, if the
|
||||||
|
restriction is violated, the backend optimizer is free to miscompile the code.
|
||||||
|
This is an **unsafe** language feature.
|
||||||
|
|
||||||
|
Ideally in later versions of the language, the restriction will be enforced at
|
||||||
|
compile time. (Which is also why the name ``noalias`` was choosen instead of a more
|
||||||
|
verbose name like ``unsafeAssumeNoAlias``.)
|
||||||
|
|
||||||
|
|
||||||
Strict funcs
|
Strict funcs
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,7 @@ func `[]`*(pattern: Captures, name: string): string =
|
||||||
template toTableImpl() {.dirty.} =
|
template toTableImpl() {.dirty.} =
|
||||||
for key in RegexMatch(pattern).pattern.captureNameId.keys:
|
for key in RegexMatch(pattern).pattern.captureNameId.keys:
|
||||||
if key in pattern:
|
if key in pattern:
|
||||||
result[key] = pattern[key]
|
result[key] = pattern[key]
|
||||||
|
|
||||||
func toTable*(pattern: Captures): Table[string, string] =
|
func toTable*(pattern: Captures): Table[string, string] =
|
||||||
result = initTable[string, string]()
|
result = initTable[string, string]()
|
||||||
|
|
|
||||||
|
|
@ -588,4 +588,7 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, ""
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define NIM_NOALIAS __restrict
|
||||||
|
/* __restrict is said to work for all the C(++) compilers out there that we support */
|
||||||
|
|
||||||
#endif /* NIMBASE_H */
|
#endif /* NIMBASE_H */
|
||||||
|
|
|
||||||
8
tests/ccgbugs/tnoalias.nim
Normal file
8
tests/ccgbugs/tnoalias.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
discard """
|
||||||
|
ccodecheck: "\\i@'NIM_CHAR* NIM_NOALIAS x,' @'void* NIM_NOALIAS q'"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc p(x {.noalias.}: openArray[char]) =
|
||||||
|
var q {.noalias.}: pointer = unsafeAddr(x[0])
|
||||||
|
|
||||||
|
p "abc"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue