.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
commit 50b6f6996a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 53 additions and 12 deletions

View file

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

View file

@ -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
@ -294,6 +294,7 @@ type
# 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]

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

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"