mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-31 02:12:11 +00:00
precise stack scanning for Nim's GCs; work in progress
This commit is contained in:
@@ -739,6 +739,8 @@ type
|
||||
OnUnknown, # location is unknown (stack, heap or static)
|
||||
OnStatic, # in a static section
|
||||
OnStack, # location is on hardware stack
|
||||
OnStackShadowDup, # location is on the stack but also replicated
|
||||
# on the shadow stack
|
||||
OnHeap # location is on heap or global
|
||||
# (reference counting needed)
|
||||
TLocFlags* = set[TLocFlag]
|
||||
@@ -748,6 +750,7 @@ type
|
||||
flags*: TLocFlags # location's flags
|
||||
t*: PType # type of location
|
||||
r*: Rope # rope value of location (code generators)
|
||||
dup*: Rope # duplicated location for precise stack scans
|
||||
|
||||
# ---------------- end of backend information ------------------------------
|
||||
|
||||
|
||||
@@ -132,32 +132,12 @@ proc genSetNode(p: BProc, n: PNode): Rope =
|
||||
else:
|
||||
result = genRawSetData(cs, size)
|
||||
|
||||
proc getStorageLoc(n: PNode): TStorageLoc =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
case n.sym.kind
|
||||
of skParam, skTemp:
|
||||
result = OnStack
|
||||
of skVar, skForVar, skResult, skLet:
|
||||
if sfGlobal in n.sym.flags: result = OnHeap
|
||||
else: result = OnStack
|
||||
of skConst:
|
||||
if sfGlobal in n.sym.flags: result = OnHeap
|
||||
else: result = OnUnknown
|
||||
else: result = OnUnknown
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
case n.sons[0].typ.kind
|
||||
of tyVar: result = OnUnknown
|
||||
of tyPtr: result = OnStack
|
||||
of tyRef: result = OnHeap
|
||||
else: internalError(n.info, "getStorageLoc")
|
||||
of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
|
||||
result = getStorageLoc(n.sons[0])
|
||||
else: result = OnUnknown
|
||||
|
||||
proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
if dest.s == OnStack or not usesNativeGC():
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
elif dest.s == OnStackShadowDup:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src))
|
||||
elif dest.s == OnHeap:
|
||||
# location is on heap
|
||||
# now the writer barrier is inlined for performance:
|
||||
@@ -184,6 +164,8 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
|
||||
addrLoc(dest), rdLoc(src))
|
||||
if preciseStack():
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src))
|
||||
|
||||
proc asgnComplexity(n: PNode): int =
|
||||
if n != nil:
|
||||
@@ -241,7 +223,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
|
||||
|
||||
proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# Consider:
|
||||
# type TMyFastString {.shallow.} = string
|
||||
# type MyFastString {.shallow.} = string
|
||||
# Due to the implementation of pragmas this would end up to set the
|
||||
# tfShallow flag for the built-in string type too! So we check only
|
||||
# here for this flag, where it is reasonably safe to do so
|
||||
@@ -259,6 +241,9 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
|
||||
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
|
||||
if dest.s == OnStackShadowDup:
|
||||
linefmt(p, cpsStmts, "#genericAssignDup((void*)&$1, (void*)$2, $3);$n",
|
||||
dupLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
|
||||
|
||||
proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# This function replaces all other methods for generating
|
||||
@@ -277,12 +262,17 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
|
||||
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
|
||||
if dest.s == OnStackShadowDup:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc)
|
||||
of tyString:
|
||||
if needToCopy notin flags and src.s != OnStatic:
|
||||
genRefAssign(p, dest, src, flags)
|
||||
else:
|
||||
if dest.s == OnStack or not usesNativeGC():
|
||||
linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
|
||||
elif dest.s == OnStackShadowDup:
|
||||
linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc)
|
||||
elif dest.s == OnHeap:
|
||||
# we use a temporary to care for the dreaded self assignment:
|
||||
var tmp: TLoc
|
||||
@@ -293,6 +283,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
|
||||
addrLoc(dest), rdLoc(src))
|
||||
if preciseStack():
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc)
|
||||
of tyProc:
|
||||
if needsComplexAssignment(dest.t):
|
||||
# optimize closure assignment:
|
||||
@@ -1170,6 +1162,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
var t = e.typ.skipTypes(abstractInst)
|
||||
getTemp(p, t, tmp)
|
||||
let isRef = t.kind == tyRef
|
||||
let stck = stackPlacement(t)
|
||||
var r = rdLoc(tmp)
|
||||
if isRef:
|
||||
rawGenNew(p, tmp, nil)
|
||||
@@ -1193,7 +1186,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
add(tmp2.r, field.loc.r)
|
||||
tmp2.k = locTemp
|
||||
tmp2.t = field.loc.t
|
||||
tmp2.s = if isRef: OnHeap else: OnStack
|
||||
tmp2.s = if isRef: OnHeap else: stck
|
||||
expr(p, it.sons[1], tmp2)
|
||||
|
||||
if d.k == locNone:
|
||||
|
||||
@@ -803,6 +803,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
|
||||
if optStackTrace in p.options:
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
|
||||
if p.gcFrameLen > 0:
|
||||
linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n")
|
||||
inc p.inExceptBlock
|
||||
var i = 1
|
||||
var catchAllPresent = false
|
||||
@@ -911,6 +913,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
||||
if optStackTrace in p.options:
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
|
||||
if p.gcFrameLen > 0:
|
||||
linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n")
|
||||
|
||||
inc p.inExceptBlock
|
||||
var i = 1
|
||||
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
||||
|
||||
@@ -282,7 +282,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
|
||||
|
||||
proc fillResult(param: PSym) =
|
||||
fillLoc(param.loc, locParam, param.typ, ~"Result",
|
||||
OnStack)
|
||||
stackPlacement(param.typ))
|
||||
if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ):
|
||||
incl(param.loc.flags, lfIndirect)
|
||||
param.loc.s = OnUnknown
|
||||
@@ -376,9 +376,9 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
|
||||
result = getTypeDescAux(m, t, check)
|
||||
|
||||
proc paramStorageLoc(param: PSym): TStorageLoc =
|
||||
if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {
|
||||
tyArray, tyOpenArray, tyVarargs}:
|
||||
result = OnStack
|
||||
let t = param.typ.skipTypes({tyVar, tyTypeDesc})
|
||||
if t.kind notin {tyArray, tyOpenArray, tyVarargs}:
|
||||
result = stackPlacement(t)
|
||||
else:
|
||||
result = OnUnknown
|
||||
|
||||
|
||||
@@ -244,6 +244,10 @@ proc addrLoc(a: TLoc): Rope =
|
||||
if lfIndirect notin a.flags and mapType(a.t) != ctArray:
|
||||
result = "(&" & result & ")"
|
||||
|
||||
proc dupLoc(a: TLoc): Rope =
|
||||
result = a.dup
|
||||
assert result != nil
|
||||
|
||||
proc rdCharLoc(a: TLoc): Rope =
|
||||
# read a location that may need a char-cast:
|
||||
result = rdLoc(a)
|
||||
@@ -288,12 +292,13 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
if not isComplexValueType(typ):
|
||||
if containsGcRef:
|
||||
var nilLoc: TLoc
|
||||
initLoc(nilLoc, locTemp, loc.t, OnStack)
|
||||
initLoc(nilLoc, locTemp, loc.t, stackPlacement(typ))
|
||||
nilLoc.r = rope("NIM_NIL")
|
||||
genRefAssign(p, loc, nilLoc, {afSrcIsNil})
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
|
||||
else:
|
||||
XXX use stackPlacement here?
|
||||
if optNilCheck in p.options:
|
||||
linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
|
||||
if loc.s != OnStack:
|
||||
@@ -343,17 +348,21 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
|
||||
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
|
||||
result.k = locTemp
|
||||
result.t = t
|
||||
result.s = OnStack
|
||||
result.s = stackPlacement(t)
|
||||
result.flags = {}
|
||||
constructLoc(p, result, not needsInit)
|
||||
|
||||
proc initGCFrame(p: BProc): Rope =
|
||||
if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType]
|
||||
if p.gcFrameLen > 0:
|
||||
result = ropegc(p.module, """
|
||||
struct {#GcFrameBase b_; $1} GCF_;$n
|
||||
GCF_.b_.L=$2;$n
|
||||
#pushGcFrame((GcFrameBase*)&GCF_);$n""" % [
|
||||
p.gcFrameType, rope(p.gcFrameLen)])
|
||||
|
||||
proc deinitGCFrame(p: BProc): Rope =
|
||||
if p.gcFrameId > 0:
|
||||
result = ropecg(p.module,
|
||||
"if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n")
|
||||
if p.gcFrameLen > 0:
|
||||
result = ropecg(p.module, "#popGcFrame();$n")
|
||||
|
||||
proc localDebugInfo(p: BProc, s: PSym) =
|
||||
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
|
||||
@@ -370,7 +379,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
|
||||
|
||||
proc localVarDecl(p: BProc; s: PSym): Rope =
|
||||
if s.loc.k == locNone:
|
||||
fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), OnStack)
|
||||
fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), stackPlacement(s.typ))
|
||||
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
|
||||
result = getTypeDesc(p.module, s.typ)
|
||||
if s.constraint.isNil:
|
||||
|
||||
@@ -90,7 +90,7 @@ type
|
||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||
# requires 'T x = T()' to become 'T x; x = T()'
|
||||
# (yes, C++ is weird like that)
|
||||
gcFrameId*: Natural # for the GC stack marking
|
||||
gcFrameLen*: int # the number of slots in the GC-Frame
|
||||
gcFrameType*: Rope # the struct {} we put the GC markers into
|
||||
sigConflicts*: CountTable[string]
|
||||
|
||||
|
||||
@@ -140,12 +140,13 @@ var
|
||||
gEvalExpr* = "" # expression for idetools --eval
|
||||
gLastCmdTime*: float # when caas is enabled, we measure each command
|
||||
gListFullPaths*: bool
|
||||
isServing*: bool = false
|
||||
gPreciseStack*: bool = false
|
||||
gNoNimblePath* = false
|
||||
gExperimentalMode*: bool
|
||||
|
||||
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
|
||||
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
|
||||
template preciseStack*(): bool = gPreciseStack
|
||||
|
||||
template compilationCachePresent*: untyped =
|
||||
{optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
|
||||
|
||||
@@ -38,20 +38,29 @@ proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
|
||||
proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
|
||||
proc chckNil(p: pointer) {.noinline, compilerproc, benign.}
|
||||
|
||||
type
|
||||
GcFrame = ptr GcFrameHeader
|
||||
GcFrameHeader {.compilerproc.} = object
|
||||
len: int
|
||||
prev: ptr GcFrameHeader
|
||||
|
||||
var
|
||||
framePtr {.threadvar.}: PFrame
|
||||
excHandler {.threadvar.}: PSafePoint
|
||||
# list of exception handlers
|
||||
# a global variable for the root of all try blocks
|
||||
currException {.threadvar.}: ref Exception
|
||||
gcFramePtr {.threadvar.}: GcFrame
|
||||
|
||||
type
|
||||
FrameState = tuple[framePtr: PFrame, excHandler: PSafePoint, currException: ref Exception]
|
||||
FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
|
||||
excHandler: PSafePoint, currException: ref Exception]
|
||||
|
||||
proc getFrameState*(): FrameState {.compilerRtl, inl.} =
|
||||
return (framePtr, excHandler, currException)
|
||||
return (gcFramePtr, framePtr, excHandler, currException)
|
||||
|
||||
proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
|
||||
gcFramePtr = state.gcFramePtr
|
||||
framePtr = state.framePtr
|
||||
excHandler = state.excHandler
|
||||
currException = state.currException
|
||||
@@ -64,6 +73,14 @@ proc popFrame {.compilerRtl, inl.} =
|
||||
proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
|
||||
framePtr = s
|
||||
|
||||
proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
|
||||
proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
|
||||
proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
|
||||
proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
|
||||
s.prev = gcFramePtr
|
||||
zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
|
||||
gcFramePtr = s
|
||||
|
||||
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
|
||||
s.hasRaiseAction = false
|
||||
s.prev = excHandler
|
||||
|
||||
Reference in New Issue
Block a user