summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim1
-rw-r--r--compiler/ccgexprs.nim24
-rw-r--r--compiler/ccgtypes.nim4
-rw-r--r--compiler/cgen.nim1
-rw-r--r--compiler/cgendata.nim1
-rw-r--r--lib/system/chcks.nim22
-rw-r--r--todo.txt1
7 files changed, 49 insertions, 5 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 80b9e9bb2..d74818e1f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -510,6 +510,7 @@ const
   tfUncheckedArray* = tfVarargs
   tfUnion* = tfNoSideEffect
   tfGcSafe* = tfThread
+  tfObjHasKids* = tfEnumHasHoles
   skError* = skUnknown
   
   # type flags that are essential for type equality:
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 39333a80d..13b3091fc 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1144,6 +1144,24 @@ proc genNewFinalize(p: BProc, e: PNode) =
   genObjectInit(p, cpsStmts, bt, a, false)
   gcUsage(e)
 
+proc genOfHelper(p: BProc; dest: PType; a: PRope): PRope =
+  # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
+  # have to call it here first:
+  let ti = genTypeInfo(p.module, dest)
+  if tfFinal in dest.flags or (p.module.objHasKidsValid and
+                               tfObjHasKids notin dest.flags):
+    result = ropef("$1.m_type == $2", a, ti)
+  else:
+    discard cgsym(p.module, "TNimType")
+    inc p.module.labels
+    let cache = con("Nim_OfCheck_CACHE", p.module.labels.toRope)
+    appf(p.module.s[cfsVars], "static TNimType* $#[2];$n", cache)
+    result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
+  when false:
+    # former version:
+    result = rfmt(p.module, "#isObj($1.m_type, $2)",
+                  a, genTypeInfo(p.module, dest))
+
 proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, x, a)
@@ -1163,11 +1181,9 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
     globalError(x.info, errGenerated, 
       "no 'of' operator available for pure objects")
   if nilCheck != nil:
-    r = rfmt(p.module, "(($1) && #isObj($2.m_type, $3))",
-             nilCheck, r, genTypeInfo(p.module, dest))
+    r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
   else:
-    r = rfmt(p.module, "#isObj($1.m_type, $2)",
-             r, genTypeInfo(p.module, dest))
+    r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
   putIntoDest(p, d, getSysType(tyBool), r)
 
 proc genOf(p: BProc, n: PNode, d: var TLoc) =
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 7a490082f..28c791e42 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -796,6 +796,10 @@ proc genObjectInfo(m: BModule, typ: PType, name: PRope) =
   var tmp = getNimNode(m)
   genObjectFields(m, typ, typ.n, tmp)
   appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
+  var t = typ.sons[0]
+  while t != nil:
+    t.skipTypes(abstractInst).flags.incl tfObjHasKids
+    t = t.sons[0]
 
 proc genTupleInfo(m: BModule, typ: PType, name: PRope) =
   genTypeInfoAuxBase(m, typ, name, toRope("0"))
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 198b1187d..7094990f7 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1383,6 +1383,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
   registerModuleToMain(m.module)
 
   if sfMainModule in m.module.flags: 
+    m.objHasKidsValid = true
     var disp = generateMethodDispatchers()
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
     genMainProc(m)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index e7d818556..12041c55b 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -96,6 +96,7 @@ type
                               # a frame var twice in an init proc
     isHeaderFile*: bool       # C source file is the header file
     includesStringh*: bool    # C source file already includes ``<string.h>``
+    objHasKidsValid*: bool    # whether we can rely on tfObjHasKids
     cfilename*: string        # filename of the module (including path,
                               # without extension)
     typeCache*: TIdTable      # cache the generated types
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index f29e222e8..387b54ef1 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -67,6 +67,28 @@ proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
   if a != b:
     sysFatal(EInvalidObjectAssignment, "invalid object assignment")
 
+type ObjCheckCache = array[0..1, PNimType]
+
+proc isObjSlowPath(obj, subclass: PNimType;
+                   cache: var ObjCheckCache): bool {.noinline.} =
+  # checks if obj is of type subclass:
+  var x = obj.base
+  while x != subclass:
+    if x == nil:
+      cache[0] = obj
+      return false
+    x = x.base
+  cache[1] = obj
+  return true
+
+proc isObjWithCache(obj, subclass: PNimType;
+                    cache: var ObjCheckCache): bool {.compilerProc, inline.} =
+  if obj == subclass: return true
+  if obj.base == subclass: return true
+  if cache[0] == obj: return false
+  if cache[1] == obj: return true
+  return isObjSlowPath(obj, subclass, cache)
+
 proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
   # checks if obj is of type subclass:
   var x = obj
diff --git a/todo.txt b/todo.txt
index 996067175..539089281 100644
--- a/todo.txt
+++ b/todo.txt
@@ -138,7 +138,6 @@ Not essential for 1.0.0
 - implement the "snoopResult" pragma; no, make a strutils with string append
   semantics instead ...
 - implement "closure tuple consists of a single 'ref'" optimization
-- optimize method dispatchers
 - new feature: ``distinct T with operations``
 - arglist as a type (iterator chaining); variable length type lists for generics
 - implement marker procs for message passing