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
parent 55b5401dc6
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)
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 ------------------------------

View File

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

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]))
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):

View File

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

View File

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

View File

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

View File

@@ -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 != {}

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