mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 14:00:35 +00:00
.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:
@@ -195,7 +195,7 @@
|
||||
is
|
||||
cool!
|
||||
"""
|
||||
```
|
||||
```
|
||||
|
||||
- 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
|
||||
`{.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
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ type
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 43 flags!
|
||||
TSymFlag* = enum # 46 flags!
|
||||
sfUsed, # read access of sym (for warnings) or simply used
|
||||
sfExported, # symbol is exported from module
|
||||
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
|
||||
# or out-of-memory
|
||||
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]
|
||||
|
||||
|
||||
@@ -474,6 +474,8 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
|
||||
else:
|
||||
params.add(getTypeDescAux(m, param.typ, check, skParam))
|
||||
params.add(~" ")
|
||||
if sfNoalias in param.flags:
|
||||
params.add(~"NIM_NOALIAS ")
|
||||
params.add(param.loc.r)
|
||||
# declare the len field for open arrays:
|
||||
var arr = param.typ
|
||||
|
||||
@@ -503,6 +503,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
|
||||
# decl.add(" GC_GUARD")
|
||||
if sfVolatile in s.flags: result.add(" volatile")
|
||||
if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
|
||||
result.add(" ")
|
||||
result.add(s.loc.r)
|
||||
else:
|
||||
@@ -563,6 +564,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
|
||||
if p.hcrOn: decl.add("*")
|
||||
if sfRegister in s.flags: decl.add(" register")
|
||||
if sfVolatile in s.flags: decl.add(" volatile")
|
||||
if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
|
||||
if value != nil:
|
||||
decl.addf(" $1 = $2;$n", [s.loc.r, value])
|
||||
else:
|
||||
@@ -1261,6 +1263,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
|
||||
if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
|
||||
if sfRegister in sym.flags: m.s[cfsVars].add(" register")
|
||||
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])
|
||||
if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
|
||||
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
|
||||
|
||||
@@ -65,14 +65,15 @@ const
|
||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||
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,
|
||||
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
||||
wNoInit, wCompileTime, wGlobal,
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor}
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias}
|
||||
constPragmas* = declPragmas + {wHeader, wMagic,
|
||||
wGensym, wInject,
|
||||
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
||||
paramPragmas* = {wNoalias, wInject, wGensym}
|
||||
letPragmas* = varPragmas
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
|
||||
wThread, wRaises, wLocks, wTags, wGcSafe,
|
||||
@@ -864,6 +865,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wRegister:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfRegister)
|
||||
of wNoalias:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfNoalias)
|
||||
of wThreadVar:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, {sfThread, sfGlobal})
|
||||
|
||||
@@ -1264,6 +1264,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
|
||||
for j in 0..<a.len-2:
|
||||
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}:
|
||||
let param = strTableGet(c.signatures, arg.name)
|
||||
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 =
|
||||
|
||||
template addSym(result: PNode, s: PSym): untyped =
|
||||
template addSym(result: PNode, s: PSym): untyped =
|
||||
if father != nil: addSonSkipIntLit(father, s.typ)
|
||||
if sfGenSym notin s.flags: addDecl(c, s)
|
||||
result.add newSymNode(s)
|
||||
@@ -2066,8 +2068,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
illFormedAst(n, c.config)
|
||||
return
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind
|
||||
var a = n[i]
|
||||
case a.kind
|
||||
of nkSym: result.addSym(a.sym)
|
||||
of nkIdentDefs:
|
||||
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 def.kind != nkEmpty: s.ast = def
|
||||
s.position = result.len
|
||||
s.position = result.len
|
||||
result.addSym(s)
|
||||
else:
|
||||
illFormedAst(n, c.config)
|
||||
|
||||
@@ -35,7 +35,7 @@ type
|
||||
wColon, wColonColon, wEquals, wDot, wDotDot,
|
||||
wStar, wMinus,
|
||||
wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks,
|
||||
wIntDefine, wStrDefine, wBoolDefine, wCursor,
|
||||
wIntDefine, wStrDefine, wBoolDefine, wCursor, wNoalias,
|
||||
|
||||
wImmediate, wConstructor, wDestructor, wDelegator, wOverride,
|
||||
wImportCpp, wImportObjC,
|
||||
@@ -124,7 +124,7 @@ const
|
||||
":", "::", "=", ".", "..",
|
||||
"*", "-",
|
||||
"magic", "thread", "final", "profiler", "memtracker", "objchecks",
|
||||
"intdefine", "strdefine", "booldefine", "cursor",
|
||||
"intdefine", "strdefine", "booldefine", "cursor", "noalias",
|
||||
|
||||
"immediate", "constructor", "destructor", "delegator", "override",
|
||||
"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.
|
||||
|
||||
|
||||
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
|
||||
============
|
||||
|
||||
|
||||
@@ -386,7 +386,7 @@ func `[]`*(pattern: Captures, name: string): string =
|
||||
template toTableImpl() {.dirty.} =
|
||||
for key in RegexMatch(pattern).pattern.captureNameId.keys:
|
||||
if key in pattern:
|
||||
result[key] = pattern[key]
|
||||
result[key] = pattern[key]
|
||||
|
||||
func toTable*(pattern: Captures): Table[string, string] =
|
||||
result = initTable[string, string]()
|
||||
|
||||
@@ -588,4 +588,7 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, ""
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NIM_NOALIAS __restrict
|
||||
/* __restrict is said to work for all the C(++) compilers out there that we support */
|
||||
|
||||
#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"
|
||||
Reference in New Issue
Block a user