#|$ACL2s-Preamble$; (include-book "m5") (begin-book t :ttags :all) ;$ACL2s-Preamble$|# (in-package "M5") ; Step Stuff (defthm step-opener (implies (consp (next-inst id s)) (equal (step id s) (if (equal (stat (find :id id (tt s))) 'active) (do-inst (next-inst id s) id s) s)))) (in-theory (disable step)) ; Run Stuff (defthm run-append (equal (run (append a b) s) (run b (run a s)))) (defthm run-opener (and (equal (run nil s) s) (equal (run (cons id sched) s) (run sched (step id s))))) (in-theory (disable run)) ; ----------------------------------------------------------------- ; The theorems above are analogous to the ones we needed for M1. But the ; presence of threads drastically complicates M5. The lemmas below are used by ; virtually every M5 code proof. They are independent of the application being ; verfied! ; In fact, you can read this file (including the lemmas above, especially ; run-append, as a collection of interesting properties of M5 (and perhaps the ; JVM) itself, rather than mere helper lemmas to prove code. ; One important concept we need is that of a well-formed thread table. In fact, ; in serious proofs we need to know A LOT about the shape of all the components of ; the M5 state. But in even the simplest proofs we need a little information about ; the thread table: ; The thread table contains entries for consecutive thread identifiers! ; Why do we need to know this? Consider just the problem of showing that a ; thread keeps its active status during a run. Suppose we're running thread 5 ; and know it is active. Suppose we take a step. Suppose in fact we execute ; (NEW "Class"). If "Class" extends "Thread" then we'll allocate a new thread ; id and make it's status inactive. Now consider the possibility that the new ; id is 5. Then thread 5 suddenly became inactive! How can we know the "new ; thread" is different from the old ones? Given that we generate a "new ; thread" by taking the length of the thread table, and knowing that the k ; threads in a table have distinct, natural number ids, we must bind all the ; nats from 0 to k-1. Strictly speaking, I don't know a reason for requiring ; that they be bound consecutively; any permutation would do. But in fact they ; are consecutive and that is easy to specify, so that's what I do. (defun well-formed-ttp (tt i) (if (endp tt) (equal tt nil) (and (equal (type (car tt)) 'THREAD) (equal (id (car tt)) i) (well-formed-ttp (cdr tt) (+ 1 i))))) ; This tells us what id's we can hope to find in tt. (defthm find-id-in-well-formed-tt (implies (and (well-formed-ttp tt i) (natp i) (natp j)) (iff (find :id j tt) (and (<= i j) (< j (+ i (len tt))))))) ; Here is how the thread table grows as we add new threads. (defthm len-replace (implies (well-formed-ttp tt i) (equal (len (replace key val new tt)) (if (find key val tt) (len tt) (+ 1 (len tt)))))) ; To prove that stepping preserves the status of a thread (under certain ; conditions) we have to know that the search up the stack done by THROW ; preserves status. (defthm execute-throw1-preserves-stat (implies (execute-throw1 ref id2 s) (equal (get :stat (find :id id (get :tt (execute-throw1 ref id2 s)))) (get :stat (find :id id (get :tt s)))))) ; So now we can prove that stepping preserves status -- if the thread table is ; well-formed and the instruction we step is not a call of "stop"! (defthm step-preserves-stat (implies (and (well-formed-ttp (tt s) 0) (not (and (equal (op-code (next-inst id s)) 'INVOKEVIRTUAL) (equal (nth 1 (arg1 (next-inst id s))) "stop"))) (not (and (equal (op-code (next-inst id s)) 'INVOKESPECIAL) (equal (nth 1 (arg1 (next-inst id s))) "stop")))) (equal (stat (find :id id (tt (step id s)))) (stat (find :id id (tt s))))) :hints (("Goal" :in-theory (enable step)))) ; Another truly obvious fact that we have to prove is that the class table is ; not changed by any instruction. On the real JVM this is not true: class ; loading changes the class table and class loading may happen in any number of ; places (basically any instruction which mentions a class). But on M5 the ; class table never changes. We have to prove it. ; We repeat the development above to establish that stepping preserves the ; class table. (defthm execute-throw1-preserves-ct (implies (execute-throw1 ref id2 s) (equal (ct (execute-throw1 ref id2 s)) (ct s)))) (defthm step-preserve-ct (equal (ct (step id s)) (ct s)) :hints (("Goal" :in-theory (enable step)))) ; We need to force repeat to open to give us the conses to eliminate runs. (defthm repeat-opener (and (equal (repeat id 0) nil) (implies (natp n) (equal (repeat id (+ 1 n)) (cons id (repeat id n)))))) ; The first of a pair of replace commands on a given key is irrelevant. This ; is what allows us to simplify (step id (step id (step id ...))) because each ; step introduces a replace into the thread table. (defthm replace-replace (implies (equal (get key new2) val) (equal (replace key val new1 (replace key val new2 x)) (replace key val new1 x)))) ; And of course, well-formedness is preserved! (defthm well-formed-ttp-replace (implies (and (well-formed-ttp tt i) (natp i) (natp j) (<= i j) (< j (+ i (len tt))) (equal (type thread) 'THREAD) (equal (id thread) j)) (well-formed-ttp (replace :id j thread tt) i)))