mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-12 22:33:49 +00:00
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:
@@ -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:
|
||||
@@ -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
|
||||
@@ -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])
|
||||
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:
|
||||
linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ",
|
||||
[r, checkFor])
|
||||
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", []
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -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])
|
||||
@@ -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])
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# ------------------------- Name Mangling --------------------------------
|
||||
|
||||
import sighashes, modulegraphs
|
||||
import std/md5
|
||||
|
||||
proc isKeyword(w: PIdent): bool =
|
||||
# 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")
|
||||
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):
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.} =
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user