An optimizer for ARC (#14962)
* WIP: an optimizer for ARC * do not optimize away destructors in 'finally' if unstructured control flow is involved * optimized the optimizer * minor code cleanup * first steps to .cursor inference * cursor inference: big steps to a working solution * baby steps * better .cursor inference * new feature: expandArc for easy inspection of the AST after ARC transformations * added topt_cursor test * adapt tests * cleanups, make tests green * optimize common traversal patterns * moved test case * fixes .cursor inference so that npeg compiles once again * cursor inference: more bugfixes Co-authored-by: Clyybber <darkmine956@gmail.com>
This commit is contained in:
parent
813dd1b670
commit
c5358b0d4b
19 changed files with 895 additions and 62 deletions
|
|
@ -1,15 +1,7 @@
|
|||
discard """
|
||||
cmd: '''nim c --newruntime $file'''
|
||||
output: '''copied
|
||||
copied
|
||||
2
|
||||
destroyed
|
||||
destroyed
|
||||
copied
|
||||
copied
|
||||
2
|
||||
destroyed
|
||||
destroyed'''
|
||||
output: '''2
|
||||
2'''
|
||||
"""
|
||||
|
||||
type
|
||||
35
tests/arc/topt_cursor.nim
Normal file
35
tests/arc/topt_cursor.nim
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
discard """
|
||||
output: '''("string here", 80)'''
|
||||
cmd: '''nim c --gc:arc --expandArc:main --hint:Performance:off $file'''
|
||||
nimout: '''--expandArc: main
|
||||
|
||||
var
|
||||
:tmpD
|
||||
:tmpD_1
|
||||
:tmpD_2
|
||||
try:
|
||||
var x = ("hi", 5)
|
||||
x = if cond:
|
||||
:tmpD = ("different", 54)
|
||||
:tmpD else:
|
||||
:tmpD_1 = ("string here", 80)
|
||||
:tmpD_1
|
||||
echo [
|
||||
:tmpD_2 = `$`(x)
|
||||
:tmpD_2]
|
||||
finally:
|
||||
`=destroy`(:tmpD_2)
|
||||
-- end of expandArc ------------------------'''
|
||||
"""
|
||||
|
||||
proc main(cond: bool) =
|
||||
var x = ("hi", 5) # goal: computed as cursor
|
||||
|
||||
x = if cond:
|
||||
("different", 54)
|
||||
else:
|
||||
("string here", 80)
|
||||
|
||||
echo x
|
||||
|
||||
main(false)
|
||||
37
tests/arc/topt_no_cursor.nim
Normal file
37
tests/arc/topt_no_cursor.nim
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
discard """
|
||||
output: '''(repo: "", package: "meo", ext: "")'''
|
||||
cmd: '''nim c --gc:arc --expandArc:newTarget --hint:Performance:off $file'''
|
||||
nimout: '''--expandArc: newTarget
|
||||
|
||||
var
|
||||
splat
|
||||
:tmp
|
||||
:tmp_1
|
||||
:tmp_2
|
||||
splat = splitFile(path)
|
||||
:tmp = splat.dir
|
||||
wasMoved(splat.dir)
|
||||
:tmp_1 = splat.name
|
||||
wasMoved(splat.name)
|
||||
:tmp_2 = splat.ext
|
||||
wasMoved(splat.ext)
|
||||
result = (
|
||||
let blitTmp = :tmp
|
||||
blitTmp,
|
||||
let blitTmp_1 = :tmp_1
|
||||
blitTmp_1,
|
||||
let blitTmp_2 = :tmp_2
|
||||
blitTmp_2)
|
||||
`=destroy`(splat)
|
||||
-- end of expandArc ------------------------'''
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
type Target = tuple[repo, package, ext: string]
|
||||
|
||||
proc newTarget*(path: string): Target =
|
||||
let splat = path.splitFile
|
||||
result = (repo: splat.dir, package: splat.name, ext: splat.ext)
|
||||
|
||||
echo newTarget("meo")
|
||||
39
tests/arc/topt_refcursors.nim
Normal file
39
tests/arc/topt_refcursors.nim
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
discard """
|
||||
output: ''''''
|
||||
cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file'''
|
||||
nimout: '''--expandArc: traverse
|
||||
|
||||
var it = root
|
||||
block :tmp:
|
||||
while (
|
||||
not (it == nil)):
|
||||
echo [it.s]
|
||||
it = it.ri
|
||||
var jt = root
|
||||
block :tmp_1:
|
||||
while (
|
||||
not (jt == nil)):
|
||||
let ri_1 = jt.ri
|
||||
echo [jt.s]
|
||||
jt = ri_1
|
||||
-- end of expandArc ------------------------'''
|
||||
"""
|
||||
|
||||
type
|
||||
Node = ref object
|
||||
le, ri: Node
|
||||
s: string
|
||||
|
||||
proc traverse(root: Node) =
|
||||
var it = root
|
||||
while it != nil:
|
||||
echo it.s
|
||||
it = it.ri
|
||||
|
||||
var jt = root
|
||||
while jt != nil:
|
||||
let ri = jt.ri
|
||||
echo jt.s
|
||||
jt = ri
|
||||
|
||||
traverse(nil)
|
||||
92
tests/arc/topt_wasmoved_destroy_pairs.nim
Normal file
92
tests/arc/topt_wasmoved_destroy_pairs.nim
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
discard """
|
||||
output: ''''''
|
||||
cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file'''
|
||||
nimout: '''--expandArc: main
|
||||
|
||||
var
|
||||
a
|
||||
b
|
||||
x
|
||||
x = f()
|
||||
if cond:
|
||||
add(a):
|
||||
let blitTmp = x
|
||||
blitTmp
|
||||
else:
|
||||
add(b):
|
||||
let blitTmp_1 = x
|
||||
blitTmp_1
|
||||
`=destroy`(b)
|
||||
`=destroy`(a)
|
||||
-- end of expandArc ------------------------
|
||||
--expandArc: tfor
|
||||
|
||||
var
|
||||
a
|
||||
b
|
||||
x
|
||||
try:
|
||||
x = f()
|
||||
block :tmp:
|
||||
var i
|
||||
var i_1 = 0
|
||||
block :tmp_1:
|
||||
while i_1 < 4:
|
||||
var :tmpD
|
||||
i = i_1
|
||||
if i == 2:
|
||||
return
|
||||
add(a):
|
||||
wasMoved(:tmpD)
|
||||
`=`(:tmpD, x)
|
||||
:tmpD
|
||||
inc i_1, 1
|
||||
if cond:
|
||||
add(a):
|
||||
let blitTmp = x
|
||||
wasMoved(x)
|
||||
blitTmp
|
||||
else:
|
||||
add(b):
|
||||
let blitTmp_1 = x
|
||||
wasMoved(x)
|
||||
blitTmp_1
|
||||
finally:
|
||||
`=destroy`(x)
|
||||
`=destroy_1`(b)
|
||||
`=destroy_1`(a)
|
||||
-- end of expandArc ------------------------'''
|
||||
"""
|
||||
|
||||
proc f(): seq[int] =
|
||||
@[1, 2, 3]
|
||||
|
||||
proc main(cond: bool) =
|
||||
var a, b: seq[seq[int]]
|
||||
var x = f()
|
||||
if cond:
|
||||
a.add x
|
||||
else:
|
||||
b.add x
|
||||
|
||||
# all paths move 'x' so no wasMoved(x); destroy(x) pair should be left in the
|
||||
# AST.
|
||||
|
||||
main(false)
|
||||
|
||||
|
||||
proc tfor(cond: bool) =
|
||||
var a, b: seq[seq[int]]
|
||||
|
||||
var x = f()
|
||||
|
||||
for i in 0 ..< 4:
|
||||
if i == 2: return
|
||||
a.add x
|
||||
|
||||
if cond:
|
||||
a.add x
|
||||
else:
|
||||
b.add x
|
||||
|
||||
tfor(false)
|
||||
|
|
@ -138,16 +138,16 @@ proc search*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]): seq[LT]
|
|||
# a R*TREE proc
|
||||
proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; level: int): H[M, D, RT, LT] =
|
||||
assert level >= 0
|
||||
var n = t.root
|
||||
while n.level > level:
|
||||
let nn = Node[M, D, RT, LT](n)
|
||||
var it = t.root
|
||||
while it.level > level:
|
||||
let nn = Node[M, D, RT, LT](it)
|
||||
var i0 = 0 # selected index
|
||||
var minLoss = type(b[0].a).high
|
||||
if n.level == 1: # childreen are leaves -- determine the minimum overlap costs
|
||||
for i in 0 ..< n.numEntries:
|
||||
if it.level == 1: # childreen are leaves -- determine the minimum overlap costs
|
||||
for i in 0 ..< it.numEntries:
|
||||
let nx = union(nn.a[i].b, b)
|
||||
var loss = 0
|
||||
for j in 0 ..< n.numEntries:
|
||||
for j in 0 ..< it.numEntries:
|
||||
if i == j: continue
|
||||
loss += (overlap(nx, nn.a[j].b) - overlap(nn.a[i].b, nn.a[j].b)) # overlap (i, j) == (j, i), so maybe cache that?
|
||||
var rep = loss < minLoss
|
||||
|
|
@ -163,7 +163,7 @@ proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; lev
|
|||
i0 = i
|
||||
minLoss = loss
|
||||
else:
|
||||
for i in 0 ..< n.numEntries:
|
||||
for i in 0 ..< it.numEntries:
|
||||
let loss = enlargement(nn.a[i].b, b)
|
||||
var rep = loss < minLoss
|
||||
if loss == minLoss:
|
||||
|
|
@ -174,8 +174,8 @@ proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; lev
|
|||
if rep:
|
||||
i0 = i
|
||||
minLoss = loss
|
||||
n = nn.a[i0].n
|
||||
return n
|
||||
it = nn.a[i0].n
|
||||
return it
|
||||
|
||||
proc pickSeeds[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; bx: Box[D, RT]): (int, int) =
|
||||
var i0, j0: int
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
discard """
|
||||
output: '''assign
|
||||
output: '''
|
||||
assign
|
||||
destroy
|
||||
destroy
|
||||
5
|
||||
|
|
@ -104,12 +105,12 @@ test()
|
|||
#------------------------------------------------------------
|
||||
# Issue #12883
|
||||
|
||||
type
|
||||
type
|
||||
TopObject = object
|
||||
internal: UniquePtr[int]
|
||||
|
||||
proc deleteTop(p: ptr TopObject) =
|
||||
if p != nil:
|
||||
if p != nil:
|
||||
`=destroy`(p[]) # !!! this operation used to leak the integer
|
||||
deallocshared(p)
|
||||
|
||||
|
|
@ -117,12 +118,12 @@ proc createTop(): ptr TopObject =
|
|||
result = cast[ptr TopObject](allocShared0(sizeof(TopObject)))
|
||||
result.internal = newUniquePtr(1)
|
||||
|
||||
proc test2() =
|
||||
proc test2() =
|
||||
let x = createTop()
|
||||
echo $x.internal
|
||||
deleteTop(x)
|
||||
|
||||
echo "---------------"
|
||||
echo "---------------"
|
||||
echo "app begin"
|
||||
test2()
|
||||
echo "app end"
|
||||
|
|
@ -21,11 +21,13 @@ proc `=sink`(dest: var Foo, src: Foo) =
|
|||
proc `=`(dest: var Foo, src: Foo) =
|
||||
assign_counter.inc
|
||||
|
||||
proc createFoo(): Foo = Foo(boo: 0)
|
||||
|
||||
proc test(): auto =
|
||||
var a, b: Foo
|
||||
var a, b = createFoo()
|
||||
return (a, b, Foo(boo: 5))
|
||||
|
||||
var (a, b, _) = test()
|
||||
var (ag, bg, _) = test()
|
||||
|
||||
doAssert assign_counter == 0
|
||||
doAssert sink_counter == 0
|
||||
|
|
|
|||
|
|
@ -17,15 +17,20 @@ proc `=sink`(self: var Foo; other: Foo) =
|
|||
|
||||
proc `=destroy`(self: var Foo) = discard
|
||||
|
||||
template preventCursorInference(x) =
|
||||
let p = unsafeAddr(x)
|
||||
|
||||
proc test(): Foo =
|
||||
result = Foo()
|
||||
let temp = result
|
||||
preventCursorInference temp
|
||||
doAssert temp.i > 0
|
||||
return result
|
||||
|
||||
proc testB(): Foo =
|
||||
result = Foo()
|
||||
let temp = result
|
||||
preventCursorInference temp
|
||||
doAssert temp.i > 0
|
||||
|
||||
discard test()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue