'parallel' proves array bounds
This commit is contained in:
parent
f12a0820e0
commit
6470bd8f87
3 changed files with 107 additions and 14 deletions
|
|
@ -246,6 +246,7 @@ proc canon*(n: PNode): PNode =
|
||||||
result.sons[0] = opLen.newSymNode
|
result.sons[0] = opLen.newSymNode
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
result = skipConv(result)
|
||||||
result = reassociation(result)
|
result = reassociation(result)
|
||||||
# most important rule: (x-4) < a.len --> x < a.len+4
|
# most important rule: (x-4) < a.len --> x < a.len+4
|
||||||
case result.getMagic
|
case result.getMagic
|
||||||
|
|
@ -672,6 +673,8 @@ proc simpleSlice*(a, b: PNode): BiggestInt =
|
||||||
else:
|
else:
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
|
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
|
||||||
|
|
||||||
proc ple(m: TModel; a, b: PNode): TImplication =
|
proc ple(m: TModel; a, b: PNode): TImplication =
|
||||||
template `<=?`(a,b): expr = ple(m,a,b) == impYes
|
template `<=?`(a,b): expr = ple(m,a,b) == impYes
|
||||||
# 0 <= 3
|
# 0 <= 3
|
||||||
|
|
@ -717,12 +720,68 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||||
if a[1] <=? b or a[2] <=? b: return impYes
|
if a[1] <=? b or a[2] <=? b: return impYes
|
||||||
|
|
||||||
# use the knowledge base:
|
# use the knowledge base:
|
||||||
return doesImply(m, opLe.buildCall(a, b))
|
return pleViaModel(m, a, b)
|
||||||
|
#return doesImply(m, opLe.buildCall(a, b))
|
||||||
|
|
||||||
|
type TReplacements = seq[tuple[a,b: PNode]]
|
||||||
|
|
||||||
|
proc replaceSubTree(n, x, by: PNode): PNode =
|
||||||
|
if sameTree(n, x):
|
||||||
|
result = by
|
||||||
|
elif hasSubTree(n, x):
|
||||||
|
result = shallowCopy(n)
|
||||||
|
for i in 0 .. safeLen(n)-1:
|
||||||
|
result.sons[i] = replaceSubTree(n.sons[i], x, by)
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
|
||||||
|
proc applyReplacements(n: PNode; rep: TReplacements): PNode =
|
||||||
|
result = n
|
||||||
|
for x in rep: result = result.replaceSubTree(x.a, x.b)
|
||||||
|
|
||||||
|
proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
|
||||||
|
# now check for inferrable facts: a <= b and b <= c implies a <= c
|
||||||
|
for i in 0..m.high:
|
||||||
|
let fact = m[i]
|
||||||
|
if fact != nil and fact.getMagic in someLe:
|
||||||
|
# x <= y implies a <= b if a <= x and y <= b
|
||||||
|
let x = fact[1]
|
||||||
|
let y = fact[2]
|
||||||
|
# mark as used:
|
||||||
|
m[i] = nil
|
||||||
|
if ple(m, a, x) == impYes:
|
||||||
|
if ple(m, y, b) == impYes: return impYes
|
||||||
|
#if pleViaModelRec(m, y, b): return impYes
|
||||||
|
|
||||||
|
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||||
|
# compute replacements:
|
||||||
|
var replacements: TReplacements = @[]
|
||||||
|
for fact in model:
|
||||||
|
if fact != nil and fact.getMagic in someEq:
|
||||||
|
let a = fact[1]
|
||||||
|
let b = fact[2]
|
||||||
|
if a.kind == nkSym: replacements.add((a,b))
|
||||||
|
else: replacements.add((b,a))
|
||||||
|
var m: TModel
|
||||||
|
var a = aa
|
||||||
|
var b = bb
|
||||||
|
if replacements.len > 0:
|
||||||
|
m = @[]
|
||||||
|
# make the other facts consistent:
|
||||||
|
for fact in model:
|
||||||
|
if fact != nil and fact.getMagic notin someEq:
|
||||||
|
# XXX 'canon' should not be necessary here, but it is
|
||||||
|
m.add applyReplacements(fact, replacements).canon
|
||||||
|
a = applyReplacements(aa, replacements)
|
||||||
|
b = applyReplacements(bb, replacements)
|
||||||
|
else:
|
||||||
|
# we have to make a copy here, because the model will be modified:
|
||||||
|
m = model
|
||||||
|
result = pleViaModelRec(m, a, b)
|
||||||
|
|
||||||
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
||||||
#echo "ROOT ", renderTree(a), " <=? ", b.rendertree
|
|
||||||
let x = canon(opLe.buildCall(a, b))
|
let x = canon(opLe.buildCall(a, b))
|
||||||
#echo renderTree(res)
|
#echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
|
||||||
result = ple(m, x[1], x[2])
|
result = ple(m, x[1], x[2])
|
||||||
if result == impUnknown:
|
if result == impUnknown:
|
||||||
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
|
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,8 @@ type
|
||||||
TDirection = enum
|
TDirection = enum
|
||||||
ascending, descending
|
ascending, descending
|
||||||
MonotonicVar = object
|
MonotonicVar = object
|
||||||
v: PSym
|
v, alias: PSym # to support the ordinary 'countup' iterator
|
||||||
|
# we need to detect aliases
|
||||||
lower, upper, stride: PNode
|
lower, upper, stride: PNode
|
||||||
dir: TDirection
|
dir: TDirection
|
||||||
blacklisted: bool # blacklisted variables that are not monotonic
|
blacklisted: bool # blacklisted variables that are not monotonic
|
||||||
|
|
@ -83,7 +84,7 @@ proc initAnalysisCtx(): AnalysisCtx =
|
||||||
|
|
||||||
proc lookupSlot(c: AnalysisCtx; s: PSym): int =
|
proc lookupSlot(c: AnalysisCtx; s: PSym): int =
|
||||||
for i in 0.. <c.locals.len:
|
for i in 0.. <c.locals.len:
|
||||||
if c.locals[i].v == s: return i
|
if c.locals[i].v == s or c.locals[i].alias == s: return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc getSlot(c: var AnalysisCtx; v: PSym): ptr MonotonicVar =
|
proc getSlot(c: var AnalysisCtx; v: PSym): ptr MonotonicVar =
|
||||||
|
|
@ -104,6 +105,11 @@ proc gatherArgs(c: var AnalysisCtx; n: PNode) =
|
||||||
c.args.add root
|
c.args.add root
|
||||||
gatherArgs(c, n[i])
|
gatherArgs(c, n[i])
|
||||||
|
|
||||||
|
proc isSingleAssignable(n: PNode): bool =
|
||||||
|
n.kind == nkSym and (let s = n.sym;
|
||||||
|
s.kind in {skTemp, skForVar, skLet} and
|
||||||
|
{sfAddrTaken, sfGlobal} * s.flags == {})
|
||||||
|
|
||||||
proc isLocal(n: PNode): bool =
|
proc isLocal(n: PNode): bool =
|
||||||
n.kind == nkSym and (let s = n.sym;
|
n.kind == nkSym and (let s = n.sym;
|
||||||
s.kind in {skResult, skTemp, skForVar, skVar, skLet} and
|
s.kind in {skResult, skTemp, skForVar, skVar, skLet} and
|
||||||
|
|
@ -290,16 +296,16 @@ proc analyseCase(c: var AnalysisCtx; n: PNode) =
|
||||||
proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
||||||
analyse(c, n.sons[0].sons[0])
|
analyse(c, n.sons[0].sons[0])
|
||||||
let oldFacts = c.guards.len
|
let oldFacts = c.guards.len
|
||||||
addFact(c.guards, n.sons[0].sons[0])
|
addFact(c.guards, canon(n.sons[0].sons[0]))
|
||||||
|
|
||||||
analyse(c, n.sons[0].sons[1])
|
analyse(c, n.sons[0].sons[1])
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
let branch = n.sons[i]
|
let branch = n.sons[i]
|
||||||
setLen(c.guards, oldFacts)
|
setLen(c.guards, oldFacts)
|
||||||
for j in 0..i-1:
|
for j in 0..i-1:
|
||||||
addFactNeg(c.guards, n.sons[j].sons[0])
|
addFactNeg(c.guards, canon(n.sons[j].sons[0]))
|
||||||
if branch.len > 1:
|
if branch.len > 1:
|
||||||
addFact(c.guards, branch.sons[0])
|
addFact(c.guards, canon(branch.sons[0]))
|
||||||
for i in 0 .. <branch.len:
|
for i in 0 .. <branch.len:
|
||||||
analyse(c, branch.sons[i])
|
analyse(c, branch.sons[i])
|
||||||
setLen(c.guards, oldFacts)
|
setLen(c.guards, oldFacts)
|
||||||
|
|
@ -307,9 +313,12 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
||||||
proc analyse(c: var AnalysisCtx; n: PNode) =
|
proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
# since we already ensure sfAddrTaken is not in s.flags, we only need to
|
if n[0].isSingleAssignable and n[1].isLocal:
|
||||||
# prevent direct assignments to the monotonic variable:
|
let slot = c.getSlot(n[1].sym)
|
||||||
if n[0].isLocal:
|
slot.alias = n[0].sym
|
||||||
|
elif n[0].isLocal:
|
||||||
|
# since we already ensure sfAddrTaken is not in s.flags, we only need to
|
||||||
|
# prevent direct assignments to the monotonic variable:
|
||||||
let slot = c.getSlot(n[0].sym)
|
let slot = c.getSlot(n[0].sym)
|
||||||
slot.blackListed = true
|
slot.blackListed = true
|
||||||
invalidateFacts(c.guards, n[0])
|
invalidateFacts(c.guards, n[0])
|
||||||
|
|
@ -348,13 +357,13 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||||
# loop may never execute:
|
# loop may never execute:
|
||||||
let oldState = c.locals.len
|
let oldState = c.locals.len
|
||||||
let oldFacts = c.guards.len
|
let oldFacts = c.guards.len
|
||||||
addFact(c.guards, n.sons[0])
|
addFact(c.guards, canon(n.sons[0]))
|
||||||
analyse(c, n.sons[1])
|
analyse(c, n.sons[1])
|
||||||
setLen(c.locals, oldState)
|
setLen(c.locals, oldState)
|
||||||
setLen(c.guards, oldFacts)
|
setLen(c.guards, oldFacts)
|
||||||
# we know after the loop the negation holds:
|
# we know after the loop the negation holds:
|
||||||
if not hasSubnodeWith(n.sons[1], nkBreakStmt):
|
if not hasSubnodeWith(n.sons[1], nkBreakStmt):
|
||||||
addFactNeg(c.guards, n.sons[0])
|
addFactNeg(c.guards, canon(n.sons[0]))
|
||||||
dec c.inLoop
|
dec c.inLoop
|
||||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||||
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma:
|
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma:
|
||||||
|
|
@ -429,6 +438,7 @@ proc liftParallel*(owner: PSym; n: PNode): PNode =
|
||||||
# - detect monotonic local integer variables
|
# - detect monotonic local integer variables
|
||||||
# - detect used slices
|
# - detect used slices
|
||||||
# - detect used arguments
|
# - detect used arguments
|
||||||
|
#echo "PAR ", renderTree(n)
|
||||||
|
|
||||||
var a = initAnalysisCtx()
|
var a = initAnalysisCtx()
|
||||||
let body = n.lastSon
|
let body = n.lastSon
|
||||||
|
|
|
||||||
24
tests/parallel/tforstmt.nim
Normal file
24
tests/parallel/tforstmt.nim
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
discard """
|
||||||
|
output: '''3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7'''
|
||||||
|
sortoutput: true
|
||||||
|
"""
|
||||||
|
|
||||||
|
import threadpool, math
|
||||||
|
|
||||||
|
proc p(x: int) =
|
||||||
|
echo x
|
||||||
|
|
||||||
|
proc testFor(a, b: int; foo: var openArray[int]) =
|
||||||
|
parallel:
|
||||||
|
for i in max(a, 0) .. min(b, foo.len-1):
|
||||||
|
spawn p(foo[i])
|
||||||
|
|
||||||
|
var arr = [0, 1, 2, 3, 4, 5, 6, 7]
|
||||||
|
|
||||||
|
testFor(3, 10, arr)
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue