Skip to content

Commit

Permalink
implements display based subtype checking (6.4x faster without thread…
Browse files Browse the repository at this point in the history
…s; 2.8x faster with threads) (#20781)

* WIP: fast 'of' operator based on the literature
* implement  display based subtype checking

Co-authored-by: Araq <rumpf_a@web.de>
  • Loading branch information
ringabout and Araq committed Nov 8, 2022
1 parent ca3b6cb commit 7d15fdd
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 53 deletions.
38 changes: 19 additions & 19 deletions compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1704,11 +1704,8 @@ proc genNewFinalize(p: BProc, e: PNode) =

proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Rope) =
if optTinyRtti in p.config.globalOptions:
let ti = genTypeInfo2Name(p.module, dest)
inc p.module.labels
let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
p.module.s[cfsVars].addf("static TNimTypeV2* $#[2];$n", [cache])
appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache])
let token = $genDisplayElem(MD5Digest(hashType(dest)))
appcg(p.module, result, "#isObjDisplayCheck($#.m_type, $#, $#)", [a, getObjDepth(dest), token])
else:
# unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we
# have to call it here first:
Expand All @@ -1722,10 +1719,6 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Ro
let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache])
appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache])
when false:
# former version:
appcg(p.module, result, "#isObj($1.m_type, $2)",
[a, genTypeInfoV1(p.module, dest, info)])

proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
var a: TLoc
Expand Down Expand Up @@ -2775,16 +2768,23 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
var nilCheck = ""
var r = newRopeAppender()
rdMType(p, a, nilCheck, r)
let checkFor = if optTinyRtti in p.config.globalOptions:
genTypeInfo2Name(p.module, dest)
else:
genTypeInfoV1(p.module, dest, n.info)
if nilCheck != "":
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ",
[nilCheck, r, checkFor])
else:
linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ",
[r, checkFor])
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(dest)
let token = $genDisplayElem(MD5Digest(hashType(dest)))
if nilCheck != "":
linefmt(p, cpsStmts, "if ($1 && !#isObjDisplayCheck($2, $3, $4)){ #raiseObjectConversionError(); ",
[nilCheck, r, checkFor, token])
else:
linefmt(p, cpsStmts, "if (!#isObjDisplayCheck($1, $2, $3)){ #raiseObjectConversionError(); ",
[r, checkFor, token])
else:
let checkFor = genTypeInfoV1(p.module, dest, n.info)
if nilCheck != "":
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ",
[nilCheck, r, checkFor])
else:
linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ",
[r, checkFor])
raiseInstr(p, p.s(cpsStmts))
linefmt p, cpsStmts, "}$n", []

Expand Down
33 changes: 18 additions & 15 deletions compiler/ccgstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1079,12 +1079,13 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
hasImportedCppExceptions = true
else:
if orExpr.len != 0: orExpr.add("||")
let checkFor = if optTinyRtti in p.config.globalOptions:
genTypeInfo2Name(p.module, typeNode.typ)
else:
genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(typeNode.typ)
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ)))])
else:
let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])

if orExpr.len != 0:
if hasIf:
Expand Down Expand Up @@ -1297,12 +1298,13 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
if orExpr.len != 0: orExpr.add("||")
let checkFor = if optTinyRtti in p.config.globalOptions:
genTypeInfo2Name(p.module, t[i][j].typ)
else:
genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(t[i][j].typ)
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ)))])
else:
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])

if i > 1: line(p, cpsStmts, "else ")
startBlock(p, "if ($1) {$n", [orExpr])
Expand Down Expand Up @@ -1441,12 +1443,13 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
if orExpr.len != 0: orExpr.add("||")
let checkFor = if optTinyRtti in p.config.globalOptions:
genTypeInfo2Name(p.module, t[i][j].typ)
else:
genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(t[i][j].typ)
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ)))])
else:
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])

if i > 1: line(p, cpsStmts, "else ")
startBlock(p, "if ($1) {$n", [orExpr])
Expand Down
45 changes: 42 additions & 3 deletions compiler/ccgtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# ------------------------- Name Mangling --------------------------------

import sighashes, modulegraphs
import std/md5

proc isKeyword(w: PIdent): bool =
# Nim and C++ share some keywords
Expand Down Expand Up @@ -1321,7 +1322,37 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
internalError(m.config, info, "no attached trace proc found")
result.add rope("NIM_NIL")

proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
proc getObjDepth(t: PType): int16 =
var x = t
result = -1
while x != nil:
x = skipTypes(x, skipPtrs)
x = x[0]
inc(result)

proc genDisplayElem(d: MD5Digest): uint32 =
result = 0
for i in 0..3:
result += uint32(d[i])
result = result shl 8

proc genDisplay(t: PType, depth: int): Rope =
result = Rope"{"
var x = t
var seqs = newSeq[string](depth+1)
var i = 0
while x != nil:
x = skipTypes(x, skipPtrs)
seqs[i] = $genDisplayElem(MD5Digest(hashType(x)))
x = x[0]
inc i

for i in countdown(depth, 1):
result.add seqs[i] & ", "
result.add seqs[0]
result.add "}"

proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
var typeName: Rope
if t.kind in {tyObject, tyDistinct}:
if incompleteType(t):
Expand All @@ -1344,8 +1375,16 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn
addf(typeEntry, "; $1.traceImpl = (void*)", [name])
genHook(m, t, info, attachedTrace, typeEntry)

addf(typeEntry, "; $1.name = $2;$n; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.flags = $4;",
[name, typeName, getTypeDesc(m, t), rope(flags)])
let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1

addf(typeEntry, "; $1.name = $2;$n; $1.size = sizeof($3); $1.align = (NI16) NIM_ALIGNOF($3); $1.depth = $4; $1.flags = $5;",
[name, typeName, getTypeDesc(m, t), rope(objDepth), rope(flags)])

if objDepth >= 0:
let objDisplay = genDisplay(t, objDepth)
let objDisplayStore = getTempName(m)
m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay])
addf(typeEntry, "; $1.display = $2;$n", [name, rope(objDisplayStore)])

m.s[cfsTypeInit3].add typeEntry

Expand Down
4 changes: 3 additions & 1 deletion lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1592,7 +1592,9 @@ when not defined(js) and defined(nimV2):
TNimTypeV2 {.compilerproc.} = object
destructor: pointer
size: int
align: int
align: int16
depth: int16
display: ptr UncheckedArray[uint32] # classToken
name: cstring
traceImpl: pointer
typeInfoV1: pointer # for backwards compat, usually nil
Expand Down
17 changes: 2 additions & 15 deletions lib/system/arc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,6 @@ template tearDownForeignThreadGc* =
## With `--gc:arc` a nop.
discard

type ObjCheckCache = array[0..1, PNimTypeV2]

proc memcmp(str1, str2: cstring, n: csize_t): cint {.importc, header: "<string.h>".}

func endsWith(s, suffix: cstring): bool {.inline.} =
Expand All @@ -242,19 +240,8 @@ func endsWith(s, suffix: cstring): bool {.inline.} =
proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} =
result = endsWith(obj.name, subclass)

proc isObjSlowPath(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl, inline.} =
if endsWith(obj.name, subclass):
cache[1] = obj
result = true
else:
cache[0] = obj
result = false

proc isObjWithCache(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl.} =
if cache[0] == obj: result = false
elif cache[1] == obj: result = true
else:
result = isObjSlowPath(obj, subclass, cache)
proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inline.} =
result = targetDepth <= source.depth and source.display[targetDepth] == token

proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} =
# checks if obj is of type subclass:
Expand Down

0 comments on commit 7d15fdd

Please sign in to comment.