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

View File

@@ -27,9 +27,11 @@ type
recurse: bool
c: PContext
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym {.discardable.}
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym
proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo)
proc at(a, i: PNode, elemType: PType): PNode =
result = newNodeI(nkBracketExpr, a.info, 2)
@@ -37,10 +39,10 @@ proc at(a, i: PNode, elemType: PType): PNode =
result.sons[1] = i
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:
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 =
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.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
of nkSym:
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 nkRecCase:
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
let prevKind = c.kind
c.kind = attachedDestructor
liftBodyObj(c, n, body, x, y)
fillBodyObj(c, n, body, x, y)
c.kind = prevKind
# copy the selector:
liftBodyObj(c, n[0], body, x, y)
fillBodyObj(c, n[0], body, x, y)
# we need to generate a case statement:
var caseStmt = newNodeI(nkCaseStmt, c.info)
# 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
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
caseStmt.add(branch)
if emptyBranches != n.len-1:
body.add(caseStmt)
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:
illFormedAstLocal(n, c.graph.config)
@@ -150,7 +152,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
else:
op = field
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:
incl c.fn.flags, sfError
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 =
var op = t.destructor
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 == 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 whileLoop = genWhileLoop(c, i, x)
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))
addIncStmt(c, whileLoop.sons[1], i)
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
of attachedAsgn, attachedDeepCopy:
# we generate:
@@ -306,17 +308,31 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
forallElements(c, t, body, x, y)
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
of attachedAsgn, attachedDeepCopy:
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:
let moveCall = genBuiltin(c.graph, mMove, "move", x)
moveCall.add y
@@ -345,7 +361,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
var actions = newNodeI(nkStmtList, c.info)
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)
if isFinal(elemType):
@@ -406,7 +422,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, xx, actions)
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
of tyNone, tyEmpty, tyVoid: discard
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)
of tySequence:
if useNoGc(c, t):
seqOp(c, t, body, x, y)
useSeqOrStrOp(c, t, body, x, y)
elif c.graph.config.selectedGC == gcDestructors:
# note that tfHasAsgn is propagated so we need the check on
# '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)
of tyString:
if useNoGc(c, t):
strOp(c, t, body, x, y)
useSeqOrStrOp(c, t, body, x, y)
elif tfHasAsgn in t.flags:
discard considerUserDefinedOp(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyObject:
if not considerUserDefinedOp(c, t, body, x, y):
liftBodyObj(c, t.n, body, x, y)
fillBodyObj(c, t.n, body, x, y)
of tyDistinct:
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:
liftBodyTup(c, t, body, x, y)
fillBodyTup(c, t, body, x, y)
of tyVarargs, tyOpenArray:
localError(c.graph.config, c.info, "cannot copy openArray")
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@@ -478,45 +494,40 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
discard
of tyVar, tyLent:
if c.kind != attachedDestructor:
liftBodyAux(c, lastSon(t), body, x, y)
fillBody(c, lastSon(t), body, x, y)
of tyOrdinal, tyRange, tyInferred,
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
let baseType = typ[0]
case kind
of attachedAsgn:
if baseType.assignment == nil:
discard liftBody(c, baseType, kind, info)
discard produceSym(c, baseType, kind, info)
typ.assignment = baseType.assignment
result = typ.assignment
of attachedSink:
if baseType.sink == nil:
discard liftBody(c, baseType, kind, info)
discard produceSym(c, baseType, kind, info)
typ.sink = baseType.sink
result = typ.sink
of attachedDeepCopy:
if baseType.deepCopy == nil:
discard liftBody(c, baseType, kind, info)
discard produceSym(c, baseType, kind, info)
typ.deepCopy = baseType.deepCopy
result = typ.deepCopy
of attachedDestructor:
if baseType.destructor == nil:
discard liftBody(c, baseType, kind, info)
discard produceSym(c, baseType, kind, info)
typ.destructor = baseType.destructor
result = typ.destructor
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym =
if typ.kind == tyDistinct:
return liftBodyDistinctType(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]
return produceSymDistinctType(c, typ, kind, info)
var a: TLiftCtx
a.info = info
@@ -552,7 +563,18 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
of attachedDeepCopy: typ.deepCopy = 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)
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, 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) =
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:
let t = n[1].typ.skipTypes(abstractVar)
if t.destructor == nil:
liftBody(c, t, attachedDestructor, info)
discard produceSym(c, t, attachedDestructor, info)
if t.destructor != nil:
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 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
## to ensure we lift assignment, destructors and moves properly.
## 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})
# we generate the destructor first so that other operators can depend on it:
if typ.destructor == nil:
liftBody(c, typ, attachedDestructor, info)
discard produceSym(c, typ, attachedDestructor, info)
else:
inst(typ.destructor, typ)
if typ.assignment == nil:
liftBody(c, typ, attachedAsgn, info)
discard produceSym(c, typ, attachedAsgn, info)
else:
inst(typ.assignment, typ)
if typ.sink == nil:
liftBody(c, typ, attachedSink, info)
discard produceSym(c, typ, attachedSink, info)
else:
inst(typ.sink, typ)

View File

@@ -8,6 +8,11 @@ import strutils, os
import core / allocators
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 =
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
when not defined(js):
@@ -177,6 +182,8 @@ proc staticTests =
doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
discard retTuple()
nonStaticTests()
staticTests()