Static Analysis
Static Analysis
Techniques
https://en.wikipedia.org/wiki/Static_program_analysis
https://en.wikipedia.org/wiki/Category:Static_program_analysis
Type Inference
https://eli.thegreenplace.net/2018/type-inference/
Escape Analysis
https://en.wikipedia.org/wiki/Escape_analysis
Control Flow Analysis
https://en.wikipedia.org/wiki/Control_flow_analysis
http://www.ccs.neu.edu/home/shivers/papers/diss.pdf
http://haskell.cs.yale.edu/wp-content/uploads/2011/02/Orbit.pdf
https://www.microsoft.com/en-us/research/wp-content/uploads/2007/10/compilingwithcontinuationscontinued.pdf?from=https%3A%2F%2Fresearch.microsoft.com%2F%7Eakenn%2Fsml%2FCompilingWithContinuationsContinued.pdf
Termination Analysis
In general: proof every recursive function has a well-founded relation < such that a_n+1 < a_n
where a
are the succesive arguments to recursive calls.
Walther Recursion is a more powerful generalization of primitive recursion, it always terminates, but is more expressive. http://www.inferenzsysteme.informatik.tu-darmstadt.de/media/is/publikationen/On-proving-termination-AIJ-71-1_1994-2.pdf
Example: flow graph for facts
fact n return: isZero n base recurse
base: return 1
recurse: sub n 1 step1
step1 m: fact m step2
step2 f: mul n f return
We would like to prove:
fact => return
Meaning: fact will either diverge or eventually call return.
Proof by induction:
Base case (no recursion): Every path either recurses or ends in return
.
fact -> isZero -> base -> return
fact -> isZero -> recurse -> sub -> step1 -> fact
Inductive case. Assume fact -> return
for n recursions. Show that this holds after one more recursion.
fact -> isZero -> base -> return
fact -> isZero -> recurse -> sub -> step1 -> fact -> step2 -> mul -> return
This analysis can probably be done fully automated. TODO: check cases of mutual recursion and recursive functions that have multiple exit points.
Example: flow graph for isEven
isEven n then else: isZero n then step
step: sub n 1 recurse
recurse m: isEven m else then
isEven -> isZero -> then
isEven -> isZero -> step -> sub -> recurse -> isEven
Try isEven => then
:
isEven -> isZero -> then
isEven -> isZero -> step -> sub -> recurse -> isEven -> else
False. Try isEven => then, isEven => else
isEven -> isZero -> then
isEven -> isZero -> step -> sub -> recurse -> isEven -> else
isEven -> isZero -> step -> sub -> recurse -> isEven -> then
Proven.
Example closures for fact
fact n return: isZero n base recurse
base: return 1
recurse: sub n 1 step1
step1 m: fact m step2
step2 f: mul n f return
main return1: fact 5 m0
m0 f: print f m1
m1: return1
Create a map from names to call symbols sets:
fact: { isZero n base recurse }
base: { return 1 }
recurse: { sub n 1 step1 }
step1: { fact m step2 }
step2: { mul n f return }
main: { fact 5 m0 }
m0: { print f m1 }
m1: { return1 }
Remove builtins:
fact: { n base recurse }
base: { return }
recurse: { n step1 }
step1: { fact m step2 }
step2: { n f return }
main: { fact m0 }
m0: { f m1 }
m1: { return1 }
Remove own arguments:
fact: { base recurse }
base: { return }
recurse: { n step1 }
step1: { fact m step2 }
step2: { n f return }
main: { fact m0 }
m0: { m1 }
m1: { return1 }
Replace names by their call, while removing own arguments:
fact: { }
base: { return }
recurse: { return n }
step1: { return n }
step2: { return n }
main: { base recurse m0 }
m0: { m1 }
m1: { return1 }
We now have a list of closure parameters.
We can turn this into a domination graph by replacing parameters by their procedures:
fact:
base: { fact }
recurse: { fact }
step1: { fact }
step2: { fact }
Example closure elimination for fact
fact n return: isZero n base recurse
base: return 1
recurse: sub n 1 step1
step1 m: fact m step2
step2 f: mul n f return
Add closure values as parameters
isZero' n base recurse return n
sub' a b k return n
fact n return: isZero' n base recurse return n
base return: return 1
recurse return n: sub' n 1 step1 return n
step1 m return n: fact' m step2 return n
step2 f return n: mul n f return
We need to modify builtins to pass parameters along. Not elegant, but doable. But then we need to do the same transform to fact'
and then we are at square one again, needing a fact''
. We are essentially building a stack, which we want to avoid.
What should the ideal result look like?
First, make it a proper tail call. How we do this is a different discussion.
fact n return: fact' n 1 return
fact' n a return: isZero n base recurse
base: return 1
recurse: sub n 1 step1
step1 n': mul n a step2
step2 a': fact' n' a' return
Now try the same procedure again:
fact n return: fact' n 1 return
fact' n a return: isZero' n base recurse n a return
base n a return: return 1
recurse n a return: sub' n 1 step1 n a return
step1 n' n a return: mul' n a step2 n' return
step2 a' n' return: fact' n' a' return
We still need modified version of the built-ins that pass parameters along, but we no longer need to recursively transfor fact
itself.