implements display based subtype checking (6.4x faster without threads; 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>
This commit is contained in:
parent
ca3b6cba5d
commit
7d15fdd375
5 changed files with 83 additions and 52 deletions
|
|
@ -1704,11 +1704,8 @@ proc genNewFinalize(p: BProc, e: PNode) =
|
||||||
|
|
||||||
proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Rope) =
|
proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Rope) =
|
||||||
if optTinyRtti in p.config.globalOptions:
|
if optTinyRtti in p.config.globalOptions:
|
||||||
let ti = genTypeInfo2Name(p.module, dest)
|
let token = $genDisplayElem(MD5Digest(hashType(dest)))
|
||||||
inc p.module.labels
|
appcg(p.module, result, "#isObjDisplayCheck($#.m_type, $#, $#)", [a, getObjDepth(dest), token])
|
||||||
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])
|
|
||||||
else:
|
else:
|
||||||
# unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we
|
# unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we
|
||||||
# have to call it here first:
|
# have to call it here first:
|
||||||
|
|
@ -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
|
let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
|
||||||
p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache])
|
p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache])
|
||||||
appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, 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) =
|
proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
|
|
@ -2775,10 +2768,17 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||||
var nilCheck = ""
|
var nilCheck = ""
|
||||||
var r = newRopeAppender()
|
var r = newRopeAppender()
|
||||||
rdMType(p, a, nilCheck, r)
|
rdMType(p, a, nilCheck, r)
|
||||||
let checkFor = if optTinyRtti in p.config.globalOptions:
|
if optTinyRtti in p.config.globalOptions:
|
||||||
genTypeInfo2Name(p.module, dest)
|
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:
|
else:
|
||||||
genTypeInfoV1(p.module, dest, n.info)
|
linefmt(p, cpsStmts, "if (!#isObjDisplayCheck($1, $2, $3)){ #raiseObjectConversionError(); ",
|
||||||
|
[r, checkFor, token])
|
||||||
|
else:
|
||||||
|
let checkFor = genTypeInfoV1(p.module, dest, n.info)
|
||||||
if nilCheck != "":
|
if nilCheck != "":
|
||||||
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ",
|
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ",
|
||||||
[nilCheck, r, checkFor])
|
[nilCheck, r, checkFor])
|
||||||
|
|
|
||||||
|
|
@ -1079,11 +1079,12 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
hasImportedCppExceptions = true
|
hasImportedCppExceptions = true
|
||||||
else:
|
else:
|
||||||
if orExpr.len != 0: orExpr.add("||")
|
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"
|
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||||
|
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])
|
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||||
|
|
||||||
if orExpr.len != 0:
|
if orExpr.len != 0:
|
||||||
|
|
@ -1297,11 +1298,12 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
for j in 0..<t[i].len - 1:
|
for j in 0..<t[i].len - 1:
|
||||||
assert(t[i][j].kind == nkType)
|
assert(t[i][j].kind == nkType)
|
||||||
if orExpr.len != 0: orExpr.add("||")
|
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"
|
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||||
|
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])
|
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||||
|
|
||||||
if i > 1: line(p, cpsStmts, "else ")
|
if i > 1: line(p, cpsStmts, "else ")
|
||||||
|
|
@ -1441,11 +1443,12 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
for j in 0..<t[i].len - 1:
|
for j in 0..<t[i].len - 1:
|
||||||
assert(t[i][j].kind == nkType)
|
assert(t[i][j].kind == nkType)
|
||||||
if orExpr.len != 0: orExpr.add("||")
|
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"
|
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||||
|
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])
|
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||||
|
|
||||||
if i > 1: line(p, cpsStmts, "else ")
|
if i > 1: line(p, cpsStmts, "else ")
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
# ------------------------- Name Mangling --------------------------------
|
# ------------------------- Name Mangling --------------------------------
|
||||||
|
|
||||||
import sighashes, modulegraphs
|
import sighashes, modulegraphs
|
||||||
|
import std/md5
|
||||||
|
|
||||||
proc isKeyword(w: PIdent): bool =
|
proc isKeyword(w: PIdent): bool =
|
||||||
# Nim and C++ share some keywords
|
# Nim and C++ share some keywords
|
||||||
|
|
@ -1321,7 +1322,37 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
|
||||||
internalError(m.config, info, "no attached trace proc found")
|
internalError(m.config, info, "no attached trace proc found")
|
||||||
result.add rope("NIM_NIL")
|
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
|
var typeName: Rope
|
||||||
if t.kind in {tyObject, tyDistinct}:
|
if t.kind in {tyObject, tyDistinct}:
|
||||||
if incompleteType(t):
|
if incompleteType(t):
|
||||||
|
|
@ -1344,8 +1375,16 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn
|
||||||
addf(typeEntry, "; $1.traceImpl = (void*)", [name])
|
addf(typeEntry, "; $1.traceImpl = (void*)", [name])
|
||||||
genHook(m, t, info, attachedTrace, typeEntry)
|
genHook(m, t, info, attachedTrace, typeEntry)
|
||||||
|
|
||||||
addf(typeEntry, "; $1.name = $2;$n; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.flags = $4;",
|
let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
|
||||||
[name, typeName, getTypeDesc(m, t), rope(flags)])
|
|
||||||
|
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
|
m.s[cfsTypeInit3].add typeEntry
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1592,7 +1592,9 @@ when not defined(js) and defined(nimV2):
|
||||||
TNimTypeV2 {.compilerproc.} = object
|
TNimTypeV2 {.compilerproc.} = object
|
||||||
destructor: pointer
|
destructor: pointer
|
||||||
size: int
|
size: int
|
||||||
align: int
|
align: int16
|
||||||
|
depth: int16
|
||||||
|
display: ptr UncheckedArray[uint32] # classToken
|
||||||
name: cstring
|
name: cstring
|
||||||
traceImpl: pointer
|
traceImpl: pointer
|
||||||
typeInfoV1: pointer # for backwards compat, usually nil
|
typeInfoV1: pointer # for backwards compat, usually nil
|
||||||
|
|
|
||||||
|
|
@ -227,8 +227,6 @@ template tearDownForeignThreadGc* =
|
||||||
## With `--gc:arc` a nop.
|
## With `--gc:arc` a nop.
|
||||||
discard
|
discard
|
||||||
|
|
||||||
type ObjCheckCache = array[0..1, PNimTypeV2]
|
|
||||||
|
|
||||||
proc memcmp(str1, str2: cstring, n: csize_t): cint {.importc, header: "<string.h>".}
|
proc memcmp(str1, str2: cstring, n: csize_t): cint {.importc, header: "<string.h>".}
|
||||||
|
|
||||||
func endsWith(s, suffix: cstring): bool {.inline.} =
|
func endsWith(s, suffix: cstring): bool {.inline.} =
|
||||||
|
|
@ -242,19 +240,8 @@ func endsWith(s, suffix: cstring): bool {.inline.} =
|
||||||
proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} =
|
proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} =
|
||||||
result = endsWith(obj.name, subclass)
|
result = endsWith(obj.name, subclass)
|
||||||
|
|
||||||
proc isObjSlowPath(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl, inline.} =
|
proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inline.} =
|
||||||
if endsWith(obj.name, subclass):
|
result = targetDepth <= source.depth and source.display[targetDepth] == token
|
||||||
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 chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} =
|
proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} =
|
||||||
# checks if obj is of type subclass:
|
# checks if obj is of type subclass:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue