Repository navigation
Expand file tree
/
Copy pathCall.lean
More file actions
151 lines (140 loc) · 9.09 KB
/
Copy pathCall.lean
File metadata and controls
151 lines (140 loc) · 9.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import LeanExe.IR.Stmt
import LeanExe.Pipeline.Implements
/-!
The rule for calls between compiled functions. A call reuses the callee's
`Implements` theorem: the caller supplies arguments that represent the callee's
input, and receives results that represent the callee's value, with every array
it held kept, or the call aborts.
-/
namespace LeanExe.IR
open Wasm LeanExe.Pipeline
variable {m : Module} {a : Bool}
/-- A call of entry `idx`, which implements `g`, from arguments that evaluate to
words representing `x`, leaves in the locals `results` values that represent
`g x` as owned, and the heap condition `Post` holds. The heap facts are those of `Implements`, with its region clause as
`Heap.Keeps`, and `hSet` says the result locals can hold any represented result. -/
theorem Stmt.callImplementsA_spec [Represent α] [Represent β] {idx : Nat} {g : α → β}
{Pre : α → Heap → Store Unit → Prop} {Post : α → Heap → Store Unit → Heap → Store Unit → Prop}
(hImpl : ImplementsA a m idx g Pre Post) {f : Wasm.Function}
(hImport : m.imports[idx]? = none) (hFunc : m.funcs[idx - m.imports.length]? = some f)
{scratch : Nat} {args : List ((type : ScalarType) × Expr type)} {results : List Nat}
(hParams : args.length = f.numParams) {initial : Store Unit} {before afterArgs : State}
{heap : Heap} {x : α} {vals : List Value}
(hArgs : Expr.evalResults initial.mem scratch args before = some (vals, afterArgs))
(hHeap : heap.At initial) (hPre : Pre x heap initial)
(hBorrowed : Represent.borrowed heap initial vals x)
(hSeparate : Separate initial (Represent.moves initial vals x) (Represent.reads initial vals x))
(hCap : initial.memoryCap m 0 ≤ 65535)
(hSet : ∀ heap' store values, Represent.owned heap' store values (g x) →
∃ next, afterArgs.setAll results.reverse values.reverse = some next) :
TripleA a m (.call idx args results) scratch
(fun store state => store = initial ∧ state = before)
(fun store state => ∃ (heap' : Heap) (values : List Value), heap'.At store ∧
Represent.owned heap' store values (g x) ∧
store.memoryCaps = initial.memoryCaps ∧
heap.Keeps initial ((Represent.moves initial vals x).map (block initial)) heap' store
(Represent.blocks store values (g x)) ∧
afterArgs.setAll results.reverse values.reverse = some state ∧
Post x heap initial heap' store) := by
refine (Stmt.call_spec hImport hFunc hParams).mono ?_ fun _ _ h => h
rintro store state ⟨hStore, hState⟩
subst store state
refine ⟨vals, afterArgs, fun final values => ∃ heap' : Heap, heap'.At final ∧
Represent.owned heap' final values.reverse (g x) ∧
final.memoryCaps = initial.memoryCaps ∧
(∀ r, heap.Region r → 0 < r.2 → Apart initial (Represent.moves initial vals x) r →
(∀ a, r.1 ≤ a → a < r.1 + r.2 → final.mem.bytes a = initial.mem.bytes a) ∧
heap'.Region r ∧ Represent.outside final values.reverse (g x) r) ∧
Post x heap initial heap' final,
hArgs, fun env => ?_, ?_⟩
· exact hImpl env initial heap vals x hHeap hPre hBorrowed hSeparate hCap
· rintro store' out ⟨heap', hAt', hOwned', hCaps, hRegion, hPost⟩
obtain ⟨next, hNext⟩ := hSet heap' store' out.reverse hOwned'
rw [List.reverse_reverse] at hNext
exact ⟨next, hNext, heap', out.reverse, hAt', hOwned', hCaps,
Heap.Keeps.implements.mpr hRegion, by rw [List.reverse_reverse]; exact hNext, hPost⟩
/-- A call of entry `idx`, which implements `g`, from arguments that evaluate to
words representing `x`, leaves in the locals `results` values that represent
`g x` as owned. The heap facts are those of `Implements`, with its region clause as
`Heap.Keeps`, and `hSet` says the result locals can hold any represented result. -/
theorem Stmt.callImplements_spec [Represent α] [Represent β] {idx : Nat} {g : α → β}
(hImpl : Implements m idx g) {f : Wasm.Function}
(hImport : m.imports[idx]? = none) (hFunc : m.funcs[idx - m.imports.length]? = some f)
{scratch : Nat} {args : List ((type : ScalarType) × Expr type)} {results : List Nat}
(hParams : args.length = f.numParams) {initial : Store Unit} {before afterArgs : State}
{heap : Heap} {x : α} {vals : List Value}
(hArgs : Expr.evalResults initial.mem scratch args before = some (vals, afterArgs))
(hHeap : heap.At initial) (hBorrowed : Represent.borrowed heap initial vals x)
(hSeparate : Separate initial (Represent.moves initial vals x) (Represent.reads initial vals x))
(hCap : initial.memoryCap m 0 ≤ 65535)
(hSet : ∀ heap' store values, Represent.owned heap' store values (g x) →
∃ next, afterArgs.setAll results.reverse values.reverse = some next) :
Triple m (.call idx args results) scratch
(fun store state => store = initial ∧ state = before)
(fun store state => ∃ (heap' : Heap) (values : List Value), heap'.At store ∧
Represent.owned heap' store values (g x) ∧
store.memoryCaps = initial.memoryCaps ∧
heap.Keeps initial ((Represent.moves initial vals x).map (block initial)) heap' store
(Represent.blocks store values (g x)) ∧
afterArgs.setAll results.reverse values.reverse = some state) := by
refine (Stmt.callImplementsA_spec (a := true) hImpl.toA hImport hFunc hParams hArgs hHeap trivial
hBorrowed hSeparate hCap hSet).mono (fun _ _ h => h) ?_
rintro store state ⟨heap', values, hAt, hOwned, hCaps, hKeeps, hState, -⟩
exact ⟨heap', values, hAt, hOwned, hCaps, hKeeps, hState⟩
/-- A call of entry `idx`, which computes `g` on scalars and keeps the store, from
arguments that evaluate to the values of `x`, keeps the store and leaves the values
of `g x` in the locals `results`. -/
theorem Stmt.callPure_spec [Scalar α] [Scalar β] {idx : Nat} {g : α → β}
(hImpl : ImplementsPureA a m idx g) {f : Wasm.Function}
(hImport : m.imports[idx]? = none) (hFunc : m.funcs[idx - m.imports.length]? = some f)
{scratch : Nat} {args : List ((type : ScalarType) × Expr type)} {results : List Nat}
(hParams : args.length = f.numParams) {initial : Store Unit}
{before afterArgs next : State} {x : α}
(hArgs : Expr.evalResults initial.mem scratch args before = some (Scalar.values x, afterArgs))
(hSet : afterArgs.setAll results.reverse (Scalar.values (g x)).reverse = some next) :
TripleA a m (.call idx args results) scratch
(fun store state => store = initial ∧ state = before)
(fun store state => store = initial ∧ state = next) := by
refine (Stmt.call_spec hImport hFunc hParams).mono ?_ fun _ _ h => h
rintro store state ⟨hStore, hState⟩
subst store state
refine ⟨Scalar.values x, afterArgs,
fun final values => final = initial ∧ values.reverse = Scalar.values (g x), hArgs,
fun env => hImpl env initial x, ?_⟩
rintro store' out ⟨rfl, hOut⟩
refine ⟨next, ?_, rfl, rfl⟩
rw [show out = (Scalar.values (g x)).reverse by rw [← hOut, List.reverse_reverse]]
exact hSet
/-- `Stmt.callPure_spec` followed by `rest` from the state the call leaves. -/
theorem Stmt.seq_callPure [Scalar α] [Scalar β] {idx : Nat} {g : α → β}
(hImpl : ImplementsPureA a m idx g) {f : Wasm.Function}
(hImport : m.imports[idx]? = none) (hFunc : m.funcs[idx - m.imports.length]? = some f)
{scratch : Nat} {args : List ((type : ScalarType) × Expr type)} {results : List Nat}
(hParams : args.length = f.numParams) {initial : Store Unit} {before : State}
{x : α} {rest : Stmt} {Q : Store Unit → State → Prop}
(h : ∃ afterArgs,
Expr.evalResults initial.mem scratch args before = some (Scalar.values x, afterArgs) ∧
∃ next, afterArgs.setAll results.reverse (Scalar.values (g x)).reverse = some next ∧
TripleA a m rest scratch (fun store state => store = initial ∧ state = next) Q) :
TripleA a m (.seq (.call idx args results) rest) scratch
(fun store state => store = initial ∧ state = before) Q :=
let ⟨_, hArgs, _, hSet, hNext⟩ := h
Stmt.seq_spec (Stmt.callPure_spec hImpl hImport hFunc hParams hArgs hSet) hNext
/-- `Stmt.callPure_spec` as the last statement: the postcondition holds of the state the call
leaves. -/
theorem Stmt.callPure_last [Scalar α] [Scalar β] {idx : Nat} {g : α → β}
(hImpl : ImplementsPureA a m idx g) {f : Wasm.Function}
(hImport : m.imports[idx]? = none) (hFunc : m.funcs[idx - m.imports.length]? = some f)
{scratch : Nat} {args : List ((type : ScalarType) × Expr type)} {results : List Nat}
(hParams : args.length = f.numParams) {initial : Store Unit} {before : State}
{x : α} {Q : Store Unit → State → Prop}
(h : ∃ afterArgs,
Expr.evalResults initial.mem scratch args before = some (Scalar.values x, afterArgs) ∧
∃ next, afterArgs.setAll results.reverse (Scalar.values (g x)).reverse = some next ∧
Q initial next) :
TripleA a m (.call idx args results) scratch
(fun store state => store = initial ∧ state = before) Q :=
let ⟨_, hArgs, _, hSet, hQ⟩ := h
(Stmt.callPure_spec hImpl hImport hFunc hParams hArgs hSet).mono (fun _ _ h => h)
fun _ _ ⟨hs, hst⟩ => hs ▸ hst ▸ hQ
end LeanExe.IR