|
| 1 | +-- Programming Language Technology DAT151/DIT231 |
| 2 | +-- 2024-12-17 Agda (version ≥2.6.0) |
| 3 | +-- From Dependent Types to Verified Compilation |
| 4 | + |
| 5 | +-- A correct compiler for Hutton's Razor to a stack machine |
| 6 | + |
| 7 | +-- Similar to an early pen-and-paper proof (register machine): |
| 8 | +-- |
| 9 | +-- John McCarthy and James Painter, 1967 |
| 10 | +-- Correctness of a Compiler for Arithmetic Expressions |
| 11 | +-- http://jmc.stanford.edu/articles/mcpain/mcpain.pdf |
| 12 | + |
| 13 | +open import Relation.Binary.PropositionalEquality |
| 14 | +open ≡-Reasoning |
| 15 | + |
| 16 | +-- Data types and pattern matching |
| 17 | +--------------------------------------------------------------------------- |
| 18 | + |
| 19 | +-- Example of a data type. |
| 20 | + |
| 21 | +data List (A : Set) : Set where |
| 22 | + [] : List A |
| 23 | + _∷_ : (x : A) (xs : List A) → List A -- \ : : A → List A → List A |
| 24 | + |
| 25 | +-- Natural numbers in unary notation. |
| 26 | + |
| 27 | +data Nat : Set where |
| 28 | + zero : Nat |
| 29 | + suc : (n : Nat) → Nat |
| 30 | + |
| 31 | +{-# BUILTIN NATURAL Nat #-} |
| 32 | + |
| 33 | +-- NATURAL gives us decimal notation for Nat. |
| 34 | + |
| 35 | +five = suc 4 |
| 36 | + |
| 37 | +infixl 10 _+_ |
| 38 | +infixr 6 _∷_ |
| 39 | + |
| 40 | +-- Addition. |
| 41 | + |
| 42 | +_+_ : (n m : Nat) → Nat |
| 43 | +zero + m = m |
| 44 | +suc n + m = suc (n + m) |
| 45 | + |
| 46 | +{- |
| 47 | +data Empty : Set where |
| 48 | +
|
| 49 | +inh : Empty |
| 50 | +inh = error "hoo" |
| 51 | +-} |
| 52 | + |
| 53 | +plus-0 : (n : Nat) → n + zero ≡ n -- \ = = |
| 54 | +plus-0 zero = refl {x = 0} |
| 55 | +plus-0 (suc n) = cong suc (plus-0 n) |
| 56 | + |
| 57 | +-- Indexed types: Fin, Vec |
| 58 | +--------------------------------------------------------------------------- |
| 59 | + |
| 60 | +-- Bounded numbers: Fin n = { m | m < n }. |
| 61 | + |
| 62 | +data Fin : Nat → Set where |
| 63 | + zero : {n : Nat} → Fin (suc n) |
| 64 | + suc : {n : Nat} (i : Fin n) → Fin (suc n) |
| 65 | + |
| 66 | +-- Example with hidden arguments. |
| 67 | + |
| 68 | +three : Fin 5 |
| 69 | +three = suc {n = 4} (suc (suc zero)) |
| 70 | + |
| 71 | +-- Vectors (length-indexed lists). |
| 72 | +-- Vec : Set → Nat → Set |
| 73 | + |
| 74 | +data Vec (A : Set) : Nat → Set where |
| 75 | + [] : Vec A zero |
| 76 | + _∷_ : {n : Nat} (x : A) (xs : Vec A n) → Vec A (suc n) |
| 77 | + |
| 78 | +-- Automatic quantification over hidden arguments. |
| 79 | + |
| 80 | +variable |
| 81 | + n : Nat |
| 82 | + A : Set |
| 83 | + |
| 84 | +-- Reading an element of a vector. |
| 85 | + |
| 86 | +lookup : {n : Nat} {A : Set} (i : Fin n) (xs : Vec A n) → A |
| 87 | +lookup zero (x ∷ xs) = x |
| 88 | +lookup (suc i) (x ∷ xs) = lookup i xs |
| 89 | + |
| 90 | +-- Expressions and interpretation: "Hutton's razor" |
| 91 | +--------------------------------------------------------------------------- |
| 92 | + |
| 93 | +-- We have a single type of (natural) numbers. |
| 94 | + |
| 95 | +Num = Nat |
| 96 | + |
| 97 | +-- Expressions over n variables. |
| 98 | + |
| 99 | +-- A variable is denoted by its number x < n. |
| 100 | + |
| 101 | +data Exp (n : Nat) : Set where |
| 102 | + var : (x : Fin n) → Exp n |
| 103 | + num : (w : Num) → Exp n |
| 104 | + plus : (e e' : Exp n) → Exp n |
| 105 | + |
| 106 | +-- (x + 3) + y |
| 107 | +ex1 : Exp 2 |
| 108 | +ex1 = plus (plus (var zero) (num 3)) (var (suc zero)) |
| 109 | + |
| 110 | +-- An environment maps each variable to its value. |
| 111 | + |
| 112 | +Value = Num |
| 113 | +Env = Vec Num |
| 114 | + |
| 115 | +-- Interpretation of expressions. |
| 116 | + |
| 117 | +eval : (e : Exp n) (γ : Env n) → Value |
| 118 | +eval (var x) γ = lookup x γ |
| 119 | +eval (num w) γ = w |
| 120 | +eval (plus e e₁) γ = eval e γ + eval e₁ γ |
| 121 | + |
| 122 | +v1 = eval ex1 (15 ∷ 1 ∷ []) |
| 123 | + |
| 124 | +-- A fragment of JVM |
| 125 | +--------------------------------------------------------------------------- |
| 126 | + |
| 127 | +StackSize = Nat -- Current stack size |
| 128 | +StoreSize = Nat -- Local variable store limit |
| 129 | +Addr = Fin -- Local variable address |
| 130 | +Word = Nat -- Word (should be 32bit signed int, but we use Nat here) |
| 131 | + |
| 132 | +-- JVM instructions. |
| 133 | +-- |
| 134 | +-- n = number of local variables |
| 135 | +-- m = stack size before instruction |
| 136 | +-- m' = stack size after instruction |
| 137 | + |
| 138 | +variable |
| 139 | + k l m m' : StackSize |
| 140 | + |
| 141 | +data Ins (n : StoreSize) : (m m' : StackSize) → Set where |
| 142 | + load : (a : Addr n) → Ins n m (1 + m) -- Load variable onto stack. |
| 143 | + add : Ins n (2 + m) (1 + m) -- Add top stack elements. |
| 144 | + ldc : (w : Word) → Ins n m (1 + m) -- Load constant onto stack. |
| 145 | + pop : Ins n (1 + m) m -- Remove top stack element. |
| 146 | + |
| 147 | +-- Instruction sequences. |
| 148 | +-- Note that for instruction concatenation, the index "l" has to match. |
| 149 | + |
| 150 | +data Inss (n : StoreSize) (m : StackSize) : (m' : StackSize) → Set where |
| 151 | + [] : Inss n m m |
| 152 | + ins : Ins n m l → Inss n m l |
| 153 | + _∙_ : (i : Inss n m l) (is : Inss n l k) → Inss n m k |
| 154 | + |
| 155 | +infixr 10 _∙_ |
| 156 | + |
| 157 | +-- Compilation of expressions |
| 158 | +--------------------------------------------------------------------------- |
| 159 | + |
| 160 | +-- The code for an expression leaves one additional value on the stack. |
| 161 | + |
| 162 | +compile : (e : Exp n) → Inss n m (suc m) |
| 163 | +compile (var x) = ins (load x) |
| 164 | +compile (num w) = ins (ldc w) |
| 165 | +compile (plus e e') = compile e ∙ compile e' ∙ ins add |
| 166 | + |
| 167 | +ss1 : Inss 2 0 1 |
| 168 | +ss1 = compile ex1 |
| 169 | + |
| 170 | +-- JVM small-step semantics |
| 171 | +--------------------------------------------------------------------------- |
| 172 | + |
| 173 | +-- JVM machine state (simplified). |
| 174 | + |
| 175 | +Store = Env |
| 176 | +Stack = Vec Word |
| 177 | + |
| 178 | +record State (n : StoreSize) (m : StackSize) : Set where |
| 179 | + constructor state |
| 180 | + field |
| 181 | + V : Store n |
| 182 | + S : Stack m |
| 183 | +open State |
| 184 | + |
| 185 | +-- Executing a JVM instruction. |
| 186 | + |
| 187 | +step : (i : Ins n m m') (s : State n m) → State n m' |
| 188 | +step (load a) (state V S) = state V (lookup a V ∷ S) |
| 189 | +step add (state V (v ∷ w ∷ S)) = state V (w + v ∷ S) |
| 190 | +step (ldc w) (state V S) = state V (w ∷ S) |
| 191 | +step pop (state V (_ ∷ S)) = state V S |
| 192 | + |
| 193 | +-- Compiler correctness |
| 194 | +--------------------------------------------------------------------------- |
| 195 | + |
| 196 | +-- Executing a series of JVM instructions. |
| 197 | + |
| 198 | +steps : (is : Inss n m m') (s : State n m) → State n m' |
| 199 | +steps (ins i) s = step i s |
| 200 | +steps [] s = s |
| 201 | +steps (is ∙ is') s = steps is' (steps is s) |
| 202 | + |
| 203 | +r1 = steps ss1 (state (15 ∷ 1 ∷ []) []) |
| 204 | + |
| 205 | +-- Pushing a word onto the stack. |
| 206 | + |
| 207 | +push : Word → State n m → State n (suc m) |
| 208 | +push w (state V S) = state V (w ∷ S) |
| 209 | + |
| 210 | +push-eval : (e : Exp n) (s : State n m) → State n (suc m) |
| 211 | +push-eval e (state V S) = state V (eval e V ∷ S) |
| 212 | + |
| 213 | + |
| 214 | +-- Compiler correctness theorem. |
| 215 | + |
| 216 | +-- Running the instructions for an expression e |
| 217 | +-- leaves the value of e on top of the stack. |
| 218 | + |
| 219 | +sound : (e : Exp n) (s : State n m) → |
| 220 | + |
| 221 | + steps (compile e) s ≡ push-eval e s |
| 222 | + |
| 223 | +-- Case: variables |
| 224 | +sound (var x) s = refl |
| 225 | + |
| 226 | +-- Case: number literals. |
| 227 | +sound (num w) s = refl |
| 228 | + |
| 229 | +-- Case: addition expression. |
| 230 | +sound (plus e e') s = begin |
| 231 | + steps (compile (plus e e')) s ≡⟨ refl ⟩ |
| 232 | + steps (compile e ∙ compile e' ∙ ins add) s ≡⟨ refl ⟩ |
| 233 | + steps (compile e' ∙ ins add) (steps (compile e) s) ≡⟨ cong (steps _) (sound e s) ⟩ |
| 234 | + steps (compile e' ∙ ins add) s' ≡⟨ refl ⟩ |
| 235 | + steps (ins add) (steps (compile e') s') ≡⟨ cong (steps _) (sound e' s) ⟩ |
| 236 | + steps (ins add) (push-eval e' s') ≡⟨ {! !} ⟩ |
| 237 | + push-eval (plus e e') s |
| 238 | + ∎ |
| 239 | + where s' = push-eval e s |
| 240 | + |
| 241 | +-- steps (compile (plus e e') s) |
| 242 | +-- = steps (compile e ∙ compile e' ∙ ins add) s |
| 243 | +-- = steps (compile e' ∙ ins add) (steps (compile e) s) -- by ind.hyp. |
| 244 | +-- = steps (compile e' ∙ ins add) (push-eval e s) |
| 245 | +-- = steps (ins add) (steps (compile e') (push-eval e s)) |
| 246 | +-- = steps (ins add) (push-eval e' (push-eval e s)) |
| 247 | +-- = step add (push-eval e' (push-eval e s)) |
| 248 | +-- = step add (push-eval e' (push-eval e (state V S))) -- s =: state V S |
| 249 | +-- = step add (push-eval e' (state V (eval e V ∷ S))) |
| 250 | +-- = step add (state V (eval e' V ∷ eval e V ∷ S)) |
| 251 | +-- = state V ((eval e V + eval e' V) ∷ S) |
| 252 | +-- = state V (eval (plus e e') V ∷ S) |
| 253 | +-- = push-eval (plus e e') (state V S) |
| 254 | +-- = push-eval (plus e e') s |
| 255 | + |
| 256 | +-- -} |
| 257 | +-- -} |
| 258 | +-- -} |
| 259 | +-- -} |
| 260 | +-- -} |
| 261 | +-- -} |
| 262 | +-- -} |
| 263 | +-- -} |
| 264 | +-- -} |
| 265 | +-- -} |
| 266 | +-- -} |
| 267 | +-- -} |
| 268 | +-- -} |
| 269 | +-- -} |
| 270 | +-- -} |
| 271 | +-- -} |
| 272 | +-- -} |
0 commit comments