precise stack scanning for Nim's GCs; work in progress

This commit is contained in:
Andreas Rumpf 2017-04-19 08:33:19 +02:00
commit 02bbfa1164
8 changed files with 68 additions and 40 deletions

View file

@ -739,6 +739,8 @@ type
OnUnknown, # location is unknown (stack, heap or static) OnUnknown, # location is unknown (stack, heap or static)
OnStatic, # in a static section OnStatic, # in a static section
OnStack, # location is on hardware stack 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 OnHeap # location is on heap or global
# (reference counting needed) # (reference counting needed)
TLocFlags* = set[TLocFlag] TLocFlags* = set[TLocFlag]
@ -748,6 +750,7 @@ type
flags*: TLocFlags # location's flags flags*: TLocFlags # location's flags
t*: PType # type of location t*: PType # type of location
r*: Rope # rope value of location (code generators) r*: Rope # rope value of location (code generators)
dup*: Rope # duplicated location for precise stack scans
# ---------------- end of backend information ------------------------------ # ---------------- end of backend information ------------------------------

View file

@ -132,32 +132,12 @@ proc genSetNode(p: BProc, n: PNode): Rope =
else: else:
result = genRawSetData(cs, size) 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) = proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
if dest.s == OnStack or not usesNativeGC(): if dest.s == OnStack or not usesNativeGC():
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) 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: elif dest.s == OnHeap:
# location is on heap # location is on heap
# now the writer barrier is inlined for performance: # now the writer barrier is inlined for performance:
@ -184,6 +164,8 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
addrLoc(dest), rdLoc(src)) addrLoc(dest), rdLoc(src))
if preciseStack():
linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src))
proc asgnComplexity(n: PNode): int = proc asgnComplexity(n: PNode): int =
if n != nil: 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) = proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# Consider: # Consider:
# type TMyFastString {.shallow.} = string # type MyFastString {.shallow.} = string
# Due to the implementation of pragmas this would end up to set the # 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 # 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 # 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: else:
linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) 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) = proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# This function replaces all other methods for generating # This function replaces all other methods for generating
@ -277,12 +262,17 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) 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: of tyString:
if needToCopy notin flags and src.s != OnStatic: if needToCopy notin flags and src.s != OnStatic:
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
else: else:
if dest.s == OnStack or not usesNativeGC(): if dest.s == OnStack or not usesNativeGC():
linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) 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: elif dest.s == OnHeap:
# we use a temporary to care for the dreaded self assignment: # we use a temporary to care for the dreaded self assignment:
var tmp: TLoc var tmp: TLoc
@ -293,6 +283,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
addrLoc(dest), rdLoc(src)) addrLoc(dest), rdLoc(src))
if preciseStack():
linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc)
of tyProc: of tyProc:
if needsComplexAssignment(dest.t): if needsComplexAssignment(dest.t):
# optimize closure assignment: # optimize closure assignment:
@ -1170,6 +1162,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
var t = e.typ.skipTypes(abstractInst) var t = e.typ.skipTypes(abstractInst)
getTemp(p, t, tmp) getTemp(p, t, tmp)
let isRef = t.kind == tyRef let isRef = t.kind == tyRef
let stck = stackPlacement(t)
var r = rdLoc(tmp) var r = rdLoc(tmp)
if isRef: if isRef:
rawGenNew(p, tmp, nil) rawGenNew(p, tmp, nil)
@ -1193,7 +1186,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
add(tmp2.r, field.loc.r) add(tmp2.r, field.loc.r)
tmp2.k = locTemp tmp2.k = locTemp
tmp2.t = field.loc.t 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) expr(p, it.sons[1], tmp2)
if d.k == locNone: if d.k == locNone:

View file

@ -803,6 +803,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
if optStackTrace in p.options: if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
if p.gcFrameLen > 0:
linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n")
inc p.inExceptBlock inc p.inExceptBlock
var i = 1 var i = 1
var catchAllPresent = false var catchAllPresent = false
@ -911,6 +913,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "#popSafePoint();$n") linefmt(p, cpsStmts, "#popSafePoint();$n")
if optStackTrace in p.options: if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
if p.gcFrameLen > 0:
linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n")
inc p.inExceptBlock inc p.inExceptBlock
var i = 1 var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):

View file

@ -282,7 +282,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
proc fillResult(param: PSym) = proc fillResult(param: PSym) =
fillLoc(param.loc, locParam, param.typ, ~"Result", fillLoc(param.loc, locParam, param.typ, ~"Result",
OnStack) stackPlacement(param.typ))
if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ): if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ):
incl(param.loc.flags, lfIndirect) incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown param.loc.s = OnUnknown
@ -376,9 +376,9 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
result = getTypeDescAux(m, t, check) result = getTypeDescAux(m, t, check)
proc paramStorageLoc(param: PSym): TStorageLoc = proc paramStorageLoc(param: PSym): TStorageLoc =
if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin { let t = param.typ.skipTypes({tyVar, tyTypeDesc})
tyArray, tyOpenArray, tyVarargs}: if t.kind notin {tyArray, tyOpenArray, tyVarargs}:
result = OnStack result = stackPlacement(t)
else: else:
result = OnUnknown result = OnUnknown

View file

@ -244,6 +244,10 @@ proc addrLoc(a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(a.t) != ctArray: if lfIndirect notin a.flags and mapType(a.t) != ctArray:
result = "(&" & result & ")" result = "(&" & result & ")"
proc dupLoc(a: TLoc): Rope =
result = a.dup
assert result != nil
proc rdCharLoc(a: TLoc): Rope = proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast: # read a location that may need a char-cast:
result = rdLoc(a) result = rdLoc(a)
@ -288,12 +292,13 @@ proc resetLoc(p: BProc, loc: var TLoc) =
if not isComplexValueType(typ): if not isComplexValueType(typ):
if containsGcRef: if containsGcRef:
var nilLoc: TLoc var nilLoc: TLoc
initLoc(nilLoc, locTemp, loc.t, OnStack) initLoc(nilLoc, locTemp, loc.t, stackPlacement(typ))
nilLoc.r = rope("NIM_NIL") nilLoc.r = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc, {afSrcIsNil}) genRefAssign(p, loc, nilLoc, {afSrcIsNil})
else: else:
linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
else: else:
XXX use stackPlacement here?
if optNilCheck in p.options: if optNilCheck in p.options:
linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
if loc.s != OnStack: 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) linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
result.k = locTemp result.k = locTemp
result.t = t result.t = t
result.s = OnStack result.s = stackPlacement(t)
result.flags = {} result.flags = {}
constructLoc(p, result, not needsInit) constructLoc(p, result, not needsInit)
proc initGCFrame(p: BProc): Rope = 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 = proc deinitGCFrame(p: BProc): Rope =
if p.gcFrameId > 0: if p.gcFrameLen > 0:
result = ropecg(p.module, result = ropecg(p.module, "#popGcFrame();$n")
"if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n")
proc localDebugInfo(p: BProc, s: PSym) = proc localDebugInfo(p: BProc, s: PSym) =
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return 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 = proc localVarDecl(p: BProc; s: PSym): Rope =
if s.loc.k == locNone: 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) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
result = getTypeDesc(p.module, s.typ) result = getTypeDesc(p.module, s.typ)
if s.constraint.isNil: if s.constraint.isNil:

View file

@ -90,7 +90,7 @@ type
splitDecls*: int # > 0 if we are in some context for C++ that splitDecls*: int # > 0 if we are in some context for C++ that
# requires 'T x = T()' to become 'T x; x = T()' # requires 'T x = T()' to become 'T x; x = T()'
# (yes, C++ is weird like that) # (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 gcFrameType*: Rope # the struct {} we put the GC markers into
sigConflicts*: CountTable[string] sigConflicts*: CountTable[string]

View file

@ -140,12 +140,13 @@ var
gEvalExpr* = "" # expression for idetools --eval gEvalExpr* = "" # expression for idetools --eval
gLastCmdTime*: float # when caas is enabled, we measure each command gLastCmdTime*: float # when caas is enabled, we measure each command
gListFullPaths*: bool gListFullPaths*: bool
isServing*: bool = false gPreciseStack*: bool = false
gNoNimblePath* = false gNoNimblePath* = false
gExperimentalMode*: bool gExperimentalMode*: bool
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
template preciseStack*(): bool = gPreciseStack
template compilationCachePresent*: untyped = template compilationCachePresent*: untyped =
{optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}

View file

@ -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 chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
proc chckNil(p: pointer) {.noinline, compilerproc, benign.} proc chckNil(p: pointer) {.noinline, compilerproc, benign.}
type
GcFrame = ptr GcFrameHeader
GcFrameHeader {.compilerproc.} = object
len: int
prev: ptr GcFrameHeader
var var
framePtr {.threadvar.}: PFrame framePtr {.threadvar.}: PFrame
excHandler {.threadvar.}: PSafePoint excHandler {.threadvar.}: PSafePoint
# list of exception handlers # list of exception handlers
# a global variable for the root of all try blocks # a global variable for the root of all try blocks
currException {.threadvar.}: ref Exception currException {.threadvar.}: ref Exception
gcFramePtr {.threadvar.}: GcFrame
type 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.} = proc getFrameState*(): FrameState {.compilerRtl, inl.} =
return (framePtr, excHandler, currException) return (gcFramePtr, framePtr, excHandler, currException)
proc setFrameState*(state: FrameState) {.compilerRtl, inl.} = proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
gcFramePtr = state.gcFramePtr
framePtr = state.framePtr framePtr = state.framePtr
excHandler = state.excHandler excHandler = state.excHandler
currException = state.currException currException = state.currException
@ -64,6 +73,14 @@ proc popFrame {.compilerRtl, inl.} =
proc setFrame*(s: PFrame) {.compilerRtl, inl.} = proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
framePtr = s 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.} = proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
s.hasRaiseAction = false s.hasRaiseAction = false
s.prev = excHandler s.prev = excHandler