a theory of termination via indirection robert dockins and aquinas hobor (princeton university)...

Post on 18-Dec-2015

217 Views

Category:

Documents

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

A Theory of Termination via Indirection

Robert Dockins and Aquinas Hobor(Princeton University) (NUS)

2

Goal

• Use step-indexed models to prove program termination in the presence of certain (approximated) recursive domain equations

• Testbed: soundness of a logic of total correctness for a language with1. Function pointers2. Semantic assertions (assert truth of a (classical)

logical formula at the current program point)

3

Goal

• Use step-indexed models to prove program termination in the presence of certain (approximated) recursive domain equations

• Testbed: soundness of a logic of total correctness for a language with1. Function pointers2. Semantic assertions (assert truth of a (classical)

logical formula at the current program point)

4

A very simple language

5

Simple semantic definitions

6

Simple semantic definitions

Uh oh…

7

We detail our predicates/assertions informally and refer to the paper for formal construction

8

We detail our predicates/assertions informally and refer to the paper for formal construction

9

We detail our predicates/assertions informally and refer to the paper for formal construction

10

We detail our predicates/assertions informally and refer to the paper for formal construction

11

What does funptr l t [P] [Q] mean?1. The program has some code c at address l .

(This is why we want predicates to be able to judge programs.)

2. When c is called with some initial store ½, if t(½) is defined then c makes at most t(½) function calls before returning to its caller.

3. P and Q are actually functions from some function-specific type A to predicates. If t(½) is defined then for all a, if P(a) holds before the call then Q(a) will hold afterwards.

12

Why it was (considered) hard

• Certain people who will remain anonymous (some in this room, some elsewhere, e.g., Princeton, NJ) thought step-indexed models were inapplicable to liveness problems

• We have discussed the difficulty of statements like, “method X cannot be used to prove Y”

13

Why it was (considered) hard

• Certain people who will remain anonymous (some in this room, some elsewhere, e.g., Princeton, NJ) thought step-indexed models were inapplicable to liveness problems

• We have discussed the difficulty of statements like, “method X cannot be used to prove Y”

14

Why it was (considered) hard

• But if you asked, such people said:1. Step-indexed models have an outer “forall n”

that quantifies (universally) over the number of steps; this works well for safety properties but very poorly for liveness properties

2. While “safe” is hereditary/monotonic/closed under approximation, “halts” is not (actually, this is related to point 1, too…)

3. It’s never been done and termination is hard

15

Why it was (considered) hard

• But if you asked, such people said:1. Step-indexed models have an outer “forall n”

that quantifies (universally) over the number of steps; this works well for safety properties but very poorly for liveness properties

2. While “safe” is hereditary/monotonic/closed under approximation, “halts” is not (actually, this is related to point 1, too…)

3. It’s never been done and termination is hard

16

Why it was (considered) hard

• But if you asked, such people said:1. Step-indexed models have an outer “forall n”

that quantifies (universally) over the number of steps; this works well for safety properties but very poorly for liveness properties

2. While “safe” is hereditary/monotonic/closed under approximation, “halts” is not (actually, this is related to point 1, too…)

3. It’s never been done and termination is hard

17

Why it’s actually not that hard

1. It turns out that the outside quantification can just as easily be existential if we can only find the right value to instantiate it…

– This is exactly what a termination proof gives us!

– “P terminates” a.k.a. “exists n. P reaches halt in n steps”

18

Why it’s actually not that hard

1. It turns out that the outside quantification can just as easily be existential if we can only find the right value to instantiate it…

– This is exactly what a termination proof gives us!

– “exists n. prog reaches halt in n steps”

19

Hoare Judgment

• Our Hoare Judgment looks a bit complex:

¡, R `n { P } c { Q }

• ¡ contains function pointer assertions and R is the postcondition of the current function

• n is an upper bound on the number of function calls c makes before it terminates

20

Hoare Rules, 1

21

Hoare Rules, 1

22

Hoare Rules, 2

23

Hoare Rules, 2

24

Hoare Rules, 3

25

Hoare Rules, 3

26

The Call Rule

The interesting rule is for call… and it’s not too bad:

27

The Call Rule

The interesting rule is for call… and it’s not too bad:

1. x must evaluate to a label l

28

The Call Rule

The interesting rule is for call… and it’s not too bad:

2. l must be a pointer to a function with termination measure t, precondition Pl and postcondition Ql

29

The Call Rule

The interesting rule is for call… and it’s not too bad:

3. The termination measure t must evaluate to some n on the current store

30

The Call Rule

The interesting rule is for call… and it’s not too bad:

3. The termination measure t must evaluate to some n on the current store… and so this call will take no more than n+1 calls.

31

The Call Rule

The interesting rule is for call… and it’s not too bad:

4. We require the (parameterized) precondition to be true at call, and guarantee the (parameterized) postcondition will be true on return.

32

So, not too bad…

• Most of these rules are really nothing to get excited about… even the call rule is pretty simple when you understand the parts…

• (This is a virtue, of course…)

• But we’re not done. We’ve only shown the rule for using the funptr, not the rules for verifying that a function actually terminates

33

Verifying function bodies

• Verifying function bodies, while very important, is not part of the main line of this talk – why proving termination with a step-indexed model isn’t so bad.

• We can verify functions in many interesting cases – recursive, mutually recursive, higher-order (we can handle map of map), etc.

• If you want, ask me about this at the end. (I have 35 slides as backup!)

34

Why it’s actually not that hard

2. The second reason people thought that termination was really hard with step-indexed models was that “halting” is not hereditary/monotonic/downward closed.

A. What is hereditary?B. Why is halting not hereditary?

35

Why it’s actually not that hard

2. P hereditary ´ (w ² P Æ wÃw’) ! w’ ² P

Here à is the “age” or “approximate” operation on step-indexed worlds.

We can only approximate a finite number of times before we “hit bottom”. The step relation approximates w during function call.

36

Why it’s actually not that hard

2. The standard definition:

w ² halts(¾) ´ 9 wh. (w,¾) * (wh, [])

Let w à w’. The problem is that the relation approximates the world (at function call), so w’ might not have enough “time” left to actually reach the halting state [].

37

Why it’s actually not that hard

2. Our definition:

w ² haltsn(¾) ´

|w| > n ! (9 wh. (|w|-|wh| · n) Æ

(w,¾) * (wh, []) )

This is actually very similar to the basic definition.

38

Why it’s actually not that hard

2. Our definition:

w ² haltsn(¾) ´

|w| > n ! (9 wh. (|w|-|wh| · n) Æ

(w,¾) * (wh, []) )

Here is the exists and requirement that we step to the halt state.

39

Why it’s actually not that hard

2. Our definition:

w ² haltsn(¾) ´

|w| > n ! (9 wh. (|w|-|wh| · n) Æ

(w,¾) * (wh, []) )

This is the key “trick” – if the amount of time left in w is not enough, then we become true (and thus hereditary) trivially.

40

Why it’s actually not that hard

2. Our definition:

w ² haltsn(¾) ´

|w| > n ! (9 wh. (|w|-|wh| · n) Æ

(w,¾) * (wh, []) )

We must be sure that n is really a bound on the number of steps required to halt. (Alternative: see paper’s Coq development.)

41

Why it’s actually not that hard

2. Our definition:

w ² haltsn(¾) ´

|w| > n ! (9 wh. (|w|-|wh| · n) Æ

(w,¾) * (wh, []) )

So, really not that bad. This is the “fundamental” trick that makes everything else possible.

42

Why it’s actually not that hard

2. See the paper (and/or Coq development) for details on how to use this definition to build up the definition for the Hoare tuple, etc.

Actually I have presented a slightly modified version (we continue to tinker), but feel free to ask us for the latest version.

43

Why it’s actually not that hard

3. The third objection is that termination is tricky and have never been done with step-indexing before. We observe the following:

• Our top-level “erased” theorems are stated in absolutely standard form: if a program is verified in our logic, then it terminates.

• No indirection theory in statement of (erased) top-level theorem.

44

Why it’s actually not that hard

3. The third objection is that termination is tricky and have never been done with step-indexing before. We observe the following:

• All our proofs are machine checked in Coq.

• Our core proofs are quite compact.

45

Compact Proofs

• The main file is 826 lines long, and contains:A. Semantic model of the terminating function

pointer, Hoare judgment, whole-program verification judgment

B. 10+ Hoare rules and soundness proofsC. 3 whole-program rules and soundness proofsD. Top-level theorems (if P is verified, then it halts)

• Really quite short…

46

47

V-rules

• We need another judgment, written ª : ¡, that says that program ª contains functions verified to “funptr” specifications in ¡.

• We will verify functions one at a time (or in the case of mutually-recursive groups, as a group).

• The “base case” is an easy rule:

48

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

49

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

1. We have verified part of ª to specification ¡

50

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

2. We want to add this specification for l

51

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

3. We must have verified the code for l

52

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

4. When the precondition P(a) holds

53

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

4. When the precondition P(a) holds and the termination measure is equal to some n.

54

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

5. This n is also an upper bound on the number of calls this function makes.

55

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

6. The postcondition is just ?: you can’t fall out the bottom

56

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

6. The postcondition is just ?: you can’t fall out the bottom, but the return condition is Q(a).

57

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

7. We can assume every funptr we have previously verified, and…

58

The “Vsimple” rule

So this looks pretty bad, but we can take it apart:

8. We can call ourselves using a modified function specification precondition: t has decreased.

59

The “Vsimple” rule

Here are the critical uses for t. If we assume the termination measure is equal to n at the functionstart, then we can make no more than n calls andcan recurse only when t < n.

60

Using Vsimple

• We will verify the unary addition function

• Our language, while Turing-complete, is extremely painful to program in; this is not fundamental but it does mean that our examples are somewhat “lame”.

• Expanding the result to a more programmable language would not be very difficult now.

61

Using Vsimple: naturals as lists

• listnat(0,0)• listnat(n,v) \Rightarrow listnat(n+1, (0,v))

• 0 0• 1 (0, 0)• 2 (0, (0, 0))• 3 (0, (0, (0, 0)))• … …

62

Precondition, postcondition, termination measure

addP(n,m) ´ 9 v1, v2. (r1 v1) Æ (r2 v2) Æ

listnat(n,v1) Æ listnat(m,v2)

addQ(n,m) ´ 9 v2. (r2 v2) Æ listnat(n+m,v2)

addt(½) ´ the unique n s.t. 9 v1. (½(r1) = v1) and

listnat(n,v1)

63

Code (stored at label 1)

64

The verification

• Actually, using the Hoare rules is very easy.

65

The verification

• Actually, using the Hoare rules is very easy.

• But very tedious. We won’t bore you.

• The more interesting question is, what does the verification obligation generated from Vsimple look like?

66

Verification obligation from Vsimple

xxx

n

¡n,

67

Vsimple, in a nutshell

• The Vsimple rule is fine for most functions, but it can only verify, well, simple recursive functions (as well as calls to both simple and complex functions previously verified).

• If we want “the goodies” (mutual recursion, polymorphic termination arguments, etc.) then we need to call in Vsimple’s big brother…

68

This is moderately horrible. We have (at least):

The “Vfull” rule

69

This is moderately horrible. We have (at least):

1. A set © of funptr specifications in the goal as well as the assumptions

The “Vfull” rule

70

This is moderately horrible. We have (at least):

2. The same basic termination measure trick

The “Vfull” rule

71

This is moderately horrible. We have (at least):

3. A parameter b used for higher-order functions

The “Vfull” rule

72

This is moderately horrible. We have (at least):

3. There can be a strong dependency between b and the other factors (e.g., the type of a)

The “Vfull” rule

73

The Vsimple rule is just a special case of Vfull.

The “Vfull” rule

74

Using Vfull

• We are going to verify the simplest nontrivial higher order function we can, “apply”.

• It takes as an argument a pair of a function f and an argument v and just applies f to v.

• The “interesting” part is how the polymorphism is verified as opposed to the function behavior.

75

Defining a calling convention

• To specify the “apply” function, we must define a calling convention for the sub-function

76

Defining a calling convention

• The registers r1 – r4 are callee-saves; registers from r5 ! 1 registers are caller-saves

77

Defining a calling convention

• A stdfun’s precondition, postcondition, and termination measure only depend on r0.

78

Apply’s precondition, postcondition, and termination measure

79

Apply’s precondition, postcondition, and termination measure

• These look “obvious” until you realize that P, Q, and t seem free in the definition. We will see how the Vfull rule “pipes” these in.

80

Apply’s precondition, postcondition, and termination measure

• Notice that we can use the called function’s termination measure to build our own

81

The code is simple, although the verification isa bit cluttered…

82

If you’ve got it, flaunt it: we toss in a higher-order assert for fun…

83

Notice how we track the termination measure

84

The verification obligation from Vfull

This is somewhat large (thank goodness for machine-checking!). There are a few points of particular interest.

85

The verification obligation from Vfull

1. We can check this function first, — i.e., ¡ = >That is, we verify this function before we verify thefunctions we will pass to it.

86

The verification obligation from Vfull

2. The Vfull rule “pipes in” t, P, and Q – in fact, thetriple (t,P,Q) is the “b” from Vfull.

87

The verification obligation from Vfull

3. As before, we thread the termination argument into the verification required.

88

“Stacking” apply

• As it happens, the apply function, itself, is a standard function (r1 – r4 are preserved, argument passed in r0, return in r0).

• In fact, we can pass “apply” to itself without difficulty. The rules will prevent us from unbounded recursion. We can “apply (apply (apply … (apply (f))))” but the function f “at the bottom” must terminate “on its own”.

89

More examples…

• We could go on to map, map of map, etc., but maybe it would be better here to just refer to the paper and mechanized development.

• Are there questions?

top related