├── .gitignore ├── LICENSE ├── Main.lean ├── README.md ├── Wasm.lean ├── Wasm ├── AuxDefs.lean ├── Binary.lean ├── Binary │ ├── Index.lean │ ├── Instr.lean │ ├── Module.lean │ ├── Opcode.lean │ └── Typ.lean ├── Dynamics.lean ├── Dynamics │ ├── Address.lean │ ├── Context.lean │ ├── Dynamics.lean │ ├── Evaluation.lean │ ├── Instance.lean │ ├── Instr.lean │ ├── Stack.lean │ └── Value.lean ├── Notation.lean ├── Syntax.lean ├── Syntax │ ├── Index.lean │ ├── Instr.lean │ ├── Module.lean │ ├── Typ.lean │ └── Value.lean ├── Text.lean ├── Text │ ├── Ident.lean │ ├── Index.lean │ ├── Instr.lean │ ├── InstrTrans.lean │ ├── Module.lean │ ├── Notation │ │ ├── Index.lean │ │ ├── Instr.lean │ │ ├── Typ.lean │ │ └── Value.lean │ ├── Trans.lean │ └── Typ.lean ├── Validation.lean ├── Validation │ ├── Context.lean │ ├── Module.lean │ ├── Statics.lean │ └── Typ.lean └── Vec.lean ├── bin └── wasm ├── lake-manifest.json ├── lakefile.lean └── lean-toolchain /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.wasm 3 | *.wat 4 | /.lake 5 | -------------------------------------------------------------------------------- /Main.lean: -------------------------------------------------------------------------------- 1 | import Wasm 2 | import Cli 3 | 4 | def version := "24.06.0" 5 | 6 | open Cli 7 | 8 | inductive Wasm.Output 9 | | wat 10 | | wasm 11 | deriving Inhabited 12 | 13 | def Wasm.outputText (p : Parsed) (msg : String) : IO UInt32 := do 14 | match p.flag? "output" with 15 | | .some file => 16 | let output := file.as! String 17 | IO.FS.writeFile output msg 18 | return 0 19 | | .none => 20 | IO.println msg 21 | return 0 22 | 23 | def Wasm.outputBin (p : Parsed) 24 | (input : System.FilePath) 25 | (data : Wasm.Binary.ByteSeq) 26 | : IO UInt32 := do 27 | let output := 28 | match p.flag? "output" with 29 | | .some file => file.as! String 30 | | .none => input.withExtension "wasm" 31 | IO.FS.writeBinFile output ⟨data.toArray⟩ 32 | return 0 33 | 34 | def run (p : Parsed) : IO UInt32 := do 35 | 36 | if !p.hasPositionalArg "input" then 37 | panic! "Missing file argument" 38 | let input : System.FilePath := 39 | p.positionalArg! "input" |>.as! String 40 | 41 | if !(← input.pathExists) then 42 | panic! s!"Input file does not exist: {input}" 43 | if ← input.isDir then 44 | panic! s!"Input path is a directory: {input}" 45 | 46 | let emit : Wasm.Output := 47 | match p.flag? "emit" |>.map (·.as! String |>.toLower) with 48 | | .some "wasm" => .wasm 49 | | .some "wat" => .wat 50 | | .some x => panic! s!"Unknown emit target '{x}'!" 51 | | .none => .wat 52 | 53 | -- must be a WASM file for now :) 54 | let contents ← IO.FS.readBinFile input 55 | let init := Wasm.Binary.Bytecode.State.new contents 56 | 57 | match (Wasm.Binary.Module.ofOpcode).run init with 58 | | (.error e, s) => 59 | IO.println s!"ERROR:\n\n{e}\n---" 60 | IO.println s!"{String.intercalate "\n" s.log.reverse}\n" 61 | return 1 62 | | (.ok mod, s) => 63 | match emit with 64 | | .wat => Wasm.outputText p (Wasm.Text.Module.toString mod) 65 | | .wasm => Wasm.outputBin p input (Wasm.Binary.Module.toOpcode mod) 66 | return 0 67 | 68 | def topCmd : Cmd := `[Cli| 69 | wasm VIA run; [version] 70 | "A verified (WIP) implementation of WebAssembly" 71 | 72 | FLAGS: 73 | e, emit : String; "Specify the output format (either WAT or WASM)" 74 | o, output : String; "Specify output file" 75 | 76 | ARGS: 77 | input : String; "The input file" 78 | ] 79 | 80 | def main (args : List String) : IO UInt32 := topCmd.validate args 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WasmLean 2 | 3 | A formalisation of the semantics of WebAssembly in Lean. 4 | 5 | This is currently intended to be used in the target of a 6 | [C0 (C subset) compiler](https://github.com/T-Brick/c0deine) also written in 7 | Lean. Hence, efforts here will mostly reflect parts of WASM that are useful 8 | there (i.e. Vector and Float related instructions may lag behind the rest of the project). 9 | 10 | ## Usage 11 | 12 | Currently only WASM (binary files) can be converted into WAT (wasm text format). 13 | Although, no checks will be performed beyond parsing (i.e. no typechecking, 14 | etc.). 15 | 16 | First, use `lake build` to compile the project. 17 | 18 | Then you can run the following: 19 | ``` 20 | bin/wasm file.wasm 21 | bin/wasm --output file.wat file.wasm 22 | ``` 23 | The first line will print the corresponding WAT program to the terminal. The 24 | second writes it to the file `file.wat`. If any error occurs an error message 25 | will be printed (for both commands). 26 | 27 | You can also specify what you are emitting: 28 | ``` 29 | bin/wasm --output file.wat --emit wat file.wasm 30 | ``` 31 | 32 | ## Project Organisation/Structure 33 | 34 | Files are generally organised into a similar layout as defined by the WASM spec: 35 | - [Syntax](Wasm/Syntax/) corresponds to [Structure](https://webassembly.github.io/spec/core/syntax/index.html) 36 | - [Validation](Wasm/Validation/) corresponds to [Validation](https://webassembly.github.io/spec/core/valid/index.html) 37 | - [Dynamics](Wasm/Dynamics/) corresponds to [Execution](https://webassembly.github.io/spec/core/exec/index.html) 38 | - [Binary](Wasm/Binary/) corresponds to [Binary Format](https://webassembly.github.io/spec/core/binary/index.html) 39 | - [Text](Wasm/Text/) corresponds to [Text Format](https://webassembly.github.io/spec/core/text/index.html) 40 | 41 | The most notably deviation to this is that the WASM definition of integers is 42 | defined in the [Numbers](https://github.com/T-Brick/Numbers) library. 43 | 44 | ## Progress/Todo 45 | 46 | Vectors are not implemented or represented. Instructions for floats are 47 | represented, but the implementation of floats isn't yet. 48 | 49 | - [ ] Syntax/Structure 50 | - [x] Values 51 | - [x] Types 52 | - [x] Instructions 53 | - [x] Modules 54 | - [ ] Statics/Validation 55 | - [x] Types 56 | - [x] Instructions 57 | - [x] Modules 58 | - [ ] Type-checker 59 | - [ ] Dynamics/Execution 60 | - [x] Runtime Structure 61 | - [ ] Numerics 62 | - [x] Integer Operations 63 | - [ ] Floating-point Operations 64 | - [ ] Conversions 65 | - [ ] Instructions 66 | - [ ] Numeric 67 | - [ ] Floating-point 68 | - [x] All other numerics 69 | - [x] Reference 70 | - [ ] Vector 71 | - [x] Parametric 72 | - [x] Variable 73 | - [x] Table 74 | - [ ] Memory 75 | - [ ] Blocks 76 | - [ ] Function Calls 77 | - [ ] Expressions 78 | - [ ] Modules 79 | - [ ] External Typing 80 | - [ ] Value Typing 81 | - [ ] Allocation 82 | - [ ] Instantiation 83 | - [ ] Invocation 84 | - [ ] Intepreter 85 | - [ ] Formats 86 | - [x] Binary 87 | - [ ] Text 88 | - [ ] Parsing 89 | - [x] Printing 90 | - [ ] Validation/Transformation 91 | -------------------------------------------------------------------------------- /Wasm.lean: -------------------------------------------------------------------------------- 1 | import Numbers 2 | import Wasm.Vec 3 | import Wasm.AuxDefs 4 | 5 | import Wasm.Syntax 6 | import Wasm.Validation 7 | import Wasm.Dynamics 8 | import Wasm.Binary 9 | import Wasm.Text 10 | -------------------------------------------------------------------------------- /Wasm/AuxDefs.lean: -------------------------------------------------------------------------------- 1 | import Batteries 2 | import Mathlib.Data.List.Basic 3 | import Mathlib.Data.List.OfFn 4 | import Mathlib.Tactic 5 | 6 | theorem Nat.lt_left_add {a b c : Nat} (h₁ : a + b < c) : a < c := by 7 | induction b with 8 | | zero => exact h₁ 9 | | succ n ih => 10 | have h₂ := Nat.lt_succ_of_lt h₁ 11 | rw [Nat.add_succ] at h₂ 12 | have h₃ := Nat.lt_of_succ_lt_succ h₂ 13 | apply ih h₃ 14 | 15 | -- theorem Nat.lt_add_left {a b c : Nat} (h : a + b < c) : b < c := by 16 | -- rw [Nat.add_comm] at h 17 | -- apply lt_left_add h 18 | 19 | 20 | theorem Nat.le_trans_lt {a b c : Nat} (h₁ : a ≤ b) (h₂ : b < c) : a < c := by 21 | cases h₁ with 22 | | refl => exact h₂ 23 | | step h => apply Nat.lt_trans (Nat.lt_succ_of_le h) h₂ 24 | 25 | theorem Nat.two_pow_succ_pred : Nat.succ (Nat.pred (2 ^ n)) = 2 ^ n := 26 | Nat.lt_iff_le_and_ne.mp (Nat.pos_pow_of_pos n (Nat.zero_lt_succ 1)) 27 | |>.right 28 | |>.symm 29 | |> Nat.succ_pred 30 | 31 | theorem Nat.zero_lt_two_pow {n : Nat} : 0 < 2 ^ n := by 32 | exact Nat.pos_pow_of_pos n ((Nat.zero_lt_succ 1)) 33 | 34 | 35 | def String.concatWith [ToString α] (str : String) (list : List α) : String := 36 | String.intercalate str (list.map toString) 37 | 38 | @[simp] def Nat.sub_lt_sub {k m n : Nat} (h₁ : n < k) (h₂ : k ≤ m) 39 | : m - k < m - n := by 40 | apply Or.elim (Nat.le_iff_lt_or_eq.mp h₂) <;> intro h₂ 41 | . exact Nat.sub_lt_sub_left (Nat.lt_trans h₁ h₂) h₁ 42 | . simp [*] at *; exact h₁ 43 | -------------------------------------------------------------------------------- /Wasm/Binary.lean: -------------------------------------------------------------------------------- 1 | /- WASM Binary Representation -/ 2 | import Wasm.Binary.Index 3 | import Wasm.Binary.Instr 4 | import Wasm.Binary.Module 5 | import Wasm.Binary.Opcode 6 | import Wasm.Binary.Typ 7 | -------------------------------------------------------------------------------- /Wasm/Binary/Index.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Binary.Opcode 2 | import Wasm.Syntax.Index 3 | open Numbers 4 | 5 | namespace Wasm.Binary.Module.Index 6 | 7 | open Wasm.Syntax.Module.Index 8 | 9 | instance : Opcode Typ := ⟨toOpcode ∘ cast (by unfold Typ ; rfl), 10 | Bytecode.err_log "Parsing type index." do 11 | let res ← ofOpcode 12 | return cast (by unfold Typ; rfl) res 13 | ⟩ 14 | instance : Opcode Function := ⟨toOpcode ∘ cast (by unfold Function; rfl), 15 | Bytecode.err_log "Parsing function index." do 16 | let res ← ofOpcode 17 | return cast (by unfold Function; rfl) res 18 | ⟩ 19 | instance : Opcode Table := ⟨toOpcode ∘ cast (by unfold Table ; rfl), 20 | Bytecode.err_log "Parsing table index." do 21 | let res ← ofOpcode 22 | return cast (by unfold Table; rfl) res 23 | ⟩ 24 | instance : Opcode Memory := ⟨toOpcode ∘ cast (by unfold Memory ; rfl), 25 | Bytecode.err_log "Parsing memory index." do 26 | let res ← ofOpcode 27 | return cast (by unfold Memory; rfl) res 28 | ⟩ 29 | instance : Opcode Global := ⟨toOpcode ∘ cast (by unfold Global ; rfl), 30 | Bytecode.err_log "Parsing global index." do 31 | let res ← ofOpcode 32 | return cast (by unfold Global; rfl) res 33 | ⟩ 34 | instance : Opcode Element := ⟨toOpcode ∘ cast (by unfold Element ; rfl), 35 | Bytecode.err_log "Parsing element index." do 36 | let res ← ofOpcode 37 | return cast (by unfold Element; rfl) res 38 | ⟩ 39 | instance : Opcode Data := ⟨toOpcode ∘ cast (by unfold Data ; rfl), 40 | Bytecode.err_log "Parsing data index." do 41 | let res ← ofOpcode 42 | return cast (by unfold Data; rfl) res 43 | ⟩ 44 | instance : Opcode Local := ⟨toOpcode ∘ cast (by unfold Local ; rfl), 45 | Bytecode.err_log "Parsing local index." do 46 | let res ← ofOpcode 47 | return cast (by unfold Local; rfl) res 48 | ⟩ 49 | instance : Opcode Label := ⟨toOpcode ∘ cast (by unfold Label ; rfl), 50 | Bytecode.err_log "Parsing label index." do 51 | let res ← ofOpcode 52 | return cast (by unfold Label; rfl) res 53 | ⟩ 54 | -------------------------------------------------------------------------------- /Wasm/Binary/Module.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Syntax.Module 2 | import Wasm.Binary.Instr 3 | import Wasm.Syntax.Instr 4 | open Numbers 5 | 6 | namespace Wasm.Binary.Module 7 | 8 | open Wasm.Syntax Wasm.Syntax.Module 9 | 10 | inductive Section (N : Byte) (B : Type) [Opcode B] 11 | | empty 12 | | data : (size : Unsigned32) → (cont : B) → Section N B 13 | 14 | def Section.mk [Opcode B] (N : Byte) (b : B) : Section N B := 15 | let size := Unsigned.ofNat (toOpcode b).length 16 | .data size b 17 | 18 | def Section.mk_opt [Opcode B] (N : Byte) : Option B → Section N B 19 | | .none => .empty 20 | | .some b => Section.mk N b 21 | 22 | def Section.toVec [Opcode B] : Section N (Vec B) → Vec B 23 | | .empty => Vec.nil 24 | | .data _ cont => cont 25 | 26 | def Section.toOption [Opcode B] : Section N B → Option B 27 | | .empty => .none 28 | | .data _ cont => .some cont 29 | 30 | nonrec def Section.toOpcode [Opcode B] : Section N B → ByteSeq 31 | | .empty => [] 32 | | .data size cont => N :: toOpcode size ++ toOpcode cont 33 | 34 | nonrec def Section.ofOpcode [Opcode B] : Bytecode (Section N B) := do 35 | if ¬(← Bytecode.at_end) then 36 | let n ← Bytecode.peekByte 37 | if N = n then 38 | let _ ← Bytecode.readByte -- correct section id 39 | let size : Unsigned32 ← ofOpcode 40 | Bytecode.err_log s!"Parsing section id={N} size={size}." do 41 | let init ← Bytecode.pos 42 | let cont : B ← ofOpcode 43 | let h_after ← Bytecode.pos 44 | let rsize := Unsigned.ofNat (h_after - init) 45 | if size = rsize then return Section.data size cont else 46 | Bytecode.errMsg s!"Section {N} expected size {size} got {rsize}." 47 | else return .empty 48 | else return .empty 49 | 50 | instance [Opcode B] : Opcode (Section N B) := 51 | ⟨Section.toOpcode, Section.ofOpcode⟩ 52 | 53 | 54 | structure Section.Custom where 55 | size : Unsigned32 56 | name : Value.Name 57 | data : List Byte 58 | 59 | abbrev Section.Custom? := Option Section.Custom 60 | 61 | -- since we need to know the size to read custom sections, we won't use the 62 | -- section inductive (rather we embed them together for parsing/output). 63 | nonrec def Section.Custom.toOpcode (custom : Custom) : ByteSeq := 64 | Wasm.Binary.toOpcode (0 : Byte) 65 | ++ Wasm.Binary.toOpcode custom.size 66 | ++ Wasm.Binary.toOpcode custom.name 67 | ++ (custom.data.map Wasm.Binary.toOpcode).join 68 | 69 | nonrec def Section.Custom.ofOpcode : Bytecode Custom := 70 | Bytecode.err_log "Parsing custom section." do 71 | let N ← Wasm.Binary.ofOpcode 72 | if N ≠ 0 then Bytecode.errMsg "Custom section id mismatch." 73 | 74 | let size : Unsigned32 ← Wasm.Binary.ofOpcode 75 | let init ← Bytecode.pos 76 | let name ← Wasm.Binary.ofOpcode 77 | let datalen := size.toNat - ((← Bytecode.pos) - init) 78 | let data ← Bytecode.takeBytes datalen 79 | return ⟨size, name, data.toList⟩ 80 | 81 | instance : Opcode Section.Custom := 82 | ⟨Section.Custom.toOpcode, Section.Custom.ofOpcode⟩ 83 | 84 | 85 | abbrev Section.Typ := Section 1 (Vec Typ.Func) 86 | 87 | 88 | nonrec def Import.Description.toOpcode : Import.Description → ByteSeq 89 | | .func x => 0x00 :: toOpcode x 90 | | .table tt => 0x01 :: toOpcode tt 91 | | .mem mt => 0x02 :: toOpcode mt 92 | | .globl gt => 0x03 :: toOpcode gt 93 | 94 | nonrec def Import.Description.ofOpcode : Bytecode Import.Description := 95 | Bytecode.err_log "Parsing import description." do 96 | match ← Bytecode.readByte with 97 | | 0x00 => return Import.Description.func (← ofOpcode) 98 | | 0x01 => return Import.Description.table (← ofOpcode) 99 | | 0x02 => return Import.Description.mem (← ofOpcode) 100 | | 0x03 => return Import.Description.globl (← ofOpcode) 101 | | _ => Bytecode.err 102 | 103 | instance : Opcode Import.Description := 104 | ⟨Import.Description.toOpcode, Import.Description.ofOpcode⟩ 105 | 106 | nonrec def Import.toOpcode (imp : Import) : ByteSeq := 107 | toOpcode imp.module ++ toOpcode imp.name ++ toOpcode imp.desc 108 | 109 | nonrec def Import.ofOpcode : Bytecode Import := 110 | Bytecode.err_log "Parsing import section." do 111 | let mod ← ofOpcode 112 | let name ← ofOpcode 113 | let desc ← ofOpcode 114 | return ⟨mod, name, desc⟩ 115 | 116 | instance : Opcode Import := ⟨Import.toOpcode, Import.ofOpcode⟩ 117 | 118 | abbrev Section.Import := Section 2 (Vec Wasm.Syntax.Module.Import) 119 | 120 | 121 | abbrev Section.Function := Section 3 (Vec Index.Typ) 122 | 123 | 124 | nonrec def Table.toOpcode (tt : Table) : ByteSeq := toOpcode tt.type 125 | nonrec def Table.ofOpcode : Bytecode Table := 126 | Bytecode.err_log "Parsing table section." do 127 | return Table.mk (← ofOpcode) 128 | instance : Opcode Table := ⟨Table.toOpcode, Table.ofOpcode⟩ 129 | 130 | abbrev Section.Table := Section 4 (Vec Wasm.Syntax.Module.Table) 131 | 132 | 133 | nonrec def Memory.toOpcode (mt : Memory) : ByteSeq := toOpcode mt.type 134 | nonrec def Memory.ofOpcode : Bytecode Memory := 135 | Bytecode.err_log "Parsing memory section." do 136 | return Memory.mk (← ofOpcode) 137 | instance : Opcode Memory := ⟨Memory.toOpcode, Memory.ofOpcode⟩ 138 | 139 | abbrev Section.Memory := Section 5 (Vec Wasm.Syntax.Module.Memory) 140 | 141 | 142 | nonrec def Global.toOpcode (gt : Global) : ByteSeq := 143 | toOpcode gt.type ++ toOpcode gt.init 144 | nonrec def Global.ofOpcode : Bytecode Global := 145 | Bytecode.err_log "Parsing global section." do 146 | let type ← ofOpcode 147 | let expr ← ofOpcode 148 | return ⟨type, expr⟩ 149 | instance : Opcode Global := ⟨Global.toOpcode, Global.ofOpcode⟩ 150 | 151 | abbrev Section.Global := Section 6 (Vec Wasm.Syntax.Module.Global) 152 | 153 | 154 | nonrec def Export.Description.toOpcode : Export.Description → ByteSeq 155 | | .func x => 0x00 :: toOpcode x 156 | | .table tt => 0x01 :: toOpcode tt 157 | | .mem mt => 0x02 :: toOpcode mt 158 | | .globl gt => 0x03 :: toOpcode gt 159 | 160 | nonrec def Export.Description.ofOpcode : Bytecode Export.Description := 161 | Bytecode.err_log "Parsing export description." do 162 | match ← Bytecode.readByte with 163 | | 0x00 => return Export.Description.func (← ofOpcode) 164 | | 0x01 => return Export.Description.table (← ofOpcode) 165 | | 0x02 => return Export.Description.mem (← ofOpcode) 166 | | 0x03 => return Export.Description.globl (← ofOpcode) 167 | | _ => Bytecode.err 168 | instance : Opcode Export.Description := 169 | ⟨Export.Description.toOpcode, Export.Description.ofOpcode⟩ 170 | 171 | nonrec def Export.toOpcode (ex : Export) : ByteSeq := 172 | toOpcode ex.name ++ toOpcode ex.desc 173 | nonrec def Export.ofOpcode : Bytecode Export := 174 | Bytecode.err_log "Parsing export section." do 175 | let name ← ofOpcode 176 | let desc ← ofOpcode 177 | return ⟨name, desc⟩ 178 | instance : Opcode Export := ⟨Export.toOpcode, Export.ofOpcode⟩ 179 | 180 | abbrev Section.Export := Section 7 (Vec Wasm.Syntax.Module.Export) 181 | 182 | 183 | nonrec def Start.toOpcode (st : Start) : ByteSeq := toOpcode st.func 184 | nonrec def Start.ofOpcode : Bytecode Start := 185 | Bytecode.err_log "Parsing start section." do 186 | return Start.mk (← ofOpcode) 187 | instance : Opcode Start := ⟨Start.toOpcode, Start.ofOpcode⟩ 188 | 189 | abbrev Section.Start := Section 8 Wasm.Syntax.Module.Start 190 | 191 | 192 | 193 | nonrec def Element.Kind.ofOpcode : Bytecode Typ.Ref := do 194 | match ← Bytecode.readByte with 195 | | 0x00 => return .func 196 | | _ => Bytecode.errMsg "Non-zero element kind." 197 | 198 | -- todo is this right? should it be more complex like the parsing? 199 | nonrec def Element.toOpcode (e : Element) : ByteSeq := 200 | match e.mode with 201 | | .passive => 202 | toOpcode (5 : Unsigned32) ++ toOpcode e.type ++ toOpcode e.init 203 | | .declarative => 204 | toOpcode (7 : Unsigned32) ++ toOpcode e.type ++ toOpcode e.init 205 | | .active x y => 206 | toOpcode (6 : Unsigned32) 207 | ++ toOpcode x 208 | ++ toOpcode y 209 | ++ toOpcode e.type 210 | ++ toOpcode e.init 211 | 212 | private def Element.ref_func (i : Index.Function) : Expr := 213 | ( Wasm.Syntax.Instr.reference (.func i) :: [], .wasm_end) 214 | 215 | nonrec def Element.ofOpcode : Bytecode Element := 216 | Bytecode.err_log "Parsing element section." do 217 | let id : Unsigned32 ← ofOpcode 218 | if id = 0 then 219 | let e : Expr ← ofOpcode 220 | let y ← Vec.ofOpcode 221 | return ⟨.func, y.map ref_func, .active (Unsigned.ofNat 0) e⟩ 222 | if id = 1 then 223 | let et ← Element.Kind.ofOpcode 224 | let y ← Vec.ofOpcode 225 | return ⟨et, y.map ref_func, .passive⟩ 226 | if id = 2 then 227 | let x ← ofOpcode 228 | let e : Expr ← ofOpcode 229 | let et ← Element.Kind.ofOpcode 230 | let y ← Vec.ofOpcode 231 | return ⟨et, y.map ref_func, .active x e⟩ 232 | if id = 3 then 233 | let et ← Element.Kind.ofOpcode 234 | let y ← Vec.ofOpcode 235 | return ⟨et, y.map ref_func, .declarative⟩ 236 | if id = 4 then 237 | let e : Expr ← ofOpcode 238 | let el ← Vec.ofOpcode 239 | return ⟨.func, el, .active (Unsigned.ofNat 0) e⟩ 240 | if id = 5 then 241 | let et ← ofOpcode 242 | let el ← Vec.ofOpcode 243 | return ⟨et, el, .passive⟩ 244 | if id = 6 then 245 | let x ← ofOpcode 246 | let e : Expr ← ofOpcode 247 | let et ← ofOpcode 248 | let el ← Vec.ofOpcode 249 | return ⟨et, el, .active x e⟩ 250 | if id = 7 then 251 | let et ← ofOpcode 252 | let el ← Vec.ofOpcode 253 | return ⟨et, el, .declarative⟩ 254 | Bytecode.err 255 | 256 | instance : Opcode Element := ⟨Element.toOpcode, Element.ofOpcode⟩ 257 | 258 | abbrev Section.Element := Section 9 (Vec Wasm.Syntax.Module.Element) 259 | 260 | 261 | structure Code.Locals where 262 | n : Unsigned32 263 | t : Typ.Val 264 | structure Code.Func where 265 | locals : Vec Typ.Val 266 | expr : Expr 267 | structure Code where 268 | -- size : Unsigned32 269 | code : Code.Func 270 | 271 | def Code.Locals.toVec (locals : Code.Locals) : Vec Typ.Val := 272 | let lst := List.ofFn (fun (_ : Fin locals.n.toNat) => locals.t) 273 | ⟨lst, by 274 | -- simp [List.length_ofFn, Vec.max_length, Unsigned.toNat, lst] 275 | sorry 276 | ⟩ 277 | 278 | def Code.dataidx (c : Code) : List Index.Data := 279 | let (ins, _) := c.code.expr 280 | ins.filterMap (fun i => 281 | match i with 282 | | .memory (.init x) => .some x 283 | | .memory (.data_drop x) => .some x 284 | | _ => .none 285 | ) 286 | 287 | nonrec def Code.Locals.toOpcode (locals : Code.Locals) : ByteSeq := 288 | toOpcode locals.n ++ toOpcode locals.t 289 | nonrec def Code.Locals.ofOpcode : Bytecode Code.Locals := 290 | Bytecode.err_log "Parsing code locals." do 291 | let n ← ofOpcode 292 | let t ← ofOpcode 293 | return ⟨n, t⟩ 294 | instance : Opcode (Code.Locals) := ⟨Code.Locals.toOpcode, Code.Locals.ofOpcode⟩ 295 | 296 | nonrec def Code.Funcs.toOpcode (funcs : Code.Func) : ByteSeq := 297 | toOpcode (funcs.locals.map (Locals.mk 1 ·)) ++ toOpcode funcs.expr 298 | nonrec def Code.Funcs.ofOpcode : Bytecode Code.Func := 299 | Bytecode.err_log "Parsing code funcs." do 300 | let t' ← Vec.ofOpcode 301 | match Vec.join (t'.map Code.Locals.toVec) with 302 | | .none => Bytecode.errMsg "Exceeded maximum vector length." 303 | | .some t => 304 | let e ← ofOpcode 305 | return ⟨t, e⟩ 306 | instance : Opcode Code.Func := ⟨Code.Funcs.toOpcode, Code.Funcs.ofOpcode⟩ 307 | 308 | nonrec def Code.toOpcode (code : Code) : ByteSeq := 309 | let fs := toOpcode code.code 310 | let size : Unsigned32 := Unsigned.ofNat fs.length 311 | toOpcode size ++ fs 312 | 313 | nonrec def Code.ofOpcode : Bytecode Code := 314 | Bytecode.err_log "Parsing code section." do 315 | let size : Unsigned32 ← ofOpcode 316 | let init ← Bytecode.pos 317 | let code ← Code.Funcs.ofOpcode 318 | let h_after ← Bytecode.pos 319 | let rsize := Unsigned.ofNat (h_after - init) 320 | if size = rsize then return ⟨code⟩ 321 | Bytecode.errMsg s!"Code section expected size {size} got {rsize}." 322 | 323 | instance : Opcode Code := ⟨Code.toOpcode, Code.ofOpcode⟩ 324 | 325 | nonrec abbrev Section.Code := Section 10 (Vec Code) 326 | 327 | 328 | nonrec def Data.toOpcode (data : Data) : ByteSeq := 329 | match data.mode with 330 | | .passive => toOpcode (1 : Unsigned32) ++ toOpcode data.init 331 | | .active (0 : Unsigned32) e => 332 | toOpcode (0 : Unsigned32) ++ toOpcode e ++ toOpcode data.init 333 | | .active x e => 334 | toOpcode (2 : Unsigned32) ++ toOpcode x ++ toOpcode e ++ toOpcode data.init 335 | 336 | nonrec def Data.ofOpcode : Bytecode Data := 337 | Bytecode.err_log "Parsing data section." do 338 | let m : Unsigned32 ← ofOpcode 339 | if m = 0 then 340 | let e ← ofOpcode 341 | let init ← ofOpcode 342 | return ⟨init, .active (0 : Unsigned32) e⟩ 343 | if m = 1 then 344 | let init ← ofOpcode 345 | return ⟨init, .passive⟩ 346 | if m = 2 then 347 | let x ← ofOpcode 348 | let e ← ofOpcode 349 | let init ← ofOpcode 350 | return ⟨init, .active x e⟩ 351 | Bytecode.errMsg "Unknown data id" 352 | 353 | instance : Opcode Data := ⟨Data.toOpcode, Data.ofOpcode⟩ 354 | 355 | abbrev Section.Data := Section 11 (Vec Wasm.Syntax.Module.Data) 356 | abbrev Section.Data.Count := Section 12 Unsigned32 357 | 358 | 359 | def Magic.toOpcode : ByteSeq := [0x00, 0x61, 0x73, 0x6D] 360 | def Magic.ofOpcode : Bytecode Unit := do 361 | match (← Bytecode.takeBytes 4).toList with 362 | | 0x00 :: 0x61 :: 0x73 :: 0x6D :: [] => return () 363 | | res => Bytecode.errMsg s!"Incorrect magic number (got {res})!" 364 | 365 | def Version.toOpcode : ByteSeq := [0x01, 0x00, 0x00, 0x00] 366 | def Version.ofOpcode : Bytecode Unit := do 367 | match (← Bytecode.takeBytes 4).toList with 368 | | 0x01 :: 0x00 :: 0x00 :: 0x00 :: [] => return () 369 | | res => Bytecode.errMsg s!"Incorrect version number (got {res})!" 370 | 371 | nonrec def toOpcode (mod : Module) : ByteSeq := 372 | let typeidx := mod.funcs.map (·.type) 373 | let code := mod.funcs.map (fun f => Code.mk ⟨f.locals, f.body⟩) 374 | let m := Unsigned.ofNat mod.datas.length 375 | 376 | let typesec : Section.Typ := Section.mk 1 mod.types 377 | let importsec : Section.Import := Section.mk 2 mod.imports 378 | let funcsec : Section.Function := Section.mk 3 typeidx 379 | let tablesec : Section.Table := Section.mk 4 mod.tables 380 | let memsec : Section.Memory := Section.mk 5 mod.mems 381 | let globalsec : Section.Global := Section.mk 6 mod.globals 382 | let exportsec : Section.Export := Section.mk 7 mod.exports 383 | let startsec : Section.Start := Section.mk_opt 8 mod.start 384 | let elemsec : Section.Element := Section.mk 9 mod.elems 385 | let codesec : Section.Code := Section.mk 10 code 386 | let datasec : Section.Data := Section.mk 11 mod.datas 387 | let datacsec : Section.Data.Count := Section.mk 12 m 388 | 389 | (Magic.toOpcode) ++ (Version.toOpcode) 390 | ++ (toOpcode typesec ) 391 | ++ (toOpcode importsec) 392 | ++ (toOpcode funcsec ) 393 | ++ (toOpcode tablesec ) 394 | ++ (toOpcode memsec ) 395 | ++ (toOpcode globalsec) 396 | ++ (toOpcode exportsec) 397 | ++ (toOpcode startsec ) 398 | ++ (toOpcode elemsec ) 399 | ++ (toOpcode datacsec ) 400 | ++ (toOpcode codesec ) 401 | ++ (toOpcode datasec ) 402 | 403 | -- todo Custom sections 404 | nonrec def ofOpcode : Bytecode Module := 405 | Bytecode.err_log "Parsing WASM module." do 406 | let () ← Magic.ofOpcode 407 | let () ← Version.ofOpcode 408 | let () ← Bytecode.log "Magic + Version valid" 409 | 410 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 411 | let typesec : Section.Typ ← Section.ofOpcode 412 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 413 | let importsec : Section.Import ← ofOpcode 414 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 415 | let funcsec : Section.Function ← ofOpcode 416 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 417 | let tablesec : Section.Table ← ofOpcode 418 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 419 | let memsec : Section.Memory ← ofOpcode 420 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 421 | let globalsec : Section.Global ← ofOpcode 422 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 423 | let exportsec : Section.Export ← ofOpcode 424 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 425 | let startsec : Section.Start ← ofOpcode 426 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 427 | let elemsec : Section.Element ← ofOpcode 428 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 429 | let datacsec : Section.Data.Count ← ofOpcode 430 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 431 | let codesec : Section.Code ← ofOpcode 432 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 433 | let datasec : Section.Data ← ofOpcode 434 | let _c : Section.Custom? ← Bytecode.opt ofOpcode 435 | 436 | let n := 437 | match funcsec with 438 | | .data _ cont => cont.length 439 | | .empty => 0 440 | if n_ge_max : n ≥ Vec.max_length then 441 | Bytecode.errMsg "Function section exceed max vector len." else 442 | have n_lt_max : n < Vec.max_length := by simp at n_ge_max; exact n_ge_max 443 | 444 | let n' := 445 | match codesec with 446 | | .data _ cont => cont.length 447 | | .empty => 0 448 | if neq_nn' : n ≠ n' then 449 | Bytecode.errMsg "Function/Code section length mismatch." else 450 | have eq_nn' : n = n' := by simp at neq_nn'; exact neq_nn' 451 | 452 | let _ ← 453 | match datacsec with 454 | | .data _ m => 455 | if m ≠ Unsigned.ofNat datasec.toVec.length then 456 | Bytecode.errMsg "Data/Datacount section mismatch." 457 | | .empty => 458 | if (codesec.toVec.list.map (Code.dataidx)).join.length ≠ 0 then 459 | Bytecode.errMsg "Data/Datacount section mismatch." 460 | 461 | let funcs : Vector Module.Function n := Vector.ofFn (fun (i : Fin n) => 462 | match fs : funcsec with 463 | | .data _ typeidx => 464 | match cs : codesec with 465 | | .data _ vcode => 466 | let code := vcode.get (cast (by simp [eq_nn', cs, n']) i) 467 | 468 | ⟨ typeidx.get (cast (by simp [Vec.length, fs, n]) i) 469 | , code.code.locals 470 | , code.code.expr 471 | ⟩ 472 | | .empty => by 473 | simp [eq_nn', n', cs] at i 474 | have := i.isLt 475 | contradiction 476 | | .empty => by 477 | simp [n, fs] at i 478 | have := i.isLt 479 | contradiction 480 | ) 481 | 482 | return ( 483 | { types := typesec.toVec 484 | , funcs := ⟨funcs.val, by rw [funcs.prop]; exact n_lt_max⟩ 485 | , tables := tablesec.toVec 486 | , mems := memsec.toVec 487 | , globals := globalsec.toVec 488 | , elems := elemsec.toVec 489 | , datas := datasec.toVec 490 | , start := startsec.toOption 491 | , imports := importsec.toVec 492 | , exports := exportsec.toVec 493 | } 494 | ) 495 | 496 | instance : Opcode Module := ⟨toOpcode, ofOpcode⟩ 497 | -------------------------------------------------------------------------------- /Wasm/Binary/Opcode.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Vec 2 | import Wasm.Syntax.Value 3 | import Numbers 4 | -- import Mathlib.Data.Vector 5 | open Numbers 6 | 7 | namespace Wasm.Binary 8 | 9 | abbrev Byte := UInt8 10 | abbrev ByteSeq := List Byte -- maybe change : ) 11 | @[inline] def Byte.toSeq : UInt8 → List UInt8 := (.cons · []) 12 | 13 | instance : ToString ByteSeq := ⟨String.concatWith " "⟩ 14 | 15 | structure Bytecode.State where 16 | seq : ByteArray 17 | pos : Fin (seq.size + 1) 18 | log : List String 19 | def Bytecode.State.new (seq : ByteArray) : Bytecode.State := 20 | ⟨seq, ⟨0, by simp⟩, []⟩ 21 | 22 | structure Bytecode.Error where 23 | log : List String -- todo maybe change ? 24 | instance : ToString Bytecode.Error := 25 | ⟨fun err => String.intercalate "\n" err.log⟩ 26 | 27 | abbrev Bytecode := ExceptT (Bytecode.Error) (StateM Bytecode.State) 28 | 29 | instance : Monad Bytecode := show Monad (ExceptT _ _) from inferInstance 30 | 31 | namespace Bytecode 32 | 33 | @[inline] def err : Bytecode α := do throw ⟨[]⟩ 34 | @[inline] def errMsg (msg : String) : Bytecode α := do throw ⟨[msg]⟩ 35 | 36 | @[inline] def err_log (msg : String) (p : Bytecode α) : Bytecode α := 37 | fun state => 38 | ExceptT.adapt (fun err => 39 | {err with log := msg :: err.log} 40 | ) p state 41 | @[inline] def err_replace (f : String → String) : Bytecode α → Bytecode α := 42 | ExceptT.adapt (fun err => 43 | match err.log with 44 | | msg :: msgs => {err with log := f msg :: msgs} 45 | | [] => err 46 | ) 47 | 48 | @[inline] def log (msg : String) : Bytecode Unit := do 49 | let s ← get; set {s with log := msg :: s.log} 50 | 51 | @[inline] def pos : Bytecode Nat := do return (← get).pos 52 | 53 | @[inline] def at_end : Bytecode Bool := do 54 | let s ← get 55 | return s.pos = s.seq.size 56 | 57 | @[inline] def readByte : Bytecode Byte := do 58 | let s ← get 59 | let p := s.pos 60 | if h : p.val < s.seq.size then 61 | let b := s.seq.get ⟨p.val, h⟩ 62 | let p' := ⟨p.val + 1, by simp [*]⟩ 63 | set (Bytecode.State.mk s.seq p' s.log) 64 | return b 65 | else errMsg "Tried parsing byte but at end of sequence." 66 | 67 | @[inline] def peekByte : Bytecode Byte := do 68 | let s ← get 69 | let p := s.pos 70 | if h : p.val < s.seq.size then return s.seq.get ⟨p.val, h⟩ 71 | else errMsg "Tried peeking byte but at end of sequence." 72 | 73 | @[inline] def takeBytes (n : Nat) : Bytecode (Vector Byte n) := do 74 | let s ← get 75 | let p := s.pos 76 | if h₁ : p.val < s.seq.size then 77 | let data := ((s.seq.toList.splitAt p).2.splitAt n).1 78 | if h : data.length = n then 79 | let p' := 80 | if h : p.val + n < s.seq.size 81 | then ⟨p.val + n, by exact Nat.lt_succ_of_lt h⟩ 82 | else ⟨s.seq.size, by simp⟩ 83 | set (Bytecode.State.mk s.seq p' s.log); 84 | return ⟨data, h⟩ 85 | else errMsg s!"Tried taking {n} byte(s) but reached end of sequence." 86 | else errMsg s!"Tried taking {n} byte(s) but at end of sequence." 87 | 88 | partial def star (p : Bytecode α) : Bytecode (List α) := fun state => do 89 | match ← p state with 90 | | (.ok a, state') => 91 | if state'.seq.size - state'.pos.val < state.seq.size - state.pos then 92 | match ← star p state' with 93 | | (.ok as, state'') => return (.ok (a :: as), state'') 94 | | (.error _, _) => return (.ok [a], state') 95 | else return (.error ⟨["Illegal backtracking in star."]⟩, state') 96 | | (.error _, _) => return (.ok [], state) 97 | -- termination_by state.seq.size - state.pos 98 | 99 | def opt (p : Bytecode α) : Bytecode (Option α) := fun state => do 100 | match ← p state with 101 | | (.ok a , state') => return (.ok (.some a), state') 102 | | (.error _, _) => return (.ok .none, state) 103 | 104 | def n (v : Nat) (p : Bytecode α) : Bytecode (Vector α v) := fun state => do 105 | if h : v = 0 then return (.ok ⟨[], by simp [*]⟩, state) else 106 | have : Nat.succ (v - 1) = v := Nat.succ_pred h 107 | 108 | match ← p state with 109 | | (.ok a, state') => 110 | if state'.pos.val > state.pos then 111 | match ← n (v - 1) p state' with 112 | | (.ok as, state'') => 113 | return (.ok (cast (by rw [this]) (Vector.cons a as)), state'') 114 | | (.error err, state'') => return (.error err, state'') 115 | else return (.error ⟨["Illegal backtracking in n."]⟩, state') 116 | | (.error err, state') => return (.error err, state') 117 | 118 | @[inline] def backtrack (p : Bytecode α) : Bytecode α := fun state => do 119 | match ← p state with 120 | | (.ok a, s') => return (.ok a, s') 121 | | (.error e, _) => return (.error e, state) 122 | 123 | def or (p₁ p₂ : Bytecode α) : Bytecode α := fun state => do 124 | match ← p₁ state with 125 | | (.ok a , state') => return (.ok a, state') 126 | | (.error _, _) => 127 | match ← p₂ state with 128 | | (.ok a , state') => return (.ok a, state') 129 | | (.error e, state') => return (.error e, state) 130 | 131 | instance : OrElse (Bytecode α) where 132 | orElse p q := or p (q ()) 133 | 134 | end Bytecode 135 | 136 | class Opcode (α : Type) where 137 | toOpcode : α → ByteSeq 138 | ofOpcode : Bytecode α 139 | 140 | export Opcode (toOpcode ofOpcode) 141 | instance {α} [Opcode α] : Opcode (id α) := inferInstanceAs (Opcode α) 142 | instance {α} [Opcode α] : Opcode (Id α) := inferInstanceAs (Opcode α) 143 | 144 | def Byte.toOpcode : Byte → ByteSeq := ([·]) 145 | def Byte.ofOpcode : Bytecode Byte := Bytecode.readByte 146 | 147 | nonrec def Unsigned.ofLEB128 (n : { i // 0 < i }) : Bytecode (Unsigned n) := do 148 | let s ← get 149 | let lst := s.seq.toList.drop s.pos 150 | let init := lst.length 151 | match Numbers.Unsigned.ofLEB128 n lst with 152 | | .none => Bytecode.errMsg "Could not parse LEB128" 153 | | .some (v, rem) => 154 | let dp := init - rem.length 155 | let pos' := 156 | if h : s.pos.val + dp ≥ s.seq.size 157 | then ⟨s.seq.size, by simp⟩ 158 | else ⟨s.pos + dp, by rw [not_le] at h; exact Nat.lt_add_right _ h⟩ 159 | set (Bytecode.State.mk s.seq pos' s.log) 160 | return v 161 | 162 | nonrec def Signed.ofLEB128 (n : { i // 0 < i }) : Bytecode (Signed n) := do 163 | let s ← get 164 | let lst := s.seq.toList.drop s.pos 165 | let init := lst.length 166 | match Numbers.Signed.ofLEB128 n lst with 167 | | .none => Bytecode.errMsg "Could not parse LEB128" 168 | | .some (v, rem) => 169 | let dp := init - rem.length 170 | let pos' := 171 | if h : s.pos.val + dp ≥ s.seq.size 172 | then ⟨s.seq.size, by simp⟩ 173 | else ⟨s.pos + dp, by rw [not_le] at h; exact Nat.lt_add_right _ h⟩ 174 | set (Bytecode.State.mk s.seq pos' s.log) 175 | return v 176 | 177 | -- todo: use Unsigned stuff 178 | instance : Opcode Byte := ⟨Byte.toOpcode, Byte.ofOpcode⟩ 179 | instance : Opcode (Unsigned n) := ⟨Unsigned.toLEB128, Unsigned.ofLEB128 n⟩ 180 | instance : Opcode (Signed n) := ⟨Signed.toLEB128 , Signed.ofLEB128 n⟩ 181 | instance : Opcode Nat := 182 | ⟨ Unsigned.toLEB128 ∘ (Unsigned.ofNat : Nat → Unsigned32) 183 | , do let r ← Unsigned.ofLEB128 ⟨32, by simp⟩; return r.toNat 184 | ⟩ 185 | instance : Opcode Wasm.Syntax.Value.Byte := ⟨Byte.toOpcode, Byte.ofOpcode⟩ 186 | instance : Opcode (Wasm.Syntax.Value.FloatN nn) := ⟨sorry, sorry⟩ 187 | 188 | nonrec def List.toOpcode [Opcode α] (list : List α) : ByteSeq := 189 | toOpcode list.length ++ (list.map toOpcode).join 190 | 191 | nonrec def Vec.toOpcode [Opcode α] (vec : Vec α) : ByteSeq := 192 | toOpcode vec.length ++ (vec.list.map toOpcode).join 193 | 194 | nonrec def Vec.ofOpcode [inst : Opcode α] : Bytecode (Vec α) := 195 | Bytecode.err_log "Parsing vector." do 196 | let len : Unsigned32 ← ofOpcode 197 | Bytecode.err_log s!"Parsing vector length={len}." do 198 | let vector ← Bytecode.n len.toNat (inst.ofOpcode) 199 | return ⟨vector.toList, by 200 | simp [Unsigned.toNat] 201 | exact len.isLt 202 | ⟩ 203 | 204 | instance [Opcode α] : Opcode (Vec α) := ⟨Vec.toOpcode, Vec.ofOpcode⟩ 205 | 206 | 207 | def Value.Name.toOpcode (name : Wasm.Syntax.Value.Name) : ByteSeq := 208 | Vec.toOpcode ⟨name.value.toUTF8.toList, sorry⟩ 209 | 210 | def Value.Name.ofOpcode : Bytecode Wasm.Syntax.Value.Name := do 211 | let name ← Vec.ofOpcode 212 | return ⟨String.fromUTF8! (name.list.toByteArray), sorry⟩ 213 | 214 | instance : Opcode Wasm.Syntax.Value.Name := 215 | ⟨Value.Name.toOpcode, Value.Name.ofOpcode⟩ 216 | -------------------------------------------------------------------------------- /Wasm/Binary/Typ.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's type defintion: 2 | https://webassembly.github.io/spec/core/binary/types.html 3 | -/ 4 | import Wasm.Vec 5 | import Wasm.Binary.Opcode 6 | import Wasm.Syntax.Typ 7 | 8 | namespace Wasm.Binary.Typ 9 | 10 | open Wasm.Syntax.Typ 11 | 12 | def Num.toOpcode : Syntax.Typ.Num → Byte 13 | | .i32 => 0x7F 14 | | .i64 => 0x7E 15 | | .f32 => 0x7D 16 | | .f64 => 0x7C 17 | 18 | def Num.ofOpcode : Bytecode Syntax.Typ.Num := do 19 | match ← Bytecode.readByte with 20 | | 0x7F => return .i32 21 | | 0x7E => return .i64 22 | | 0x7D => return .f32 23 | | 0x7C => return .f64 24 | | _ => Bytecode.errMsg "Parsing numtype." 25 | 26 | instance : Opcode Syntax.Typ.Num := ⟨Byte.toSeq ∘ Num.toOpcode, Num.ofOpcode⟩ 27 | 28 | 29 | def Vec.toOpcode : Syntax.Typ.Vec → UInt8 30 | | .v128 => 0x7B 31 | 32 | def Vec.ofOpcode : Bytecode Syntax.Typ.Vec := do 33 | match ← Bytecode.readByte with 34 | | 0x7B => return .v128 35 | | _ => Bytecode.errMsg "Parsing vectype." 36 | 37 | instance : Opcode Syntax.Typ.Vec := ⟨Byte.toSeq ∘ Vec.toOpcode, Vec.ofOpcode⟩ 38 | 39 | 40 | def Ref.toOpcode : Ref → UInt8 41 | | .func => 0x70 42 | | .extern => 0x6F 43 | 44 | def Ref.ofOpcode : Bytecode Ref := do 45 | match ← Bytecode.readByte with 46 | | 0x70 => return .func 47 | | 0x6F => return .extern 48 | | _ => Bytecode.errMsg "Parsing vectype." 49 | 50 | instance : Opcode Ref := ⟨Byte.toSeq ∘ Ref.toOpcode, Ref.ofOpcode⟩ 51 | 52 | 53 | def Val.toOpcode : Val → UInt8 54 | | .num v => Num.toOpcode v 55 | | .vec v => Vec.toOpcode v 56 | | .ref v => Ref.toOpcode v 57 | 58 | def Val.ofOpcode : Bytecode Val := 59 | Bytecode.err_log "Parsing valtype." do 60 | (return Val.num (← Num.ofOpcode)) 61 | <|> (return Val.vec (← Vec.ofOpcode)) 62 | <|> (return Val.ref (← Ref.ofOpcode)) 63 | 64 | instance : Opcode Val := ⟨Byte.toSeq ∘ Val.toOpcode, Val.ofOpcode⟩ 65 | 66 | 67 | def Result.toOpcode (res : Result) : ByteSeq := Wasm.Binary.Vec.toOpcode res 68 | def Result.ofOpcode : Bytecode Result := Wasm.Binary.Vec.ofOpcode 69 | instance : Opcode Result := ⟨Result.toOpcode, Result.ofOpcode⟩ 70 | 71 | 72 | nonrec def Func.toOpcode (func : Func) : ByteSeq := 73 | 0x60 :: toOpcode func.args ++ toOpcode func.result 74 | 75 | def Func.ofOpcode : Bytecode Func := 76 | Bytecode.err_log "Parsing function type." do 77 | match ← Bytecode.readByte with 78 | | 0x60 => 79 | let args ← Wasm.Binary.Vec.ofOpcode 80 | let res ← Wasm.Binary.Vec.ofOpcode 81 | return ⟨args, res⟩ 82 | | _ => Bytecode.err 83 | 84 | instance : Opcode Func := ⟨Func.toOpcode, Func.ofOpcode⟩ 85 | 86 | 87 | nonrec def Limit.toOpcode (lim : Limit) : ByteSeq := 88 | match lim.max with 89 | | .none => 0x00 :: toOpcode lim.min 90 | | .some m => 0x01 :: toOpcode lim.min ++ toOpcode m 91 | 92 | nonrec def Limit.ofOpcode : Bytecode Limit := 93 | Bytecode.err_log "Parsing limit." do 94 | match ← Bytecode.readByte with 95 | | 0x00 => 96 | let min ← ofOpcode 97 | return ⟨min, .none⟩ 98 | | 0x01 => 99 | let min ← ofOpcode 100 | let max ← ofOpcode 101 | return ⟨min, .some max⟩ 102 | | _ => Bytecode.err 103 | 104 | instance : Opcode Limit := ⟨Limit.toOpcode, Limit.ofOpcode⟩ 105 | instance : Opcode Mem := ⟨Limit.toOpcode, Limit.ofOpcode⟩ 106 | 107 | 108 | nonrec def Table.toOpcode (tab : Table) : ByteSeq := 109 | toOpcode tab.2 ++ toOpcode tab.1 110 | 111 | nonrec def Table.ofOpcode : Bytecode Table := 112 | Bytecode.err_log "Parsing table type." do 113 | let ref ← Ref.ofOpcode 114 | let lim ← Limit.ofOpcode 115 | return ⟨lim, ref⟩ 116 | 117 | instance : Opcode Table := ⟨Table.toOpcode, Table.ofOpcode⟩ 118 | 119 | def Mut.toOpcode : Mut → Byte 120 | | .const => 0x00 121 | | .var => 0x01 122 | 123 | def Mut.ofOpcode : Bytecode Mut := do 124 | match ← Bytecode.readByte with 125 | | 0x00 => return .const 126 | | 0x01 => return .var 127 | | _ => Bytecode.errMsg "Parsing mutablility." 128 | 129 | instance : Opcode Mut := ⟨Byte.toSeq ∘ Mut.toOpcode, Mut.ofOpcode⟩ 130 | 131 | 132 | nonrec def Global.toOpcode (g : Global) : ByteSeq := 133 | toOpcode g.2 ++ toOpcode g.1 134 | 135 | nonrec def Global.ofOpcode : Bytecode Global := 136 | Bytecode.err_log "Parsing global type." do 137 | let val ← ofOpcode 138 | let m ← ofOpcode 139 | return (m, val) 140 | 141 | instance : Opcode Global := ⟨Global.toOpcode, Global.ofOpcode⟩ 142 | 143 | -- todo Extern 144 | -------------------------------------------------------------------------------- /Wasm/Dynamics.lean: -------------------------------------------------------------------------------- 1 | /- Dynamic Semantics -/ 2 | import Wasm.Dynamics.Address 3 | import Wasm.Dynamics.Instr 4 | import Wasm.Dynamics.Value 5 | import Wasm.Dynamics.Instance 6 | import Wasm.Dynamics.Stack 7 | import Wasm.Dynamics.Evaluation 8 | import Wasm.Dynamics.Context 9 | import Wasm.Dynamics.Dynamics 10 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Address.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Syntax 2 | import Numbers 3 | 4 | namespace Wasm.Dynamics 5 | 6 | open Numbers 7 | 8 | def Address := Unsigned32 9 | 10 | namespace Address 11 | 12 | def Function := Address 13 | def Table := Address 14 | def Memory := Address 15 | def Global := Address 16 | def Element := Address 17 | def Data := Address 18 | def Extern := Address 19 | 20 | -- deriving instance Inhabited for Function 21 | -- deriving instance Inhabited for Table 22 | -- deriving instance Inhabited for Memory 23 | -- deriving instance Inhabited for Global 24 | -- deriving instance Inhabited for Element 25 | -- deriving instance Inhabited for Data 26 | -- deriving instance Inhabited for Extern 27 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Context.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Vec 2 | import Wasm.Dynamics.Address 3 | import Wasm.Dynamics.Value 4 | import Wasm.Dynamics.Instance 5 | import Wasm.Dynamics.Stack 6 | import Wasm.Syntax 7 | 8 | namespace Wasm.Dynamics.Context 9 | 10 | inductive Block : Nat → Type 11 | | hole : List Dynamics.Value → List Instr.Dynamic → Block 0 12 | | step : List Dynamics.Value → Stack.Label → Block k → {i : Syntax.Instr.Pseudo // i = .wasm_end} → List Instr.Dynamic → Block (k + 1) 13 | 14 | def Thread := Stack.Frame × List Instr.Dynamic 15 | def Config := Instance.Store × Thread 16 | 17 | inductive Evaluation 18 | | nil 19 | | val : List Dynamics.Value → Evaluation → List Instr.Dynamic → Evaluation 20 | | label : Stack.Label → Evaluation → {i : Syntax.Instr.Pseudo // i = .wasm_end} → Evaluation 21 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Dynamics.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Vec 2 | import Wasm.Syntax 3 | import Wasm.Dynamics.Address 4 | import Wasm.Dynamics.Value 5 | import Wasm.Dynamics.Instance 6 | import Wasm.Dynamics.Stack 7 | import Wasm.Dynamics.Evaluation 8 | import Wasm.Dynamics.Context 9 | import Wasm.Dynamics.Instr 10 | import Numbers 11 | open Numbers 12 | 13 | namespace Wasm.Dynamics 14 | 15 | @[inline] def Step := Dynamics.Context.Config → Dynamics.Context.Config → Prop 16 | 17 | open Wasm.Syntax.Instr 18 | open Wasm.Syntax.Value 19 | open Wasm.Dynamics.Evaluation 20 | open Wasm.Dynamics.Evaluation.Numeric 21 | 22 | def int {nn : Numeric.Size} 23 | (instr : Syntax.Instr.Numeric.Integer nn) 24 | : Dynamics.Instr.Dynamic := 25 | .real (.numeric ((.integer instr) : (Syntax.Instr.Numeric nn))) 26 | 27 | def const {nn : Numeric.Size} (v : Unsigned nn.toBits) := 28 | int (.const v) 29 | 30 | namespace Step.Numeric.Integer 31 | 32 | def iunop {nn : Numeric.Size} 33 | (op : Syntax.Instr.Numeric.Integer.Unop) 34 | : Dynamics.Instr.Dynamic := 35 | @int nn (.unop op) 36 | 37 | inductive Unop : Step 38 | | clz : {nn : Numeric.Size} 39 | → {c : Unsigned (Numeric.Size.toBits nn)} 40 | → Unop (s, (f, iunop .clz :: const c :: is)) 41 | (s, (f, const (Unsigned.clz c) :: is)) 42 | | ctz : {nn : Numeric.Size} 43 | → {c : Unsigned (Numeric.Size.toBits nn)} 44 | → Unop (s, (f, iunop .ctz :: const c :: is)) 45 | (s, (f, const (Unsigned.clz c) :: is)) 46 | | popcnt : {nn : Numeric.Size} 47 | → {c : Unsigned (Numeric.Size.toBits nn)} 48 | → Unop (s, (f, iunop .popcnt :: const c :: is)) 49 | (s, (f, const (Unsigned.clz c) :: is)) 50 | 51 | def ibinop {nn : Numeric.Size} 52 | (op : Syntax.Instr.Numeric.Integer.Binop) 53 | : Dynamics.Instr.Dynamic := 54 | @int nn (.binop op) 55 | 56 | 57 | inductive Binop : Step 58 | | add 59 | : {nn : Numeric.Size} 60 | → {c₁ c₂ : Unsigned nn.toBits} 61 | → Binop (s, (f, ibinop .add :: const c₂ :: const c₁ :: is)) 62 | (s, (f, const (c₁ + c₂) :: is)) 63 | | sub 64 | : {nn : Numeric.Size} 65 | → {c₁ c₂ : Unsigned nn.toBits} 66 | → Binop (s, (f, ibinop .sub :: const c₂ :: const c₁ :: is)) 67 | (s, (f, const (c₁ - c₂) :: is)) 68 | | mul 69 | : {nn : Numeric.Size} 70 | → {c₁ c₂ : Unsigned nn.toBits} 71 | → Binop (s, (f, ibinop .mul :: const c₂ :: const c₁ :: is)) 72 | (s, (f, const (c₁ * c₂) :: is)) 73 | | div_u_t 74 | : Unsigned.divOpt c₁ c₂ = .none 75 | → Binop (s, (f, ibinop (.div .u) :: const c₂ :: const c₁ :: is)) 76 | (s, (f, .admin .trap :: is)) 77 | | div_u 78 | : Unsigned.divOpt c₁ c₂ = .some c 79 | → Binop (s, (f, ibinop (.div .u) :: const c₂ :: const c₁ :: is)) 80 | (s, (f, const c :: is)) 81 | | div_s_t 82 | : Signed.divOpt c₁ c₂ = .none 83 | → Binop (s, (f, ibinop (.div .s) :: const c₂ :: const c₁ :: is)) 84 | (s, (f, .admin .trap :: is)) 85 | | div_s 86 | : Signed.divOpt c₁ c₂ = .some c 87 | → Binop (s, (f, ibinop (.div .s) :: const c₂ :: const c₁ :: is)) 88 | (s, (f, const c :: is)) 89 | | rem_u_t 90 | : Unsigned.remOpt c₁ c₂ = .none 91 | → Binop (s, (f, ibinop (.rem .u) :: const c₂ :: const c₁ :: is)) 92 | (s, (f, .admin .trap :: is)) 93 | | rem_u 94 | : Unsigned.remOpt c₁ c₂ = .some c 95 | → Binop (s, (f, ibinop (.rem .u) :: const c₂ :: const c₁ :: is)) 96 | (s, (f, const c :: is)) 97 | | rem_s_t 98 | : Signed.remOpt c₁ c₂ = .none 99 | → Binop (s, (f, ibinop (.rem .s) :: const c₂ :: const c₁ :: is)) 100 | (s, (f, .admin .trap :: is)) 101 | | rem_s 102 | : Signed.remOpt c₁ c₂ = .some c 103 | → Binop (s, (f, ibinop (.rem .s) :: const c₂ :: const c₁ :: is)) 104 | (s, (f, const c :: is)) 105 | | and 106 | : Binop (s, (f, ibinop .and :: const c₂ :: const c₁ :: is)) 107 | (s, (f, const (Unsigned.and c₁ c₂) :: is)) 108 | | or 109 | : Binop (s, (f, ibinop .or :: const c₂ :: const c₁ :: is)) 110 | (s, (f, const (Unsigned.or c₁ c₂) :: is)) 111 | | xor 112 | : Binop (s, (f, ibinop .xor :: const c₂ :: const c₁ :: is)) 113 | (s, (f, const (Unsigned.xor c₁ c₂) :: is)) 114 | | shl 115 | : Binop (s, (f, ibinop .shl :: const c₂ :: const c₁ :: is)) 116 | (s, (f, const (Unsigned.shl c₁ c₂) :: is)) 117 | | shr_u 118 | : Binop (s, (f, ibinop (.shr .u) :: const c₂ :: const c₁ :: is)) 119 | (s, (f, const (Unsigned.shr c₁ c₂) :: is)) 120 | | shr_s 121 | : Binop (s, (f, ibinop (.shr .s) :: const c₂ :: const c₁ :: is)) 122 | (s, (f, const (Signed.shr c₁ c₂) :: is)) 123 | | rotl 124 | : Binop (s, (f, ibinop .rotl :: const c₂ :: const c₁ :: is)) 125 | (s, (f, const (Unsigned.rotl c₁ c₂) :: is)) 126 | | rotr 127 | : Binop (s, (f, ibinop .rotr :: const c₂ :: const c₁ :: is)) 128 | (s, (f, const (Unsigned.rotr c₁ c₂) :: is)) 129 | 130 | inductive Test : Step 131 | | eqz : Test (s, (f, int (.test .eqz) :: const c₁ :: is)) 132 | (s, (f, const (Unsigned.eqz c₁) :: is)) 133 | 134 | def irelop {nn : Numeric.Size} 135 | (op : Syntax.Instr.Numeric.Integer.Relation) 136 | : Dynamics.Instr.Dynamic := 137 | @int nn (.relation op) 138 | 139 | inductive Relation : Step 140 | | eq : Relation (s, (f, irelop .eq :: const c₂ :: const c₁ :: is)) 141 | (s, (f, const (Unsigned.eq c₁ c₂) :: is)) 142 | | ne : Relation (s, (f, irelop .ne :: const c₂ :: const c₁ :: is)) 143 | (s, (f, const (Unsigned.ne c₁ c₂) :: is)) 144 | | lt_u : Relation (s, (f, irelop (.lt .u) :: const c₂ :: const c₁ :: is)) 145 | (s, (f, const (Unsigned.lt c₁ c₂) :: is)) 146 | | gt_u : Relation (s, (f, irelop (.gt .u) :: const c₂ :: const c₁ :: is)) 147 | (s, (f, const (Unsigned.gt c₁ c₂) :: is)) 148 | | le_u : Relation (s, (f, irelop (.le .u) :: const c₂ :: const c₁ :: is)) 149 | (s, (f, const (Unsigned.le c₁ c₂) :: is)) 150 | | ge_u : Relation (s, (f, irelop (.ge .u) :: const c₂ :: const c₁ :: is)) 151 | (s, (f, const (Unsigned.ge c₁ c₂) :: is)) 152 | | lt_s : Relation (s, (f, irelop (.lt .s) :: const c₂ :: const c₁ :: is)) 153 | (s, (f, const (Signed.lt c₁ c₂) :: is)) 154 | | gt_s : Relation (s, (f, irelop (.gt .s) :: const c₂ :: const c₁ :: is)) 155 | (s, (f, const (Signed.gt c₁ c₂) :: is)) 156 | | le_s : Relation (s, (f, irelop (.le .s) :: const c₂ :: const c₁ :: is)) 157 | (s, (f, const (Signed.le c₁ c₂) :: is)) 158 | | ge_s : Relation (s, (f, irelop (.ge .s) :: const c₂ :: const c₁ :: is)) 159 | (s, (f, const (Signed.ge c₁ c₂) :: is)) 160 | 161 | end Integer 162 | 163 | inductive Integer : Step 164 | | unop : Integer.Unop config config' → Integer config config' 165 | | binop : Integer.Binop config config' → Integer config config' 166 | | test : Integer.Test config config' → Integer config config' 167 | | relation : Integer.Relation config config' → Integer config config' 168 | | extend8_s : {nn mm : Numeric.Size} 169 | → {c₁ : Signed (Numeric.Size.toBits mm)} 170 | → {c₂ : Signed (Numeric.Size.toBits nn)} 171 | → {_ : (Signed.extend ((Signed.extend c₁) : Signed8)) = c₂} 172 | → Integer (s, (f, int .extend8_s :: const c₁ :: is)) 173 | (s, (f, const c₂ :: is)) 174 | | extend16_s : {nn mm : Numeric.Size} 175 | → {c₁ : Signed (Numeric.Size.toBits mm)} 176 | → {c₂ : Signed (Numeric.Size.toBits nn)} 177 | → {_ : (Signed.extend ((Signed.extend c₁) : Signed16)) = c₂} 178 | → Integer (s, (f, int .extend16_s :: const c₁ :: is)) 179 | (s, (f, const c₂ :: is)) 180 | | extend32_s : {nn mm : Numeric.Size} 181 | → {c₁ : Signed (Numeric.Size.toBits mm)} 182 | → {c₂ : Signed (Numeric.Size.toBits nn)} 183 | → {_ : (Signed.extend ((Signed.extend c₁) : Signed32)) = c₂} 184 | → Integer (s, (f, int .extend32_s :: const c₁ :: is)) 185 | (s, (f, const c₂ :: is)) 186 | | wrap_i64 : {mm : Numeric.Size} 187 | → {c₁ : Signed (Numeric.Size.toBits mm)} 188 | → {c₂ : Signed (Numeric.Size.toBits .quad)} 189 | → {_ : Signed.wrap c₁ = c₂} 190 | → Integer (s, (f, int .wrap_i64 :: const c₁ :: is)) 191 | (s, (f, const c₂ :: is)) 192 | | extend_u32 : {c₁ : Unsigned (Numeric.Size.toBits .double)} 193 | → {c₂ : Unsigned (Numeric.Size.toBits .quad)} 194 | → {_ : (Unsigned.extend c₁) = c₂} 195 | → Integer (s, (f, int (.extend_i32 .u) :: const c₁ :: is)) 196 | (s, (f, const c₂ :: is)) 197 | | extend_s32 : {c₁ : Signed (Numeric.Size.toBits .double)} 198 | → {c₂ : Signed (Numeric.Size.toBits .quad)} 199 | → {_ : (Signed.extend c₁) = c₂} 200 | → Integer (s, (f, int (.extend_i32 .s) :: const c₁ :: is)) 201 | (s, (f, const c₂ :: is)) 202 | -- todo: add trunc/reinterpret 203 | 204 | end Numeric 205 | 206 | inductive Numeric : Step 207 | | integer : Numeric.Integer config config' → Numeric config config' 208 | | float : False → Numeric config config' -- todo 209 | 210 | 211 | 212 | def ref (instr : Syntax.Instr.Reference) 213 | : Dynamics.Instr.Dynamic := 214 | .real (.reference instr) 215 | 216 | inductive Reference : Step 217 | | null : IsValue (ref (.null t)) (.ref (.null _)) 218 | → Reference (s, (f, ref .is_null :: ref (.null t) :: is)) 219 | (s, (f, const (.ofNat 1) :: is)) 220 | | non_null : IsValue val _ 221 | → Reference (s, (f, ref .is_null :: val :: is)) 222 | (s, (f, const (.ofNat 0) :: is)) 223 | | func : Reference (s, (f, ref (.func (Vec.index f.module.funcaddrs x)) :: is)) 224 | (s, (f, .admin (.ref (f.module.funcaddrs.get x)) :: is)) 225 | 226 | def locl (instr : Syntax.Instr.Local) 227 | : Dynamics.Instr.Dynamic := 228 | .real (.locl instr) 229 | 230 | inductive Local : Step 231 | | get : Local (s, (f, locl (.get (Vec.index f.locals x)) :: is)) 232 | (s, (f, Value.instr (f.locals.get x) :: is)) 233 | | set : {_ : IsValue val v} 234 | → Local (s, (f, locl (.set (Vec.index f.locals x)) :: val :: is)) 235 | (s, ({f with locals := f.locals.set x v}, is)) 236 | | tee : {_ : IsValue val₁ v₁} 237 | → Local (s, (f, locl (.tee (Vec.index f.locals x)) :: val :: is)) 238 | (s, (f, locl (.set (Vec.index f.locals x)) :: val :: val :: is)) 239 | 240 | def globl (instr : Syntax.Instr.Global) 241 | : Dynamics.Instr.Dynamic := 242 | .real (.globl instr) 243 | 244 | inductive Global : Step 245 | | get : {a : Fin (Vec.length s.globals)} 246 | → {_ : Vec.index s.globals a = f.module.globaladdrs.get x} 247 | → Global (s, (f, globl (.get (Vec.index f.module.globaladdrs x)) :: is)) 248 | (s, (f, Value.instr (s.globals.get a).value :: is)) 249 | | set : {a : Fin (Vec.length s.globals)} 250 | → {_ : Vec.index s.globals a = f.module.globaladdrs.get x} 251 | → {_ : IsValue val v} 252 | → Global (s, (f, globl (.set (Vec.index f.module.globaladdrs x)) :: val :: is)) 253 | ({s with globals := s.globals.set a {s.globals.get a with value := v}}, (f, is)) 254 | 255 | def table (instr : Syntax.Instr.Table) 256 | : Dynamics.Instr.Dynamic := 257 | .real (.table instr) 258 | 259 | inductive Table : Step 260 | | get : {a : Fin (Vec.length s.tables)} 261 | → {_ : Vec.index s.tables a = f.module.tableaddrs.get x} 262 | → {_ : tab = s.tables.get a} 263 | → {h : i.toNat < tab.elem.length} 264 | → Table (s, (f, table (.get (Vec.index f.module.tableaddrs x)) :: @const .double i :: is)) 265 | (s, (f, Value.instr (tab.elem.get ⟨i.toNat, h⟩ |> .ref) :: is)) 266 | | get_t : {a : Fin (Vec.length s.tables)} 267 | → {_ : Vec.index s.tables a = f.module.tableaddrs.get x} 268 | → {_ : tab = s.tables.get a} 269 | → {_ : i.toNat ≥ tab.elem.length} 270 | → Table (s, (f, table (.get (Vec.index f.module.tableaddrs x)) :: @const .double i :: is)) 271 | (s, (f, .admin .trap :: is)) 272 | | set : {a : Fin (Vec.length s.tables)} 273 | → {_ : Vec.index s.tables a = f.module.tableaddrs.get x} 274 | → {_ : tab = s.tables.get a} 275 | → {_ : IsValue val (.ref v)} 276 | → {h : i.toNat < tab.elem.length} 277 | → Table (s, (f, table (.set (Vec.index f.module.tableaddrs x)) :: val :: @const .double i :: is)) 278 | ({s with tables := s.tables.set a {tab with elem := tab.elem.set ⟨i.toNat, h⟩ v}}, (f, is)) 279 | | set_t : {a : Fin (Vec.length s.tables)} 280 | → {_ : Vec.index s.tables a = f.module.tableaddrs.get x} 281 | → {_ : tab = s.tables.get a} 282 | → {_ : IsValue val (.ref v)} 283 | → {_ : i.toNat ≥ tab.elem.length} 284 | → Table (s, (f, table (.set (Vec.index f.module.tableaddrs x)) :: val :: @const .double i :: is)) 285 | (s, (f, .admin .trap :: is)) 286 | | size : {a : Fin (Vec.length s.tables)} 287 | → {_ : Vec.index s.tables a = f.module.tableaddrs.get x} 288 | → {_ : tab = s.tables.get a} 289 | → Table (s, (f, table (.size (Vec.index f.module.tableaddrs x)) :: is)) 290 | (s, (f, @const .double ⟨tab.elem.length, tab.elem.maxLen⟩ :: is)) 291 | | grow : {a : Fin (Vec.length s.tables)} 292 | → {_ : Vec.index s.tables a = f.module.tableaddrs.get x} 293 | → {_ : tab = s.tables.get a} 294 | → {h : sz = tab.elem.length} 295 | → {_ : IsValue val (.ref v)} 296 | → {_ : False} -- todo implement growtable {_ : .some tab' = growtable (tab, n, v)} 297 | → {_ : s' = {s with tables := s.tables.set a tab'}} 298 | → Table (s, (f, table (.grow (Vec.index f.module.tableaddrs x)) :: @const .double n :: val :: is)) 299 | (s', (f, @const .double ⟨sz, by rw [h]; exact tab.elem.maxLen⟩ :: is)) 300 | | grow_e : {a : Fin (Vec.length s.tables)} 301 | → {_ : Vec.index s.tables a = f.module.tableaddrs.get x} 302 | → {_ : tab = s.tables.get a} 303 | → {_ : sz = tab.elem.length} 304 | → {_ : IsValue val (.ref v)} 305 | → {_ : False} -- todo implement growtable {_ : .none = growtable (tab, n, v)} 306 | → Table (s, (f, table (.grow (Vec.index f.module.tableaddrs x)) :: @const .double n :: val :: is)) 307 | (s, (f, @const .double (Signed.ofInt (-1)) :: is)) 308 | | fill : {ta : Fin (Vec.length s.tables)} 309 | → {_ : Vec.index s.tables ta = f.module.tableaddrs.get x} 310 | → {_ : tab = s.tables.get ta} 311 | → {_ : IsValue val (.ref v)} 312 | → {_ : i.toNat + n.toNat + 1 ≤ tab.elem.length} 313 | → Table (s, (f, table (.fill (Vec.index f.module.tableaddrs x)) 314 | :: @const .double (n + 1) 315 | :: val 316 | :: @const .double i 317 | :: is)) 318 | (s, (f, table (.fill (Vec.index f.module.tableaddrs x)) 319 | :: const n 320 | :: val 321 | :: const (i + 1) 322 | :: table (.set (Vec.index f.module.tableaddrs x)) 323 | :: val 324 | :: const (i) 325 | :: is)) 326 | | fill_z : {ta : Fin (Vec.length s.tables)} 327 | → {_ : Vec.index s.tables ta = f.module.tableaddrs.get x} 328 | → {_ : tab = s.tables.get ta} 329 | → {_ : IsValue val (.ref v)} 330 | → {_ : i.toNat ≤ tab.elem.length} 331 | → Table (s, (f, table (.fill (Vec.index f.module.tableaddrs x)) 332 | :: @const .double 0 333 | :: val 334 | :: @const .double i 335 | :: is)) 336 | (s, (f, is)) 337 | | fill_t : {ta : Fin (Vec.length s.tables)} 338 | → {_ : Vec.index s.tables ta = f.module.tableaddrs.get x} 339 | → {_ : tab = s.tables.get ta} 340 | → {_ : IsValue val (.ref v)} 341 | → {_ : i.toNat + n.toNat > tab.elem.length} 342 | → Table (s, (f, table (.fill (Vec.index f.module.tableaddrs x)) 343 | :: @const .double n 344 | :: val 345 | :: @const .double i 346 | :: is)) 347 | (s, (f, .admin .trap :: is)) 348 | | copy_le : {ta_x ta_y : Fin (Vec.length s.tables)} 349 | → {_ : Vec.index s.tables ta_x = f.module.tableaddrs.get x} 350 | → {_ : tab_x = s.tables.get ta_x} 351 | → {_ : Vec.index s.tables ta_y = f.module.tableaddrs.get y} 352 | → {_ : tab_y = s.tables.get ta_y} 353 | → {_ : (src.toNat + n.toNat ≤ tab_y.elem.length)} 354 | → {_ : d.toNat + n.toNat + 1 ≤ tab_x.elem.length} 355 | → {_ : d ≤ src} 356 | → Table (s, (f, table (.copy (Vec.index f.module.tableaddrs x) (Vec.index f.module.tableaddrs y)) 357 | :: @const .double (n + 1) 358 | :: @const .double src 359 | :: @const .double d 360 | :: is)) 361 | (s, (f, table (.copy (Vec.index f.module.tableaddrs x) (Vec.index f.module.tableaddrs y)) 362 | :: const n 363 | :: const (src + 1) 364 | :: const (d + 1) 365 | :: table (.set (Vec.index f.module.tableaddrs x)) 366 | :: table (.get (Vec.index f.module.tableaddrs y)) 367 | :: const src 368 | :: const d 369 | :: is)) 370 | | copy_gt : {ta_x ta_y : Fin (Vec.length s.tables)} 371 | → {_ : Vec.index s.tables ta_x = f.module.tableaddrs.get x} 372 | → {_ : tab_x = s.tables.get ta_x} 373 | → {_ : Vec.index s.tables ta_y = f.module.tableaddrs.get y} 374 | → {_ : tab_y = s.tables.get ta_y} 375 | → {_ : (src.toNat + n.toNat + 1 ≤ tab_y.elem.length)} 376 | → {_ : d.toNat + n.toNat + 1 ≤ tab_x.elem.length} 377 | → {_ : d > src} 378 | → Table (s, (f, table (.copy (Vec.index f.module.tableaddrs x) (Vec.index f.module.tableaddrs y)) 379 | :: @const .double (n + 1) 380 | :: @const .double src 381 | :: @const .double d 382 | :: is)) 383 | (s, (f, table (.copy (Vec.index f.module.tableaddrs x) (Vec.index f.module.tableaddrs y)) 384 | :: const n 385 | :: const src 386 | :: const d 387 | :: table (.set (Vec.index f.module.tableaddrs x)) 388 | :: table (.get (Vec.index f.module.tableaddrs y)) 389 | :: const (src + n) 390 | :: const (d + n) 391 | :: is)) 392 | | copy_z : {ta_x ta_y : Fin (Vec.length s.tables)} 393 | → {_ : Vec.index s.tables ta_x = f.module.tableaddrs.get x} 394 | → {_ : tab_x = s.tables.get ta_x} 395 | → {_ : Vec.index s.tables ta_y = f.module.tableaddrs.get y} 396 | → {_ : tab_y = s.tables.get ta_y} 397 | → {_ : src.toNat ≤ tab_y.elem.length} 398 | → {_ : d.toNat ≤ tab_x.elem.length} 399 | → Table (s, (f, table (.copy (Vec.index f.module.tableaddrs x) (Vec.index f.module.tableaddrs y)) 400 | :: @const .double 0 401 | :: @const .double src 402 | :: @const .double d 403 | :: is)) 404 | (s, (f, is)) 405 | | copy_t : {ta_x ta_y : Fin (Vec.length s.tables)} 406 | → {_ : Vec.index s.tables ta_x = f.module.tableaddrs.get x} 407 | → {_ : tab_x = s.tables.get ta_x} 408 | → {_ : Vec.index s.tables ta_y = f.module.tableaddrs.get y} 409 | → {_ : tab_y = s.tables.get ta_y} 410 | → {_ : (src.toNat + n.toNat > tab_y.elem.length) 411 | ∨ (d.toNat + n.toNat > tab_x.elem.length)} 412 | → Table (s, (f, table (.copy (Vec.index f.module.tableaddrs x) (Vec.index f.module.tableaddrs y)) 413 | :: @const .double n 414 | :: @const .double src 415 | :: @const .double d 416 | :: is)) 417 | (s, (f, .admin .trap :: is)) 418 | | init : {ta : Fin (Vec.length s.tables)} 419 | → {ea : Fin (Vec.length s.elems)} 420 | → {_ : Vec.index s.tables ta = f.module.tableaddrs.get x} 421 | → {_ : tab = s.tables.get ta} 422 | → {_ : Vec.index s.elems ea = f.module.elemaddrs.get y} 423 | → {_ : elem = s.elems.get ea} 424 | → {h₁ : src.toNat + (n.toNat + 1) ≤ elem.elem.length} 425 | → {h₂ : d.toNat + (n.toNat + 1) ≤ elem.elem.length} 426 | → {_ : IsValue val (.ref v)} 427 | → {_h : v = elem.elem.get ⟨src.toNat, by rw [Nat.add_succ, Nat.succ_le] at h₁; exact Nat.lt_left_add h₁⟩} 428 | → {_ : d.toNat + 1 < Unsigned.MAX ⟨32, by simp⟩} -- prove using h₂, like in h 429 | → {_ : src.toNat + 1 < Unsigned.MAX ⟨32, by simp⟩} -- prove using h₁, like in h 430 | → Table (s, (f, table (.init (Vec.index f.module.tableaddrs x) (Vec.index f.module.elemaddrs y)) 431 | :: @const .double (n + 1) 432 | :: @const .double src 433 | :: @const .double d 434 | :: is)) 435 | (s, (f, table (.init (Vec.index f.module.tableaddrs x) (Vec.index f.module.elemaddrs y)) 436 | :: const n 437 | :: const (src + 1) 438 | :: const (d + 1) 439 | :: table (.set (Vec.index f.module.tableaddrs x)) 440 | :: val 441 | :: const d 442 | :: is)) 443 | | init_z : {ta : Fin (Vec.length s.tables)} 444 | → {ea : Fin (Vec.length s.elems)} 445 | → {_ : Vec.index s.tables ta = f.module.tableaddrs.get x} 446 | → {_ : tab = s.tables.get ta} 447 | → {_ : Vec.index s.elems ea = f.module.elemaddrs.get y} 448 | → {_ : elem = s.elems.get ea} 449 | → {_ : src.toNat ≤ elem.elem.length} 450 | → {_ : d.toNat ≤ elem.elem.length} 451 | → Table (s, (f, table (.init (Vec.index f.module.tableaddrs x) (Vec.index f.module.elemaddrs y)) 452 | :: @const .double 0 453 | :: @const .double src 454 | :: @const .double d 455 | :: is)) 456 | (s, (f, is)) 457 | | init_t : {ta : Fin (Vec.length s.tables)} 458 | → {ea : Fin (Vec.length s.elems)} 459 | → {_ : Vec.index s.tables ta = f.module.tableaddrs.get x} 460 | → {_ : tab = s.tables.get ta} 461 | → {_ : Vec.index s.elems ea = f.module.elemaddrs.get y} 462 | → {_ : elem = s.elems.get ea} 463 | → {_ : src.toNat + n.toNat > elem.elem.length 464 | ∨ d.toNat + n.toNat > elem.elem.length} 465 | → Table (s, (f, table (.init (Vec.index f.module.tableaddrs x) (Vec.index f.module.elemaddrs y)) 466 | :: @const .double n 467 | :: @const .double src 468 | :: @const .double d 469 | :: is)) 470 | (s, (f, .admin .trap :: is)) 471 | 472 | def mem (instr : Syntax.Instr.Memory) 473 | : Dynamics.Instr.Dynamic := 474 | .real (.memory instr) 475 | 476 | def mem_int (instr : Syntax.Instr.Memory.Integer nn) 477 | : Dynamics.Instr.Dynamic := 478 | mem (.integer instr) 479 | 480 | namespace Memory 481 | 482 | /- todo: rewriting read/write memory 483 | 484 | inductive Integer.Load (instr : Numeric.Sign → Memory.Arg → Memory.Integer nn) 485 | : (nBits : { i // 0 < i }) → Step 486 | | unsigned : {h : f.module.memaddrs.length > 0} 487 | → {a : Fin s.mems.length} 488 | → {n : Unsigned nBits} 489 | → {c : Unsigned nn.toBits} 490 | → {_ : Vec.index s.mems a = f.module.memaddrs.get ⟨0, h⟩} 491 | → {_ : m = s.mems.get a} 492 | → {_ : ea = i.toNat + arg.offset.toNat} 493 | → {_ : ea + (nBits.val / 8) ≤ m.data.length} 494 | → {_ : b = (m.data.list.drop ea).take (nBits.val / 8)} 495 | → {_ : b = Unsigned.toBytes nBits n} 496 | → {_ : c = Unsigned.extend n} 497 | → Integer.Load instr nBits 498 | (s, (f, @mem_int nn (instr .u arg) :: @const .double i :: is)) 499 | (s, (f, @const nn c :: is)) 500 | | signed : {h : f.module.memaddrs.length > 0} 501 | → {a : Fin s.mems.length} 502 | → {n : Unsigned nBits} 503 | → {c : Signed nn.toBits} 504 | → {_ : Vec.index s.mems a = f.module.memaddrs.get ⟨0, h⟩} 505 | → {_ : m = s.mems.get a} 506 | → {_ : ea = i.toNat + arg.offset.toNat} 507 | → {_ : ea + (nBits.val / 8) ≤ m.data.length} 508 | → {_ : b = (m.data.list.drop ea).take (nBits.val / 8)} 509 | → {_ : b = Unsigned.toBytes nBits n} 510 | → {_ : c = Signed.extend n} 511 | → Integer.Load instr nBits 512 | (s, (f, @mem_int nn (instr .s arg) :: @const .double i :: is)) 513 | (s, (f, @const nn c :: is)) 514 | | trap : {h : f.module.memaddrs.length > 0} 515 | → {a : Fin s.mems.length} 516 | → {n : Unsigned nBits} 517 | → {c : Unsigned nn.toBits} 518 | → {_ : Vec.index s.mems a = f.module.memaddrs.get ⟨0, h⟩} 519 | → {_ : m = s.mems.get a} 520 | → {_ : ea = i.toNat + arg.offset.toNat} 521 | → {_ : ea + (nBits.val / 8) > m.data.length} 522 | → Integer.Load instr nBits 523 | (s, (f, @mem_int nn (instr .u arg) :: @const .double i :: is)) 524 | (s, (f, .admin .trap :: is)) 525 | 526 | inductive Integer : Step 527 | | load : {nn : Numeric.Size} 528 | → {h₁ : f.module.memaddrs.length > 0} 529 | → {a : Fin s.mems.length} 530 | → {_ : nBytes = nn.toBytes} 531 | → {c : Unsigned nn.toBits} 532 | → {_ : Vec.index s.mems a = f.module.memaddrs.get ⟨0, h₁⟩} 533 | → {_ : m = s.mems.get a} 534 | → {_ : ea = i.toNat + arg.offset.toNat} 535 | → {_ : ea + nBytes ≤ m.data.length} 536 | → {_ : b = ((m.data.list.drop ea).take nBytes)} 537 | → {_ : b = Unsigned.toBytes nn.toBits c} 538 | → Integer (s, (f, @mem_int nn (.load arg) :: @const .double i :: is)) 539 | (s, (f, @const nn c :: is)) 540 | | load_t : {nn : Numeric.Size} 541 | → {h₁ : f.module.memaddrs.length > 0} 542 | → {a : Fin s.mems.length} 543 | → {_ : nBytes = nn.toBytes} 544 | → {c : Unsigned nn.toBits} 545 | → {_ : Vec.index s.mems a = f.module.memaddrs.get ⟨0, h₁⟩} 546 | → {_ : m = s.mems.get a} 547 | → {_ : ea = i.toNat + arg.offset.toNat} 548 | → {_ : ea + nBytes > m.data.length} 549 | → Integer (s, (f, @mem_int nn (.load arg) :: @const .double i :: is)) 550 | (s, (f, .admin .trap :: is)) 551 | | load8 : Integer.Load .load8 ⟨8, by simp⟩ config config' 552 | → Integer config config' 553 | | load16 : Integer.Load .load16 ⟨16, by simp⟩ config config' 554 | → Integer config config' 555 | | load32 : Integer.Load .load32 ⟨32, by simp⟩ config config' 556 | → Integer config config' 557 | | store : {nn : Numeric.Size} 558 | → {h₁ : f.module.memaddrs.length > 0} 559 | → {a : Fin s.mems.length} 560 | → {c : Unsigned (Numeric.Size.toBits nn)} 561 | → {_ : nBytes = nn.toBytes} 562 | → {_ : Vec.index s.mems a = f.module.memaddrs.get ⟨0, h₁⟩} 563 | → {_ : m = s.mems.get a} 564 | → {_ : ea = i.toNat + arg.offset.toNat} 565 | → {_ : ea + nBytes ≤ m.data.length} 566 | → {_ : b = Unsigned.toBytes nn.toBits c} 567 | → {_ : m' = Instance.Memory.write m b ea} 568 | -- → {_ : s' = } 569 | → Integer (s, (f, @mem_int nn (.store arg) :: @const nn c :: @const .double i :: is)) 570 | (s, (f, @const nn c :: is)) 571 | -- todo more 572 | -/ 573 | 574 | end Memory 575 | 576 | inductive Instr : Step 577 | | numeric : Numeric config config' 578 | → Instr config config' 579 | | reference : Reference config config' 580 | → Instr config config' 581 | -- todo vector 582 | | drop : IsValue val _ 583 | → Instr (s, (f, .real .drop :: val :: is)) 584 | (s, (f, is)) 585 | | select_t : {c : Unsigned (Numeric.Size.toBits .double)} 586 | → {_ : IsValue val₁ v₁} 587 | → {_ : IsValue val₂ v₂} 588 | → {_ : Value.type v₁ = Value.type v₂} 589 | → {_ : c ≠ 0} 590 | → Instr (s, (f, .real (.select t) :: const c :: val₂ :: val₁ :: is)) 591 | (s, (f, val₁ :: is)) 592 | | select_f : {c : Unsigned (Numeric.Size.toBits .double)} 593 | → {_ : IsValue val₁ v₁} 594 | → {_ : IsValue val₂ v₂} 595 | → {_ : Value.type v₁ = Value.type v₂} 596 | → {_ : c = 0} 597 | → Instr (s, (f, .real (.select t) :: const c :: val₂ :: val₁ :: is)) 598 | (s, (f, val₂ :: is)) 599 | | locl : Local config config' 600 | → Instr config config' 601 | | globl : Global config config' 602 | → Instr config config' 603 | | table : Table config config' 604 | → Instr config config' 605 | | elem_drop : {a : Fin (Vec.length s.elems)} 606 | → {_ : Vec.index s.elems a = f.module.elemaddrs.get x} 607 | → {_ : elem = s.elems.get a} 608 | → Instr (s, (f, .real (.elem_drop (Vec.index f.module.elemaddrs x)) :: is)) 609 | ({s with elems := s.elems.set a {elem with elem := Vec.nil}}, (f, is)) 610 | -- todo memory 611 | | nop : Instr (s, (f, .real .nop :: is)) 612 | (s, (f, is)) 613 | | trap : Instr (s, (f, .real .unreachable :: is)) 614 | (s, (f, .admin .trap :: is)) 615 | -- todo rest 616 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Evaluation.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Vec 2 | import Wasm.Syntax 3 | 4 | namespace Wasm.Dynamics.Evaluation 5 | 6 | namespace Numeric 7 | 8 | end Numeric 9 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Instance.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Vec 2 | import Wasm.Syntax 3 | import Wasm.Dynamics.Address 4 | import Wasm.Dynamics.Value 5 | 6 | namespace Wasm.Dynamics.Instance 7 | 8 | structure Export where 9 | name : Syntax.Value.Name 10 | value : Value.Extern 11 | 12 | structure Module where 13 | types : Vec Syntax.Typ.Func 14 | funcaddrs : Vec Address.Function 15 | tableaddrs : Vec Address.Table 16 | memaddrs : Vec Address.Memory 17 | globaladdrs : Vec Address.Global 18 | elemaddrs : Vec Address.Element 19 | dataaddrs : Vec Address.Data 20 | exports : Vec Instance.Export 21 | 22 | structure Function.Internal where 23 | type : Syntax.Typ.Func 24 | module : Instance.Module 25 | code : Syntax.Module.Function 26 | 27 | structure Function.Host where 28 | type : Syntax.Typ.Func 29 | hostcode : Unit -- todo add 30 | 31 | inductive Function 32 | | internal : Function.Internal → Function 33 | | host : Function.Host → Function 34 | 35 | structure Table where 36 | type : Syntax.Typ.Table 37 | elem : Vec Value.Reference 38 | 39 | structure Memory where 40 | type : Syntax.Typ.Mem 41 | data : Vec Syntax.Value.Byte 42 | pagesize : data.length % 65536 = 0 43 | -- todo more invariants 44 | 45 | -- in internal representations, the least significant byte is at the end 46 | -- def Memory.read (mem : Memory) 47 | -- (pos len : Nat) 48 | -- : List Syntax.Value.Byte := 49 | -- mem.data.list.drop pos |>.take len |>.reverse 50 | 51 | -- def Memory.write' 52 | -- (mem : Memory) 53 | -- (bytes : Syntax.Value.Bytes) 54 | -- (pos : Nat) 55 | -- (h₁ : pos + bytes.length ≤ mem.data.length) 56 | -- : Memory := 57 | -- match h₂ :bytes with 58 | -- | .fst b => writeOne mem b ⟨pos, by 59 | -- simp [Syntax.Value.Bytes.length_fst, ←Nat.succ_eq_add_one] at h₁ 60 | -- exact Nat.lt_of_succ_lt_succ (Nat.lt_succ_of_le h₁) 61 | -- ⟩ 62 | -- | .cons b bs => 63 | -- let last := Syntax.Value.Bytes.last bytes 64 | -- let bs' := Syntax.Value.Bytes.drop_last bytes (by 65 | -- sorry 66 | -- ) 67 | -- let pos' := ⟨pos, by 68 | -- rw [Syntax.Value.Bytes.length_cons] at h₁ 69 | -- exact lt_left_add (Nat.lt_of_succ_le h₁) 70 | -- ⟩ 71 | -- Memory.write' (writeOne mem last pos') bs' (pos + 1) (by 72 | -- simp [Syntax.Value.Bytes.length_cons, Nat.succ_eq_add_one] at h₁ 73 | -- simp [Nat.add_assoc, Nat.add_comm 1, Vec.set, Vec.length] 74 | -- sorry 75 | -- exact h₁ 76 | -- ) 77 | -- where writeOne (mem : Memory) 78 | -- (byte : Syntax.Value.Byte) 79 | -- (pos : Fin mem.data.length) 80 | -- : Memory := 81 | -- let data' := mem.data.set pos byte 82 | -- ⟨mem.type, data', by simp [Vec.set, Vec.length]; exact mem.pagesize⟩ 83 | 84 | -- def Memory.write 85 | -- (mem : Memory) 86 | -- (bytes : List Syntax.Value.Byte) 87 | -- (pos : Nat) 88 | -- (h : pos + bytes.length ≤ mem.data.length) 89 | -- : Memory := 90 | -- match bytes with 91 | -- | .nil => mem 92 | -- | .cons b bs => 93 | -- let pos' := ⟨pos, by 94 | -- rw [List.length_cons] at h 95 | -- exact Nat.lt_left_add (Nat.lt_of_succ_le h) 96 | -- ⟩ 97 | -- let data' := mem.data.set pos' b 98 | -- let mem' : Memory := ⟨mem.type, data', by 99 | -- simp [Vec.set, Vec.length]; exact mem.pagesize 100 | -- ⟩ 101 | -- Memory.write mem' bs (pos + 1) (by 102 | -- simp [List.length_cons, Nat.succ_eq_add_one] at h 103 | -- simp [Nat.add_assoc, Nat.add_comm 1, Vec.set, Vec.length] 104 | -- exact h 105 | -- ) 106 | 107 | 108 | structure Global where 109 | type : Syntax.Typ.Global 110 | value : Value 111 | 112 | structure Element where 113 | type : Syntax.Typ.Ref 114 | elem : Vec Value.Reference 115 | 116 | structure Data where 117 | data : Vec Syntax.Value.Byte 118 | 119 | 120 | structure Store where 121 | funcs : Vec Function 122 | tables : Vec Table 123 | mems : Vec Memory 124 | globals : Vec Global 125 | elems : Vec Element 126 | datas : Vec Data 127 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Instr.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Syntax 2 | import Wasm.Dynamics.Address 3 | 4 | namespace Wasm.Dynamics 5 | 6 | mutual 7 | -- the List Instr can be thought of as a continuation that can be branched to 8 | inductive Stack.Label 9 | | label : Nat → List Instr.Dynamic → Stack.Label 10 | 11 | inductive Instr.Dynamic 12 | | real : Syntax.Instr → Instr.Dynamic 13 | | admin : Instr.Admin → Instr.Dynamic 14 | 15 | inductive Instr.Admin 16 | | trap 17 | | ref : Address.Function → Instr.Admin 18 | | ref_extern : Dynamics.Address.Extern → Instr.Admin 19 | | invoke : Dynamics.Address.Function → Instr.Admin 20 | | label : Stack.Label → List Instr.Dynamic → Syntax.Instr.Pseudo → Instr.Admin 21 | | frame : List Instr.Dynamic → List Instr.Dynamic → Syntax.Instr.Pseudo → Instr.Admin 22 | end 23 | 24 | 25 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Stack.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Vec 2 | import Wasm.Syntax 3 | import Wasm.Dynamics.Address 4 | import Wasm.Dynamics.Value 5 | import Wasm.Dynamics.Instance 6 | 7 | namespace Wasm.Dynamics.Stack 8 | 9 | structure Frame where 10 | locals : Vec Value 11 | module : Instance.Module 12 | 13 | def expand (f : Frame) 14 | (typeIdx : Syntax.Module.Index.Typ) 15 | (h : typeIdx.val < f.module.types.list.length) 16 | : Syntax.Typ.Func := 17 | f.module.types.list.get ⟨typeIdx.val, h⟩ 18 | -------------------------------------------------------------------------------- /Wasm/Dynamics/Value.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Vec 2 | import Wasm.Syntax 3 | import Wasm.Dynamics.Address 4 | import Wasm.Dynamics.Instr 5 | import Numbers 6 | 7 | open Numbers 8 | 9 | namespace Wasm.Dynamics 10 | 11 | open Wasm.Syntax 12 | open Syntax.Instr 13 | 14 | inductive Value.Numeric 15 | | int_const : (nn : Numeric.Size) 16 | → {v : Unsigned (Numeric.Size.toBits nn)} 17 | → {i : Instr.Dynamic // i = .real (.numeric (.integer (.const v)))} 18 | → Value.Numeric 19 | | float_const : (nn : Numeric.Size) 20 | → {v : Value.FloatN (Numeric.Size.toBits nn)} 21 | → {i : Instr.Dynamic // i = .real (.numeric (.float (.const v) : Instr.Numeric nn))} 22 | → Value.Numeric 23 | 24 | inductive Value.Vec : Type 25 | -- todo Vectors 26 | 27 | -- Instr.Dynamic 28 | inductive Value.Reference 29 | | null : {i : Instr.Dynamic // i = .real (.reference (.null t))} → Value.Reference 30 | | ref : {i : Instr.Dynamic // i = .admin (.ref addr)} → Value.Reference 31 | | ref_extern : {i : Instr.Dynamic // i = .admin (.ref_extern addr)} → Value.Reference 32 | 33 | inductive Value 34 | | num : Value.Numeric → Value 35 | | vec : Value.Vec → Value 36 | | ref : Value.Reference → Value 37 | 38 | def Value.instr : Value → Instr.Dynamic 39 | | .num (.int_const _ instr) => instr 40 | | .num (.float_const _ instr) => instr 41 | | .ref (.null instr) => instr 42 | | .ref (.ref instr) => instr 43 | | .ref (.ref_extern instr) => instr 44 | 45 | def Value.type : Value → Syntax.Typ.Val 46 | | .num (.int_const .double _instr) => .num .i32 47 | | .num (.int_const .quad _instr) => .num .i64 48 | | .num (.float_const .double _instr) => .num .f32 49 | | .num (.float_const .quad _instr) => .num .f64 50 | | .vec _ => .vec .v128 51 | | .ref (.null _instr) => .ref .func 52 | | .ref (.ref _instr) => .ref .func 53 | | .ref (.ref_extern _instr) => .ref .extern 54 | 55 | def Value.default : Typ.Val → Instr.Dynamic 56 | | .num n => 57 | match n with 58 | | .i32 => .real <| .numeric 59 | (.integer (.const ⟨0, by simp⟩) : Instr.Numeric .double) 60 | | .i64 => .real <| .numeric 61 | (.integer (.const ⟨0, by simp⟩) : Instr.Numeric .quad) 62 | | .f32 => .real <| .numeric 63 | (.float (.const (Float.ofNat 0)) : Instr.Numeric .double) 64 | | .f64 => .real <| .numeric 65 | (.float (.const (Float.ofNat 0)) : Instr.Numeric .quad) 66 | | .vec v => .admin .trap -- todo vector 67 | | .ref r => .real <| .reference (.null r) 68 | 69 | inductive Value.Result : Prop 70 | | result : List Value → Result 71 | | trap 72 | 73 | inductive Value.Extern 74 | | func : Address.Function → Extern 75 | | table : Address.Table → Extern 76 | | mem : Address.Memory → Extern 77 | | globl : Address.Global → Extern 78 | 79 | def Value.funcs (vals : List Value.Extern) : List Address.Function := 80 | vals.filterMap (fun v => 81 | match v with 82 | | .func addr => .some addr 83 | | _ => .none 84 | ) 85 | def Value.tables (vals : List Value.Extern) : List Address.Table := 86 | vals.filterMap (fun v => 87 | match v with 88 | | .table addr => .some addr 89 | | _ => .none 90 | ) 91 | def Value.mems (vals : List Value.Extern) : List Address.Memory := 92 | vals.filterMap (fun v => 93 | match v with 94 | | .mem addr => .some addr 95 | | _ => .none 96 | ) 97 | def Value.global (vals : List Value.Extern) : List Address.Global := 98 | vals.filterMap (fun v => 99 | match v with 100 | | .globl addr => .some addr 101 | | _ => .none 102 | ) 103 | 104 | inductive IsValue : Instr.Dynamic → Value → Prop 105 | | int_const : IsValue instr.val (.num (.int_const nn instr)) 106 | | float_const : IsValue instr.val (.num (.float_const nn instr)) 107 | -- todo Vectors 108 | | null : IsValue instr.val (.ref (.null instr)) 109 | | ref : IsValue instr.val (.ref (.ref instr)) 110 | | ref_extern : IsValue instr.val (.ref (.ref_extern instr)) 111 | -------------------------------------------------------------------------------- /Wasm/Notation.lean: -------------------------------------------------------------------------------- 1 | /- WIP: This imports a WAT notation that can be used in Lean to build the CSTs -/ 2 | 3 | import Wasm.Text.Notation.Value 4 | import Wasm.Text.Notation.Typ 5 | import Wasm.Text.Notation.Index 6 | import Wasm.Text.Notation.Instr 7 | 8 | /- Currently only instructions/expressions can be made with the following 9 | syntax: 10 | 11 | ``` 12 | def example := [wat_expr| 13 | i32.const 2 14 | i32.const 5 15 | i32.mul 16 | ] 17 | ``` 18 | 19 | Currently a little buggy atm :) 20 | -/ 21 | -------------------------------------------------------------------------------- /Wasm/Syntax.lean: -------------------------------------------------------------------------------- 1 | /- AST Representation of WASM Syntax -/ 2 | import Wasm.Syntax.Index 3 | import Wasm.Syntax.Typ 4 | import Wasm.Syntax.Value 5 | import Wasm.Syntax.Instr 6 | import Wasm.Syntax.Module 7 | -------------------------------------------------------------------------------- /Wasm/Syntax/Index.lean: -------------------------------------------------------------------------------- 1 | /- WASM Module indices 2 | https://webassembly.github.io/spec/core/syntax/modules.html#indices 3 | -/ 4 | 5 | import Wasm.Syntax.Value 6 | import Numbers 7 | open Numbers 8 | 9 | namespace Wasm.Syntax.Module.Index 10 | 11 | def Typ := Unsigned32 12 | def Function := Unsigned32 13 | def Table := Unsigned32 14 | def Memory := Unsigned32 15 | def Global := Unsigned32 16 | def Element := Unsigned32 17 | def Data := Unsigned32 18 | def Local := Unsigned32 19 | def Label := Unsigned32 20 | 21 | instance : DecidableEq Typ := Unsigned.deq 22 | instance : DecidableEq Function := Unsigned.deq 23 | instance : DecidableEq Table := Unsigned.deq 24 | instance : DecidableEq Memory := Unsigned.deq 25 | instance : DecidableEq Global := Unsigned.deq 26 | instance : DecidableEq Element := Unsigned.deq 27 | instance : DecidableEq Data := Unsigned.deq 28 | instance : DecidableEq Local := Unsigned.deq 29 | instance : DecidableEq Label := Unsigned.deq 30 | 31 | instance : ToString Typ := ⟨Unsigned.toString⟩ 32 | instance : ToString Function := ⟨Unsigned.toString⟩ 33 | instance : ToString Table := ⟨Unsigned.toString⟩ 34 | instance : ToString Memory := ⟨Unsigned.toString⟩ 35 | instance : ToString Global := ⟨Unsigned.toString⟩ 36 | instance : ToString Element := ⟨Unsigned.toString⟩ 37 | instance : ToString Data := ⟨Unsigned.toString⟩ 38 | instance : ToString Local := ⟨Unsigned.toString⟩ 39 | instance : ToString Label := ⟨Unsigned.toString⟩ 40 | -------------------------------------------------------------------------------- /Wasm/Syntax/Instr.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's instruction defintion: 2 | https://webassembly.github.io/spec/core/syntax/instructions.html 3 | -/ 4 | import Wasm.Vec 5 | import Wasm.Syntax.Typ 6 | import Wasm.Syntax.Value 7 | import Wasm.Syntax.Index 8 | import Numbers 9 | open Numbers 10 | 11 | namespace Wasm.Syntax.Instr 12 | 13 | namespace Numeric 14 | 15 | inductive Size 16 | | double | quad -- 32/64 17 | 18 | def Size.toBits : Size → {i : Nat // i > 0} 19 | | double => ⟨32, by simp⟩ 20 | | quad => ⟨64, by simp⟩ 21 | 22 | abbrev Size.max_double : Nat := Nat.pow 2 (Size.toBits .double) 23 | abbrev Size.max_quad : Nat := Nat.pow 2 (Size.toBits .quad) 24 | def Size.max_val : Size → Nat 25 | | double => Size.max_double 26 | | quad => Size.max_quad 27 | 28 | theorem Size.max_val_gt_zero : Size.max_val n > 0 := by 29 | cases n with 30 | | double => simp [Size.max_val] 31 | | quad => simp [Size.max_val] 32 | 33 | def Size.toBytes : Size → Nat 34 | | double => 4 35 | | quad => 8 36 | 37 | def Size.toIntVal : Size → Typ.Val 38 | | .double => .num .i32 39 | | .quad => .num .i64 40 | 41 | def Size.toFloatVal : Size → Typ.Val 42 | | .double => .num .f32 43 | | .quad => .num .f64 44 | 45 | inductive Sign 46 | | u | s 47 | 48 | inductive Integer.Unop 49 | | clz | ctz | popcnt 50 | 51 | inductive Integer.Binop 52 | | add 53 | | sub 54 | | mul 55 | | div : Sign → Integer.Binop 56 | | rem : Sign → Integer.Binop 57 | | and 58 | | or 59 | | xor 60 | | shl 61 | | shr : Sign → Integer.Binop 62 | | rotl 63 | | rotr 64 | 65 | inductive Integer.Test 66 | | eqz 67 | 68 | inductive Integer.Relation 69 | | eq 70 | | ne 71 | | lt : Sign → Integer.Relation 72 | | gt : Sign → Integer.Relation 73 | | le : Sign → Integer.Relation 74 | | ge : Sign → Integer.Relation 75 | 76 | inductive Integer : (nn : Numeric.Size) → Type 77 | | const : (v : Unsigned nn.toBits) → Integer nn 78 | | unop : Integer.Unop → Integer nn 79 | | binop : Integer.Binop → Integer nn 80 | | test : Integer.Test → Integer nn 81 | | relation : Integer.Relation → Integer nn 82 | | extend8_s : Integer nn 83 | | extend16_s : Integer nn 84 | | extend32_s : Integer .quad 85 | | wrap_i64 : Integer .double 86 | | extend_i32 : Sign → Integer .quad 87 | | trunc_f : (mm : Size) → Sign → Integer nn 88 | | trunc_sat_f : (mm : Size) → Sign → Integer nn 89 | | reinterpret_f : Integer nn 90 | 91 | inductive Float.Unop 92 | | abs 93 | | neg 94 | | sqrt 95 | | ceil 96 | | floor 97 | | trunc 98 | | nearest 99 | 100 | inductive Float.Binop 101 | | add 102 | | sub 103 | | mul 104 | | div 105 | | min 106 | | max 107 | | copysign 108 | 109 | inductive Float.Relation 110 | | eq 111 | | ne 112 | | lt 113 | | gt 114 | | le 115 | | ge 116 | 117 | inductive Float : (nn : Numeric.Size) → Type 118 | | const : (v : Value.FloatN nn.toBits) → Float nn 119 | | unop : Float.Unop → Float nn 120 | | binop : Float.Binop → Float nn 121 | | relation : Float.Relation → Float nn 122 | | demote_f64 : Float .double 123 | | promote_f32 : Float .quad 124 | | convert_i : (mm : Size) → Sign → Float nn 125 | | reinterpret_i : Float nn 126 | 127 | end Numeric 128 | 129 | inductive Numeric : (nn : Numeric.Size) → Type 130 | | integer : Numeric.Integer nn → Numeric nn 131 | | float : Numeric.Float nn → Numeric nn 132 | 133 | 134 | 135 | -- todo vector instructions 136 | 137 | inductive Reference 138 | | null : Typ.Ref → Reference 139 | | is_null 140 | | func : Module.Index.Function → Reference 141 | 142 | inductive Local 143 | | get : Module.Index.Local → Local 144 | | set : Module.Index.Local → Local 145 | | tee : Module.Index.Local → Local 146 | 147 | inductive Global 148 | | get : Module.Index.Global → Global 149 | | set : Module.Index.Global → Global 150 | 151 | inductive Table 152 | | get : Module.Index.Table → Table 153 | | set : Module.Index.Table → Table 154 | | size : Module.Index.Table → Table 155 | | grow : Module.Index.Table → Table 156 | | fill : Module.Index.Table → Table 157 | | copy : Module.Index.Table → Module.Index.Table → Table 158 | | init : Module.Index.Table → Module.Index.Element → Table 159 | 160 | namespace Memory 161 | 162 | structure Arg where 163 | offset : Unsigned32 := 0 164 | align : Unsigned32 := 0 165 | deriving Inhabited 166 | 167 | inductive Integer : (nn : Numeric.Size) → Type 168 | | load : Arg → Integer nn 169 | | store : Arg → Integer nn 170 | | load8 : Numeric.Sign → Arg → Integer nn 171 | | load16 : Numeric.Sign → Arg → Integer nn 172 | | load32 : Numeric.Sign → Arg → Integer .quad 173 | | store8 : Arg → Integer nn 174 | | store16 : Arg → Integer nn 175 | | store32 : Arg → Integer .quad 176 | 177 | 178 | inductive Float : (nn : Numeric.Size) → Type 179 | | load : Arg → Float nn 180 | | store : Arg → Float nn 181 | 182 | -- todo: do vectors 183 | 184 | end Memory 185 | 186 | inductive Memory 187 | | integer : Memory.Integer nn → Memory 188 | | float : Memory.Float nn → Memory 189 | | size 190 | | grow 191 | | fill 192 | | copy 193 | | init : Module.Index.Data → Memory 194 | | data_drop : Module.Index.Data → Memory 195 | 196 | 197 | 198 | end Instr 199 | 200 | inductive Instr.BlockType 201 | | index : Module.Index.Typ → BlockType 202 | | value : Option Typ.Val → BlockType 203 | 204 | inductive Instr.Pseudo 205 | | wasm_end 206 | | wasm_else 207 | 208 | inductive Instr : Type 209 | | numeric : (Instr.Numeric nn) → Instr 210 | | reference : Instr.Reference → Instr 211 | -- Parametric 212 | | drop 213 | | select : Option (List Typ.Val) → Instr 214 | | locl : Instr.Local → Instr 215 | | globl : Instr.Global → Instr 216 | | table : Instr.Table → Instr 217 | | elem_drop : Module.Index.Element → Instr 218 | | memory : Instr.Memory → Instr 219 | -- Control 220 | | nop 221 | | unreachable 222 | | block : Instr.BlockType → List Instr → (wasm_end : Instr.Pseudo) → Instr 223 | | loop : Instr.BlockType → List Instr → (wasm_end : Instr.Pseudo) → Instr 224 | | wasm_if : Instr.BlockType → List Instr → (wasm_else : Instr.Pseudo) 225 | → List Instr → (wasm_end : Instr.Pseudo) → Instr 226 | | br : Module.Index.Label → Instr 227 | | br_if : Module.Index.Label → Instr 228 | | br_table : (Vec Module.Index.Label) → Module.Index.Label → Instr 229 | | wasm_return 230 | | call : Module.Index.Function → Instr 231 | | call_indirect : Module.Index.Table → Module.Index.Typ → Instr 232 | 233 | abbrev Expr := List Instr × Instr.Pseudo 234 | -------------------------------------------------------------------------------- /Wasm/Syntax/Module.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's module defintion: 2 | https://webassembly.github.io/spec/core/syntax/instructions.html 3 | -/ 4 | import Wasm.Vec 5 | import Wasm.Syntax.Index 6 | import Wasm.Syntax.Typ 7 | import Wasm.Syntax.Value 8 | import Wasm.Syntax.Instr 9 | 10 | namespace Wasm.Syntax.Module 11 | 12 | structure Function where 13 | type : Index.Typ 14 | locals : Vec Typ.Val 15 | body : Expr 16 | 17 | structure Table where 18 | type : Typ.Table 19 | 20 | structure Memory where 21 | type : Typ.Mem 22 | 23 | structure Global where 24 | type : Typ.Global 25 | init : Expr 26 | 27 | inductive Element.Mode 28 | | passive 29 | | active : (table : Index.Table) → (offset : Expr) → Mode 30 | | declarative 31 | 32 | structure Element where 33 | type : Typ.Ref 34 | init : Vec Expr 35 | mode : Element.Mode 36 | 37 | inductive Data.Mode 38 | | passive 39 | | active : (memory : Index.Memory) → (offset : Expr) → Mode 40 | 41 | structure Data where 42 | init : Vec Value.Byte 43 | mode : Data.Mode 44 | 45 | structure Start where 46 | func : Index.Function 47 | 48 | inductive Export.Description 49 | | func : Index.Function → Description 50 | | table : Index.Table → Description 51 | | mem : Index.Memory → Description 52 | | globl : Index.Global → Description 53 | 54 | structure Export where 55 | name : Value.Name 56 | desc : Export.Description 57 | 58 | inductive Import.Description 59 | | func : Index.Typ → Description 60 | | table : Typ.Table → Description 61 | | mem : Typ.Mem → Description 62 | | globl : Typ.Global → Description 63 | 64 | structure Import where 65 | module : Value.Name 66 | name : Value.Name 67 | desc : Import.Description 68 | 69 | end Module 70 | 71 | structure Module where 72 | types : Vec Typ.Func := Vec.nil 73 | funcs : Vec Module.Function := Vec.nil 74 | tables : Vec Module.Table := Vec.nil 75 | mems : Vec Module.Memory := Vec.nil 76 | globals : Vec Module.Global := Vec.nil 77 | elems : Vec Module.Element := Vec.nil 78 | datas : Vec Module.Data := Vec.nil 79 | start : Option Module.Start := none 80 | imports : Vec Module.Import := Vec.nil 81 | exports : Vec Module.Export := Vec.nil 82 | -------------------------------------------------------------------------------- /Wasm/Syntax/Typ.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's type defintion: 2 | https://webassembly.github.io/spec/core/syntax/types.html 3 | -/ 4 | import Numbers 5 | import Wasm.Vec 6 | 7 | namespace Wasm.Syntax.Typ 8 | 9 | open Numbers 10 | 11 | inductive Num 12 | | i32 13 | | i64 14 | | f32 15 | | f64 16 | deriving DecidableEq, Inhabited, Repr 17 | 18 | inductive Vec 19 | | v128 20 | deriving DecidableEq, Inhabited, Repr 21 | 22 | inductive Ref 23 | | func 24 | | extern 25 | deriving DecidableEq, Inhabited, Repr 26 | 27 | inductive Val 28 | | num : Num → Val 29 | | vec : Vec → Val 30 | | ref : Ref → Val 31 | deriving DecidableEq, Inhabited, Repr 32 | 33 | @[inline] def Result := Wasm.Vec Val 34 | deriving DecidableEq, Inhabited 35 | 36 | structure Func where 37 | args : Result 38 | result : Result 39 | deriving DecidableEq, Inhabited 40 | 41 | structure Limit where 42 | min : Unsigned32 -- number of page sizes 43 | max : Option Unsigned32 44 | 45 | @[inline] def Mem := Limit 46 | @[inline] def Table := Limit × Ref 47 | 48 | inductive Mut 49 | | const 50 | | var 51 | 52 | def Global := Mut × Val 53 | def Global.mk : Mut → Val → Global := Prod.mk 54 | 55 | inductive Extern 56 | | func : Func → Extern 57 | | table : Table → Extern 58 | | mem : Mem → Extern 59 | | globl : Global → Extern 60 | 61 | def Extern.funcs (externs : List Extern) : List Func := 62 | externs.filterMap (fun extern => 63 | match extern with 64 | | .func f => .some f 65 | | _ => .none 66 | ) 67 | 68 | def Extern.tables (externs : List Extern) : List Table := 69 | externs.filterMap (fun extern => 70 | match extern with 71 | | .table t => .some t 72 | | _ => .none 73 | ) 74 | 75 | def Extern.mems (externs : List Extern) : List Mem := 76 | externs.filterMap (fun extern => 77 | match extern with 78 | | .mem m => .some m 79 | | _ => .none 80 | ) 81 | 82 | def Extern.globals (externs : List Extern) : List Global := 83 | externs.filterMap (fun extern => 84 | match extern with 85 | | .globl g => .some g 86 | | _ => .none 87 | ) 88 | -------------------------------------------------------------------------------- /Wasm/Syntax/Value.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's value defintion: 2 | https://webassembly.github.io/spec/core/syntax/values.html 3 | -/ 4 | import Wasm.Vec 5 | import Numbers 6 | open Numbers 7 | 8 | namespace Wasm.Syntax.Value 9 | 10 | -- TODO: actually do floating point 11 | def FloatN (n : Nat) := Float 12 | 13 | def Byte := UInt8 14 | 15 | -- use this structure to ensure there's always a byte 16 | inductive Bytes 17 | | fst : Byte → Bytes 18 | | cons : Byte → Bytes → Bytes 19 | 20 | def Bytes.length (bytes : Bytes) : Nat := 21 | match bytes with 22 | | .fst _ => 1 23 | | .cons _ bs => Nat.succ (length bs) 24 | 25 | theorem Bytes.zero_lt_length : 0 < Bytes.length bytes := by 26 | cases bytes <;> simp [Bytes.length] 27 | 28 | theorem Bytes.length_cons : Bytes.length (.cons b bs) = Nat.succ (Bytes.length bs) := by 29 | rw [Bytes.length] 30 | 31 | theorem Bytes.length_fst : Bytes.length (.fst b) = 1 := by rw [Bytes.length] 32 | 33 | def Bytes.reverse (bytes : Bytes) : Bytes := 34 | match bytes with 35 | | .fst b => .fst b 36 | | .cons b bs => rev bs (.fst b) 37 | where rev (bytes acc : Bytes) : Bytes := 38 | match bytes with 39 | | .fst b => .cons b acc 40 | | .cons b bs => rev bs (.cons b acc) 41 | 42 | theorem Bytes.length_rev : ∀ acc, Bytes.length (reverse.rev bytes acc) = Bytes.length bytes + Bytes.length acc := by 43 | induction bytes 44 | case fst b => 45 | exact fun acc => by 46 | simp only [ 47 | reverse.rev, 48 | length_cons, 49 | Nat.succ_eq_add_one, 50 | length_fst, 51 | Nat.add_comm 52 | ] 53 | case cons b bs ih => 54 | intro acc 55 | simp [reverse.rev, ih, length_cons, length_cons, Nat.succ_add_eq_add_succ] 56 | 57 | theorem Bytes.length_reverse : Bytes.length (Bytes.reverse bytes) = Bytes.length bytes := by 58 | cases bytes <;> simp [reverse] 59 | case cons b bs => simp [length_rev, length_fst, length_cons] 60 | 61 | def Bytes.last (bytes : Bytes) : Byte := 62 | match bytes with 63 | | .fst b => b 64 | | .cons _ bs => last bs 65 | 66 | def Bytes.drop_last (bytes : Bytes) (h₁ : Bytes.length bytes > 1) : Bytes := 67 | match h₂ : bytes.reverse with 68 | | .fst b => by 69 | rw [←Bytes.length_reverse, h₂] at h₁ 70 | contradiction 71 | | .cons _ bs => bs.reverse 72 | 73 | -- theorem Bytes.length_drop_last (h₁ : Bytes.length bs = n) 74 | -- : Bytes.length (Bytes.drop_last (.cons b bs) h₂) = n := by 75 | -- simp [drop_last] 76 | -- split 77 | -- . contradiction 78 | -- . 79 | -- sorry 80 | 81 | -- def Bytes.last (bytes : Bytes) (_ : bytes = .cons b bs) : Byte := 82 | -- match bytes with 83 | -- | .fst _ => by contradiction 84 | -- | .cons _b₂ (.fst b₁) => b₁ 85 | -- | .cons _b₂ (.cons b₁ bs₁) => @Bytes.last b₁ bs₁ (.cons b₁ bs₁) (by simp) 86 | -- 87 | -- def Bytes.drop_last (bytes : Bytes) (_ : bytes = .cons b bs) : Bytes := 88 | -- match bytes with 89 | -- | .fst _ => by contradiction 90 | -- | .cons b₂ (.fst _b₁) => .fst b₂ 91 | -- | .cons b₂ (.cons b₁ bs₁) => 92 | -- .cons b₂ (@Bytes.drop_last b₁ bs₁ (.cons b₁ bs₁) (by simp)) 93 | -- 94 | -- theorem Bytes.length_drop_last 95 | -- : Bytes.length (Bytes.drop_last (.cons b bs) h) = Bytes.length bs := by 96 | -- cases bs <;> rw [drop_last] 97 | -- case fst _ => rfl 98 | -- case cons x y => 99 | -- rw [Bytes.length_cons, Bytes.length_cons, Nat.succ_inj'] 100 | -- induction y <;> rw [drop_last] 101 | -- case fst _ => simp [Bytes.length_fst] 102 | -- case cons i j ih => 103 | -- sorry 104 | 105 | -- least significant byte is at the end of the list 106 | def Unsigned.toBytes (n : { i // 0 < i }) (v : Unsigned n) : Bytes := 107 | if h : n.val ≤ 8 108 | then .fst (UInt8.ofNat (v.toNat % 256)) 109 | else 110 | let next := ⟨n.val - 8, Nat.zero_lt_sub_of_lt (Nat.lt_of_not_le h)⟩ 111 | .cons (UInt8.ofNat (v.toNat % 256)) (toBytes next (Unsigned.ofNat (v.toNat >>> 8))) 112 | termination_by n.val 113 | decreasing_by 114 | simp_wf 115 | apply Nat.lt_iff_le_and_ne.mp (Nat.lt_of_not_le h) 116 | |>.left 117 | |> Nat.sub_lt_self (Nat.zero_lt_succ 7) 118 | 119 | -- likewise, least significant byte is at the end of the list 120 | def Unsigned.ofBytes (lst : Bytes) 121 | : Unsigned ⟨ 8 * lst.length 122 | , Nat.mul_lt_mul_of_pos_left Bytes.zero_lt_length (Nat.zero_lt_succ 7) 123 | ⟩ := 124 | match lst with 125 | | .fst b => Unsigned.ofNat b.toNat 126 | | .cons b bs => 127 | let res := Unsigned.ofBytes bs 128 | Unsigned.ofNat ((res.toNat <<< 8) + b.toNat) 129 | 130 | structure Name where 131 | value : String 132 | maxLength : value.length < Vec.max_length 133 | 134 | end Syntax.Value 135 | 136 | def Vec.index (v : Vec α) (i : Fin v.length) : Unsigned32 := by 137 | have h := i.isLt 138 | have h' := v.maxLen 139 | have h'' := Nat.lt_trans h h' 140 | exact ⟨i.val, h''⟩ 141 | -------------------------------------------------------------------------------- /Wasm/Text.lean: -------------------------------------------------------------------------------- 1 | /- WASM Text Representation (WAT) -/ 2 | import Wasm.Text.Ident 3 | import Wasm.Text.Index 4 | import Wasm.Text.Instr 5 | import Wasm.Text.InstrTrans 6 | import Wasm.Text.Module 7 | import Wasm.Text.Trans 8 | import Wasm.Text.Typ 9 | -------------------------------------------------------------------------------- /Wasm/Text/Ident.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Syntax.Typ 2 | import Wasm.Syntax.Value 3 | 4 | namespace Wasm.Text 5 | 6 | abbrev Name := Wasm.Syntax.Value.Name 7 | instance : ToString Name := ⟨fun name => s!"\"{name.value}\""⟩ 8 | 9 | def Ident.validChar (c : Char) : Bool := 10 | c.isAlphanum || "!#$%'*+-./:<=>?@\\^_`|~".any (· = c) 11 | 12 | structure Ident where 13 | name : String 14 | name_nonempty : name ≠ "" 15 | name_valid_chars : name.all Ident.validChar 16 | deriving DecidableEq 17 | instance : ToString Ident := ⟨(s!"${·.name}")⟩ 18 | instance : ToString (Option Ident) := ⟨(·.map toString |>.getD "")⟩ 19 | 20 | def Ident.mkValid (name : String) : Option Ident := 21 | if h₁ : name = "" 22 | then none 23 | else if h₂ : name.all Ident.validChar 24 | then some ⟨name, h₁, h₂⟩ 25 | else none 26 | 27 | def Ident.mkValid! (name : String) : Ident := 28 | match Ident.mkValid name with 29 | | .some i => i 30 | | .none => ⟨"!(INVALID IDENT)!", sorry ,sorry⟩ 31 | 32 | def Ident.parse (name : String) : Option Ident := 33 | if name.startsWith "$" then Ident.mkValid (name.drop 1) else none 34 | 35 | def Ident.parse! (name : String) : Ident := 36 | match Ident.parse name with 37 | | .some id => id 38 | | .none => ⟨"!(INVALID IDENT)!", sorry ,sorry⟩ 39 | -------------------------------------------------------------------------------- /Wasm/Text/Index.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Text.Typ 2 | import Wasm.Text.Ident 3 | import Wasm.Text.Trans 4 | import Wasm.Syntax.Index 5 | import Numbers 6 | open Numbers 7 | 8 | namespace Wasm.Text.Module 9 | 10 | inductive Index 11 | | num : (x : Unsigned32) → Index 12 | | name : (v : Ident) → Index 13 | 14 | nonrec def Index.toString : Index → String 15 | | .num x => toString x 16 | | .name v => toString v 17 | instance : ToString Index := ⟨Index.toString⟩ 18 | 19 | instance : OfNat Index n := ⟨.num (Unsigned.ofNat n)⟩ 20 | instance : Coe Unsigned32 Index := ⟨.num⟩ 21 | instance : Coe Ident Index := ⟨.name⟩ 22 | 23 | namespace Index 24 | 25 | @[inline] def Typ := Index 26 | @[inline] def Function := Index 27 | @[inline] def Table := Index 28 | @[inline] def Memory := Index 29 | @[inline] def Global := Index 30 | @[inline] def Element := Index 31 | @[inline] def Data := Index 32 | @[inline] def Local := Index 33 | @[inline] def Label := Index 34 | 35 | instance : ToString Typ := ⟨Index.toString⟩ 36 | instance : ToString Function := ⟨Index.toString⟩ 37 | instance : ToString Table := ⟨Index.toString⟩ 38 | instance : ToString Memory := ⟨Index.toString⟩ 39 | instance : ToString Global := ⟨Index.toString⟩ 40 | instance : ToString Element := ⟨Index.toString⟩ 41 | instance : ToString Data := ⟨Index.toString⟩ 42 | instance : ToString Local := ⟨Index.toString⟩ 43 | instance : ToString Label := ⟨Index.toString⟩ 44 | 45 | instance : Coe Syntax.Module.Index.Typ Typ := ⟨(.num ·)⟩ 46 | instance : Coe Syntax.Module.Index.Function Function := ⟨(.num ·)⟩ 47 | instance : Coe Syntax.Module.Index.Table Table := ⟨(.num ·)⟩ 48 | instance : Coe Syntax.Module.Index.Memory Memory := ⟨(.num ·)⟩ 49 | instance : Coe Syntax.Module.Index.Global Global := ⟨(.num ·)⟩ 50 | instance : Coe Syntax.Module.Index.Element Element := ⟨(.num ·)⟩ 51 | instance : Coe Syntax.Module.Index.Data Data := ⟨(.num ·)⟩ 52 | instance : Coe Syntax.Module.Index.Local Local := ⟨(.num ·)⟩ 53 | instance : Coe Syntax.Module.Index.Label Label := ⟨(.num ·)⟩ 54 | 55 | instance : Coe (Vec Syntax.Module.Index.Typ) (Vec Typ) := ⟨(·.map (.num ·))⟩ 56 | instance : Coe (Vec Syntax.Module.Index.Function) (Vec Function) := ⟨(·.map (.num ·))⟩ 57 | instance : Coe (Vec Syntax.Module.Index.Table) (Vec Table) := ⟨(·.map (.num ·))⟩ 58 | instance : Coe (Vec Syntax.Module.Index.Memory) (Vec Memory) := ⟨(·.map (.num ·))⟩ 59 | instance : Coe (Vec Syntax.Module.Index.Global) (Vec Global) := ⟨(·.map (.num ·))⟩ 60 | instance : Coe (Vec Syntax.Module.Index.Element) (Vec Element) := ⟨(·.map (.num ·))⟩ 61 | instance : Coe (Vec Syntax.Module.Index.Data) (Vec Data) := ⟨(·.map (.num ·))⟩ 62 | instance : Coe (Vec Syntax.Module.Index.Local) (Vec Local) := ⟨(·.map (.num ·))⟩ 63 | instance : Coe (Vec Syntax.Module.Index.Label) (Vec Label) := ⟨(·.map (.num ·))⟩ 64 | 65 | instance : OfNat Typ n := ⟨.num (Unsigned.ofNat n)⟩ 66 | instance : OfNat Function n := ⟨.num (Unsigned.ofNat n)⟩ 67 | instance : OfNat Table n := ⟨.num (Unsigned.ofNat n)⟩ 68 | instance : OfNat Memory n := ⟨.num (Unsigned.ofNat n)⟩ 69 | instance : OfNat Global n := ⟨.num (Unsigned.ofNat n)⟩ 70 | instance : OfNat Element n := ⟨.num (Unsigned.ofNat n)⟩ 71 | instance : OfNat Data n := ⟨.num (Unsigned.ofNat n)⟩ 72 | instance : OfNat Local n := ⟨.num (Unsigned.ofNat n)⟩ 73 | instance : OfNat Label n := ⟨.num (Unsigned.ofNat n)⟩ 74 | 75 | instance : Coe Ident Typ := ⟨.name⟩ 76 | instance : Coe Ident Function := ⟨.name⟩ 77 | instance : Coe Ident Table := ⟨.name⟩ 78 | instance : Coe Ident Memory := ⟨.name⟩ 79 | instance : Coe Ident Global := ⟨.name⟩ 80 | instance : Coe Ident Element := ⟨.name⟩ 81 | instance : Coe Ident Data := ⟨.name⟩ 82 | instance : Coe Ident Local := ⟨.name⟩ 83 | instance : Coe Ident Label := ⟨.name⟩ 84 | 85 | open Ident.Context 86 | 87 | @[inline] def Typ.trans : Typ → Trans Wasm.Syntax.Module.Index.Typ 88 | | .num x => return x 89 | | .name v => indexOf types (.some v) (msg := "Types") 90 | instance : OfText Typ Wasm.Syntax.Module.Index.Typ := ⟨Typ.trans⟩ 91 | 92 | @[inline] def Function.trans : Function → Trans Syntax.Module.Index.Function 93 | | .num x => return x 94 | | .name v => indexOf funcs (.some v) (msg := "Functions") 95 | instance : OfText Function Syntax.Module.Index.Function := ⟨Function.trans⟩ 96 | 97 | @[inline] def Table.trans : Table → Trans Wasm.Syntax.Module.Index.Table 98 | | .num x => return x 99 | | .name v => indexOf tables (.some v) (msg := "Tables") 100 | instance : OfText Table Syntax.Module.Index.Table := ⟨Table.trans⟩ 101 | 102 | @[inline] def Memory.trans : Memory → Trans Wasm.Syntax.Module.Index.Memory 103 | | .num x => return x 104 | | .name v => indexOf mems (.some v) (msg := "Memories") 105 | instance : OfText Memory Syntax.Module.Index.Memory := ⟨Memory.trans⟩ 106 | 107 | @[inline] def Global.trans : Global → Trans Wasm.Syntax.Module.Index.Global 108 | | .num x => return x 109 | | .name v => indexOf globals (.some v) (msg := "Globals") 110 | instance : OfText Global Syntax.Module.Index.Global := ⟨Global.trans⟩ 111 | 112 | @[inline] def Element.trans : Element → Trans Wasm.Syntax.Module.Index.Element 113 | | .num x => return x 114 | | .name v => indexOf elem (.some v) (msg := "Elements") 115 | instance : OfText Element Syntax.Module.Index.Element := ⟨Element.trans⟩ 116 | 117 | @[inline] def Data.trans : Data → Trans Wasm.Syntax.Module.Index.Data 118 | | .num x => return x 119 | | .name v => indexOf data (.some v) (msg := "Data") 120 | instance : OfText Data Syntax.Module.Index.Data := ⟨Data.trans⟩ 121 | 122 | @[inline] def Local.trans : Local → Trans Wasm.Syntax.Module.Index.Local 123 | | .num x => return x 124 | | .name v => indexOf locals (.some v) (msg := "Locals") 125 | instance : OfText Local Syntax.Module.Index.Local := ⟨Local.trans⟩ 126 | 127 | @[inline] def Label.trans : Label → Trans Wasm.Syntax.Module.Index.Label 128 | | .num x => return x 129 | | .name v => indexOf labels (.some v) (msg := "Labels") 130 | instance : OfText Label Syntax.Module.Index.Label := ⟨Label.trans⟩ 131 | 132 | end Index 133 | 134 | /- since the abbreviation requires altering the program we allow it here as 135 | part of the CST -/ 136 | inductive Typeuse 137 | | type_ind : Index.Typ → Typeuse 138 | | param_res : Index.Typ → List Typ.Param → List Typ.Result → Typeuse 139 | | elab_param_res : List Typ.Param → List Typ.Result → Typeuse 140 | 141 | -- alters the state, if it needs to be restored it should be saved there 142 | def Typeuse.trans : Typeuse 143 | → Trans Wasm.Syntax.Module.Index.Typ 144 | | .type_ind x => do 145 | let s ← get 146 | let i : Wasm.Syntax.Module.Index.Typ ← ofText x 147 | match s.I.typedefs.get? (Unsigned.toNat i) with 148 | | .some ty => 149 | let n := ty.args.length 150 | let locals := ty.args.list.map (fun _ => none) 151 | Trans.updateI {Ident.Context.empty with locals} 152 | return i 153 | | .none => Trans.Error.errMsg s!"Typeuse could not find typedef[{i}]" 154 | | .param_res x t₁ t₂ => do 155 | let s ← get 156 | let i : Wasm.Syntax.Module.Index.Typ ← ofText x 157 | match s.I.typedefs.get? (Unsigned.toNat i) with 158 | | .some ty => 159 | if ty.args == t₁ && ty.result == t₂ then 160 | let locals := t₁.map (·.1) 161 | -- todo verify well-formed 162 | Trans.updateI {Ident.Context.empty with locals} 163 | return i 164 | else Trans.Error.errMsg <| 165 | s!"Typeuse given params/results {t₁}/{t₂} does not match {ty}" 166 | | .none => Trans.Error.errMsg s!"Typeuse could not find typedef[{i}]" 167 | | .elab_param_res t₁ t₂ => do 168 | let s ← get 169 | let args := ⟨t₁.map (·.2), by sorry⟩ 170 | let res := ⟨t₂, by sorry⟩ 171 | let func := ⟨args, res⟩ 172 | match s.I.typedefs.indexOf? func with 173 | | .some i => 174 | let locals := t₁.map (·.1) 175 | Trans.updateI {Ident.Context.empty with locals} 176 | return Unsigned.ofNat i 177 | | .none => 178 | let locals := t₁.map (·.1) 179 | set { s with I := { Ident.Context.empty with locals } 180 | , types := s.types ++ [func] 181 | } 182 | return Unsigned.ofNat (s.I.typedefs.length) 183 | instance : OfText Typeuse Wasm.Syntax.Module.Index.Typ := ⟨Typeuse.trans⟩ 184 | 185 | def Typeuse.toString : Typeuse → String 186 | | .type_ind x => s!"(type {x})" 187 | | .param_res x params res => s!"(type {x} {params} {res})" 188 | | .elab_param_res params res => s!"{params} {res}" 189 | instance : ToString Typeuse := ⟨Typeuse.toString⟩ 190 | -------------------------------------------------------------------------------- /Wasm/Text/Instr.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's instruction defintion: 2 | https://webassembly.github.io/spec/core/text/instructions.html 3 | -/ 4 | 5 | import Wasm.Syntax.Instr 6 | import Wasm.Text.Typ 7 | import Wasm.Text.Index 8 | 9 | namespace Wasm.Text 10 | 11 | open Text.Module 12 | 13 | inductive Label 14 | | name : Ident → Label 15 | | no_label 16 | deriving Inhabited 17 | instance : Coe Ident Label := ⟨.name⟩ 18 | 19 | def Label.toOption : Label → Option Ident 20 | | .no_label => .none 21 | | .name id => .some id 22 | def Label.ofOption : Option Ident → Label 23 | | .none => .no_label 24 | | .some id => .name id 25 | 26 | namespace Instr 27 | 28 | inductive Reference 29 | | null : Typ.Heap → Reference 30 | | is_null 31 | | func : Index.Function → Reference 32 | instance : Coe Syntax.Instr.Reference Reference := 33 | ⟨ fun | .null t => .null t 34 | | .is_null => .is_null 35 | | .func i => .func i 36 | ⟩ 37 | 38 | inductive Local 39 | | get : Index.Local → Local 40 | | set : Index.Local → Local 41 | | tee : Index.Local → Local 42 | instance : Coe Syntax.Instr.Local Local := 43 | ⟨ fun | .get i => .get i 44 | | .set i => .set i 45 | | .tee i => .tee i 46 | ⟩ 47 | 48 | inductive Global 49 | | get : Index.Global → Global 50 | | set : Index.Global → Global 51 | instance : Coe Syntax.Instr.Global Global := 52 | ⟨ fun | .get i => .get i 53 | | .set i => .set i 54 | ⟩ 55 | 56 | inductive Table 57 | | get : Index.Table → Table 58 | | set : Index.Table → Table 59 | | size : Index.Table → Table 60 | | grow : Index.Table → Table 61 | | fill : Index.Table → Table 62 | | copy : Index.Table → Index.Table → Table 63 | | init : Index.Table → Index.Element → Table 64 | instance : Coe Syntax.Instr.Table Table := 65 | ⟨ fun | .get x => .get x 66 | | .set x => .set x 67 | | .size x => .size x 68 | | .grow x => .grow x 69 | | .fill x => .fill x 70 | | .copy x y => .copy x y 71 | | .init x y => .init x y 72 | ⟩ 73 | 74 | inductive Memory 75 | | integer : Syntax.Instr.Memory.Integer nn → Memory 76 | | float : Syntax.Instr.Memory.Float nn → Memory 77 | | size 78 | | grow 79 | | fill 80 | | copy 81 | | init : Index.Data → Memory 82 | | data_drop : Index.Data → Memory 83 | instance : Coe Syntax.Instr.Memory Memory := 84 | ⟨ fun | .integer i => .integer i 85 | | .float f => .float f 86 | | .size => .size 87 | | .grow => .grow 88 | | .fill => .fill 89 | | .copy => .copy 90 | | .init x => .init x 91 | | .data_drop x => .data_drop x 92 | ⟩ 93 | 94 | end Instr 95 | 96 | inductive Instr.Plain 97 | | numeric : (Syntax.Instr.Numeric nn) → Instr.Plain 98 | | reference : Instr.Reference → Instr.Plain 99 | -- Parametric 100 | | drop 101 | | select : Option (List Syntax.Typ.Val) → Instr.Plain 102 | | locl : Instr.Local → Instr.Plain 103 | | globl : Instr.Global → Instr.Plain 104 | | table : Instr.Table → Instr.Plain 105 | | elem_drop : Index.Element → Instr.Plain 106 | | memory : Instr.Memory → Instr.Plain 107 | -- Control 108 | | nop 109 | | unreachable 110 | | br : Index.Label → Instr.Plain 111 | | br_if : Index.Label → Instr.Plain 112 | | br_table : Vec Index.Label → Index.Label → Instr.Plain 113 | | wasm_return 114 | | call : Index.Function → Instr.Plain 115 | | call_indirect : Index.Table → Typeuse → Instr.Plain 116 | 117 | 118 | inductive Instr.BlockType 119 | | value : (t : Option Typ.Result) → BlockType 120 | | typeuse : Module.Typeuse → BlockType 121 | 122 | instance : Coe (Syntax.Instr.BlockType) Instr.BlockType := 123 | ⟨ fun | .index i => .typeuse (.type_ind i) 124 | | .value .none => .value .none 125 | | .value (.some v) => .value (.some v) 126 | ⟩ 127 | 128 | mutual 129 | inductive Instr.Block 130 | | block 131 | : Label 132 | → Instr.BlockType 133 | → List Instr 134 | → Syntax.Instr.Pseudo 135 | → Option Ident 136 | → Instr.Block 137 | | loop 138 | : Label 139 | → Instr.BlockType 140 | → List Instr 141 | → Syntax.Instr.Pseudo 142 | → Option Ident 143 | → Instr.Block 144 | | wasm_if 145 | : Label 146 | → Instr.BlockType 147 | → (if_body : List Instr) 148 | → Syntax.Instr.Pseudo 149 | → (id₁ : Option Ident) 150 | → (else_body : List Instr) 151 | → Syntax.Instr.Pseudo 152 | → (id₂ : Option Ident) 153 | → Instr.Block 154 | 155 | inductive Instr 156 | | plain : Instr.Plain → Instr 157 | | block : Instr.Block → Instr 158 | | comment : String → Instr 159 | end 160 | 161 | mutual 162 | def Instr.ofSyntaxInstr : Syntax.Instr → Instr 163 | | .numeric n => .plain (.numeric n) 164 | | .reference r => .plain (.reference r) 165 | | .drop => .plain .drop 166 | | .select v => .plain (.select v) 167 | | .locl l => .plain (.locl l) 168 | | .globl g => .plain (.globl g) 169 | | .table t => .plain (.table t) 170 | | .elem_drop e => .plain (.elem_drop e) 171 | | .memory m => .plain (.memory m) 172 | | .nop => .plain .nop 173 | | .unreachable => .plain .unreachable 174 | | .block bt is p => 175 | .block (.block .no_label bt (Instr.ofSyntaxInstrList is) p .none) 176 | | .loop bt is p => 177 | .block (.loop .no_label bt (Instr.ofSyntaxInstrList is) p .none) 178 | | .wasm_if bt ib p₁ ie p₂ => .block ( 179 | .wasm_if .no_label bt (Instr.ofSyntaxInstrList ib) p₁ .none 180 | (Instr.ofSyntaxInstrList ie) p₂ .none 181 | ) 182 | | .br l => .plain (.br l) 183 | | .br_if l => .plain (.br_if l) 184 | | .br_table t l => .plain (.br_table t l) 185 | | .wasm_return => .plain (.wasm_return) 186 | | .call f => .plain (.call f) 187 | | .call_indirect f t => .plain (.call_indirect f (.type_ind t)) 188 | termination_by i => sizeOf i 189 | 190 | def Instr.ofSyntaxInstrList : List Syntax.Instr → List Instr 191 | | [] => [] 192 | | i :: is => Instr.ofSyntaxInstr i :: ofSyntaxInstrList is 193 | termination_by is => sizeOf is 194 | 195 | end 196 | 197 | instance : Coe Syntax.Instr Instr := ⟨Instr.ofSyntaxInstr⟩ 198 | instance : Coe Instr.Plain Instr := ⟨Instr.plain⟩ 199 | instance : Coe Instr.Block Instr := ⟨Instr.block⟩ 200 | 201 | nonrec def Label.toString : Label → String 202 | | .name n => toString n 203 | | .no_label => "" 204 | instance : ToString Label := ⟨Label.toString⟩ 205 | 206 | namespace Instr 207 | 208 | namespace Numeric 209 | 210 | def Size.toString : Syntax.Instr.Numeric.Size → String 211 | | .double => "32" 212 | | .quad => "64" 213 | instance : ToString Syntax.Instr.Numeric.Size := ⟨Size.toString⟩ 214 | 215 | def Sign.toString : Syntax.Instr.Numeric.Sign → String 216 | | .s => "s" 217 | | .u => "u" 218 | instance : ToString Syntax.Instr.Numeric.Sign := ⟨Sign.toString⟩ 219 | 220 | namespace Integer 221 | 222 | def Unop.toString : Syntax.Instr.Numeric.Integer.Unop → String 223 | | .clz => "clz" 224 | | .ctz => "ctz" 225 | | .popcnt => "popcnt" 226 | instance : ToString Syntax.Instr.Numeric.Integer.Unop := ⟨Unop.toString⟩ 227 | 228 | def Binop.toString : Syntax.Instr.Numeric.Integer.Binop → String 229 | | .add => "add" 230 | | .sub => "sub" 231 | | .mul => "mul" 232 | | .div s => s!"div_{s}" 233 | | .rem s => s!"rem_{s}" 234 | | .and => "and" 235 | | .or => "or" 236 | | .xor => "xor" 237 | | .shl => "shl" 238 | | .shr s => s!"shr_{s}" 239 | | .rotl => "rotl" 240 | | .rotr => "rotr" 241 | instance : ToString Syntax.Instr.Numeric.Integer.Binop := ⟨Binop.toString⟩ 242 | 243 | def Test.toString : Syntax.Instr.Numeric.Integer.Test → String 244 | | .eqz => "eqz" 245 | instance : ToString Syntax.Instr.Numeric.Integer.Test := ⟨Test.toString⟩ 246 | 247 | def Relation.toString : Syntax.Instr.Numeric.Integer.Relation → String 248 | | .eq => "eq" 249 | | .ne => "ne" 250 | | .lt s => s!"lt_{s}" 251 | | .gt s => s!"gt_{s}" 252 | | .le s => s!"le_{s}" 253 | | .ge s => s!"ge_{s}" 254 | instance : ToString Syntax.Instr.Numeric.Integer.Relation := ⟨Relation.toString⟩ 255 | 256 | def toString : Syntax.Instr.Numeric.Integer nn → String 257 | | .const v => s!"i{nn}.const {v}" 258 | | .unop op => s!"i{nn}.{op}" 259 | | .binop op => s!"i{nn}.{op}" 260 | | .test op => s!"i{nn}.{op}" 261 | | .relation op => s!"i{nn}.{op}" 262 | | .extend8_s => s!"i{nn}.extend8_s" 263 | | .extend16_s => s!"i{nn}.extend16_s" 264 | | .extend32_s => s!"i{nn}.extend32_s" 265 | | .wrap_i64 => s!"i{nn}.wrap_i64" 266 | | .extend_i32 s => s!"i{nn}.extend_i32_{s}" 267 | | .trunc_f mm s => s!"i{nn}.trunc_f{mm}_{s}" 268 | | .trunc_sat_f mm s => s!"i{nn}.trunc_sat_f{mm}_{s}" 269 | | .reinterpret_f => s!"i{nn}.reinterpret_f{nn}" 270 | instance : ToString (Syntax.Instr.Numeric.Integer nn) := ⟨toString⟩ 271 | 272 | end Integer 273 | 274 | namespace Float 275 | 276 | def Unop.toString : Syntax.Instr.Numeric.Float.Unop → String 277 | | .abs => "abs" 278 | | .neg => "neg" 279 | | .sqrt => "sqrt" 280 | | .ceil => "ceil" 281 | | .floor => "floor" 282 | | .trunc => "trunc" 283 | | .nearest => "nearest" 284 | instance : ToString Syntax.Instr.Numeric.Float.Unop := ⟨Unop.toString⟩ 285 | 286 | def Binop.toString : Syntax.Instr.Numeric.Float.Binop → String 287 | | .add => "add" 288 | | .sub => "sub" 289 | | .mul => "mul" 290 | | .div => "div" 291 | | .min => "min" 292 | | .max => "max" 293 | | .copysign => "copysign" 294 | instance : ToString Syntax.Instr.Numeric.Float.Binop := ⟨Binop.toString⟩ 295 | 296 | def Relation.toString : Syntax.Instr.Numeric.Float.Relation → String 297 | | .eq => "eq" 298 | | .ne => "ne" 299 | | .lt => "lt" 300 | | .gt => "gt" 301 | | .le => "le" 302 | | .ge => "ge" 303 | instance : ToString Syntax.Instr.Numeric.Float.Relation := ⟨Relation.toString⟩ 304 | 305 | def toString : Syntax.Instr.Numeric.Float (nn : Syntax.Instr.Numeric.Size) 306 | → String 307 | | .const v => s!"f{nn}.const v" 308 | | .unop op => s!"f{nn}.{op}" 309 | | .binop op => s!"f{nn}.{op}" 310 | | .relation op => s!"f{nn}.{op}" 311 | | .demote_f64 => s!"f{nn}.demote_f64" 312 | | .promote_f32 => s!"f{nn}.promote_f32" 313 | | .convert_i mm s => s!"f{nn}.convert_i{mm}_{s}" 314 | | .reinterpret_i => s!"f{nn}.reinterpret_i{nn}" 315 | instance : ToString (Syntax.Instr.Numeric.Float nn) := ⟨Float.toString⟩ 316 | 317 | end Float 318 | 319 | nonrec def toString : Syntax.Instr.Numeric nn → String 320 | | .integer i => toString i 321 | | .float f => toString f 322 | instance : ToString (Syntax.Instr.Numeric nn) := ⟨Numeric.toString⟩ 323 | 324 | end Numeric 325 | 326 | def Reference.toString : Reference → String 327 | | .null ty => s!"ref.null {ty}" 328 | | .is_null => "ref.is_null" 329 | | .func idx => s!"ref.func {idx}" 330 | instance : ToString Reference := ⟨Reference.toString⟩ 331 | instance : ToString Syntax.Instr.Reference := ⟨(Reference.toString ·)⟩ 332 | 333 | def Local.toString : Local → String 334 | | .get x => s!"local.get {x}" 335 | | .set x => s!"local.set {x}" 336 | | .tee x => s!"local.tee {x}" 337 | instance : ToString Local := ⟨Local.toString⟩ 338 | instance : ToString Syntax.Instr.Local := ⟨(Local.toString ·)⟩ 339 | 340 | 341 | def Global.toString : Global → String 342 | | .get x => s!"global.get {x}" 343 | | .set x => s!"global.set {x}" 344 | instance : ToString Global := ⟨Global.toString⟩ 345 | instance : ToString Syntax.Instr.Global := ⟨(Global.toString ·)⟩ 346 | 347 | def Table.toString : Table → String 348 | | .get x => s!"table.get {x}" 349 | | .set x => s!"table.set {x}" 350 | | .size x => s!"table.size {x}" 351 | | .grow x => s!"table.grow {x}" 352 | | .fill x => s!"table.fill {x}" 353 | | .copy x y => s!"table.copy {x} {y}" 354 | | .init x y => s!"table.init {x} {y}" 355 | instance : ToString Table := ⟨Table.toString⟩ 356 | instance : ToString Syntax.Instr.Table := ⟨(Table.toString ·)⟩ 357 | 358 | namespace Memory 359 | 360 | def Arg.toString (a : Syntax.Instr.Memory.Arg) : String := 361 | s!"offset={a.offset} align={2 ^ a.align.toNat}" 362 | instance : ToString Syntax.Instr.Memory.Arg := ⟨Arg.toString⟩ 363 | 364 | def Integer.toString : Syntax.Instr.Memory.Integer nn → String 365 | | .load m => s!"i{nn}.load {m}" 366 | | .store m => s!"i{nn}.store {m}" 367 | | .load8 s m => s!"i{nn}.load8_{s} {m}" 368 | | .load16 s m => s!"i{nn}.load16_{s} {m}" 369 | | .load32 s m => s!"i{nn}.load32_{s} {m}" 370 | | .store8 m => s!"i{nn}.store8 {m}" 371 | | .store16 m => s!"i{nn}.store16 {m}" 372 | | .store32 m => s!"i{nn}.store32 {m}" 373 | instance : ToString (Syntax.Instr.Memory.Integer nn) := ⟨Integer.toString⟩ 374 | 375 | 376 | def Float.toString : Syntax.Instr.Memory.Float nn → String 377 | | .load m => s!"f{nn}.load {m}" 378 | | .store m => s!"f{nn}.store {m}" 379 | instance : ToString (Syntax.Instr.Memory.Float nn) := ⟨Float.toString⟩ 380 | 381 | nonrec def toString : Memory → String 382 | | .integer i => toString i 383 | | .float f => toString f 384 | | .size => "memory.size" 385 | | .grow => "memory.grow" 386 | | .fill => "memory.fill" 387 | | .copy => "memory.copy" 388 | | .init x => s!"memory.init {x}" 389 | | .data_drop x => s!"data.drop {x}" 390 | instance : ToString Memory := ⟨toString⟩ 391 | instance : ToString Syntax.Instr.Memory := ⟨(toString ·)⟩ 392 | 393 | end Memory 394 | 395 | def Plain.toString : Instr.Plain → String 396 | | .numeric n => s!"({n})" 397 | | .reference r => s!"({r})" 398 | -- Parametric 399 | | .drop => "drop" 400 | | .select t? => 401 | match t? with 402 | | .none => "select" 403 | | .some t => s!"(select {t})" 404 | | .locl l => s!"({l})" 405 | | .globl g => s!"({g})" 406 | | .table t => s!"({t})" 407 | | .elem_drop x => s!"(elem.drop {x})" 408 | | .memory m => s!"({m})" 409 | -- Control 410 | | .nop => "nop" 411 | | .unreachable => "unreachable" 412 | | .br l => s!"(br {l})" 413 | | .br_if l => s!"(br_if {l})" 414 | | .br_table ls l => s!"(br_table {ls} {l})" 415 | | .wasm_return => "return" 416 | | .call x => s!"(call {x})" 417 | | .call_indirect t tu => s!"(call_indirect {t} {tu})" 418 | instance : ToString Instr.Plain := ⟨Plain.toString⟩ 419 | 420 | def BlockType.toString : BlockType → String 421 | | .value .none => "" 422 | | .value (.some x) => x.toString 423 | | .typeuse tu => tu.toString 424 | instance : ToString BlockType := ⟨BlockType.toString⟩ 425 | 426 | mutual 427 | 428 | def Block.toString : Instr.Block → String 429 | | .block lbl bt ins p id => 430 | let ins := listToString ins |>.replace "\n" "\n " 431 | s!"(block {lbl} {bt}\n {ins}\n)" 432 | | .loop lbl bt ins p id => 433 | let ins := listToString ins |>.replace "\n" "\n " 434 | s!"(loop {lbl} {bt}\n {ins}\n)" 435 | | .wasm_if lbl bt ins p₁ el [] p₂ e => 436 | let ins := listToString ins |>.replace "\n" "\n " 437 | s!"(if {lbl} {bt} (then\n {ins}\n))" 438 | | .wasm_if lbl bt ins p₁ el ins' p₂ e => 439 | let ins := listToString ins |>.replace "\n" "\n " 440 | let ins' := listToString ins' |>.replace "\n" "\n " 441 | s!"(if {lbl} {bt}\n (then\n {ins}\n ) (else\n {ins'}\n )\n)" 442 | termination_by i => sizeOf i 443 | 444 | def toString : Instr → String 445 | | .plain i => Plain.toString i 446 | | .block i => Block.toString i 447 | | .comment s => s!"(; {s} ;)" 448 | termination_by i => sizeOf i 449 | 450 | def listToString : List Instr → String 451 | | [] => "" 452 | | i :: [] => toString i 453 | | i :: is => toString i ++ "\n" ++ listToString is 454 | termination_by is => sizeOf is 455 | 456 | end 457 | 458 | instance : ToString Instr.Block := ⟨Instr.Block.toString⟩ 459 | instance : ToString Instr := ⟨Instr.toString⟩ 460 | instance : ToString Syntax.Instr := ⟨(Instr.toString ·)⟩ 461 | instance : ToString (List Instr) := ⟨Instr.listToString⟩ 462 | instance : ToString (List Syntax.Instr) := ⟨(Instr.listToString ·)⟩ 463 | 464 | end Instr 465 | 466 | @[reducible] def Expr := List Instr 467 | 468 | end Wasm.Text 469 | 470 | namespace Wasm.Syntax.Instr 471 | 472 | def Pseudo.toString : Pseudo → String 473 | | .wasm_end => "end" 474 | | .wasm_else => "else" 475 | instance : ToString Pseudo := ⟨Pseudo.toString⟩ 476 | 477 | def Expr.toString : Expr → String := (Text.Instr.listToString ·.fst) 478 | instance : ToString Expr := ⟨Expr.toString⟩ 479 | -------------------------------------------------------------------------------- /Wasm/Text/InstrTrans.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's instruction defintion: 2 | https://webassembly.github.io/spec/core/text/instructions.html 3 | -/ 4 | 5 | 6 | -- TODO WORK IN PROGRESS 7 | 8 | import Wasm.Syntax.Instr 9 | import Wasm.Text.Typ 10 | import Wasm.Text.Ident 11 | import Wasm.Text.Index 12 | import Wasm.Text.Instr 13 | 14 | namespace Wasm.Text 15 | 16 | open Wasm.Text.Module Wasm.Text.Instr 17 | 18 | def Label.add : Label → Trans Unit 19 | | .name v => do 20 | let s ← get 21 | match s.I.labels.indexOf? (.some v) with 22 | | .none => Trans.updateI { s.I with labels := .some v :: s.I.labels } 23 | | .some i => 24 | Trans.updateI { s.I with labels := .some v :: s.I.labels.set i .none } 25 | | .no_label => do 26 | let s ← get 27 | Trans.updateI { s.I with labels := .none :: s.I.labels } 28 | 29 | namespace Instr 30 | 31 | def Reference.trans : Instr.Reference → Trans Syntax.Instr.Reference 32 | | .null t => return .null t 33 | | .is_null => return .is_null 34 | | .func x => return .func (← ofText x) 35 | instance : OfText Instr.Reference Syntax.Instr.Reference := ⟨Reference.trans⟩ 36 | 37 | def Local.trans : Instr.Local → Trans Syntax.Instr.Local 38 | | .get i => return .get (← ofText i) 39 | | .set i => return .set (← ofText i) 40 | | .tee i => return .tee (← ofText i) 41 | instance : OfText Instr.Local Syntax.Instr.Local := ⟨Local.trans⟩ 42 | 43 | def Global.trans : Instr.Global → Trans Syntax.Instr.Global 44 | | .get i => return .get (← ofText i) 45 | | .set i => return .get (← ofText i) 46 | instance : OfText Instr.Global Syntax.Instr.Global := ⟨Global.trans⟩ 47 | 48 | def Table.trans : Instr.Table → Trans Syntax.Instr.Table 49 | | .get x => return .get (← ofText x) 50 | | .set x => return .set (← ofText x) 51 | | .size x => return .size (← ofText x) 52 | | .grow x => return .grow (← ofText x) 53 | | .fill x => return .fill (← ofText x) 54 | | .copy x y => return .copy (← ofText x) (← ofText y) 55 | | .init x y => return .init (← ofText x) (← ofText y) 56 | instance : OfText Instr.Table Syntax.Instr.Table := ⟨Table.trans⟩ 57 | 58 | def Memory.trans : Instr.Memory → Trans Syntax.Instr.Memory 59 | | .integer i => return .integer i 60 | | .float f => return .float f 61 | | .size => return .size 62 | | .grow => return .grow 63 | | .fill => return .fill 64 | | .copy => return .copy 65 | | .init d => return .init (← ofText d) 66 | | .data_drop d => return .data_drop (← ofText d) 67 | instance : OfText Instr.Memory Syntax.Instr.Memory := ⟨Memory.trans⟩ 68 | 69 | end Instr 70 | 71 | def Instr.Plain.trans : Instr.Plain → Trans Syntax.Instr 72 | | .numeric n => return .numeric n 73 | | .reference r => return .reference (← ofText r) 74 | -- Parametric 75 | | .drop => return .drop 76 | | .select s => return .select s 77 | | .locl l => return .locl (← ofText l) 78 | | .globl g => return .globl (← ofText g) 79 | | .table t => return .table (← ofText t) 80 | | .elem_drop e => return .elem_drop (← ofText e) 81 | | .memory m => return .memory (← ofText m) 82 | -- Control 83 | | .nop => return .nop 84 | | .unreachable => return .unreachable 85 | | .br l => return .br (← ofText l) 86 | | .br_if l => return .br_if (← ofText l) 87 | | .br_table ls l => return .br_table (← ofText ls) (← ofText l) 88 | | .wasm_return => return .wasm_return 89 | | .call f => return .call (← ofText f) 90 | | .call_indirect x y => return .call_indirect (← ofText x) (← ofText y) 91 | instance : OfText Instr.Plain Syntax.Instr := ⟨Instr.Plain.trans⟩ 92 | 93 | def Instr.BlockType.trans : Instr.BlockType → Trans Wasm.Syntax.Instr.BlockType 94 | | .value t => return .value t 95 | | .typeuse tu => do 96 | let I := (← get).I 97 | let x : Wasm.Syntax.Module.Index.Typ ← ofText tu 98 | let s' ← get 99 | if s'.I.locals.all (Option.isNone) then 100 | Trans.updateI I 101 | return .index x 102 | Trans.Error.errMsg "Parameters in block type cannot be named!" 103 | instance : OfText Instr.BlockType Wasm.Syntax.Instr.BlockType := 104 | ⟨Instr.BlockType.trans⟩ 105 | 106 | mutual 107 | def Instr.Block.trans : Instr.Block → Trans Wasm.Syntax.Instr 108 | | .block lbl bt ins wend id => do 109 | let s ← get 110 | let bt' ← ofText bt 111 | Label.add lbl 112 | let ins' ← Instr.List.trans ins 113 | match wend with 114 | | .wasm_end => do 115 | let _ ← (do -- check id and label match 116 | if let some _ := id then 117 | if id ≠ lbl.toOption then 118 | Trans.Error.errMsg s!"Block end label {id} doesn't match start {lbl.toOption}." 119 | else (pure () : Trans Unit) 120 | pure ()) 121 | Trans.updateI s.I -- restore context 122 | return .block bt' ins' wend 123 | | _ => Trans.Error.errMsg s!"Block cannot be terminated by {wend}." 124 | 125 | | .loop lbl bt ins wend id => do 126 | let s ← get 127 | let bt' ← ofText bt 128 | Label.add lbl 129 | let ins' ← Instr.List.trans ins 130 | match wend with 131 | | .wasm_end => 132 | let _ ← (do -- check id and label match 133 | if let some _ := id then 134 | if id ≠ lbl.toOption then 135 | Trans.Error.errMsg s!"Loop end label {id} doesn't match start {lbl.toOption}." 136 | else (pure () : Trans Unit) 137 | pure ()) 138 | Trans.updateI s.I -- restore context 139 | return .loop bt' ins' wend 140 | 141 | | _ => Trans.Error.errMsg s!"Loop cannot be terminated by {wend}." 142 | 143 | | .wasm_if lbl bt ins₁ welse id₁ ins₂ wend id₂ => do 144 | let s ← get 145 | let bt' ← ofText bt 146 | Label.add lbl 147 | let ins₁' ← Instr.List.trans ins₁ 148 | match welse with 149 | | .wasm_else => 150 | let _ ← (do -- check id and label match 151 | if let some _ := id₁ then 152 | if id₁ ≠ lbl.toOption then 153 | Trans.Error.errMsg s!"Else label {id₁} doesn't match start {lbl.toOption}." 154 | else (pure () : Trans Unit) 155 | pure ()) 156 | let ins₂' ← Instr.List.trans ins₂ 157 | match wend with 158 | | .wasm_end => 159 | let _ ← (do -- check id and label match 160 | if let some _ := id₂ then 161 | if id₂ ≠ lbl.toOption then 162 | Trans.Error.errMsg s!"If end label {id₂} doesn't match start {lbl.toOption}." 163 | else (pure () : Trans Unit) 164 | pure ()) 165 | Trans.updateI s.I -- restore context 166 | return .wasm_if bt' ins₁' welse ins₂' wend 167 | | _ => Trans.Error.errMsg s!"If cannot be terminated by {wend}." 168 | | _ => Trans.Error.errMsg s!"If-then body cannot be terminated by {wend}." 169 | 170 | def Instr.trans : Instr → Trans Wasm.Syntax.Instr 171 | | .plain i => ofText i 172 | | .block b => Instr.Block.trans b 173 | | .comment _ => return .nop -- todo : maybe change this? 174 | 175 | def Instr.List.trans : List Instr → Trans (List Wasm.Syntax.Instr) 176 | | .nil => return [] 177 | | .cons x xs => return (← Instr.trans x) :: (← Instr.List.trans xs) 178 | end 179 | 180 | instance : OfText Instr.Block Wasm.Syntax.Instr := ⟨Instr.Block.trans⟩ 181 | instance : OfText Instr Wasm.Syntax.Instr := ⟨Instr.trans⟩ 182 | instance : OfText (List Instr) (List Wasm.Syntax.Instr) := ⟨Instr.List.trans⟩ 183 | instance : OfText Expr Wasm.Syntax.Expr := 184 | ⟨fun e => do return (← Instr.List.trans e, .wasm_end)⟩ 185 | 186 | end Wasm.Text 187 | -------------------------------------------------------------------------------- /Wasm/Text/Module.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Text.Index 2 | import Wasm.Text.Typ 3 | import Wasm.Text.Instr 4 | import Wasm.Text.InstrTrans 5 | import Wasm.Syntax.Module 6 | 7 | namespace Wasm.Text.Module 8 | 9 | structure Typ where 10 | lbl : Option Ident 11 | ft : Typ.Func 12 | instance : Coe Syntax.Typ.Func Typ := ⟨(⟨.none, ·⟩)⟩ 13 | instance : OfText Typ Syntax.Typ.Func := ⟨(return ← ofText ·.ft)⟩ 14 | 15 | inductive Import.Description 16 | | func : Option Ident → Typeuse → Description 17 | | table : Option Ident → Syntax.Typ.Table → Description 18 | | mem : Option Ident → Syntax.Typ.Mem → Description 19 | | globl : Option Ident → Syntax.Typ.Global → Description 20 | instance : Coe Syntax.Module.Import.Description Import.Description := 21 | ⟨ fun | .func i => .func .none (.type_ind i) 22 | | .table t => .table .none t 23 | | .mem m => .mem .none m 24 | | .globl g => .globl .none g 25 | ⟩ 26 | 27 | def Import.Description.trans 28 | : Import.Description → Trans Syntax.Module.Import.Description 29 | | .func _id tu => do 30 | let s ← get 31 | let x ← ofText tu 32 | Trans.updateI s.I 33 | return .func x 34 | | .table _id t => return .table t 35 | | .mem _id m => return .mem m 36 | | .globl _id g => return .globl g 37 | instance : OfText Import.Description Syntax.Module.Import.Description := 38 | ⟨Import.Description.trans⟩ 39 | 40 | structure Import where 41 | module : Name 42 | name : Name 43 | desc : Import.Description 44 | instance : Coe (Syntax.Module.Import) Import := 45 | ⟨ fun imp => ⟨imp.module, imp.name, imp.desc⟩ ⟩ 46 | 47 | def Import.trans (imp : Import) : Trans Syntax.Module.Import := do 48 | return ⟨imp.module, imp.name, ← ofText imp.desc⟩ 49 | instance : OfText Import Syntax.Module.Import := ⟨Import.trans⟩ 50 | 51 | 52 | structure Local where 53 | lbl : Option Ident 54 | typ : Syntax.Typ.Val 55 | instance : OfText Local Syntax.Typ.Val := ⟨(return ·.typ)⟩ 56 | 57 | structure Function where 58 | lbl : Option Ident 59 | typeuse : Typeuse 60 | locals : List Local 61 | body : List Instr 62 | instance : Coe Syntax.Module.Function Function := 63 | ⟨ fun f => ⟨ .none 64 | , .type_ind f.type 65 | , f.locals.list.map (⟨.none, .⟩) 66 | , f.body.1 67 | ⟩ 68 | ⟩ 69 | 70 | def Function.trans (func : Function) : Trans Syntax.Module.Function := do 71 | let s ← get 72 | let x ← ofText func.typeuse 73 | let locals ← ofText func.locals 74 | let I'' := s.I ++ (← get).I ++ {locals := func.locals.map (·.1)} 75 | Trans.updateI I'' 76 | let ins ← ofText func.body 77 | -- todo: check I'' well-formed 78 | return ⟨x, ⟨locals, sorry⟩, ins⟩ 79 | instance : OfText Function Syntax.Module.Function := ⟨Function.trans⟩ 80 | 81 | 82 | structure Table where 83 | lbl : Option Ident 84 | type : Syntax.Typ.Table 85 | instance : Coe Syntax.Module.Table Table := ⟨ fun tab => ⟨.none, tab.type⟩ ⟩ 86 | instance : OfText Table Syntax.Module.Table := ⟨(return ⟨·.type⟩)⟩ 87 | 88 | 89 | structure Memory where 90 | lbl : Option Ident 91 | type : Syntax.Typ.Mem 92 | instance : Coe Syntax.Module.Memory Memory := 93 | ⟨ fun mem => ⟨.none, mem.type⟩ ⟩ 94 | instance : OfText Memory Syntax.Module.Memory := ⟨(return ⟨·.type⟩)⟩ 95 | 96 | 97 | structure Global where 98 | lbl : Option Ident 99 | type : Syntax.Typ.Global 100 | init : Expr 101 | instance : Coe Syntax.Module.Global Global := 102 | ⟨fun g => ⟨.none, g.type, g.init.1⟩⟩ 103 | instance : OfText Global Syntax.Module.Global := 104 | ⟨fun g => return ⟨g.type, ← ofText g.init⟩⟩ 105 | 106 | 107 | inductive Export.Description 108 | | func : Index.Function → Description 109 | | table : Index.Table → Description 110 | | mem : Index.Memory → Description 111 | | globl : Index.Global → Description 112 | instance : Coe Syntax.Module.Export.Description Export.Description := 113 | ⟨ fun | .func i => .func i 114 | | .table t => .table t 115 | | .mem m => .mem m 116 | | .globl g => .globl g 117 | ⟩ 118 | instance : OfText Export.Description Syntax.Module.Export.Description := 119 | ⟨ fun | .func i => return .func (← ofText i) 120 | | .table t => return .table (← ofText t) 121 | | .mem m => return .mem (← ofText m) 122 | | .globl g => return .globl (← ofText g) 123 | ⟩ 124 | 125 | structure Export where 126 | name : Name 127 | desc : Export.Description 128 | instance : Coe Syntax.Module.Export Export := 129 | ⟨ fun exp => ⟨exp.name, exp.desc⟩ ⟩ 130 | instance : OfText Export Syntax.Module.Export := 131 | ⟨ fun exp => return ⟨exp.name, ← ofText exp.desc⟩ ⟩ 132 | 133 | 134 | structure Start where 135 | func : Index.Function 136 | instance : Coe Syntax.Module.Start Start := ⟨(⟨·.func⟩)⟩ 137 | instance : OfText Start Syntax.Module.Start := ⟨(return ⟨← ofText ·.func⟩)⟩ 138 | 139 | inductive Element.Mode 140 | | passive 141 | | active : (table : Index.Table) → (offset : Expr) → Mode 142 | | declarative 143 | instance : Coe Syntax.Module.Element.Mode Element.Mode := 144 | ⟨ fun | .passive => .passive 145 | | .active t e => .active t e.1 146 | | .declarative => .declarative 147 | ⟩ 148 | instance : OfText Element.Mode Syntax.Module.Element.Mode := 149 | ⟨ fun | .passive => return .passive 150 | | .active t e => return .active (← ofText t) (← ofText e) 151 | | .declarative => return .declarative 152 | ⟩ 153 | 154 | structure Element where 155 | lbl : Option Ident 156 | type : Syntax.Typ.Ref 157 | init : Vec Expr 158 | mode : Element.Mode 159 | instance : Coe Syntax.Module.Element Element := 160 | ⟨ fun elem => ⟨ .none, elem.type, elem.init.map (·.1), elem.mode⟩ ⟩ 161 | instance : OfText Element Syntax.Module.Element := 162 | ⟨ fun elem => return ⟨ elem.type, ← ofText elem.init, ← ofText elem.mode ⟩⟩ 163 | 164 | 165 | inductive Data.Mode 166 | | passive 167 | | active : (memory : Index.Memory) → (offset : Expr) → Mode 168 | instance : Coe (Syntax.Module.Data.Mode) Data.Mode := 169 | ⟨ fun | .passive => .passive 170 | | .active m e => .active m e.1 171 | ⟩ 172 | instance : OfText Data.Mode Syntax.Module.Data.Mode := 173 | ⟨ fun | .passive => return .passive 174 | | .active m e => return .active (← ofText m) (← ofText e) 175 | ⟩ 176 | 177 | structure Data where 178 | lbl : Option Ident 179 | init : Vec Wasm.Syntax.Value.Byte -- todo maybe change fix? 180 | mode : Data.Mode 181 | instance : Coe Syntax.Module.Data Data := 182 | ⟨ fun data => ⟨.none, data.init, data.mode⟩ ⟩ 183 | instance : OfText Data Syntax.Module.Data := 184 | ⟨ fun data => return ⟨data.init, ← ofText data.mode⟩ ⟩ 185 | 186 | end Module 187 | 188 | inductive Module.Field 189 | | types : Module.Typ → Module.Field 190 | | funcs : Module.Function → Module.Field 191 | | tables : Module.Table → Module.Field 192 | | mems : Module.Memory → Module.Field 193 | | globals : Module.Global → Module.Field 194 | | elems : Module.Element → Module.Field 195 | | datas : Module.Data → Module.Field 196 | | start : Module.Start → Module.Field 197 | | imports : Module.Import → Module.Field 198 | | exports : Module.Export → Module.Field 199 | 200 | def Module.Field.trans : Module.Field → Trans Syntax.Module 201 | | .types t => return { types := Vec.single (← ofText t)} 202 | | .funcs f => return { funcs := Vec.single (← ofText f)} 203 | | .tables t => return { tables := Vec.single (← ofText t)} 204 | | .mems m => return { mems := Vec.single (← ofText m)} 205 | | .globals g => return { globals := Vec.single (← ofText g)} 206 | | .elems e => return { elems := Vec.single (← ofText e)} 207 | | .datas d => return { datas := Vec.single (← ofText d)} 208 | | .start s => return { start := some (← ofText s)} 209 | | .imports i => return { imports := Vec.single (← ofText i)} 210 | | .exports e => return { exports := Vec.single (← ofText e)} 211 | instance : OfText Module.Field Syntax.Module := ⟨Module.Field.trans⟩ 212 | 213 | /- Initial Identifier Context -/ 214 | def Module.Field.idc : Module.Field → Ident.Context 215 | | .types t => { types := [t.lbl] 216 | , typedefs := [⟨t.ft.args.map (·.2), t.ft.result⟩] 217 | } 218 | | .funcs f => { funcs := [f.lbl] } 219 | | .tables t => { tables := [t.lbl] } 220 | | .mems m => { mems := [m.lbl] } 221 | | .globals g => { globals := [g.lbl] } 222 | | .elems e => { elem := [e.lbl] } 223 | | .datas d => { data := [d.lbl] } 224 | | .start _ => {} 225 | | .imports i => 226 | match i.desc with 227 | | .func id _tu => { funcs := [id] } 228 | | .table id _t => { tables := [id] } 229 | | .mem id _m => { mems := [id] } 230 | | .globl id _g => { globals := [id] } 231 | | .exports _ => {} 232 | 233 | structure Module where 234 | lbl : Option Ident := .none 235 | fields : List Module.Field := [] 236 | 237 | def Module.findAll (mod : Module) (p : Module.Field → Option α) : List α := 238 | aux mod.fields 239 | where aux : List Module.Field → List α 240 | | [] => [] 241 | | f :: fs => if let some r := p f then r :: aux fs else aux fs 242 | 243 | def Module.findOne (mod : Module) (p : Module.Field → Option α) : Option α := 244 | match mod.findAll p with 245 | | [] => .none 246 | | r :: _ => .some r 247 | 248 | def Module.types : Module → List Module.Typ := 249 | (·.findAll (fun | .types t => .some t | _ => .none)) 250 | def Module.funcs : Module → List Module.Function := 251 | (·.findAll (fun | .funcs f => .some f | _ => .none)) 252 | def Module.tables : Module → List Module.Table := 253 | (·.findAll (fun | .tables t => .some t | _ => .none)) 254 | def Module.mems : Module → List Module.Memory := 255 | (·.findAll (fun | .mems m => .some m | _ => .none)) 256 | def Module.globals : Module → List Module.Global := 257 | (·.findAll (fun | .globals g => .some g | _ => .none)) 258 | def Module.elems : Module → List Module.Element := 259 | (·.findAll (fun | .elems e => .some e | _ => .none)) 260 | def Module.datas : Module → List Module.Data := 261 | (·.findAll (fun | .datas d => .some d | _ => .none)) 262 | def Module.start : Module → Option Module.Start := 263 | (·.findOne (fun | .start s => .some s | _ => .none)) 264 | def Module.imports : Module → List Module.Import := 265 | (·.findAll (fun | .imports i => .some i | _ => .none)) 266 | def Module.exports : Module → List Module.Export := 267 | (·.findAll (fun | .exports e => .some e | _ => .none)) 268 | 269 | def Module.compose (m₁ m₂ : Syntax.Module) : Trans Syntax.Module := do 270 | match m₁.start, m₂.start with 271 | | .some _, .some _ => 272 | Trans.Error.errMsg s!"Fields contain multiple start functions!" 273 | | _, _ => 274 | if m₂.imports.length > 0 then (do 275 | if m₁.funcs.length ≠ 0 then 276 | Trans.Error.errMsg s!"Imports must preceed definitions of functions" 277 | if m₁.tables.length ≠ 0 then 278 | Trans.Error.errMsg s!"Imports must preceed definitions of tables" 279 | if m₁.mems.length ≠ 0 then 280 | Trans.Error.errMsg s!"Imports must preceed definitions of memories" 281 | if m₁.globals.length ≠ 0 then 282 | Trans.Error.errMsg s!"Imports must preceed definitions of globals" 283 | ) 284 | return { types := ⟨m₁.types.list ++ m₂.types.list , sorry⟩ 285 | funcs := ⟨m₁.funcs.list ++ m₂.funcs.list , sorry⟩ 286 | tables := ⟨m₁.tables.list ++ m₂.tables.list , sorry⟩ 287 | mems := ⟨m₁.mems.list ++ m₂.mems.list , sorry⟩ 288 | globals := ⟨m₁.globals.list ++ m₂.globals.list, sorry⟩ 289 | elems := ⟨m₁.elems.list ++ m₂.elems.list , sorry⟩ 290 | datas := ⟨m₁.datas.list ++ m₂.datas.list , sorry⟩ 291 | start := if m₁.start.isNone then m₂.start else m₁.start 292 | imports := ⟨m₁.imports.list ++ m₂.imports.list, sorry⟩ 293 | exports := ⟨m₁.exports.list ++ m₂.exports.list, sorry⟩ 294 | } 295 | 296 | instance : Coe Syntax.Module Module := 297 | ⟨ fun mod => Module.mk .none 298 | <| (mod.types.list.map (Module.Field.types )) 299 | ++ (mod.imports.list.map (Module.Field.imports)) 300 | ++ (mod.funcs.list.map (Module.Field.funcs )) 301 | ++ (mod.tables.list.map (Module.Field.tables )) 302 | ++ (mod.mems.list.map (Module.Field.mems )) 303 | ++ (mod.globals.list.map (Module.Field.globals)) 304 | ++ (mod.elems.list.map (Module.Field.elems )) 305 | ++ (mod.datas.list.map (Module.Field.datas )) 306 | ++ (mod.exports.list.map (Module.Field.exports)) 307 | ++ (match mod.start with | .some s => [Module.Field.start s] | _ => []) 308 | ⟩ 309 | 310 | def Module.trans (m : Module) : Trans Syntax.Module := do 311 | let I := m.fields.map Module.Field.idc 312 | |>.foldl Ident.Context.append {} 313 | -- todo check I well-formed! 314 | Trans.updateI I 315 | let m ← m.fields.foldlM (fun m₁ f => do 316 | let s ← get 317 | let m₂' ← ofText f 318 | let m₂ ← Module.compose m₂' {types := ⟨s.types, sorry⟩} 319 | Trans.updateI s.I 320 | Trans.mergeTypes 321 | return ← Module.compose m₁ m₂ 322 | ) {} 323 | let s ← get 324 | return { m with types := ⟨m.types.list ++ s.I.typedefs, sorry⟩} 325 | instance : OfText Module Syntax.Module := ⟨Module.trans⟩ 326 | 327 | namespace Module 328 | 329 | nonrec def Typ.toString (ty : Typ) : String := 330 | s!"(type {ty.lbl} {toString ty.ft})" 331 | instance : ToString Typ := ⟨Typ.toString⟩ 332 | 333 | def Import.Description.toString : Import.Description → String 334 | | .func id ty => s!"(func {id} {ty})" 335 | | .table id tt => s!"(table {id} {tt})" 336 | | .mem id mt => s!"(memory {id} {mt})" 337 | | .globl id gt => s!"(global {id} {gt})" 338 | instance : ToString (Import.Description) := ⟨Import.Description.toString⟩ 339 | 340 | def Import.toString (imp : Import) : String := 341 | s!"(import {imp.module} {imp.name} {imp.desc})" 342 | instance : ToString Import := ⟨Import.toString⟩ 343 | instance : ToString (List Import) := 344 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 345 | 346 | def Local.toString (locl : Local) : String := s!"(local {locl.lbl} {locl.typ})" 347 | instance : ToString Local := ⟨Local.toString⟩ 348 | instance : ToString (List Local) := ⟨String.intercalate " " ∘ List.map toString⟩ 349 | 350 | def Function.toString (f : Function) : String := 351 | let body := Instr.listToString f.body |>.replace "\n" "\n " 352 | s!"(func {f.lbl} {f.typeuse} {f.locals}\n {body})" 353 | instance : ToString Function := ⟨Function.toString⟩ 354 | instance : ToString (List Function) := 355 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 356 | 357 | def Table.toString (tab : Table) : String := s!"(table {tab.lbl} {tab.type})" 358 | instance : ToString Table := ⟨Table.toString⟩ 359 | instance : ToString (List Table) := 360 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 361 | 362 | def Memory.toString (mem : Memory) : String := s!"(memory {mem.lbl} {mem.type})" 363 | instance : ToString Memory := ⟨Memory.toString⟩ 364 | instance : ToString (List Memory) := 365 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 366 | 367 | def Global.toString (globl : Global) : String := 368 | s!"(global {globl.lbl} {globl.type} {globl.init})" 369 | instance : ToString Global := ⟨Global.toString⟩ 370 | instance : ToString (List Global) := 371 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 372 | 373 | def Export.Description.toString : Export.Description → String 374 | | .func x => s!"(func {x})" 375 | | .table x => s!"(table {x})" 376 | | .mem x => s!"(memory {x})" 377 | | .globl x => s!"(global {x})" 378 | instance : ToString (Export.Description) := ⟨Export.Description.toString⟩ 379 | 380 | def Export.toString (ex : Export) : String := s!"(export {ex.name} {ex.desc})" 381 | instance : ToString Export := ⟨Export.toString⟩ 382 | instance : ToString (List Export) := 383 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 384 | 385 | def Start.toString (m : Start) : String := s!"(start {m.func})" 386 | instance : ToString Start := ⟨Start.toString⟩ 387 | 388 | def Element.Mode.toString : Element.Mode → String 389 | | .passive => s!"" 390 | | .active x offset => s!"{x} (offset {offset})" 391 | | .declarative => s!"declare" 392 | instance : ToString Element.Mode := ⟨Element.Mode.toString⟩ 393 | 394 | def Element.toString (elem : Element) : String := 395 | s!"(elem {elem.lbl} {elem.mode} {elem.type} {elem.init})" 396 | instance : ToString Element := ⟨Element.toString⟩ 397 | instance : ToString (List Element) := 398 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 399 | 400 | def Data.Mode.toString : Data.Mode → String 401 | | .passive => "" 402 | | .active i e => s!"(memory {i}) (offset {e})" 403 | instance : ToString Data.Mode := ⟨Data.Mode.toString⟩ 404 | 405 | def Data.toString (data : Data) : String := 406 | let str := data.init.list.map (fun b => 407 | match b.toNat with 408 | | 0x09 => "\\t" 409 | | 0x0A => "\\n" 410 | | 0x0D => "\\r" 411 | | 0x22 => "\\\"" 412 | | 0x27 => "\\'" 413 | | 0x5C => "\\\\" 414 | | c => 415 | if c ≥ 0x20 && c < 0x7F 416 | then Char.ofNat c |>.toString 417 | else s!"\\u\{{c.toHexNumString}}" 418 | ) |> String.intercalate "" 419 | s!"(data {data.lbl} {data.mode} \"{str}\")" 420 | instance : ToString Data := ⟨Data.toString⟩ 421 | instance : ToString (List Data) := ⟨String.intercalate "\n" ∘ List.map toString⟩ 422 | 423 | end Module 424 | 425 | nonrec def Module.Field.toString : Module.Field → String 426 | | .types ty => toString ty 427 | | .funcs fn => toString fn 428 | | .tables ta => toString ta 429 | | .mems me => toString me 430 | | .globals gl => toString gl 431 | | .elems el => toString el 432 | | .datas da => toString da 433 | | .start st => toString st 434 | | .imports im => toString im 435 | | .exports ex => toString ex 436 | instance : ToString Module.Field := ⟨Module.Field.toString⟩ 437 | instance : ToString (List Module.Field) := 438 | ⟨String.intercalate "\n" ∘ List.map toString⟩ 439 | 440 | def Module.toString (m : Module) : String := s!"(module {m.lbl}\n{m.fields}\n)" 441 | instance : ToString Module := ⟨Module.toString⟩ 442 | -------------------------------------------------------------------------------- /Wasm/Text/Notation/Index.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Text.Notation.Value 2 | import Wasm.Text.Notation.Typ 3 | import Wasm.Text.Index 4 | 5 | namespace Wasm.Text.Notation 6 | 7 | open Wasm.Text.Module 8 | 9 | declare_syntax_cat wat_index 10 | scoped syntax wat_u32 : wat_index 11 | scoped syntax wat_ident : wat_index 12 | scoped syntax "↑" term:max : wat_index 13 | 14 | scoped syntax "[wat_index|" wat_index "]" : term 15 | scoped syntax "[wat_vec_index|" wat_index* "]" : term 16 | 17 | macro_rules 18 | | `([wat_index| $x:wat_u32 ]) => `(Index.num [wat_u32| $x]) 19 | | `([wat_index| $id:wat_ident ]) => `(Index.name [wat_ident| $id]) 20 | | `([wat_index| ↑$e ]) => `(Index.name $e) 21 | 22 | macro_rules 23 | | `([wat_vec_index| ]) => `(Vec.nil) 24 | | `([wat_vec_index| $i:wat_index]) => `(Vec.single [wat_index| $i]) 25 | | `([wat_vec_index| $i:wat_index $is:wat_index*]) => 26 | `(Vec.cons [wat_index| $i] [wat_vec_index| $is*]) 27 | 28 | 29 | /- Various indices -- these don't really matter since we check the context 30 | later but to try and best comply with the spec they are implemented like 31 | this. 32 | -/ 33 | declare_syntax_cat wat_typeidx 34 | declare_syntax_cat wat_funcidx 35 | declare_syntax_cat wat_tableidx 36 | declare_syntax_cat wat_memidx 37 | declare_syntax_cat wat_globalidx 38 | declare_syntax_cat wat_elemidx 39 | declare_syntax_cat wat_dataidx 40 | declare_syntax_cat wat_localidx 41 | declare_syntax_cat wat_labelidx 42 | 43 | scoped syntax wat_index : wat_typeidx 44 | scoped syntax wat_index : wat_funcidx 45 | scoped syntax wat_index : wat_tableidx 46 | scoped syntax wat_index : wat_memidx 47 | scoped syntax wat_index : wat_globalidx 48 | scoped syntax wat_index : wat_elemidx 49 | scoped syntax wat_index : wat_dataidx 50 | scoped syntax wat_index : wat_localidx 51 | scoped syntax wat_index : wat_labelidx 52 | 53 | scoped syntax "[wat_typeidx|" wat_typeidx "]" : term 54 | scoped syntax "[wat_funcidx|" wat_funcidx "]" : term 55 | scoped syntax "[wat_tableidx|" wat_tableidx "]" : term 56 | scoped syntax "[wat_memidx|" wat_memidx "]" : term 57 | scoped syntax "[wat_globalidx|" wat_globalidx "]" : term 58 | scoped syntax "[wat_elemidx|" wat_elemidx "]" : term 59 | scoped syntax "[wat_dataidx|" wat_dataidx "]" : term 60 | scoped syntax "[wat_localidx|" wat_localidx "]" : term 61 | scoped syntax "[wat_labelidx|" wat_labelidx "]" : term 62 | 63 | scoped syntax "[wat_vec_typeidx|" wat_typeidx* "]" : term 64 | scoped syntax "[wat_vec_funcidx|" wat_funcidx* "]" : term 65 | scoped syntax "[wat_vec_tableidx|" wat_tableidx* "]" : term 66 | scoped syntax "[wat_vec_memidx|" wat_memidx* "]" : term 67 | scoped syntax "[wat_vec_globalidx|" wat_globalidx* "]" : term 68 | scoped syntax "[wat_vec_elemidx|" wat_elemidx* "]" : term 69 | scoped syntax "[wat_vec_dataidx|" wat_dataidx* "]" : term 70 | scoped syntax "[wat_vec_localidx|" wat_localidx* "]" : term 71 | scoped syntax "[wat_vec_labelidx|" wat_labelidx* "]" : term 72 | 73 | macro_rules 74 | | `([wat_typeidx| $i:wat_index ]) => `([wat_index| $i]) 75 | | `([wat_funcidx| $i:wat_index ]) => `([wat_index| $i]) 76 | | `([wat_tableidx| $i:wat_index ]) => `([wat_index| $i]) 77 | | `([wat_memidx| $i:wat_index ]) => `([wat_index| $i]) 78 | | `([wat_globalidx| $i:wat_index ]) => `([wat_index| $i]) 79 | | `([wat_elemidx| $i:wat_index ]) => `([wat_index| $i]) 80 | | `([wat_dataidx| $i:wat_index ]) => `([wat_index| $i]) 81 | | `([wat_localidx| $i:wat_index ]) => `([wat_index| $i]) 82 | | `([wat_labelidx| $i:wat_index ]) => `([wat_index| $i]) 83 | 84 | macro_rules 85 | | `([wat_vec_typeidx| $i:wat_typeidx $is:wat_typeidx * ]) => 86 | `(Vec.cons [wat_typeidx| $i] [wat_vec_typeidx| $is* ] sorry) 87 | | `([wat_vec_funcidx| $i:wat_funcidx $is:wat_funcidx * ]) => 88 | `(Vec.cons [wat_funcidx| $i] [wat_vec_funcidx| $is* ] sorry) 89 | | `([wat_vec_tableidx| $i:wat_tableidx $is:wat_tableidx * ]) => 90 | `(Vec.cons [wat_tableidx| $i] [wat_vec_tableidx| $is* ] sorry) 91 | | `([wat_vec_memidx| $i:wat_memidx $is:wat_memidx * ]) => 92 | `(Vec.cons [wat_memidx| $i] [wat_vec_memidx| $is* ] sorry) 93 | | `([wat_vec_globalidx| $i:wat_globalidx $is:wat_globalidx* ]) => 94 | `(Vec.cons [wat_globalidx| $i] [wat_vec_globalidx| $is* ] sorry) 95 | | `([wat_vec_elemidx| $i:wat_elemidx $is:wat_elemidx * ]) => 96 | `(Vec.cons [wat_elemidx| $i] [wat_vec_elemidx| $is* ] sorry) 97 | | `([wat_vec_dataidx| $i:wat_dataidx $is:wat_dataidx * ]) => 98 | `(Vec.cons [wat_dataidx| $i] [wat_vec_dataidx| $is* ] sorry) 99 | | `([wat_vec_localidx| $i:wat_localidx $is:wat_localidx * ]) => 100 | `(Vec.cons [wat_localidx| $i] [wat_vec_localidx| $is* ] sorry ) 101 | | `([wat_vec_labelidx| $i:wat_labelidx $is:wat_labelidx * ]) => 102 | `(Vec.cons [wat_labelidx| $i] [wat_vec_labelidx| $is* ] sorry ) 103 | 104 | | `([wat_vec_typeidx| ]) => `(Vec.nil) 105 | | `([wat_vec_funcidx| ]) => `(Vec.nil) 106 | | `([wat_vec_tableidx| ]) => `(Vec.nil) 107 | | `([wat_vec_memidx| ]) => `(Vec.nil) 108 | | `([wat_vec_globalidx| ]) => `(Vec.nil) 109 | | `([wat_vec_elemidx| ]) => `(Vec.nil) 110 | | `([wat_vec_dataidx| ]) => `(Vec.nil) 111 | | `([wat_vec_localidx| ]) => `(Vec.nil) 112 | | `([wat_vec_labelidx| ]) => `(Vec.nil) 113 | 114 | declare_syntax_cat wat_typeuse 115 | scoped syntax "(" "type" wat_typeidx ")" : wat_typeuse 116 | scoped syntax "(" "type" wat_typeidx ")" wat_param* wat_result* : wat_typeuse 117 | scoped syntax wat_param* wat_result* : wat_typeuse 118 | 119 | scoped syntax "[wat_typeuse|" wat_typeuse "]" : term 120 | 121 | macro_rules 122 | | `([wat_typeuse| (type $x)]) => `(Typeuse.type_ind [wat_typeidx| $x]) 123 | | `([wat_typeuse| (type $x) $t₁:wat_param* $t₂:wat_result*]) => 124 | `(Typeuse.param_res [wat_typeidx| $x] 125 | [wat_param_list| $t₁*] 126 | [wat_result_list| $t₂*] 127 | ) 128 | | `([wat_typeuse| $t₁:wat_param* $t₂:wat_result*]) => 129 | `(Typeuse.elab_param_res [wat_param_list| $t₁*] [wat_result_list| $t₂*]) 130 | -------------------------------------------------------------------------------- /Wasm/Text/Notation/Typ.lean: -------------------------------------------------------------------------------- 1 | /- This is just a test for trying out meta-programmings 2 | -/ 3 | import Wasm.Text.Notation.Value 4 | import Wasm.Syntax.Typ 5 | import Wasm.Text.Typ 6 | 7 | namespace Wasm.Text.Notation 8 | 9 | open Numbers Wasm.Text Wasm.Syntax.Typ 10 | 11 | /- Num Types -/ 12 | declare_syntax_cat wat_numtype 13 | scoped syntax "i32" : wat_numtype 14 | scoped syntax "i64" : wat_numtype 15 | scoped syntax "f32" : wat_numtype 16 | scoped syntax "f64" : wat_numtype 17 | 18 | scoped syntax "[wat_numtype|" wat_numtype "]" : term 19 | 20 | macro_rules 21 | | `([wat_numtype| i32 ]) => `(Wasm.Syntax.Typ.Num.i32 ) 22 | | `([wat_numtype| i64 ]) => `(Wasm.Syntax.Typ.Num.i64 ) 23 | | `([wat_numtype| f32 ]) => `(Wasm.Syntax.Typ.Num.f32 ) 24 | | `([wat_numtype| f64 ]) => `(Wasm.Syntax.Typ.Num.f64 ) 25 | 26 | 27 | /- Vector Types -/ 28 | declare_syntax_cat wat_vectype 29 | scoped syntax "v128" : wat_vectype 30 | 31 | scoped syntax "[wat_vectype|" wat_vectype "]" : term 32 | 33 | macro_rules 34 | | `([wat_vectype| v128 ]) => `(Wasm.Syntax.Typ.Vec.v128) 35 | 36 | 37 | /- Reference Types -/ 38 | declare_syntax_cat wat_reftype 39 | declare_syntax_cat wat_heaptype 40 | scoped syntax "funcref" : wat_reftype 41 | scoped syntax "externref" : wat_reftype 42 | 43 | scoped syntax "func" : wat_heaptype 44 | scoped syntax "extern" : wat_heaptype 45 | 46 | scoped syntax "[wat_reftype|" wat_reftype "]" : term 47 | scoped syntax "[wat_heaptype|" wat_heaptype "]" : term 48 | 49 | macro_rules 50 | | `([wat_reftype| funcref ]) => `(Wasm.Syntax.Typ.Ref.func ) 51 | | `([wat_reftype| externref ]) => `(Wasm.Syntax.Typ.Ref.extern) 52 | 53 | macro_rules 54 | | `([wat_heaptype| func ]) => `(Wasm.Syntax.Typ.Ref.func ) 55 | | `([wat_heaptype| extern ]) => `(Wasm.Syntax.Typ.Ref.extern) 56 | 57 | 58 | /- Value Types -/ 59 | declare_syntax_cat wat_valtype 60 | scoped syntax wat_numtype : wat_valtype 61 | scoped syntax wat_vectype : wat_valtype 62 | scoped syntax wat_reftype : wat_valtype 63 | 64 | scoped syntax "[wat_valtype|" wat_valtype "]" : term 65 | 66 | macro_rules 67 | | `([wat_valtype| $t:wat_numtype]) => `(Val.num [wat_numtype| $t]) 68 | | `([wat_valtype| $t:wat_vectype]) => `(Val.vec [wat_vectype| $t]) 69 | | `([wat_valtype| $t:wat_reftype]) => `(Val.ref [wat_reftype| $t]) 70 | 71 | 72 | /- Parameters -/ 73 | declare_syntax_cat wat_param_core 74 | declare_syntax_cat wat_param 75 | scoped syntax "(" "param" wat_valtype ")" : wat_param_core 76 | scoped syntax "(" "param" (wat_ident)? wat_valtype ")" : wat_param_core 77 | 78 | scoped syntax "(" "param" wat_valtype* ")" : wat_param 79 | scoped syntax wat_param_core : wat_param 80 | 81 | scoped syntax "[wat_param_core|" wat_param_core "]" : term 82 | scoped syntax "[wat_param|" wat_param "]" : term 83 | scoped syntax "[wat_param_list|" wat_param* "]" : term 84 | 85 | macro_rules 86 | | `([wat_param_core| (param $t:wat_valtype)]) => 87 | `(Typ.Param.mk .none [wat_valtype| $t]) 88 | | `([wat_param_core| (param $id:wat_ident $t:wat_valtype)]) => 89 | `(Typ.Param.mk (.some [wat_ident| $id]) [wat_valtype| $t]) 90 | 91 | macro_rules 92 | | `([wat_param| (param $t:wat_valtype $ts:wat_valtype)]) => 93 | `([wat_param_core| (param $t)] :: [wat_param| (param $ts)]) 94 | | `([wat_param| (param $t:wat_valtype )]) => 95 | `([[wat_param_core| (param $t)]]) 96 | | `([wat_param| (param )]) => `([]) -- todo: double check this is valid 97 | | `([wat_param| $p:wat_param_core]) => 98 | `([wat_param_core| $p]) 99 | 100 | macro_rules 101 | | `([wat_param_list| ]) => `([]) 102 | | `([wat_param_list| $p:wat_param]) => `([[wat_param| $p]]) 103 | | `([wat_param_list| $p:wat_param $ps:wat_param*]) => 104 | `([wat_param| $p] :: [wat_param_list| $ps*]) 105 | 106 | 107 | /- Results -/ 108 | declare_syntax_cat wat_result_core 109 | declare_syntax_cat wat_result 110 | scoped syntax "(" "result" wat_valtype ")" : wat_result_core 111 | 112 | scoped syntax wat_result_core : wat_result 113 | scoped syntax "(" "result" wat_valtype* ")" : wat_result 114 | 115 | scoped syntax "[wat_result_core|" wat_result_core "]" : term 116 | scoped syntax "[wat_result|" wat_result "]" : term 117 | scoped syntax "[wat_result_list|" wat_result* "]" : term 118 | 119 | macro_rules 120 | | `([wat_result_core| (result $t:wat_valtype)]) => `([wat_valtype| $t]) 121 | 122 | macro_rules 123 | | `([wat_result| (result $r:wat_valtype $rs:wat_valtype)]) => 124 | `([wat_result_core| (result $r)] :: [wat_result| (result $rs)]) 125 | | `([wat_result| (result $t:wat_valtype )]) => 126 | `([[wat_result_core| (result $t)]]) 127 | | `([wat_result| (result )]) => `([]) -- todo: double check this is valid 128 | | `([wat_result| $r:wat_result_core]) => 129 | `([wat_result_core| $r]) 130 | 131 | macro_rules 132 | | `([wat_result_list| ]) => `([]) 133 | | `([wat_result_list| $r:wat_result]) => `([[wat_result| $r]]) 134 | | `([wat_result_list| $r:wat_result $rs:wat_result*]) => 135 | `([wat_result| $r] :: [wat_result_list| $rs*]) 136 | 137 | 138 | /- Functions -/ 139 | declare_syntax_cat wat_functype 140 | scoped syntax "(" "func" wat_param* wat_result* ")" : wat_functype 141 | scoped syntax "[wat_functype|" wat_functype "]" : term 142 | macro_rules 143 | | `([wat_functype| (func $t₁:wat_param* $t₂:wat_result*)]) => 144 | `(Typ.Func.mk [wat_param_list| $t₁*] [wat_result_list| $t₂*]) 145 | 146 | 147 | /- Limits -/ 148 | declare_syntax_cat wat_limits 149 | scoped syntax wat_u32 : wat_limits 150 | scoped syntax wat_u32 wat_u32 : wat_limits 151 | 152 | scoped syntax "[wat_limits|" wat_limits "]" : term 153 | 154 | macro_rules 155 | | `([wat_limits| $n:wat_u32 ]) => 156 | `(Wasm.Syntax.Typ.Limit.mk [wat_u32| $n] .none) 157 | | `([wat_limits| $n:wat_u32 $m:wat_u32 ]) => 158 | `(Wasm.Syntax.Typ.Limit.mk [wat_u32| $n] (.some [wat_u32| $m])) 159 | 160 | 161 | /- Memory Types -/ 162 | declare_syntax_cat wat_memtype 163 | scoped syntax wat_limits : wat_memtype 164 | 165 | scoped syntax "[wat_memtype|" wat_memtype "]" : term 166 | 167 | macro_rules 168 | | `([wat_memtype| $lim:wat_limits]) => `([wat_limits| $lim]) 169 | 170 | 171 | /- Table Types -/ 172 | declare_syntax_cat wat_tabletype 173 | scoped syntax wat_limits wat_reftype : wat_tabletype 174 | 175 | scoped syntax "[wat_tabletype|" wat_tabletype "]" : term 176 | 177 | macro_rules 178 | | `([wat_tabletype| $lim:wat_limits $et:wat_reftype]) => 179 | `(Typ.Table.mk [wat_limits| $lim] [wat_reftype| $et]) 180 | 181 | 182 | declare_syntax_cat wat_globaltype 183 | scoped syntax wat_valtype : wat_globaltype 184 | scoped syntax "(" "mut" wat_valtype ")" : wat_globaltype 185 | 186 | scoped syntax "[wat_globaltype|" wat_globaltype "]" : term 187 | 188 | macro_rules 189 | | `([wat_globaltype| $t:wat_valtype]) => `(Global.mk Mut.const [wat_valtype| $t]) 190 | | `([wat_globaltype| (mut $t:wat_valtype)]) => 191 | `(Global.mk Mut.var [wat_valtype| $t]) 192 | -------------------------------------------------------------------------------- /Wasm/Text/Notation/Value.lean: -------------------------------------------------------------------------------- 1 | import Numbers 2 | import Wasm.Text.Ident 3 | 4 | namespace Wasm.Text.Notation 5 | 6 | open Numbers 7 | 8 | declare_syntax_cat wat_u32 9 | declare_syntax_cat wat_u64 10 | 11 | declare_syntax_cat wat_s32 12 | declare_syntax_cat wat_s33 13 | declare_syntax_cat wat_s64 14 | 15 | declare_syntax_cat wat_i32 16 | declare_syntax_cat wat_i64 17 | 18 | scoped syntax num : wat_u32 19 | scoped syntax "↑" term:max : wat_u32 20 | scoped syntax num : wat_u64 21 | scoped syntax "↑" term:max : wat_u64 22 | 23 | scoped syntax num : wat_s32 24 | scoped syntax "↑" term:max : wat_s32 25 | scoped syntax num : wat_s33 26 | scoped syntax "↑" term:max : wat_s33 27 | scoped syntax num : wat_s64 28 | scoped syntax "↑" term:max : wat_s64 29 | 30 | scoped syntax num : wat_i32 31 | scoped syntax "↑" term:max : wat_i32 32 | scoped syntax wat_u32 : wat_i32 33 | scoped syntax wat_s32 : wat_i32 34 | scoped syntax num : wat_i64 35 | scoped syntax "↑" term:max : wat_i64 36 | scoped syntax wat_u64 : wat_i64 37 | scoped syntax wat_s64 : wat_i64 38 | 39 | scoped syntax "[wat_u32|" wat_u32 "]" : term 40 | scoped syntax "[wat_u64|" wat_u64 "]" : term 41 | 42 | scoped syntax "[wat_s32|" wat_s32 "]" : term 43 | scoped syntax "[wat_s33|" wat_s33 "]" : term 44 | scoped syntax "[wat_s64|" wat_s64 "]" : term 45 | 46 | scoped syntax "[wat_i32|" wat_i32 "]" : term 47 | scoped syntax "[wat_i64|" wat_i64 "]" : term 48 | 49 | macro_rules 50 | | `([wat_u32| $x:num]) => `(Unsigned.ofNat (n:=⟨32, by simp⟩) $x) 51 | | `([wat_u32| ↑$e:term]) => `($e) 52 | | `([wat_u64| $x:num]) => `(Unsigned.ofNat (n:=⟨64, by simp⟩) $x) 53 | | `([wat_u64| ↑$e:term]) => `($e) 54 | 55 | | `([wat_s32| $x:num]) => `(Signed.ofNat (n:=⟨32, by simp⟩) $x) 56 | | `([wat_s32| ↑$e:term]) => `($e) 57 | | `([wat_s33| $x:num]) => `(Signed.ofNat (n:=⟨33, by simp⟩) $x) 58 | | `([wat_s33| ↑$e:term]) => `($e) 59 | | `([wat_s64| $x:num]) => `(Signed.ofNat (n:=⟨64, by simp⟩) $x) 60 | | `([wat_s64| ↑$e:term]) => `($e) 61 | 62 | | `([wat_i32| $x:num]) => `(Unsigned.ofNat (n:=⟨32, by simp⟩) $x) 63 | | `([wat_i32| ↑$e:term]) => `($e) 64 | | `([wat_i32| $x:wat_u32]) => `([wat_u32| $x]) 65 | | `([wat_i32| $x:wat_s32]) => `([wat_s32| $x]) 66 | | `([wat_i64| $x:num]) => `(Unsigned.ofNat (n:=⟨64, by simp⟩) $x) 67 | | `([wat_i64| ↑$e:term]) => `($e) 68 | | `([wat_i64| $x:wat_u64]) => `([wat_u64| $x]) 69 | | `([wat_i64| $x:wat_s64]) => `([wat_s64| $x]) 70 | 71 | 72 | declare_syntax_cat wat_ident 73 | declare_syntax_cat wat_ident? 74 | scoped syntax ident : wat_ident 75 | scoped syntax "↑" term:max : wat_ident 76 | scoped syntax wat_ident ? : wat_ident? 77 | 78 | scoped syntax "[wat_ident_str|" ident "]" : term 79 | scoped syntax "[wat_ident|" wat_ident "]" : term 80 | scoped syntax "[wat_ident?|" wat_ident? "]" :term 81 | 82 | /- TODO: identifiers in WAT should be preceded by a `$` but this clashes with -/ 83 | macro_rules 84 | | `([wat_ident_str| $id:ident]) => 85 | return Lean.Syntax.mkStrLit id.getId.toString 86 | | `([wat_ident| $id:ident]) => 87 | `(Ident.mk [wat_ident_str| $id] sorry sorry) 88 | | `([wat_ident| ↑$e]) => `($e) 89 | | `([wat_ident?| $id:wat_ident]) => `(.some [wat_ident| $id]) 90 | | `([wat_ident?| ]) => `(.none) 91 | 92 | 93 | declare_syntax_cat wat_value 94 | scoped syntax wat_u32 : wat_value 95 | scoped syntax wat_u64 : wat_value 96 | scoped syntax wat_s32 : wat_value 97 | scoped syntax wat_s64 : wat_value 98 | scoped syntax wat_ident : wat_value 99 | scoped syntax str : wat_value 100 | scoped syntax "↑" term:max : wat_value 101 | 102 | scoped syntax "[wat_value|" wat_value "]" : term 103 | 104 | macro_rules 105 | | `([wat_value| $n:wat_u32 ]) => `([wat_u32| $n]) 106 | | `([wat_value| $n:wat_u64 ]) => `([wat_u64| $n]) 107 | | `([wat_value| $n:wat_s32 ]) => `([wat_s32| $n]) 108 | | `([wat_value| $n:wat_s64 ]) => `([wat_s64| $n]) 109 | | `([wat_value| $id:wat_ident ]) => `([wat_ident| $id]) 110 | | `([wat_value| ↑$e ]) => `($e) 111 | -------------------------------------------------------------------------------- /Wasm/Text/Trans.lean: -------------------------------------------------------------------------------- 1 | import Wasm.Syntax.Typ 2 | import Wasm.Syntax.Value 3 | import Wasm.Text.Ident 4 | import Numbers 5 | open Numbers 6 | 7 | namespace Wasm.Text 8 | 9 | namespace Ident 10 | 11 | structure Context where 12 | types : List (Option Ident) := [] 13 | funcs : List (Option Ident) := [] 14 | tables : List (Option Ident) := [] 15 | mems : List (Option Ident) := [] 16 | globals : List (Option Ident) := [] 17 | elem : List (Option Ident) := [] 18 | data : List (Option Ident) := [] 19 | locals : List (Option Ident) := [] 20 | labels : List (Option Ident) := [] 21 | typedefs : List Syntax.Typ.Func := [] 22 | deriving Inhabited 23 | 24 | def Context.Wellformed (I : Context) : Prop := 25 | ∀ l₁ l₂, 26 | l₁ ++ l₂ = 27 | I.types.filterMap (·) ++ I.funcs.filterMap (·) ++ I.tables.filterMap (·) 28 | ++ I.mems.filterMap (·) ++ I.globals.filterMap (·) ++ I.elem.filterMap (·) 29 | ++ I.data.filterMap (·) ++ I.locals.filterMap (·) 30 | ++ I.labels.filterMap (·) 31 | → List.Disjoint l₁ l₂ 32 | 33 | def Context.empty : Context := 34 | { types := [] 35 | , funcs := [] 36 | , tables := [] 37 | , mems := [] 38 | , globals := [] 39 | , elem := [] 40 | , data := [] 41 | , locals := [] 42 | , labels := [] 43 | , typedefs := [] 44 | } 45 | 46 | theorem Context.Wellformed.empty : Context.Wellformed Context.empty := by 47 | intro l₁ l₂; simp [Context.empty]; intro l₁_nil l₂_nil; simp [*] 48 | 49 | def Context.append (r₁ r₂ : Context) : Context := 50 | { types := r₁.types ++ r₂.types 51 | , funcs := r₁.funcs ++ r₂.funcs 52 | , tables := r₁.tables ++ r₂.tables 53 | , mems := r₁.mems ++ r₂.mems 54 | , globals := r₁.globals ++ r₂.globals 55 | , elem := r₁.elem ++ r₂.elem 56 | , data := r₁.data ++ r₂.data 57 | , locals := r₁.locals ++ r₂.locals 58 | , labels := r₁.labels ++ r₂.labels 59 | , typedefs := r₁.typedefs ++ r₂.typedefs 60 | } 61 | instance : Append Context := ⟨Context.append⟩ 62 | 63 | end Ident 64 | 65 | structure Trans.Error where 66 | log : List String 67 | 68 | structure Trans.State where 69 | I : Ident.Context 70 | types : List Syntax.Typ.Func 71 | 72 | abbrev Trans := ExceptT (Trans.Error) (StateM Trans.State) 73 | instance : Monad Trans := show Monad (ExceptT _ _) from inferInstance 74 | 75 | class OfText (α β : Type) where 76 | ofText : α → Trans β 77 | 78 | export OfText (ofText) 79 | instance [OfText α β] : OfText (id α) β := inferInstanceAs (OfText α β) 80 | instance [OfText α β] : OfText α (id β) := inferInstanceAs (OfText α β) 81 | instance [OfText α β] : OfText (Id α) β := inferInstanceAs (OfText α β) 82 | instance [OfText α β] : OfText α (Id β) := inferInstanceAs (OfText α β) 83 | 84 | instance [OfText α β] : OfText (List α) (List β) where 85 | ofText vs := vs.mapM ofText 86 | instance [OfText α β] : OfText (Vec α) (Vec β) where 87 | ofText vs := vs.mapM ofText 88 | 89 | def Trans.updateI (I' : Ident.Context) : Trans Unit := do 90 | let s ← get 91 | set { s with I := I' } 92 | 93 | def Trans.getTypes : Trans (List Syntax.Typ.Func) := do 94 | return (← get).types 95 | def Trans.mergeTypes : Trans Unit := do 96 | let s ← get 97 | let I' := {s.I with types := s.I.types ++ s.types.map (fun _ => none) 98 | typedefs := s.I.typedefs ++ s.types} 99 | set (Trans.State.mk I' []) 100 | 101 | namespace Trans.Error 102 | 103 | def err : Trans α := throw ⟨[]⟩ 104 | def errMsg : String → Trans α := fun msg => throw (⟨[msg]⟩) 105 | @[inline] def err_log (msg : String) (p : Trans α) : Trans α := 106 | fun state => 107 | ExceptT.adapt (fun err => 108 | {err with log := msg :: err.log} 109 | ) p state 110 | 111 | end Trans.Error 112 | 113 | @[inline] def Ident.Context.indexOf 114 | (f : Ident.Context → List (Option Ident)) 115 | (v : Option Ident) 116 | (msg : String := "") 117 | : Trans Unsigned32 := do 118 | match (f (← get).I).indexOf? v with 119 | | .some x => return Numbers.Unsigned.ofNat x 120 | | .none => Trans.Error.errMsg s!"Error finding {msg} Ident." 121 | -------------------------------------------------------------------------------- /Wasm/Text/Typ.lean: -------------------------------------------------------------------------------- 1 | /- Encoding of defintion WASM's type defintion: 2 | https://webassembly.github.io/spec/core/text/types.html# 3 | -/ 4 | import Wasm.Vec 5 | import Wasm.Syntax.Typ 6 | import Wasm.Text.Ident 7 | import Wasm.Text.Trans 8 | 9 | namespace Wasm 10 | 11 | open Std 12 | 13 | namespace Syntax.Typ 14 | 15 | def Num.toString : Num → String 16 | | .i32 => "i32" 17 | | .i64 => "i64" 18 | | .f32 => "f32" 19 | | .f64 => "f64" 20 | instance : ToString Num := ⟨Num.toString⟩ 21 | instance : ToFormat Num := ⟨Format.text ∘ Num.toString⟩ 22 | 23 | def Vec.toString : Syntax.Typ.Vec → String 24 | | .v128 => "v128" 25 | instance : ToString Syntax.Typ.Vec := ⟨Vec.toString⟩ 26 | instance : ToFormat Syntax.Typ.Vec := ⟨Format.text ∘ Vec.toString⟩ 27 | 28 | def Ref.toString : Ref → String 29 | | .func => "funcref" 30 | | .extern => "externref" 31 | instance : ToString Ref := ⟨Ref.toString⟩ 32 | instance : ToFormat Ref := ⟨Format.text ∘ Ref.toString⟩ 33 | 34 | def Val.toString : Val → String 35 | | .num v => v.toString 36 | | .vec v => v.toString 37 | | .ref v => v.toString 38 | instance : ToString Val := ⟨Val.toString⟩ 39 | instance : ToFormat Val := ⟨Format.text ∘ Val.toString⟩ 40 | 41 | 42 | def Limit.toString (lim : Limit) : String := 43 | match lim.max with 44 | | .none => s!"{lim.min}" 45 | | .some m => s!"{lim.min} {m}" 46 | instance : ToString Limit := ⟨Limit.toString⟩ 47 | instance : ToFormat Limit := ⟨Format.text ∘ Limit.toString⟩ 48 | 49 | instance : ToString Mem := ⟨Limit.toString⟩ 50 | instance : ToFormat Mem := ⟨Format.text ∘ Limit.toString⟩ 51 | 52 | 53 | instance : ToString Table := ⟨fun (lim, ref) => s!"{lim} {ref}"⟩ 54 | 55 | nonrec def Global.toString (glb : Global) : String := 56 | let (m, v) := glb 57 | match m with 58 | | .const => toString v 59 | | .var => s!"(mut {v})" 60 | instance : ToString Global := ⟨Global.toString⟩ 61 | instance : ToFormat Global := ⟨Format.text ∘ Global.toString⟩ 62 | 63 | end Syntax.Typ 64 | 65 | namespace Text.Typ 66 | 67 | -- For some reason the text section defines a heaptype, which is the same as 68 | -- as a reftype. 69 | def Heap := Syntax.Typ.Ref 70 | deriving DecidableEq, Inhabited, Repr 71 | 72 | instance : ToString Heap := ⟨Syntax.Typ.Ref.toString⟩ 73 | instance : ToFormat Heap := ⟨Format.text ∘ Syntax.Typ.Ref.toString⟩ 74 | 75 | 76 | def Param := Option Ident × Syntax.Typ.Val 77 | deriving DecidableEq, Inhabited 78 | 79 | def Param.mk : Option Ident → Syntax.Typ.Val → Param := Prod.mk 80 | def Param.toString : Param → String 81 | | (.some id, v) => s!"(param {id} {v})" 82 | | (.none, v) => s!"(param {v})" 83 | instance : Coe Syntax.Typ.Val Param := ⟨(.none, ·)⟩ 84 | instance : Coe Syntax.Typ.Result (Vec Param) := ⟨(·.map ((.none, ·)))⟩ 85 | 86 | instance : OfText Param Syntax.Typ.Val := ⟨(return ·.2)⟩ 87 | instance : OfText (Vec Param) Syntax.Typ.Result := ⟨(return ·.map (·.2))⟩ 88 | 89 | instance : ToString Param := ⟨Param.toString⟩ 90 | instance : ToString (List Param) := ⟨String.concatWith " "⟩ 91 | instance : ToString (Vec Param) := ⟨String.concatWith " " ∘ Vec.list⟩ 92 | 93 | instance : ToFormat Param := ⟨Format.text ∘ Param.toString⟩ 94 | 95 | 96 | def Result := Syntax.Typ.Val 97 | deriving DecidableEq, Inhabited, Repr 98 | instance : Coe Syntax.Typ.Result (Vec Result) := ⟨(·)⟩ 99 | 100 | instance : ToString Result := ⟨(s!"(result {·.toString})")⟩ 101 | instance : ToString (List Result) := ⟨String.concatWith " "⟩ 102 | instance : ToString (Vec Result) := ⟨String.concatWith " " ∘ Vec.list⟩ 103 | 104 | 105 | structure Func where 106 | args : Vec Param 107 | result : Vec Result 108 | deriving DecidableEq, Inhabited 109 | instance : Coe Syntax.Typ.Func Func := ⟨fun f => ⟨f.args, f.result⟩⟩ 110 | instance : OfText Func Syntax.Typ.Func := 111 | ⟨fun f => return ⟨← ofText f.args, f.result⟩⟩ 112 | 113 | instance : ToString Func := ⟨fun f => s!"(func {f.args} {f.result})"⟩ 114 | instance : ToString Syntax.Typ.Func := 115 | ⟨(fun f => toString (Func.mk f.args f.result))⟩ 116 | -------------------------------------------------------------------------------- /Wasm/Validation.lean: -------------------------------------------------------------------------------- 1 | /- Static Semantics -/ 2 | import Wasm.Validation.Context 3 | import Wasm.Validation.Typ 4 | import Wasm.Validation.Module 5 | import Wasm.Validation.Statics 6 | -------------------------------------------------------------------------------- /Wasm/Validation/Context.lean: -------------------------------------------------------------------------------- 1 | /- https://webassembly.github.io/spec/core/valid/conventions.html -/ 2 | import Wasm.Syntax 3 | 4 | namespace Wasm.Validation 5 | 6 | structure Context where 7 | types : Vec Syntax.Typ.Func 8 | funcs : Vec Syntax.Typ.Func 9 | tables : Vec Syntax.Typ.Table 10 | mems : Vec Syntax.Typ.Mem 11 | globals : Vec Syntax.Typ.Global 12 | elems : Vec Syntax.Typ.Ref 13 | datas : Vec Unit -- todo is this right? 14 | locals : Vec Syntax.Typ.Val 15 | labels : Vec Syntax.Typ.Result 16 | wasm_return : Option Syntax.Typ.Result 17 | refs : Vec Syntax.Module.Index.Function 18 | -------------------------------------------------------------------------------- /Wasm/Validation/Module.lean: -------------------------------------------------------------------------------- 1 | /- https://webassembly.github.io/spec/core/valid/modules.html -/ 2 | 3 | import Wasm.Syntax 4 | import Mathlib.Data.List.Join 5 | import Wasm.Validation.Context 6 | import Wasm.Validation.Typ 7 | import Wasm.Validation.Statics 8 | 9 | namespace Wasm.Validation.Module 10 | 11 | open Syntax 12 | open Syntax.Module 13 | open Statics 14 | 15 | inductive Function : (Γ : Context) → Function → Typ.Func → Prop 16 | | function : {h : t1.length + t.list.length < Vec.max_length} 17 | → Γ.types.list.get x = ⟨t1, t2⟩ 18 | → Expr {Γ with locals := Vec.append t1 t h 19 | , labels := ⟨[t2], by simp [Vec.max_length]⟩ 20 | , wasm_return := .some t2 21 | } expr t2 22 | → Function Γ ⟨Vec.index Γ.types x, t, expr⟩ ⟨t1, t2⟩ 23 | 24 | inductive Table : (Γ : Context) → Table → Syntax.Typ.Table → Prop 25 | | table : Validation.Typ.Table type → Table Γ ⟨type⟩ type 26 | 27 | inductive Memory : (Γ : Context) → Memory → Syntax.Typ.Mem → Prop 28 | | table : Validation.Typ.Memory type → Memory Γ ⟨type⟩ type 29 | 30 | inductive Global : (Γ : Context) → Global → Syntax.Typ.Global → Prop 31 | | globl : Validation.Typ.Global ⟨m, t⟩ 32 | → Statics.Expr Γ expr (Vec.single t) 33 | → Expr.Constant Γ expr 34 | → Global Γ ⟨⟨m, t⟩, expr⟩ ⟨m, t⟩ 35 | 36 | inductive Element.Mode : (Γ : Context) → Element.Mode → Syntax.Typ.Ref → Prop 37 | | passive : Mode Γ .passive t 38 | | active : Γ.tables.list.get x = ⟨limits, t⟩ 39 | → Statics.Expr Γ expr (Vec.single (.num .i32)) 40 | → Expr.Constant Γ expr 41 | → Mode Γ (.active (Vec.index Γ.tables x) expr) t 42 | | declarative : Mode Γ .declarative t 43 | 44 | inductive Element : (Γ : Context) → Element → Syntax.Typ.Ref → Prop 45 | | nil : Element.Mode Γ mode t 46 | → Element Γ ⟨t, Vec.nil, mode⟩ t 47 | | cons : {h : List.length exprs.list + 1 < Vec.max_length} 48 | → Statics.Expr Γ expr (Vec.single (.ref t)) 49 | → Expr.Constant Γ expr 50 | → Element Γ ⟨t, exprs, mode⟩ t 51 | → Element Γ ⟨t, Vec.cons expr exprs h, mode⟩ t 52 | 53 | inductive Data.Mode : (Γ : Context) → Data.Mode → Prop 54 | | passive : Mode Γ .passive 55 | | active : Γ.mems.list.get x = limits 56 | → Statics.Expr Γ expr (Vec.single (.num .i32)) 57 | → Expr.Constant Γ expr 58 | → Mode Γ (.active (Vec.index Γ.mems x) expr) 59 | 60 | inductive Data : (Γ : Context) → Data → Prop 61 | | data : Data.Mode Γ mode → Data Γ ⟨b, mode⟩ 62 | 63 | inductive Start : (Γ : Context) → Start → Prop 64 | | start : Γ.funcs.list.get x = ⟨Vec.nil,Vec.nil⟩ → Start Γ ⟨Vec.index Γ.funcs x⟩ 65 | 66 | inductive Export.Description : (Γ : Context) → Export.Description → Typ.Extern → Prop 67 | | func : Γ.funcs.list.get x = type 68 | → Description Γ (.func (Vec.index Γ.funcs x)) (.func type) 69 | | table : Γ.tables.list.get x = type 70 | → Description Γ (.table (Vec.index Γ.tables x)) (.table type) 71 | | mem : Γ.mems.list.get x = type 72 | → Description Γ (.mem (Vec.index Γ.mems x)) (.mem type) 73 | | globl : Γ.globals.list.get x = type 74 | → Description Γ (.globl (Vec.index Γ.globals x)) (.globl type) 75 | 76 | inductive Export : (Γ : Context) → Export → Typ.Extern → Prop 77 | | exprt : Export.Description Γ desc type → Export Γ ⟨name, desc⟩ type 78 | 79 | inductive Import.Description : (Γ : Context) → Import.Description → Typ.Extern → Prop 80 | | func : Γ.funcs.list.get x = ⟨t1, t2⟩ 81 | → Description Γ (.func (Vec.index Γ.funcs x)) (.func ⟨t1, t2⟩) 82 | | table : Validation.Typ.Table type 83 | → Description Γ (.table type) (.table type) 84 | | mem : Validation.Typ.Memory type 85 | → Description Γ (.mem type) (.mem type) 86 | | globl : Validation.Typ.Global type 87 | → Description Γ (.globl type) (.globl type) 88 | 89 | inductive Import : (Γ : Context) → Import → Typ.Extern → Prop 90 | | imprt : Import.Description Γ desc type → Import Γ ⟨name1, name2, desc⟩ type 91 | 92 | def Function.Index.Instr (instr : Syntax.Instr) : Option Index.Function := 93 | match instr with 94 | | .call funcidx 95 | | .reference (.func funcidx) => .some funcidx 96 | | _ => .none 97 | 98 | def Function.Index.Instrs (expr : List Syntax.Instr) 99 | (h₁ : expr.length < Vec.max_length) 100 | : (res : Vec Index.Function) 101 | ×' (res.list.length ≤ expr.length) := 102 | match expr with 103 | | .nil => ⟨Vec.nil, by simp [Vec.nil, Vec.list]⟩ 104 | | .cons i is => 105 | let ⟨v, len⟩ := Function.Index.Instrs is (by rw [List.length_cons] at h₁; apply Nat.lt_left_add h₁) 106 | match Function.Index.Instr i with 107 | | .none => ⟨v, by rw [List.length_cons]; apply Nat.le_succ_of_le len⟩ 108 | | .some index => 109 | let ⟨res, vlen⟩ := Vec.length_cons index v (by 110 | rw [List.length_cons] at h₁ 111 | apply Nat.le_trans_lt (Nat.succ_le_succ len) h₁ 112 | ) 113 | ⟨res, by rw [List.length_cons, vlen]; apply Nat.succ_le_succ len⟩ 114 | 115 | def Function.Index.List (globals : List Syntax.Instr) 116 | (elems : List Syntax.Instr) 117 | (exports : List Index.Function) 118 | (len : globals.length + elems.length + exports.length < Vec.max_length) 119 | : Vec Index.Function := 120 | let ⟨gidx, glen⟩ := Index.Instrs globals (by rw [Nat.add_assoc] at len; apply Nat.lt_left_add len) 121 | let ⟨eidx, elen⟩ := Index.Instrs elems (by rw [Nat.add_right_comm] at len; linarith) 122 | let xidx : Vec Index.Function := ⟨exports, by linarith⟩ 123 | let gelen := by exact Nat.le_trans_lt (Nat.add_le_add glen elen) (Nat.lt_left_add len) 124 | let ⟨geidx, gelen⟩ := Vec.length_append gidx eidx gelen 125 | Vec.append geidx xidx (by 126 | have xlen : xidx.list.length = exports.length := by rfl 127 | have h := Nat.add_le_add (Nat.add_le_add glen elen) (Nat.le_of_eq xlen) 128 | rw [gelen] 129 | exact Nat.le_trans_lt h len 130 | ) 131 | 132 | def Function.Index (globals : Vec Syntax.Module.Global) 133 | (elems : Vec Syntax.Module.Element) 134 | (exports : Vec Syntax.Module.Export) 135 | : Vec Index.Function := 136 | let globals_lst := globals.list.map (fun g => g.init.fst) |>.join 137 | let elems_lst := elems.list.map (fun e => e.init.list.map (fun (exp, _) => exp)) |>.join |>.join 138 | let has_index := (fun x => 139 | match x.desc with 140 | | .func idx => .some idx 141 | | _ => .none 142 | ) 143 | let exports_lst := exports.list.filterMap has_index 144 | let exports_len : exports_lst.length < Vec.max_length := by 145 | exact Nat.le_trans_lt (List.length_filterMap_le has_index exports.list) exports.maxLen 146 | let res := Function.Index.List globals_lst elems_lst exports_lst sorry 147 | res 148 | 149 | inductive Concat : (Γ : Context) → (Judgement : Context → α → β → Prop) → List α → List β → Prop 150 | | nil : Concat Γ Judgement [] [] 151 | | cons : Judgement Γ a b 152 | → Concat Γ Judgement as bs 153 | → Concat Γ Judgement (a :: as) (b :: bs) 154 | 155 | inductive ConcatOk : (Γ : Context) → (Judgement : Context → α → Prop) → List α → Prop 156 | | nil : ConcatOk Γ Judgement [] 157 | | cons : Judgement Γ a 158 | → ConcatOk Γ Judgement as 159 | → ConcatOk Γ Judgement (a :: as) 160 | 161 | inductive OptionOk : (Γ : Context) → (Judgement : Context → α → Prop) → Option α → Prop 162 | | none : OptionOk Γ Judgement .none 163 | | some : Judgement Γ a → OptionOk Γ Judgement (.some a) 164 | 165 | inductive DisjointNames : List Value.Name → Prop 166 | | nil : DisjointNames [] 167 | | cons : name ∉ names → DisjointNames names → DisjointNames (name :: names) 168 | 169 | inductive Module : Module → List Typ.Extern → List Typ.Extern → Prop 170 | | module : {funcsMax : List.length (Typ.Extern.funcs it) + List.length ft < Vec.max_length} 171 | → {tablesMax : List.length (Typ.Extern.tables it) + List.length tt < Vec.max_length} 172 | → {memsMax : List.length (Typ.Extern.mems it) + List.length mt' < Vec.max_length} 173 | → {globalsMax : List.length (Typ.Extern.globals it) + List.length gt < Vec.max_length} 174 | → {elemsMax : List.length rt < Vec.max_length} 175 | → (C : Context) = 176 | { types := type 177 | , funcs := Vec.append 178 | ⟨Typ.Extern.funcs it, Nat.lt_left_add funcsMax⟩ 179 | ⟨ft, by linarith⟩ 180 | funcsMax 181 | , tables := Vec.append 182 | ⟨Typ.Extern.tables it, Nat.lt_left_add tablesMax⟩ 183 | ⟨tt, by linarith⟩ 184 | tablesMax 185 | , mems := Vec.append 186 | ⟨Typ.Extern.mems it, Nat.lt_left_add memsMax⟩ 187 | ⟨mt', by linarith⟩ 188 | memsMax 189 | , globals := Vec.append 190 | ⟨Typ.Extern.globals it, Nat.lt_left_add globalsMax⟩ 191 | ⟨gt, by linarith⟩ 192 | globalsMax 193 | , elems := ⟨rt, elemsMax⟩ 194 | , datas := data.map (fun _ => ()) 195 | , locals := Vec.nil 196 | , labels := Vec.nil 197 | , wasm_return := .none 198 | , refs := Function.Index globl elem exprt 199 | } 200 | → (C' : Context) = 201 | { types := Vec.nil 202 | , funcs := C.funcs 203 | , tables := Vec.nil 204 | , mems := Vec.nil 205 | , globals := ⟨Typ.Extern.globals it, Nat.lt_left_add globalsMax⟩ 206 | , elems := Vec.nil 207 | , datas := Vec.nil 208 | , locals := Vec.nil 209 | , labels := Vec.nil 210 | , wasm_return := .none 211 | , refs := C.refs 212 | } 213 | → mem.list.length ≤ 1 214 | → DisjointNames (exprt.list.map (fun e => e.name)) 215 | → ConcatOk C (fun (_ : Context) => Validation.Typ.Function) type.list 216 | → Concat C Validation.Module.Function func.list ft 217 | → Concat C' Validation.Module.Table table.list tt 218 | → Concat C' Validation.Module.Memory mem.list mt' 219 | → Concat C' Validation.Module.Global globl.list gt 220 | → Concat C' Validation.Module.Element elem.list rt 221 | → ConcatOk C' Validation.Module.Data data.list 222 | → OptionOk C Validation.Module.Start strt 223 | → Concat C' Validation.Module.Import imprt.list it 224 | → Concat C' Validation.Module.Export exprt.list et 225 | → Module ⟨type, func, table, mem, globl, elem, data, strt, imprt, exprt⟩ it et 226 | -------------------------------------------------------------------------------- /Wasm/Validation/Statics.lean: -------------------------------------------------------------------------------- 1 | /- https://webassembly.github.io/spec/core/valid/instructions.html -/ 2 | 3 | import Wasm.Syntax 4 | import Wasm.Validation.Context 5 | import Wasm.Validation.Typ 6 | 7 | namespace Wasm.Validation.Statics 8 | 9 | open Syntax 10 | 11 | inductive Typ.Operand 12 | | bot 13 | | val : Typ.Val → Operand 14 | 15 | def toIntOpd (nn : Syntax.Instr.Numeric.Size): Typ.Operand := 16 | .val (Syntax.Instr.Numeric.Size.toIntVal nn) 17 | 18 | def toFloatOpd (nn : Syntax.Instr.Numeric.Size): Typ.Operand := 19 | .val (Syntax.Instr.Numeric.Size.toFloatVal nn) 20 | 21 | def i32 : Typ.Operand := .val (.num .i32) 22 | def f32 : Typ.Operand := .val (.num .f32) 23 | def i64 : Typ.Operand := .val (.num .i64) 24 | def f64 : Typ.Operand := .val (.num .f64) 25 | def ref (t : Typ.Ref) : Typ.Operand := .val (.ref t) 26 | 27 | 28 | structure Typ.Stack where 29 | input : List Typ.Operand 30 | output : List Typ.Operand 31 | 32 | inductive Typ.MatchSingle : Typ.Operand → Typ.Operand → Prop 33 | | refl : Typ.MatchSingle t1 t2 34 | | min : Typ.MatchSingle .bot t 35 | 36 | inductive Typ.MatchList : List Typ.Operand → List Typ.Operand → Prop 37 | | empty : Typ.MatchList [] [] 38 | | cons : Typ.MatchSingle t1 t2 → Typ.MatchList t1s t2s → Typ.MatchList (t1 :: t1s) (t2 :: t2s) 39 | 40 | namespace Numeric 41 | 42 | inductive Integer.Instr (Γ : Context) : Instr.Numeric.Integer nn → Typ.Stack → Prop 43 | | const : Instr Γ (.const v) ⟨[], [toIntOpd nn]⟩ 44 | | unop : Instr Γ (.unop op) ⟨[toIntOpd nn], [toIntOpd nn]⟩ 45 | | binop : Instr Γ (.binop op) ⟨[toIntOpd nn, toIntOpd nn], [toIntOpd nn]⟩ 46 | | test : Instr Γ (.test op) ⟨[toIntOpd nn], []⟩ 47 | | relation : Instr Γ (.relation op) ⟨[toIntOpd nn, toIntOpd nn], [i32]⟩ 48 | | extend8_s : Instr Γ .extend8_s ⟨[i32], [toIntOpd nn]⟩ 49 | | extend16_s : Instr Γ .extend16_s ⟨[i32], [toIntOpd nn]⟩ 50 | | extend32_s : Instr Γ .extend32_s ⟨[i32], [toIntOpd nn]⟩ 51 | | wrap_i64 : Instr Γ .wrap_i64 ⟨[i64], [toIntOpd nn]⟩ 52 | | extend_i32 : Instr Γ (.extend_i32 s) ⟨[i32], [toIntOpd nn]⟩ 53 | | trunc_f : Instr Γ (.trunc_f mm s) ⟨[toFloatOpd mm], [toIntOpd nn]⟩ 54 | | trunc_sat_f : Instr Γ (.trunc_sat_f mm s) ⟨[toFloatOpd mm], [toIntOpd nn]⟩ 55 | | reinterpret_f : Instr Γ .reinterpret_f ⟨[toFloatOpd nn], [toIntOpd nn]⟩ 56 | 57 | inductive Float.Instr (Γ : Context) : Instr.Numeric.Float nn → Typ.Stack → Prop 58 | | const : Instr Γ (.const v) ⟨[], [toFloatOpd nn]⟩ 59 | | unop : Instr Γ (.unop op) ⟨[toFloatOpd nn], [toFloatOpd nn]⟩ 60 | | binop : Instr Γ (.binop op) ⟨[toFloatOpd nn, toFloatOpd nn], [toFloatOpd nn]⟩ 61 | | relation : Instr Γ (.relation op) ⟨[toFloatOpd nn, toFloatOpd nn], [i32]⟩ 62 | | demote_f64 : Instr Γ .demote_f64 ⟨[f64], [toFloatOpd nn]⟩ 63 | | promote_f32 : Instr Γ .promote_f32 ⟨[f32], [toFloatOpd nn]⟩ 64 | | convert_i : Instr Γ (.convert_i mm s) ⟨[toIntOpd mm], [toFloatOpd nn]⟩ 65 | | reinterpret_i : Instr Γ .reinterpret_i ⟨[toIntOpd nn], [toFloatOpd nn]⟩ 66 | 67 | inductive Instr (Γ : Context) : Instr.Numeric nn → Typ.Stack → Prop 68 | | integer : Integer.Instr Γ instr type → Instr Γ (.integer instr) type 69 | | float : Float.Instr Γ instr type → Instr Γ (.float instr) type 70 | 71 | end Numeric 72 | 73 | -- todo vector instructions 74 | 75 | inductive Reference.Instr (Γ : Context) : Instr.Reference → Typ.Stack → Prop 76 | | null : Instr Γ (.null t) ⟨[], [ref t]⟩ 77 | | is_null : Instr Γ .is_null ⟨[ref t], [i32]⟩ 78 | | func : Γ.funcs.list.get x = f 79 | → Γ.refs.list.contains (Vec.index Γ.funcs x) 80 | → Instr Γ (.func (Vec.index Γ.funcs x)) ⟨[], [ref t]⟩ 81 | 82 | inductive Local.Instr (Γ : Context) : Instr.Local → Typ.Stack → Prop 83 | | get : Γ.locals.list.get x = t 84 | → Instr Γ (.get (Vec.index Γ.locals x)) ⟨[], [.val t]⟩ 85 | | set : Γ.locals.list.get x = t 86 | → Instr Γ (.set (Vec.index Γ.locals x)) ⟨[.val t], []⟩ 87 | | tee : Γ.locals.list.get x = t 88 | → Instr Γ (.tee (Vec.index Γ.locals x)) ⟨[.val t], [.val t]⟩ 89 | 90 | inductive Global.Instr (Γ : Context) : Instr.Global → Typ.Stack → Prop 91 | | get : Γ.globals.list.get x = (m, t) 92 | → Instr Γ (.get (Vec.index Γ.globals x)) ⟨[], [.val t]⟩ 93 | | set : Γ.globals.list.get x = (.var, t) 94 | → Instr Γ (.set (Vec.index Γ.globals x)) ⟨[.val t], []⟩ 95 | 96 | inductive Table.Instr (Γ : Context) : Instr.Table → Typ.Stack → Prop 97 | | get : Γ.tables.list.get x = (l, t) 98 | → Instr Γ (.get (Vec.index Γ.tables x)) ⟨[i32], [ref t]⟩ 99 | | set : Γ.tables.list.get x = (l, t) 100 | → Instr Γ (.set (Vec.index Γ.tables x)) ⟨[i32, ref t], []⟩ 101 | | size : Γ.tables.list.get x = tt 102 | → Instr Γ (.size (Vec.index Γ.tables x)) ⟨[], [i32]⟩ 103 | | grow : Γ.tables.list.get x = (l, t) 104 | → Instr Γ (.grow (Vec.index Γ.tables x)) ⟨[ref t, i32], [i32]⟩ 105 | | fill : Γ.tables.list.get x = (l, t) 106 | → Instr Γ (.fill (Vec.index Γ.tables x)) ⟨[i32, ref t, i32], []⟩ 107 | | copy : Γ.tables.list.get x = (lim1, t) 108 | → Γ.tables.list.get y = (lim2, t) 109 | → Instr Γ (.copy (Vec.index Γ.tables x) (Vec.index Γ.tables y)) 110 | ⟨[i32, i32, i32], []⟩ 111 | | init : Γ.tables.list.get x = (lim1, t) 112 | → Γ.elems.list.get y = t 113 | → Instr Γ (.init (Vec.index Γ.tables x) (Vec.index Γ.elems y)) 114 | ⟨[i32, i32, i32], []⟩ 115 | 116 | inductive Memory.Integer.Instr (Γ : Context) : Instr.Memory.Integer nn → Typ.Stack → Prop 117 | | load : Γ.mems.list.length > 0 118 | → Nat.pow 2 arg.align.val ≤ nn.toBytes 119 | → Instr Γ (.load arg) ⟨[i32], [toIntOpd nn]⟩ 120 | | store : Γ.mems.list.length > 0 121 | → Nat.pow 2 arg.align.val ≤ nn.toBytes 122 | → Instr Γ (.store arg) ⟨[i32, toIntOpd nn], []⟩ 123 | | load8 : Γ.mems.list.length > 0 124 | → Nat.pow 2 arg.align.val ≤ 1 125 | → Instr Γ (.load8 s arg) ⟨[i32], [toIntOpd nn]⟩ 126 | | load16 : Γ.mems.list.length > 0 127 | → Nat.pow 2 arg.align.val ≤ 2 128 | → Instr Γ (.load16 s arg) ⟨[i32], [toIntOpd nn]⟩ 129 | | load32 : Γ.mems.list.length > 0 130 | → Nat.pow 2 arg.align.val ≤ 4 131 | → Instr Γ (.load32 s arg) ⟨[i32], [toIntOpd nn]⟩ 132 | | store8 : Γ.mems.list.length > 0 133 | → Nat.pow 2 arg.align.val ≤ 1 134 | → Instr Γ (.store8 arg) ⟨[i32, toIntOpd nn], []⟩ 135 | | store16 : Γ.mems.list.length > 0 136 | → Nat.pow 2 arg.align.val ≤ 2 137 | → Instr Γ (.store16 arg) ⟨[i32, toIntOpd nn], []⟩ 138 | | store32 : Γ.mems.list.length > 0 139 | → Nat.pow 2 arg.align.val ≤ 4 140 | → Instr Γ (.store32 arg) ⟨[i32, toIntOpd nn], []⟩ 141 | 142 | inductive Memory.Float.Instr (Γ : Context) : Instr.Memory.Float nn → Typ.Stack → Prop 143 | | load : Γ.mems.list.length > 0 144 | → Nat.pow 2 arg.align.val ≤ nn.toBytes 145 | → Instr Γ (.load arg) ⟨[i32], [toFloatOpd nn]⟩ 146 | | store : Γ.mems.list.length > 0 147 | → Nat.pow 2 arg.align.val ≤ nn.toBytes 148 | → Instr Γ (.store arg) ⟨[i32, toFloatOpd nn], []⟩ 149 | 150 | inductive Memory.Instr (Γ : Context) : Instr.Memory → Typ.Stack → Prop 151 | | integer : Memory.Integer.Instr Γ instr type 152 | → Instr Γ (.integer instr) type 153 | | float : Memory.Float.Instr Γ instr type 154 | → Instr Γ (.float instr) type 155 | | size : Γ.mems.list.length > 0 156 | → Instr Γ .size ⟨[], [i32]⟩ 157 | | grow : Γ.mems.list.length > 0 158 | → Instr Γ .grow ⟨[i32], [i32]⟩ 159 | | fill : Γ.mems.list.length > 0 160 | → Instr Γ .fill ⟨[i32, i32, i32], []⟩ 161 | | copy : Γ.mems.list.length > 0 162 | → Instr Γ .copy ⟨[i32, i32, i32], []⟩ 163 | | init : Γ.mems.list.length > 0 164 | → Γ.datas.list.get x = () 165 | → Instr Γ (.init (Vec.index Γ.datas x)) ⟨[i32, i32, i32], []⟩ 166 | | data_drop : Γ.datas.list.get x = () 167 | → Instr Γ (.data_drop (Vec.index Γ.datas x)) ⟨[], []⟩ 168 | 169 | mutual 170 | inductive Instr : (Γ : Context) → Instr → Typ.Stack → Prop 171 | | numeric : Numeric.Instr Γ instr type 172 | → Instr Γ (.numeric instr) type 173 | | reference : Reference.Instr Γ instr type 174 | → Instr Γ (.reference instr) type 175 | -- Parametric 176 | | drop : Instr Γ .drop ⟨[t], []⟩ 177 | | select_none : Instr Γ (.select .none) ⟨[t, t, i32], [t]⟩ 178 | | select_num : Instr Γ (.select (.some [.num t])) ⟨[.val (.num t), .val (.num t), i32], [.val (.num t)]⟩ 179 | | select_vec : Instr Γ (.select (.some [.vec t])) ⟨[.val (.vec t), .val (.vec t), i32], [.val (.vec t)]⟩ 180 | | locl : Local.Instr Γ instr type 181 | → Instr Γ (.locl instr) type 182 | | globl : Global.Instr Γ instr type 183 | → Instr Γ (.globl instr) type 184 | | table : Table.Instr Γ instr type 185 | → Instr Γ (.table instr) type 186 | | elem_drop : Γ.elems.list.get x = t 187 | → Instr Γ (.elem_drop (Vec.index Γ.elems x)) ⟨[], []⟩ 188 | | memory : Memory.Instr Γ instr type 189 | → Instr Γ (.memory instr) type 190 | -- Control 191 | | nop : Instr Γ .nop ⟨[], []⟩ 192 | | unreachable : Instr Γ .unreachable ⟨t1, t2⟩ 193 | | block : {h : List.length Γ.labels.list + 1 < Vec.max_length} 194 | → Typ.Block Γ blocktype ⟨t1, t2⟩ 195 | → Instrs {Γ with labels := Vec.cons t2 Γ.labels h} instrs ⟨t1.list.map .val, t2.list.map .val⟩ 196 | → Instr Γ (.block blocktype instrs .wasm_end) ⟨t1.list.map .val, t2.list.map .val⟩ 197 | | loop : {h : List.length Γ.labels.list + 1 < Vec.max_length} 198 | → Typ.Block Γ blocktype ⟨t1, t2⟩ 199 | → Instrs {Γ with labels := Vec.cons t2 Γ.labels h} instrs ⟨t1.list.map .val, t2.list.map .val⟩ 200 | → Instr Γ (.block blocktype instrs .wasm_end) ⟨t1.list.map .val, t2.list.map .val⟩ 201 | | wasm_if : {h : List.length Γ.labels.list + 1 < Vec.max_length} 202 | → Typ.Block Γ blocktype ⟨t1, t2⟩ 203 | → Instrs {Γ with labels := Vec.cons t2 Γ.labels h} instrs1 ⟨t1.list.map .val, t2.list.map .val⟩ 204 | → Instrs {Γ with labels := Vec.cons t2 Γ.labels h} instrs2 ⟨t1.list.map .val, t2.list.map .val⟩ 205 | → Instr Γ (.wasm_if blocktype instrs1 .wasm_else instrs2 .wasm_end) 206 | ⟨t1.list.map .val |>.append [i32], t2.list.map .val⟩ 207 | | br : Γ.labels.list.get l = t 208 | → Instr Γ (.br (Vec.index Γ.labels l)) ⟨List.append t1 (t.list.map .val), t2⟩ 209 | | br_if : Γ.labels.list.get l = t 210 | → Instr Γ (.br (Vec.index Γ.labels l)) ⟨(t.list.map .val).append [i32], t2⟩ 211 | | br_table_nil : Typ.MatchList t (Γ.labels.list.get ln |>.list.map .val) 212 | → Instr Γ (.br_table Vec.nil (Vec.index Γ.labels ln)) 213 | ⟨List.append t1 t |>.append [i32], t2⟩ 214 | | br_table_cons : {h : List.length ls.list + 1 < Vec.max_length} 215 | → Typ.MatchList t (Γ.labels.list.get l |>.list.map .val) 216 | → Instr Γ (.br_table ls ln) 217 | ⟨List.append t1 t |>.append [i32], t2⟩ 218 | → Instr Γ (.br_table (Vec.cons (Vec.index Γ.labels l) ls h) ln) 219 | ⟨List.append t1 t |>.append [i32], t2⟩ 220 | | wasm_return : Γ.labels.list.get l = t 221 | → Instr Γ (.br (Vec.index Γ.labels l)) ⟨List.append t1 (t.list.map .val), t2⟩ 222 | | call : Γ.funcs.list.get x = ⟨t1, t2⟩ 223 | → Instr Γ (.call (Vec.index Γ.funcs x)) ⟨t1.list.map .val, t2.list.map .val⟩ 224 | | call_indirect : Γ.tables.list.get x = ⟨limits, fref⟩ 225 | → Γ.types.list.get y = ⟨t1, t2⟩ 226 | → Instr Γ (.call_indirect (Vec.index Γ.tables x) (Vec.index Γ.types y)) 227 | ⟨t1.list.map .val |>.append [i32], t2.list.map .val⟩ 228 | 229 | inductive Instrs : (Γ : Context) → List Instr → Typ.Stack → Prop 230 | | nil : Instrs Γ [] ⟨t1, t2⟩ 231 | | cons : Instrs Γ instrs ⟨t1, List.append t0 t'⟩ 232 | → Typ.MatchList t' t 233 | → Instr Γ instr ⟨t, t3⟩ 234 | → Instrs Γ (instr :: instrs) ⟨t1, t3⟩ 235 | end 236 | 237 | inductive Expr : (Γ : Context) → Syntax.Expr → Syntax.Typ.Result → Prop 238 | | expr : Statics.Instrs Γ instrs ⟨[], t'⟩ → Statics.Typ.MatchList t' (t.list.map .val) → Expr Γ (instrs, .wasm_end) t 239 | 240 | inductive Instr.Constant : (Γ : Context) → Syntax.Instr → Prop 241 | | const_int : Constant Γ (.numeric (.integer (.const v))) 242 | | const_float : Constant Γ (.numeric (.float (.const v))) 243 | | ref_null : Constant Γ (.reference (.null t)) 244 | | ref_func : Constant Γ (.reference (.func t)) 245 | | global_get : Γ.globals.list.get x = (.const, t) → Constant Γ (.globl (.get (Vec.index Γ.globals x))) 246 | 247 | inductive Expr.Constant : (Γ : Context) → Syntax.Expr → Prop 248 | | nil : Constant Γ ([], .wasm_end) 249 | | cons : Constant Γ (instrs, .wasm_end) → Constant Γ (instr :: instrs, .wasm_end) 250 | -------------------------------------------------------------------------------- /Wasm/Validation/Typ.lean: -------------------------------------------------------------------------------- 1 | /- https://webassembly.github.io/spec/core/valid/types.html -/ 2 | import Numbers 3 | import Wasm.Syntax 4 | import Wasm.Validation.Context 5 | 6 | namespace Wasm.Validation.Typ 7 | 8 | open Syntax Numbers 9 | 10 | inductive Limit : Syntax.Typ.Limit → Unsigned32 → Prop 11 | | minOnly : n ≤ k → Limit ⟨n, .none⟩ k 12 | | minMax : n ≤ k → m ≤ k → n ≤ m → Limit ⟨n, .some m⟩ k 13 | 14 | inductive Block.Index (Γ : Context) : Module.Index.Typ → Syntax.Typ.Func → Prop 15 | | typeIndex : Γ.types.list.get i = f → Block.Index Γ (Vec.index Γ.types i) f 16 | 17 | inductive Block.ValType (Γ : Context) : Option Typ.Val → Syntax.Typ.Func → Prop 18 | | valTypeEmpty : Block.ValType Γ .none ⟨Vec.nil, Vec.nil⟩ 19 | | valTypeValue : Block.ValType Γ (.some v) ⟨Vec.nil, Vec.single v⟩ 20 | 21 | inductive Block (Γ : Context) : Syntax.Instr.BlockType → Syntax.Typ.Func → Prop 22 | | index : Block.Index Γ index type → Block Γ (.index index) type 23 | | value : Block.ValType Γ valtype type → Block Γ (.value valtype) type 24 | 25 | inductive Function : Syntax.Typ.Func → Prop 26 | | func : Function ⟨τ1, τ2⟩ 27 | 28 | inductive Table : Syntax.Typ.Table → Prop 29 | | table : Limit limit (Unsigned.ofNat <| Vec.max_length - 1) → Table ⟨limit, ref⟩ 30 | 31 | inductive Memory : Syntax.Typ.Mem → Prop 32 | | memory : Limit limit (Unsigned.ofNat (Nat.pow 2 16)) → Memory limit 33 | 34 | inductive Global : Syntax.Typ.Global → Prop 35 | | globl : Global ⟨m, valtype⟩ 36 | 37 | inductive External : Syntax.Typ.Extern → Prop 38 | | func : Function f → External (.func f) 39 | | table : Table t → External (.table t) 40 | | mem : Memory m → External (.mem m) 41 | | globl : Global g → External (.globl g) 42 | 43 | 44 | inductive Import.Limit : Syntax.Typ.Limit → Syntax.Typ.Limit → Prop 45 | | empty : n1 ≥ n2 → Import.Limit ⟨n1, m1⟩ ⟨n2, .none⟩ 46 | | nonempty : n1 ≥ n2 → m1 ≤ m2 → Import.Limit ⟨n1, .some m1⟩ ⟨n2, .some m2⟩ 47 | 48 | inductive Import.Function : Syntax.Typ.Func → Syntax.Typ.Func → Prop 49 | | refl : Import.Function f f 50 | 51 | inductive Import.Table : Syntax.Typ.Table → Syntax.Typ.Table → Prop 52 | | table : Import.Limit limit1 limit2 → Import.Table ⟨limit1, reftype⟩ ⟨limit2, reftype⟩ 53 | 54 | inductive Import.Memory : Syntax.Typ.Mem → Syntax.Typ.Mem → Prop 55 | | mem : Import.Limit limit1 limit2 → Import.Memory limit1 limit2 56 | 57 | inductive Import.Global : Syntax.Typ.Global → Syntax.Typ.Global → Prop 58 | | refl : Import.Global g g 59 | -------------------------------------------------------------------------------- /Wasm/Vec.lean: -------------------------------------------------------------------------------- 1 | import Wasm.AuxDefs 2 | 3 | namespace Wasm 4 | 5 | def Vec.max_length : Nat := Nat.pow 2 32 6 | 7 | abbrev Vec (α : Type u) := {list : List α // list.length < Vec.max_length} 8 | instance [DecidableEq α ]: DecidableEq (Vec α) := inferInstance 9 | 10 | namespace Vec 11 | 12 | @[reducible] def list : Vec α → List α := (·.val) 13 | @[reducible] def maxLen (self : Vec α) : List.length self.list < Vec.max_length := 14 | self.property 15 | 16 | nonrec def toString [ToString α] (v : Vec α) : String := 17 | String.intercalate " " (v.list.map toString) 18 | 19 | def nil : Vec α := ⟨[], by simp [max_length]⟩ 20 | def single : α → Vec α := (⟨[·], by simp [max_length]⟩) 21 | 22 | instance : Coe (Vec α) (List α) := ⟨fun v => v.list⟩ 23 | instance : Inhabited (Vec α) := ⟨nil⟩ 24 | instance [ToString α] : ToString (Vec α) := ⟨toString⟩ 25 | instance [Repr α] : Repr (Vec α) := ⟨fun v => reprPrec v.list⟩ 26 | 27 | def length_cons (x : α) 28 | (xs : Vec α) 29 | (h : xs.list.length + 1 < Vec.max_length) 30 | : (res : Vec α) ×' (res.list.length = xs.list.length + 1) := 31 | let res := ⟨x :: xs.list, h⟩ 32 | ⟨res, by rw [List.length_cons]⟩ 33 | 34 | def cons (x : α) (xs : Vec α) (h : xs.list.length + 1 < Vec.max_length) : Vec α := 35 | Vec.length_cons x xs h |>.fst 36 | 37 | def length_append (xs : Vec α) (ys : Vec α) 38 | (h : xs.list.length + ys.list.length < Vec.max_length) 39 | : (res : Vec α ) 40 | ×' (res.list.length = xs.list.length + ys.list.length) := 41 | let res := ⟨xs.list ++ ys.list, by rw [List.length_append]; exact h⟩ 42 | ⟨res, by rw [List.length_append]⟩ 43 | 44 | def append (xs : Vec α) (ys : Vec α) 45 | (h : xs.list.length + ys.list.length < Vec.max_length) 46 | : Vec α := 47 | length_append xs ys h |>.fst 48 | 49 | def map (f : α → β) (v : Vec α) : Vec β := 50 | ⟨v.list.map f, by 51 | rw [List.length_map] 52 | exact v.maxLen 53 | ⟩ 54 | def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} 55 | (f : α → m β) (v : Vec α) : m (Vec β) := do 56 | return ⟨ ← v.list.mapM f 57 | , sorry 58 | ⟩ 59 | 60 | def reverse (v : Vec α) : Vec α := ⟨v.list.reverse, by simp [v.maxLen]⟩ 61 | 62 | @[inline, simp] def length (v : Vec α) := v.list.length 63 | @[inline, simp] def get (v : Vec α) (i : Fin v.length) := v.list.get i 64 | @[inline, simp] def set (v : Vec α) (i : Fin v.length) (x : α) : Vec α := 65 | ⟨v.list.set i x, by rw [List.length_set]; exact v.maxLen⟩ 66 | 67 | @[simp] def tl? (v : Vec α) : Option (Vec α) := 68 | match h : v.list with 69 | | [] => .none 70 | | x :: xs => 71 | .some ⟨xs, by 72 | have := v.maxLen 73 | rw [h, List.length, Nat.add_comm] at this 74 | linarith 75 | ⟩ 76 | 77 | @[simp] def tl (v : Vec α) (len : v.length > 0) : Vec α := 78 | match h : v.list with 79 | | [] => by simp [length, h] at len 80 | | x :: xs => 81 | ⟨xs, by 82 | have := v.maxLen 83 | rw [h, List.length, Nat.add_comm] at this 84 | linarith 85 | ⟩ 86 | 87 | theorem length_lt_tl_length {v : Vec α} {len : v.length > 0} 88 | : v.length > (tl v len).length := by 89 | sorry 90 | 91 | def join (v : Vec (Vec α)) : Option (Vec α) := 92 | match h₁ : v.list with 93 | | [] => .some Vec.nil 94 | | x :: xs => do 95 | have len : v.length > 0 := by simp [length, h₁] 96 | have := length_lt_tl_length (v := v) (len := len) 97 | 98 | let res ← Vec.join (v.tl len) 99 | if h₂ : res.length + x.length < Vec.max_length then 100 | return Vec.append x res (by simp [Nat.add_comm] at h₂; exact h₂) 101 | else none 102 | termination_by v.length 103 | 104 | @[inline] def dropLast (v : Vec α) : Vec α := 105 | ⟨ v.list.dropLast, by 106 | have := v.property 107 | simp [Vec.list, Vec.max_length] at * 108 | cases h : List.length v.val <;> (simp [h] at *; try linarith) 109 | ⟩ 110 | 111 | @[inline] def getLastD (v : Vec α) : α → α := v.list.getLastD 112 | 113 | end Vec 114 | -------------------------------------------------------------------------------- /bin/wasm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # this makes it easier to interface with compilers autograder for testing 3 | .lake/build/bin/wasm $@ 4 | -------------------------------------------------------------------------------- /lake-manifest.json: -------------------------------------------------------------------------------- 1 | {"version": 7, 2 | "packagesDir": ".lake/packages", 3 | "packages": 4 | [{"url": "https://github.com/leanprover-community/batteries", 5 | "type": "git", 6 | "subDir": null, 7 | "rev": "551ff2d7dffd7af914cdbd01abbd449fe3e3d428", 8 | "name": "batteries", 9 | "manifestFile": "lake-manifest.json", 10 | "inputRev": "main", 11 | "inherited": true, 12 | "configFile": "lakefile.lean"}, 13 | {"url": "https://github.com/leanprover-community/quote4", 14 | "type": "git", 15 | "subDir": null, 16 | "rev": "53156671405fbbd5402ed17a79bd129b961bd8d6", 17 | "name": "Qq", 18 | "manifestFile": "lake-manifest.json", 19 | "inputRev": "master", 20 | "inherited": true, 21 | "configFile": "lakefile.lean"}, 22 | {"url": "https://github.com/leanprover-community/aesop", 23 | "type": "git", 24 | "subDir": null, 25 | "rev": "53ba96ad7666d4a2515292974631629b5ea5dfee", 26 | "name": "aesop", 27 | "manifestFile": "lake-manifest.json", 28 | "inputRev": "master", 29 | "inherited": true, 30 | "configFile": "lakefile.toml"}, 31 | {"url": "https://github.com/leanprover-community/ProofWidgets4", 32 | "type": "git", 33 | "subDir": null, 34 | "rev": "e6b6247c61280c77ade6bbf0bc3c66a44fe2e0c5", 35 | "name": "proofwidgets", 36 | "manifestFile": "lake-manifest.json", 37 | "inputRev": "v0.0.36", 38 | "inherited": true, 39 | "configFile": "lakefile.lean"}, 40 | {"url": "https://github.com/mhuisi/lean4-cli", 41 | "type": "git", 42 | "subDir": null, 43 | "rev": "10d88b52fa8d717fa8e29af3abf0c3a2bf175497", 44 | "name": "Cli", 45 | "manifestFile": "lake-manifest.json", 46 | "inputRev": "nightly", 47 | "inherited": false, 48 | "configFile": "lakefile.lean"}, 49 | {"url": "https://github.com/leanprover-community/import-graph.git", 50 | "type": "git", 51 | "subDir": null, 52 | "rev": "77e081815b30b0d30707e1c5b0c6a6761f7a2404", 53 | "name": "importGraph", 54 | "manifestFile": "lake-manifest.json", 55 | "inputRev": "main", 56 | "inherited": true, 57 | "configFile": "lakefile.toml"}, 58 | {"url": "https://github.com/leanprover-community/mathlib4", 59 | "type": "git", 60 | "subDir": null, 61 | "rev": "b5eba595428809e96f3ed113bc7ba776c5f801ac", 62 | "name": "mathlib", 63 | "manifestFile": "lake-manifest.json", 64 | "inputRev": "v4.8.0", 65 | "inherited": false, 66 | "configFile": "lakefile.lean"}, 67 | {"url": "https://github.com/T-Brick/Numbers", 68 | "type": "git", 69 | "subDir": null, 70 | "rev": "a7093c6d0ba77139ab79d8651b26fa8a4fd1a376", 71 | "name": "numbers", 72 | "manifestFile": "lake-manifest.json", 73 | "inputRev": "main", 74 | "inherited": false, 75 | "configFile": "lakefile.lean"}], 76 | "name": "wasm", 77 | "lakeDir": ".lake"} 78 | -------------------------------------------------------------------------------- /lakefile.lean: -------------------------------------------------------------------------------- 1 | import Lake 2 | open Lake DSL 3 | 4 | package wasm { 5 | -- add package configuration options here 6 | } 7 | 8 | lean_lib Wasm { 9 | -- add library configuration options here 10 | } 11 | 12 | @[default_target] 13 | lean_exe wasm { 14 | root := `Main 15 | } 16 | 17 | -- require std from git "https://github.com/leanprover/std4" @ "main" 18 | require mathlib from git 19 | "https://github.com/leanprover-community/mathlib4" @ "v4.8.0" 20 | require Cli from git "https://github.com/mhuisi/lean4-cli" @ "nightly" 21 | require numbers from git 22 | "https://github.com/T-Brick/Numbers" @ "main" 23 | -------------------------------------------------------------------------------- /lean-toolchain: -------------------------------------------------------------------------------- 1 | leanprover/lean4:v4.8.0 2 | --------------------------------------------------------------------------------