summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/ccgexprs.nim61
-rw-r--r--compiler/ccgstmts.nim5
-rw-r--r--compiler/ccgtypes.nim8
-rw-r--r--compiler/cgen.nim23
-rw-r--r--compiler/cgendata.nim2
-rw-r--r--compiler/options.nim3
-rw-r--r--lib/system/excpt.nim21
8 files changed, 86 insertions, 40 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 6e651ed00..ac917346f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -745,6 +745,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]
@@ -754,6 +756,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 ------------------------------
 
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 0ec16710f..e62956a0c 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -134,32 +134,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:
@@ -186,6 +166,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:
@@ -243,7 +225,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
@@ -261,6 +243,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
@@ -279,12 +264,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
@@ -295,6 +285,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:
@@ -743,6 +735,9 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   else: internalError(e.info, "genTupleElem")
   addf(r, ".Field$1", [rope(i)])
   putIntoDest(p, d, tupType.sons[i], r, a.s)
+  if a.s == OnStackShadowDup:
+    d.s = OnStackShadowDup
+    d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(tupType, i))
 
 proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym =
   var ty = ty
@@ -767,12 +762,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
     # so we use Field$i
     addf(r, ".Field$1", [rope(f.position)])
     putIntoDest(p, d, f.typ, r, a.s)
+    if a.s == OnStackShadowDup:
+      d.s = OnStackShadowDup
+      d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(ty, f.position))
   else:
     let field = lookupFieldAgain(p, ty, f, r)
     if field.loc.r == nil: fillObjectFields(p.module, ty)
     if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty))
     addf(r, ".$1", [field.loc.r])
     putIntoDest(p, d, field.typ, r, a.s)
+    if a.s == OnStackShadowDup and field.loc.dup != nil:
+      d.s = OnStackShadowDup
+      d.dup = ropef("$1.$2", a.dup, field.loc.dup)
   #d.s = a.s
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
@@ -824,6 +825,9 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
     genFieldCheck(p, e, r, field, ty)
     add(r, rfmt(nil, ".$1", field.loc.r))
     putIntoDest(p, d, field.typ, r, a.s)
+    if a.s == OnStackShadowDup and field.loc.dup != nil:
+      d.s = OnStackShadowDup
+      d.dup = ropef("$1.$2", a.dup, field.loc.dup)
   else:
     genRecordField(p, e.sons[0], d)
 
@@ -851,6 +855,9 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
   d.inheritLocation(a)
   putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
               rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s)
+  if a.s == OnStackShadowDup:
+    d.s = OnStackShadowDup
+    d.dup = ropef("$1[($2)- $3]", a.dup, rdCharLoc(b), first)
 
 proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -871,6 +878,9 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
   if d.k == locNone: d.s = a.s
   putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
               rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
+  if a.s == OnStackShadowDup:
+    d.s = OnStackShadowDup
+    d.dup = ropef("$1[$2]", a.dup, rdCharLoc(b))
 
 proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -1175,6 +1185,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)
@@ -1198,7 +1209,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:
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 378951d9d..27bb89f60 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -802,6 +802,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
@@ -910,6 +912,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):
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 0c81ca814..1c14d95f1 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -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
 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 3797a92c2..d9e771ce7 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -242,6 +242,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)
@@ -286,12 +290,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:
@@ -341,17 +346,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
@@ -368,7 +377,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:
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index be087095f..2f927c83e 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -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]
 
diff --git a/compiler/options.nim b/compiler/options.nim
index 5c39faf3b..bf1b04c2c 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -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 != {}
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index e35b0bd5d..6c163f711 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -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