nipkow@43982: (* Author: Tobias Nipkow *) nipkow@10343: nipkow@43982: header "A Compiler for IMP" nipkow@10342: kleing@44296: theory Compiler imports Big_Step nipkow@43982: begin kleing@12429: kleing@44296: subsection "List setup" kleing@44296: kleing@44296: text {* kleing@44296: We are going to define a small machine language where programs are kleing@44296: lists of instructions. For nicer algebraic properties in our lemmas kleing@44296: later, we prefer @{typ int} to @{term nat} as program counter. kleing@44296: kleing@44296: Therefore, we define notation for size and indexing for lists kleing@44296: on @{typ int}: kleing@44296: *} kleing@44296: abbreviation "isize xs == int (length xs)" kleing@44296: kleing@44296: primrec kleing@44296: inth :: "'a list => int => 'a" (infixl "!!" 100) where kleing@44296: inth_Cons: "(x # xs) !! n = (if n = 0 then x else xs !! (n - 1))" kleing@44296: kleing@44296: text {* kleing@44296: The only additional lemma we need is indexing over append: kleing@44296: *} kleing@44296: lemma inth_append [simp]: kleing@44296: "0 \ n \ kleing@44296: (xs @ ys) !! n = (if n < isize xs then xs !! n else ys !! (n - isize xs))" kleing@44296: by (induct xs arbitrary: n) (auto simp: algebra_simps) kleing@44296: nipkow@43982: subsection "Instructions and Stack Machine" nipkow@43982: nipkow@43982: datatype instr = kleing@44296: LOADI int | kleing@44296: LOAD string | kleing@44296: ADD | nipkow@43982: STORE string | kleing@44296: JMP int | kleing@44296: JMPFLESS int | kleing@44296: JMPFGE int nipkow@43982: kleing@44296: (* reads slightly nicer *) kleing@44296: abbreviation kleing@44296: "JMPB i == JMP (-i)" kleing@44296: kleing@44296: type_synonym stack = "val list" kleing@44296: type_synonym config = "int\state\stack" nipkow@43982: nipkow@43982: abbreviation "hd2 xs == hd(tl xs)" nipkow@43982: abbreviation "tl2 xs == tl(tl xs)" nipkow@43982: kleing@44296: inductive iexec1 :: "instr \ config \ config \ bool" kleing@44296: ("(_/ \i (_ \/ _))" [50,0,0] 50) wenzelm@27362: where kleing@44296: "LOADI n \i (i,s,stk) \ (i+1,s, n#stk)" | kleing@44296: "LOAD x \i (i,s,stk) \ (i+1,s, s x # stk)" | kleing@44296: "ADD \i (i,s,stk) \ (i+1,s, (hd2 stk + hd stk) # tl2 stk)" | kleing@44296: "STORE n \i (i,s,stk) \ (i+1,s(n := hd stk),tl stk)" | kleing@44296: "JMP n \i (i,s,stk) \ (i+1+n,s,stk)" | kleing@44296: "JMPFLESS n \i (i,s,stk) \ (if hd2 stk < hd stk then i+1+n else i+1,s,tl2 stk)" | kleing@44296: "JMPFGE n \i (i,s,stk) \ (if hd2 stk >= hd stk then i+1+n else i+1,s,tl2 stk)" kleing@44296: kleing@44296: code_pred iexec1 . kleing@44296: kleing@44296: declare iexec1.intros kleing@44296: kleing@44871: definition kleing@44871: exec1 :: "instr list \ config \ config \ bool" ("(_/ \ (_ \/ _))" [50,0,0] 50) kleing@44871: where kleing@44296: "P \ c \ c' = kleing@44296: (\i s stk. c = (i,s,stk) \ P!!i \i (i,s,stk) \ c' \ 0 \ i \ i < isize P)" kleing@44871: kleing@44871: declare exec1_def [simp] kleing@44871: kleing@44871: lemma exec1I [intro, code_pred_intro]: kleing@44871: "\ P!!i \i (i,s,stk) \ c'; 0 \ i; i < isize P \ \ P \ (i,s,stk) \ c'" kleing@44871: by simp nipkow@43982: nipkow@43982: inductive exec :: "instr list \ config \ config \ bool" ("_/ \ (_ \*/ _)" 50) nipkow@43982: where nipkow@43982: refl: "P \ c \* c" | nipkow@43982: step: "P \ c \ c' \ P \ c' \* c'' \ P \ c \* c''" nipkow@43982: kleing@44296: declare refl[intro] step[intro] nipkow@43982: nipkow@43982: lemmas exec_induct = exec.induct[split_format(complete)] nipkow@43982: kleing@44871: code_pred exec by force nipkow@43982: nipkow@43982: values nipkow@43982: "{(i,map t [''x'',''y''],stk) | i t stk. nipkow@43982: [LOAD ''y'', STORE ''x''] \ kleing@43999: (0, [''x'' \ 3, ''y'' \ 4], []) \* (i,t,stk)}" nipkow@43982: nipkow@43982: nipkow@43982: subsection{* Verification infrastructure *} nipkow@43982: nipkow@43982: lemma exec_trans: "P \ c \* c' \ P \ c' \* c'' \ P \ c \* c''" kleing@44296: by (induct rule: exec.induct) fastsimp+ nipkow@43982: kleing@44296: inductive_cases iexec1_cases [elim!]: kleing@44296: "LOADI n \i c \ c'" kleing@44296: "LOAD x \i c \ c'" kleing@44296: "ADD \i c \ c'" kleing@44296: "STORE n \i c \ c'" kleing@44296: "JMP n \i c \ c'" kleing@44296: "JMPFLESS n \i c \ c'" kleing@44296: "JMPFGE n \i c \ c'" nipkow@43982: kleing@44296: text {* Simplification rules for @{const iexec1}. *} kleing@44296: lemma iexec1_simps [simp]: kleing@44296: "LOADI n \i c \ c' = (\i s stk. c = (i, s, stk) \ c' = (i + 1, s, n # stk))" kleing@44296: "LOAD x \i c \ c' = (\i s stk. c = (i, s, stk) \ c' = (i + 1, s, s x # stk))" kleing@44296: "ADD \i c \ c' = kleing@44296: (\i s stk. c = (i, s, stk) \ c' = (i + 1, s, (hd2 stk + hd stk) # tl2 stk))" kleing@44296: "STORE x \i c \ c' = kleing@44296: (\i s stk. c = (i, s, stk) \ c' = (i + 1, s(x \ hd stk), tl stk))" kleing@44296: "JMP n \i c \ c' = (\i s stk. c = (i, s, stk) \ c' = (i + 1 + n, s, stk))" kleing@44296: "JMPFLESS n \i c \ c' = kleing@44296: (\i s stk. c = (i, s, stk) \ kleing@44296: c' = (if hd2 stk < hd stk then i + 1 + n else i + 1, s, tl2 stk))" kleing@44296: "JMPFGE n \i c \ c' = kleing@44296: (\i s stk. c = (i, s, stk) \ kleing@44296: c' = (if hd stk \ hd2 stk then i + 1 + n else i + 1, s, tl2 stk))" kleing@44296: by (auto split del: split_if intro!: iexec1.intros) kleing@44296: nipkow@43982: nipkow@43982: text{* Below we need to argue about the execution of code that is embedded in nipkow@43982: larger programs. For this purpose we show that execution is preserved by nipkow@43982: appending code to the left or right of a program. *} nipkow@43982: kleing@44296: lemma exec1_appendR: "P \ c \ c' \ P@P' \ c \ c'" kleing@44296: by auto nipkow@11275: nipkow@43982: lemma exec_appendR: "P \ c \* c' \ P@P' \ c \* c'" kleing@44296: by (induct rule: exec.induct) (fastsimp intro: exec1_appendR)+ nipkow@13095: kleing@44296: lemma iexec1_shiftI: kleing@44296: assumes "X \i (i,s,stk) \ (i',s',stk')" kleing@44296: shows "X \i (n+i,s,stk) \ (n+i',s',stk')" kleing@44296: using assms by cases auto kleing@44296: kleing@44296: lemma iexec1_shiftD: kleing@44296: assumes "X \i (n+i,s,stk) \ (n+i',s',stk')" kleing@44296: shows "X \i (i,s,stk) \ (i',s',stk')" kleing@44296: using assms by cases auto kleing@44296: kleing@44296: lemma iexec_shift [simp]: kleing@44296: "(X \i (n+i,s,stk) \ (n+i',s',stk')) = (X \i (i,s,stk) \ (i',s',stk'))" kleing@44296: by (blast intro: iexec1_shiftI dest: iexec1_shiftD) kleing@44296: nipkow@43982: lemma exec1_appendL: kleing@44296: "P \ (i,s,stk) \ (i',s',stk') \ kleing@44296: P' @ P \ (isize(P')+i,s,stk) \ (isize(P')+i',s',stk')" kleing@44296: by simp nipkow@13095: nipkow@43982: lemma exec_appendL: nipkow@43982: "P \ (i,s,stk) \* (i',s',stk') \ kleing@44296: P' @ P \ (isize(P')+i,s,stk) \* (isize(P')+i',s',stk')" kleing@44296: by (induct rule: exec_induct) (blast intro!: exec1_appendL)+ nipkow@43982: nipkow@43982: text{* Now we specialise the above lemmas to enable automatic proofs of nipkow@43982: @{prop "P \ c \* c'"} where @{text P} is a mixture of concrete instructions and nipkow@43982: pieces of code that we already know how they execute (by induction), combined nipkow@43982: by @{text "@"} and @{text "#"}. Backward jumps are not supported. nipkow@43982: The details should be skipped on a first reading. nipkow@43982: kleing@44296: If we have just executed the first instruction of the program, drop it: *} nipkow@43982: kleing@44296: lemma exec_Cons_1 [intro]: kleing@44296: "P \ (0,s,stk) \* (j,t,stk') \ kleing@44296: instr#P \ (1,s,stk) \* (1+j,t,stk')" kleing@44296: by (drule exec_appendL[where P'="[instr]"]) simp nipkow@10342: nipkow@43982: lemma exec_appendL_if[intro]: kleing@44296: "isize P' <= i kleing@44296: \ P \ (i - isize P',s,stk) \* (i',s',stk') kleing@44296: \ P' @ P \ (i,s,stk) \* (isize P' + i',s',stk')" kleing@44296: by (drule exec_appendL[where P'=P']) simp nipkow@13095: nipkow@43982: text{* Split the execution of a compound program up into the excution of its nipkow@43982: parts: *} nipkow@13095: nipkow@43982: lemma exec_append_trans[intro]: nipkow@43982: "P \ (0,s,stk) \* (i',s',stk') \ kleing@44296: isize P \ i' \ kleing@44296: P' \ (i' - isize P,s',stk') \* (i'',s'',stk'') \ kleing@44296: j'' = isize P + i'' nipkow@43982: \ nipkow@43982: P @ P' \ (0,s,stk) \* (j'',s'',stk'')" kleing@44296: by(metis exec_trans[OF exec_appendR exec_appendL_if]) nipkow@13095: nipkow@43982: kleing@44296: declare Let_def[simp] nipkow@43982: nipkow@43982: nipkow@43982: subsection "Compilation" nipkow@43982: nipkow@43982: fun acomp :: "aexp \ instr list" where nipkow@43982: "acomp (N n) = [LOADI n]" | nipkow@43982: "acomp (V x) = [LOAD x]" | nipkow@43982: "acomp (Plus a1 a2) = acomp a1 @ acomp a2 @ [ADD]" nipkow@43982: nipkow@43982: lemma acomp_correct[intro]: kleing@44296: "acomp a \ (0,s,stk) \* (isize(acomp a),s,aval a s#stk)" kleing@44296: by (induct a arbitrary: stk) fastsimp+ nipkow@13095: kleing@44296: fun bcomp :: "bexp \ bool \ int \ instr list" where kleing@44296: "bcomp (B bv) c n = (if bv=c then [JMP n] else [])" | nipkow@43982: "bcomp (Not b) c n = bcomp b (\c) n" | nipkow@43982: "bcomp (And b1 b2) c n = nipkow@43982: (let cb2 = bcomp b2 c n; kleing@44296: m = (if c then isize cb2 else isize cb2+n); nipkow@43982: cb1 = bcomp b1 False m nipkow@43982: in cb1 @ cb2)" | nipkow@43982: "bcomp (Less a1 a2) c n = nipkow@43982: acomp a1 @ acomp a2 @ (if c then [JMPFLESS n] else [JMPFGE n])" nipkow@43982: nipkow@43982: value nipkow@43982: "bcomp (And (Less (V ''x'') (V ''y'')) (Not(Less (V ''u'') (V ''v'')))) nipkow@43982: False 3" nipkow@43982: nipkow@43982: lemma bcomp_correct[intro]: kleing@44296: "0 \ n \ kleing@44296: bcomp b c n \ kleing@44296: (0,s,stk) \* (isize(bcomp b c n) + (if c = bval b s then n else 0),s,stk)" nipkow@43982: proof(induct b arbitrary: c n m) nipkow@43982: case Not kleing@44296: from Not(1)[where c="~c"] Not(2) show ?case by fastsimp nipkow@13095: next nipkow@43982: case (And b1 b2) kleing@44296: from And(1)[of "if c then isize (bcomp b2 c n) else isize (bcomp b2 c n) + n" kleing@44296: "False"] kleing@44296: And(2)[of n "c"] And(3) kleing@44296: show ?case by fastsimp nipkow@43982: qed fastsimp+ nipkow@13095: nipkow@43982: fun ccomp :: "com \ instr list" where nipkow@43982: "ccomp SKIP = []" | nipkow@43982: "ccomp (x ::= a) = acomp a @ [STORE x]" | nipkow@43982: "ccomp (c\<^isub>1;c\<^isub>2) = ccomp c\<^isub>1 @ ccomp c\<^isub>2" | nipkow@43982: "ccomp (IF b THEN c\<^isub>1 ELSE c\<^isub>2) = kleing@44296: (let cc\<^isub>1 = ccomp c\<^isub>1; cc\<^isub>2 = ccomp c\<^isub>2; cb = bcomp b False (isize cc\<^isub>1 + 1) kleing@44296: in cb @ cc\<^isub>1 @ JMP (isize cc\<^isub>2) # cc\<^isub>2)" | nipkow@43982: "ccomp (WHILE b DO c) = kleing@44296: (let cc = ccomp c; cb = bcomp b False (isize cc + 1) kleing@44296: in cb @ cc @ [JMPB (isize cb + isize cc + 1)])" nipkow@13095: nipkow@43982: value "ccomp nipkow@43982: (IF Less (V ''u'') (N 1) THEN ''u'' ::= Plus (V ''u'') (N 1) nipkow@43982: ELSE ''v'' ::= V ''u'')" nipkow@13095: nipkow@43982: value "ccomp (WHILE Less (V ''u'') (N 1) DO (''u'' ::= Plus (V ''u'') (N 1)))" nipkow@13095: nipkow@13095: nipkow@43982: subsection "Preservation of sematics" nipkow@43982: kleing@44296: lemma ccomp_bigstep: kleing@44296: "(c,s) \ t \ ccomp c \ (0,s,stk) \* (isize(ccomp c),t,stk)" nipkow@43982: proof(induct arbitrary: stk rule: big_step_induct) nipkow@43982: case (Assign x a s) kleing@44296: show ?case by (fastsimp simp:fun_upd_def cong: if_cong) nipkow@13095: next nipkow@43982: case (Semi c1 s1 s2 c2 s3) nipkow@43982: let ?cc1 = "ccomp c1" let ?cc2 = "ccomp c2" kleing@44296: have "?cc1 @ ?cc2 \ (0,s1,stk) \* (isize ?cc1,s2,stk)" kleing@44296: using Semi.hyps(2) by fastsimp nipkow@43982: moreover kleing@44296: have "?cc1 @ ?cc2 \ (isize ?cc1,s2,stk) \* (isize(?cc1 @ ?cc2),s3,stk)" kleing@44296: using Semi.hyps(4) by fastsimp nipkow@43982: ultimately show ?case by simp (blast intro: exec_trans) nipkow@13095: next nipkow@43982: case (WhileTrue b s1 c s2 s3) nipkow@43982: let ?cc = "ccomp c" kleing@44296: let ?cb = "bcomp b False (isize ?cc + 1)" nipkow@43982: let ?cw = "ccomp(WHILE b DO c)" kleing@44296: have "?cw \ (0,s1,stk) \* (isize ?cb + isize ?cc,s2,stk)" nipkow@43982: using WhileTrue(1,3) by fastsimp nipkow@43982: moreover kleing@44296: have "?cw \ (isize ?cb + isize ?cc,s2,stk) \* (0,s2,stk)" kleing@44296: by fastsimp nipkow@43982: moreover kleing@44296: have "?cw \ (0,s2,stk) \* (isize ?cw,s3,stk)" by(rule WhileTrue(5)) nipkow@43982: ultimately show ?case by(blast intro: exec_trans) nipkow@43982: qed fastsimp+ nipkow@13095: webertj@20217: end