.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:
Andreas Rumpf
2020-09-28 13:51:39 +02:00
committed by GitHub
parent 453167a41e
commit 50b6f6996a
11 changed files with 53 additions and 12 deletions

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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,

View File

@@ -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})

View File

@@ -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)

View File

@@ -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",

View File

@@ -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
============

View File

@@ -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]()

View File

@@ -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 */

View 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"