mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-14 02:54:59 +00:00
Miscellaneous frontend optimizations (#26090)
Boostrap without linking is (conservatively) 17% faster on my machine. Test setup runs the compiler compiling itself under ORC in release mode from a clean cache and no C compilation (`--compileOnly`.) Multiple samples are gathered before judging a potential optimization. The test suite passes locally before push and bootstrap is tested with strict views (mostly as a sanity check). Tests are fair in the sense that they compile the same git worktree and produce identical C output. Refc will see less or no benefit. Changes: - Save tree traversals in `considerGenSyms` when no mappings exist - Inline `maybeSkipDistinct` into `typeRel` for a safe cursor - Only skip to static when there is a static type to reach in `paramTypesMatchAux` - Disable overflow checks for hashes - Disable bounds checks for `nextIdentIter` - Use cursor annotation when judged safe - Use lent annotation for ast accessors Spiritual companion to #26084
This commit is contained in:
@@ -87,11 +87,11 @@ proc unsealForTransform*(t: PType) {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
if t.state == Sealed: t.state = Complete
|
||||
|
||||
proc owner*(s: PSym): PSym {.inline.} =
|
||||
proc owner*(s: PSym): lent PSym {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.ownerFieldImpl
|
||||
|
||||
proc owner*(s: PType): PSym {.inline.} =
|
||||
proc owner*(s: PType): lent PSym {.inline.} =
|
||||
if s.state == Partial: loadType(s)
|
||||
result = s.ownerFieldImpl
|
||||
|
||||
@@ -114,7 +114,7 @@ proc `kind=`*(s: PSym, val: TSymKind) {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.kindImpl = val
|
||||
|
||||
proc gcUnsafetyReason*(s: PSym): PSym {.inline.} =
|
||||
proc gcUnsafetyReason*(s: PSym): lent PSym {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.gcUnsafetyReasonImpl
|
||||
|
||||
@@ -123,7 +123,7 @@ proc `gcUnsafetyReason=`*(s: PSym, val: PSym) {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.gcUnsafetyReasonImpl = val
|
||||
|
||||
proc transformedBody*(s: PSym): PNode {.inline.} =
|
||||
proc transformedBody*(s: PSym): lent PNode {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.transformedBodyImpl
|
||||
|
||||
@@ -133,7 +133,7 @@ proc `transformedBody=`*(s: PSym, val: PNode) {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.transformedBodyImpl = val
|
||||
|
||||
proc guard*(s: PSym): PSym {.inline.} =
|
||||
proc guard*(s: PSym): lent PSym {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.guardImpl
|
||||
|
||||
@@ -169,7 +169,7 @@ proc `magic=`*(s: PSym, val: TMagic) {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.magicImpl = val
|
||||
|
||||
proc typ*(s: PSym): PType {.inline.} =
|
||||
proc typ*(s: PSym): lent PType {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.typImpl
|
||||
|
||||
@@ -215,7 +215,7 @@ proc `flags=`*(s: PSym, val: TSymFlags) {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.flagsImpl = val
|
||||
|
||||
proc ast*(s: PSym): PNode {.inline.} =
|
||||
proc ast*(s: PSym): lent PNode {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.astImpl
|
||||
|
||||
@@ -263,7 +263,7 @@ proc `loc=`*(s: PSym, val: TLoc) {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.locImpl = val
|
||||
|
||||
proc annex*(s: PSym): PLib {.inline.} =
|
||||
proc annex*(s: PSym): lent PLib {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.annexImpl
|
||||
|
||||
@@ -282,7 +282,7 @@ when hasFFI:
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.cnameImpl = val
|
||||
|
||||
proc constraint*(s: PSym): PNode {.inline.} =
|
||||
proc constraint*(s: PSym): lent PNode {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.constraintImpl
|
||||
|
||||
@@ -291,7 +291,7 @@ proc `constraint=`*(s: PSym, val: PNode) {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
s.constraintImpl = val
|
||||
|
||||
proc instantiatedFrom*(s: PSym): PSym {.inline.} =
|
||||
proc instantiatedFrom*(s: PSym): lent PSym {.inline.} =
|
||||
if s.state == Partial: loadSym(s)
|
||||
result = s.instantiatedFromImpl
|
||||
|
||||
@@ -367,7 +367,7 @@ proc `sons=`*(t: PType, val: sink TTypeSeq) {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
t.sonsImpl = val
|
||||
|
||||
proc n*(t: PType): PNode {.inline.} =
|
||||
proc n*(t: PType): lent PNode {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
result = t.nImpl
|
||||
|
||||
@@ -376,7 +376,7 @@ proc `n=`*(t: PType, val: PNode) {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
t.nImpl = val
|
||||
|
||||
proc sym*(t: PType): PSym {.inline.} =
|
||||
proc sym*(t: PType): lent PSym {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
result = t.symImpl
|
||||
|
||||
@@ -418,7 +418,7 @@ proc `loc=`*(t: PType, val: TLoc) {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
t.locImpl = val
|
||||
|
||||
proc typeInst*(t: PType): PType {.inline.} =
|
||||
proc typeInst*(t: PType): lent PType {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
result = t.typeInstImpl
|
||||
|
||||
@@ -447,7 +447,7 @@ proc excl*(t: PType; flags: set[TTypeFlag]) {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
t.flagsImpl.excl(flags)
|
||||
|
||||
proc typ*(n: PNode): PType {.inline.} =
|
||||
proc typ*(n: PNode): lent PType {.inline.} =
|
||||
result = n.typField
|
||||
if result == nil and nfLazyType in n.flags:
|
||||
result = n.sym.typ
|
||||
@@ -866,7 +866,7 @@ proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = castToInt64(intVal)
|
||||
|
||||
proc lastSon*(n: PNode): PNode {.inline.} = n.sons[^1]
|
||||
proc lastSon*(n: PNode): lent PNode {.inline.} = n.sons[^1]
|
||||
template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s
|
||||
|
||||
template firstSon*(n: PNode): PNode = n.sons[0]
|
||||
@@ -888,29 +888,29 @@ proc last*(n: PType): PType {.inline.} =
|
||||
else:
|
||||
n.sonsImpl[^1]
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} =
|
||||
proc elementType*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
result = n.sonsImpl[^1]
|
||||
|
||||
proc skipModifier*(n: PType): PType {.inline.} =
|
||||
proc skipModifier*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
result = n.sonsImpl[^1]
|
||||
|
||||
proc indexType*(n: PType): PType {.inline.} =
|
||||
proc indexType*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
result = n.sonsImpl[0]
|
||||
|
||||
proc baseClass*(n: PType): PType {.inline.} =
|
||||
proc baseClass*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
result = n.sonsImpl[0]
|
||||
|
||||
proc base*(t: PType): PType {.inline.} =
|
||||
proc base*(t: PType): lent PType {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
result = t.sonsImpl[0]
|
||||
|
||||
proc returnType*(n: PType): PType {.inline.} =
|
||||
proc returnType*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
result = n.sonsImpl[0]
|
||||
|
||||
proc setReturnType*(n, r: PType) {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
@@ -927,17 +927,17 @@ proc firstParamType*(n: PType): PType {.inline.} =
|
||||
else:
|
||||
n.sonsImpl[1]
|
||||
|
||||
proc firstGenericParam*(n: PType): PType {.inline.} =
|
||||
proc firstGenericParam*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[1]
|
||||
result = n.sonsImpl[1]
|
||||
|
||||
proc typeBodyImpl*(n: PType): PType {.inline.} =
|
||||
proc typeBodyImpl*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
result = n.sonsImpl[^1]
|
||||
|
||||
proc genericHead*(n: PType): PType {.inline.} =
|
||||
proc genericHead*(n: PType): lent PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
result = n.sonsImpl[0]
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## Used throughout the compiler code to test whether a type tree contains or
|
||||
|
||||
@@ -529,8 +529,11 @@ proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool =
|
||||
type
|
||||
TIdentIter* = object # iterator over all syms with same identifier
|
||||
h*: Hash # current hash
|
||||
name*: PIdent
|
||||
name* {.cursor.}: PIdent
|
||||
|
||||
# String tables are always initialized with non-empty, power-of-two storage,
|
||||
# and every probe is masked by `high(tab.data)`.
|
||||
{.push boundChecks: off.}
|
||||
proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
|
||||
# hot spots
|
||||
var h = ti.h and high(tab.data)
|
||||
@@ -548,6 +551,7 @@ proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
|
||||
else:
|
||||
result = nil
|
||||
ti.h = nextTry(h, high(tab.data))
|
||||
{.pop.}
|
||||
|
||||
proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
|
||||
ti.h = s.h
|
||||
|
||||
@@ -1105,8 +1105,11 @@ const # for all kind of hash tables:
|
||||
GrowthFactor* = 2 # must be power of 2, > 0
|
||||
StartSize* = 8 # must be power of 2, > 0
|
||||
|
||||
{.push overflowChecks: off.}
|
||||
proc nextTry*(h, maxHash: Hash): Hash {.inline.} =
|
||||
# Overflow is intentional: only the low bits selected by maxHash are used.
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
{.pop.}
|
||||
# For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
# random-number generation for proof).
|
||||
|
||||
@@ -306,7 +306,7 @@ proc getGenSym*(c: PContext; s: PSym): PSym =
|
||||
it = it.next
|
||||
result = s
|
||||
|
||||
proc considerGenSyms*(c: PContext; n: PNode) =
|
||||
proc considerGenSymsAux(c: PContext; n: PNode) =
|
||||
if n == nil:
|
||||
discard "can happen for nkFormalParams/nkArgList"
|
||||
elif n.kind == nkSym:
|
||||
@@ -315,7 +315,16 @@ proc considerGenSyms*(c: PContext; n: PNode) =
|
||||
n.sym = s
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
considerGenSyms(c, n[i])
|
||||
considerGenSymsAux(c, n[i])
|
||||
|
||||
proc considerGenSyms*(c: PContext; n: PNode) =
|
||||
var it = c.p
|
||||
while it != nil:
|
||||
if it.mappingExists:
|
||||
# Save a tree traversal when no mapping exists
|
||||
considerGenSymsAux(c, n)
|
||||
return
|
||||
it = it.next
|
||||
|
||||
proc newOptionEntry*(conf: ConfigRef): POptionEntry =
|
||||
result = POptionEntry(
|
||||
|
||||
@@ -649,7 +649,7 @@ type
|
||||
SkippedPtr = enum skippedNone, skippedRef, skippedPtr
|
||||
|
||||
proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
|
||||
var r = t
|
||||
var r {.cursor.} = t
|
||||
# we're allowed to skip one level of ptr/ref:
|
||||
var ptrs = 0
|
||||
while r != nil:
|
||||
@@ -993,13 +993,6 @@ proc shouldSkipDistinct(m: TCandidate; rules: PNode, callIdent: PIdent): bool =
|
||||
if considerQuotedIdent(m.c, r) == callIdent: return false
|
||||
return true
|
||||
|
||||
proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
|
||||
if t != nil and t.kind == tyDistinct and t.n != nil and
|
||||
shouldSkipDistinct(m, t.n, callee.name):
|
||||
result = t.base
|
||||
else:
|
||||
result = t
|
||||
|
||||
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
|
||||
allowUnresolved = false,
|
||||
allowCalls = false,
|
||||
@@ -1242,17 +1235,22 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
|
||||
assert(aOrig != nil)
|
||||
|
||||
var
|
||||
useTypeLoweringRuleInTypeClass = c.c.matchedConcept != nil and
|
||||
not c.isNoCall and
|
||||
f.kind != tyTypeDesc and
|
||||
tfExplicit notin aOrig.flags and
|
||||
tfConceptMatchedTypeSym notin aOrig.flags
|
||||
let useTypeLoweringRuleInTypeClass = c.c.matchedConcept != nil and
|
||||
not c.isNoCall and
|
||||
f.kind != tyTypeDesc and
|
||||
tfExplicit notin aOrig.flags and
|
||||
tfConceptMatchedTypeSym notin aOrig.flags
|
||||
|
||||
aOrig = if useTypeLoweringRuleInTypeClass:
|
||||
aOrig.skipTypes({tyTypeDesc})
|
||||
else:
|
||||
aOrig
|
||||
template skipTypeCursor(it, kinds: untyped) =
|
||||
while it.kind in kinds:
|
||||
if it.kind == tyProc and it.nImpl.len > 1:
|
||||
it = it.nImpl[^1].sym.typ
|
||||
else:
|
||||
it = it.sonsImpl[^1]
|
||||
|
||||
var aOrig {.cursor.} = aOrig
|
||||
if useTypeLoweringRuleInTypeClass:
|
||||
skipTypeCursor(aOrig, {tyTypeDesc})
|
||||
|
||||
if aOrig.kind == tyInferred:
|
||||
let prev = aOrig.previouslyInferred
|
||||
@@ -1289,8 +1287,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
template doBind: bool = trDontBind notin flags
|
||||
|
||||
# var, sink and static arguments match regular modifier-free types
|
||||
var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent, tySink}), c.calleeSym)
|
||||
# XXX: Theoretically, maybeSkipDistinct could be called before we even
|
||||
var a {.cursor.} = aOrig
|
||||
skipTypeCursor(a, {tyStatic, tyVar, tyLent, tySink})
|
||||
# Keep this expanded: an expression template materializes a PType temporary
|
||||
# here, adding an otherwise avoidable reference-counting pair.
|
||||
if a.kind == tyDistinct and a.n != nil and
|
||||
shouldSkipDistinct(c, a.n, c.calleeSym.name):
|
||||
a = a.base
|
||||
# XXX: Theoretically, distinct types could be skipped before we even
|
||||
# start the param matching process. This could be done in `prepareOperand`
|
||||
# for example, but unfortunately `prepareOperand` is not called in certain
|
||||
# situation when nkDotExpr are rotated to nkDotCalls
|
||||
@@ -2448,11 +2452,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
argSemantized, argOrig: PNode): PNode =
|
||||
result = nil
|
||||
var
|
||||
fMaybeStatic = f.skipTypes({tyDistinct})
|
||||
arg = argSemantized
|
||||
a = a
|
||||
c = m.c
|
||||
if tfHasStatic in fMaybeStatic.flags:
|
||||
let hasStatic = tfHasStatic in f.flags or
|
||||
(f.kind == tyDistinct and tfHasStatic in f.skipTypes({tyDistinct}).flags)
|
||||
if hasStatic:
|
||||
let fMaybeStatic = if f.kind == tyDistinct: f.skipTypes({tyDistinct}) else: f
|
||||
# XXX: When implicit statics are the default
|
||||
# this will be done earlier - we just have to
|
||||
# make sure that static types enter here
|
||||
|
||||
@@ -56,11 +56,14 @@ proc mustRehash[T](t: T): bool {.inline.} =
|
||||
assert length > t.counter
|
||||
result = (length * 2 < t.counter * 3) or (length - t.counter < 4)
|
||||
|
||||
{.push overflowChecks: off.}
|
||||
proc nextTry(h, maxHash: Hash, perturb: var Hash): Hash {.inline.} =
|
||||
const PERTURB_SHIFT = 5
|
||||
var perturb2 = cast[uint](perturb) shr PERTURB_SHIFT
|
||||
perturb = cast[Hash](perturb2)
|
||||
# Overflow is intentional: only the low bits selected by maxHash are used.
|
||||
result = ((5 * h) + 1 + perturb) and maxHash
|
||||
{.pop.}
|
||||
|
||||
proc packedSetGet[A](t: PackedSet[A], key: int): Trunk =
|
||||
var h = key and t.max
|
||||
|
||||
Reference in New Issue
Block a user