This commit is contained in:
Araq 2019-04-12 13:05:57 +02:00
commit 572735bbfa
2 changed files with 79 additions and 60 deletions

View file

@ -27,9 +27,11 @@ type
recurse: bool recurse: bool
c: PContext c: PContext
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym {.discardable.} info: TLineInfo): PSym
proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo)
proc at(a, i: PNode, elemType: PType): PNode = proc at(a, i: PNode, elemType: PType): PNode =
result = newNodeI(nkBracketExpr, a.info, 2) result = newNodeI(nkBracketExpr, a.info, 2)
@ -37,10 +39,10 @@ proc at(a, i: PNode, elemType: PType): PNode =
result.sons[1] = i result.sons[1] = i
result.typ = elemType result.typ = elemType
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i in 0 ..< t.len: for i in 0 ..< t.len:
let lit = lowerings.newIntLit(c.graph, x.info, i) let lit = lowerings.newIntLit(c.graph, x.info, i)
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i])) fillBody(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
proc dotField(x: PNode, f: PSym): PNode = proc dotField(x: PNode, f: PSym): PNode =
result = newNodeI(nkDotExpr, x.info, 2) result = newNodeI(nkDotExpr, x.info, 2)
@ -48,11 +50,11 @@ proc dotField(x: PNode, f: PSym): PNode =
result.sons[1] = newSymNode(f, x.info) result.sons[1] = newSymNode(f, x.info)
result.typ = f.typ result.typ = f.typ
proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
case n.kind case n.kind
of nkSym: of nkSym:
let f = n.sym let f = n.sym
liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f)) fillBody(c, f.typ, body, x.dotField(f), y.dotField(f))
of nkNilLit: discard of nkNilLit: discard
of nkRecCase: of nkRecCase:
if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}: if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
@ -60,11 +62,11 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
## or the value is lost ## or the value is lost
let prevKind = c.kind let prevKind = c.kind
c.kind = attachedDestructor c.kind = attachedDestructor
liftBodyObj(c, n, body, x, y) fillBodyObj(c, n, body, x, y)
c.kind = prevKind c.kind = prevKind
# copy the selector: # copy the selector:
liftBodyObj(c, n[0], body, x, y) fillBodyObj(c, n[0], body, x, y)
# we need to generate a case statement: # we need to generate a case statement:
var caseStmt = newNodeI(nkCaseStmt, c.info) var caseStmt = newNodeI(nkCaseStmt, c.info)
# XXX generate 'if' that checks same branches # XXX generate 'if' that checks same branches
@ -78,13 +80,13 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
let L = branch.len let L = branch.len
branch.sons[L-1] = newNodeI(nkStmtList, c.info) branch.sons[L-1] = newNodeI(nkStmtList, c.info)
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y) fillBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
if branch.sons[L-1].len == 0: inc emptyBranches if branch.sons[L-1].len == 0: inc emptyBranches
caseStmt.add(branch) caseStmt.add(branch)
if emptyBranches != n.len-1: if emptyBranches != n.len-1:
body.add(caseStmt) body.add(caseStmt)
of nkRecList: of nkRecList:
for t in items(n): liftBodyObj(c, t, body, x, y) for t in items(n): fillBodyObj(c, t, body, x, y)
else: else:
illFormedAstLocal(n, c.graph.config) illFormedAstLocal(n, c.graph.config)
@ -150,7 +152,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
else: else:
op = field op = field
if op == nil: if op == nil:
op = liftBody(c.c, t, c.kind, c.info) op = produceSym(c.c, t, c.kind, c.info)
if sfError in op.flags: if sfError in op.flags:
incl c.fn.flags, sfError incl c.fn.flags, sfError
else: else:
@ -172,7 +174,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool = proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
var op = t.destructor var op = t.destructor
if op == nil and useNoGc(c, t): if op == nil and useNoGc(c, t):
op = liftBody(c.c, t, attachedDestructor, c.info) op = produceSym(c.c, t, attachedDestructor, c.info)
doAssert op != nil doAssert op != nil
doAssert op == t.destructor doAssert op == t.destructor
@ -280,12 +282,12 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let i = declareCounter(c, body, firstOrd(c.graph.config, t)) let i = declareCounter(c, body, firstOrd(c.graph.config, t))
let whileLoop = genWhileLoop(c, i, x) let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), fillBody(c, elemType, whileLoop.sons[1], x.at(i, elemType),
y.at(i, elemType)) y.at(i, elemType))
addIncStmt(c, whileLoop.sons[1], i) addIncStmt(c, whileLoop.sons[1], i)
body.add whileLoop body.add whileLoop
proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind case c.kind
of attachedAsgn, attachedDeepCopy: of attachedAsgn, attachedDeepCopy:
# we generate: # we generate:
@ -306,17 +308,31 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
forallElements(c, t, body, x, y) forallElements(c, t, body, x, y)
body.add genBuiltin(c.graph, mDestroy, "destroy", x) body.add genBuiltin(c.graph, mDestroy, "destroy", x)
proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
createTypeBoundOps(c.c, t, body.info)
case c.kind
of attachedAsgn, attachedDeepCopy:
doAssert t.assignment != nil
body.add newAsgnCall(c.graph, t.assignment, x, y)
of attachedSink:
# we always inline the move for better performance:
let moveCall = genBuiltin(c.graph, mMove, "move", x)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c.graph, t.destructor, x)
body.add moveCall
# alternatively we could do this:
when false:
doAssert t.sink != nil
body.add newAsgnCall(c.graph, t.sink, x, y)
of attachedDestructor:
doAssert t.destructor != nil
body.add destructorCall(c.graph, t.destructor, x)
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind case c.kind
of attachedAsgn, attachedDeepCopy: of attachedAsgn, attachedDeepCopy:
body.add callCodegenProc(c.graph, "nimAsgnStrV2", c.info, genAddr(c.graph, x), y) body.add callCodegenProc(c.graph, "nimAsgnStrV2", c.info, genAddr(c.graph, x), y)
# we generate:
# setLen(dest, y.len)
# var i = 0
# while i < y.len: dest[i] = y[i]; inc(i)
# This is usually more efficient than a destroy/create pair.
#body.add setLenStrCall(c.graph, x, y)
#forallElements(c, t, body, x, y)
of attachedSink: of attachedSink:
let moveCall = genBuiltin(c.graph, mMove, "move", x) let moveCall = genBuiltin(c.graph, mMove, "move", x)
moveCall.add y moveCall.add y
@ -345,7 +361,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
var actions = newNodeI(nkStmtList, c.info) var actions = newNodeI(nkStmtList, c.info)
let elemType = t.lastSon let elemType = t.lastSon
#liftBodyAux(c, elemType, actions, genDeref(x), genDeref(y)) #fillBody(c, elemType, actions, genDeref(x), genDeref(y))
#var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x) #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
if isFinal(elemType): if isFinal(elemType):
@ -406,7 +422,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, xx, actions) body.add genIf(c, xx, actions)
of attachedDeepCopy: assert(false, "cannot happen") of attachedDeepCopy: assert(false, "cannot happen")
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind case t.kind
of tyNone, tyEmpty, tyVoid: discard of tyNone, tyEmpty, tyVoid: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
@ -442,7 +458,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tySequence: of tySequence:
if useNoGc(c, t): if useNoGc(c, t):
seqOp(c, t, body, x, y) useSeqOrStrOp(c, t, body, x, y)
elif c.graph.config.selectedGC == gcDestructors: elif c.graph.config.selectedGC == gcDestructors:
# note that tfHasAsgn is propagated so we need the check on # note that tfHasAsgn is propagated so we need the check on
# 'selectedGC' here to determine if we have the new runtime. # 'selectedGC' here to determine if we have the new runtime.
@ -455,19 +471,19 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyString: of tyString:
if useNoGc(c, t): if useNoGc(c, t):
strOp(c, t, body, x, y) useSeqOrStrOp(c, t, body, x, y)
elif tfHasAsgn in t.flags: elif tfHasAsgn in t.flags:
discard considerUserDefinedOp(c, t, body, x, y) discard considerUserDefinedOp(c, t, body, x, y)
else: else:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyObject: of tyObject:
if not considerUserDefinedOp(c, t, body, x, y): if not considerUserDefinedOp(c, t, body, x, y):
liftBodyObj(c, t.n, body, x, y) fillBodyObj(c, t.n, body, x, y)
of tyDistinct: of tyDistinct:
if not considerUserDefinedOp(c, t, body, x, y): if not considerUserDefinedOp(c, t, body, x, y):
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y) fillBody(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
of tyTuple: of tyTuple:
liftBodyTup(c, t, body, x, y) fillBodyTup(c, t, body, x, y)
of tyVarargs, tyOpenArray: of tyVarargs, tyOpenArray:
localError(c.graph.config, c.info, "cannot copy openArray") localError(c.graph.config, c.info, "cannot copy openArray")
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@ -478,45 +494,40 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
discard discard
of tyVar, tyLent: of tyVar, tyLent:
if c.kind != attachedDestructor: if c.kind != attachedDestructor:
liftBodyAux(c, lastSon(t), body, x, y) fillBody(c, lastSon(t), body, x, y)
of tyOrdinal, tyRange, tyInferred, of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyStatic, tyAlias, tySink: tyGenericInst, tyStatic, tyAlias, tySink:
liftBodyAux(c, lastSon(t), body, x, y) fillBody(c, lastSon(t), body, x, y)
proc liftBodyDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
assert typ.kind == tyDistinct assert typ.kind == tyDistinct
let baseType = typ[0] let baseType = typ[0]
case kind case kind
of attachedAsgn: of attachedAsgn:
if baseType.assignment == nil: if baseType.assignment == nil:
discard liftBody(c, baseType, kind, info) discard produceSym(c, baseType, kind, info)
typ.assignment = baseType.assignment typ.assignment = baseType.assignment
result = typ.assignment result = typ.assignment
of attachedSink: of attachedSink:
if baseType.sink == nil: if baseType.sink == nil:
discard liftBody(c, baseType, kind, info) discard produceSym(c, baseType, kind, info)
typ.sink = baseType.sink typ.sink = baseType.sink
result = typ.sink result = typ.sink
of attachedDeepCopy: of attachedDeepCopy:
if baseType.deepCopy == nil: if baseType.deepCopy == nil:
discard liftBody(c, baseType, kind, info) discard produceSym(c, baseType, kind, info)
typ.deepCopy = baseType.deepCopy typ.deepCopy = baseType.deepCopy
result = typ.deepCopy result = typ.deepCopy
of attachedDestructor: of attachedDestructor:
if baseType.destructor == nil: if baseType.destructor == nil:
discard liftBody(c, baseType, kind, info) discard produceSym(c, baseType, kind, info)
typ.destructor = baseType.destructor typ.destructor = baseType.destructor
result = typ.destructor result = typ.destructor
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym = info: TLineInfo): PSym =
if typ.kind == tyDistinct: if typ.kind == tyDistinct:
return liftBodyDistinctType(c, typ, kind, info) return produceSymDistinctType(c, typ, kind, info)
when false:
var typ = typ
if c.config.selectedGC == gcDestructors and typ.kind == tySequence:
# use the canonical type to access the =sink and =destroy etc.
typ = c.graph.sysTypes[tySequence]
var a: TLiftCtx var a: TLiftCtx
a.info = info a.info = info
@ -552,7 +563,18 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
of attachedDeepCopy: typ.deepCopy = result of attachedDeepCopy: typ.deepCopy = result
of attachedDestructor: typ.destructor = result of attachedDestructor: typ.destructor = result
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) var tk: TTypeKind
if optNimV2 in c.graph.config.globalOptions:
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
else:
tk = tyNone # no special casing for strings and seqs
case tk
of tySequence:
fillSeqOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
of tyString:
fillStrOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
else:
fillBody(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
var n = newNodeI(nkProcDef, info, bodyPos+1) var n = newNodeI(nkProcDef, info, bodyPos+1)
for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info) for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
@ -563,16 +585,6 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
incl result.flags, sfFromGeneric incl result.flags, sfFromGeneric
incl result.flags, sfGeneratedOp incl result.flags, sfGeneratedOp
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
result = t.assignment
if result.isNil:
result = liftBody(c, t, attachedAsgn, info)
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
result = newAsgnCall(c.graph, a, dest, src)
template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
discard "now a nop" discard "now a nop"
@ -581,7 +593,7 @@ proc patchBody(c: PContext; n: PNode; info: TLineInfo) =
if n[0].kind == nkSym and n[0].sym.magic == mDestroy: if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
let t = n[1].typ.skipTypes(abstractVar) let t = n[1].typ.skipTypes(abstractVar)
if t.destructor == nil: if t.destructor == nil:
liftBody(c, t, attachedDestructor, info) discard produceSym(c, t, attachedDestructor, info)
if t.destructor != nil: if t.destructor != nil:
if t.destructor.ast[genericParamsPos].kind != nkEmpty: if t.destructor.ast[genericParamsPos].kind != nkEmpty:
@ -600,7 +612,7 @@ template inst(field, t) =
proc isTrival(s: PSym): bool {.inline.} = s == nil or s.ast[bodyPos].len == 0 proc isTrival(s: PSym): bool {.inline.} = s == nil or s.ast[bodyPos].len == 0
proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) = proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
## In the semantic pass this is called in strategic places ## In the semantic pass this is called in strategic places
## to ensure we lift assignment, destructors and moves properly. ## to ensure we lift assignment, destructors and moves properly.
## The later 'injectdestructors' pass depends on it. ## The later 'injectdestructors' pass depends on it.
@ -625,15 +637,15 @@ proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) =
let typ = canon.skipTypes({tyGenericInst, tyAlias}) let typ = canon.skipTypes({tyGenericInst, tyAlias})
# we generate the destructor first so that other operators can depend on it: # we generate the destructor first so that other operators can depend on it:
if typ.destructor == nil: if typ.destructor == nil:
liftBody(c, typ, attachedDestructor, info) discard produceSym(c, typ, attachedDestructor, info)
else: else:
inst(typ.destructor, typ) inst(typ.destructor, typ)
if typ.assignment == nil: if typ.assignment == nil:
liftBody(c, typ, attachedAsgn, info) discard produceSym(c, typ, attachedAsgn, info)
else: else:
inst(typ.assignment, typ) inst(typ.assignment, typ)
if typ.sink == nil: if typ.sink == nil:
liftBody(c, typ, attachedSink, info) discard produceSym(c, typ, attachedSink, info)
else: else:
inst(typ.sink, typ) inst(typ.sink, typ)

View file

@ -8,6 +8,11 @@ import strutils, os
import core / allocators import core / allocators
import system / ansi_c import system / ansi_c
# bug #11004
proc retTuple(): (seq[int], int) =
# XXX this doesn't allocate yet but probably it should
return (@[1], 1)
proc nonStaticTests = proc nonStaticTests =
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
when not defined(js): when not defined(js):
@ -177,6 +182,8 @@ proc staticTests =
doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "] doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"] doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
discard retTuple()
nonStaticTests() nonStaticTests()
staticTests() staticTests()