🗣 | Alejandro GΓ³mez-LondoΓ±o |

Magnus O. Myreen |

- Functional language
- Proven-correct compiler
- Verified cost semantics

```
fun yes = (print "yes"; yes)
```

π

You can prove it!

π

It's complicated...

The aim of this work is to simplify this process

`is_safe(dataLang)`

` ==> `

`sem(CakeML)`

`<->`

`sem(machine)`

`dataLang`

- Imperative
- Abstract values
- Stateful storage
- An explicit call-stack

```
v = Number int
| Word64 word64
| Block ts tag (v list) -- ts = tag = num
| CodePtr num
| RefPtr num
```

`γ`

`[1,2,3]`

`γ`

```
Block 67 cons_tag [Number 1;
Block 66 cons_tag [Number 2;
Block 65 cons_tag [Number 3;
Block 0 nil_tag []]]]
```

`evaluate (prog,s) = (res,s')`

`evaluate (prog,s) = (res,s')`

```
state = <| clock : num
; locals : v num_map
; stack : stack list
; refs : v ref num_map
; global : num option
; limits : limits
; safe_for_space : bool
...
|>
```

A measurement of space

```
safe_for_space :=
s.safe_for_space
β§ size_of_heap s + k <= s.limits.heap_limit
```

- At every allocation
- At every function call

`size_of_heap s`

`=`

`size_of reachabe_values s.refs {}`

```
<|s.locals|> ++ <|s.stack|> ++ <|s.global|>
```

`size_of vals refs seen`

Where:

- (
`vals`

) is a list of`v`

values - (
`refs`

) is a mapping from numbers to values - (
`seen`

) timestamps we have already seen

```
size_of lims [] refs seen = 0
size_of lims (x::y::ys) refs seen =
let (n1,refs1,seen1) = size_of' lims (y::ys) refs seen
let (n2,refs2,seen2) = size_of' lims [x] refs1 seen1
in n1+n2
...
```

```
size_of [Block 65 some_tag [Number 1];
Block 65 some_tag [Number 1]]
refs seen
```

```
2 +
size_of [Block 65 some_tag [Number 1]]
refs ({65} βͺ seen)
```

```
2 + 0
```

`size_of vals refs seen`

- Precise measurements
- Handles aliasing
- Trivial to evaluate given concrete values

How does one prove space safety?

```
ftail [] = []
ftail (x::xs) = ...
ftail xs
```

```
size_of_heap s + C s <= s.limits.heap_limit
β§ s.safe_for_space
β§ s.locals = ...
β§ ... ==>
is_safe_for_space (ftail_body,s)
```

Where:

`C s`

is any extra space required`is_safe_for_space`

checks the corresponding flag after evaluation

Proof recipe

- Induct on the semantic clock
- Evaluate the body of the function
- Use the IH at the recursive call

- Use the IH at the recursive call

```
size_of_heap s + C s <= s.limits.heap_limit
β§ ... βΉ
size_of_heap s' + C s' <= s'.limits.heap_limit
```

```
ββββ ftail [1,2,3] ββββ€ <= heap_limit
ββββ ftail [2,3] ββββ€ <= heap_limit
ββββ ftail [3] ββββ€ <= heap_limit
ββββ ftail [] ββββ€ <= heap_limit
```

`ftail [1,2,...,n]`

```
size_of (γ[1,2,...,n]γ: rest) s.refs {}
<= s.limits.heap_limit
β§ ...
βΉ
size_of (γ[2,...,n]γ: rest) s'.refs {}
<= s'.limits.heap_limit
```

```
size_of (γ[2,...,n]γ: rest) s.refs {}
<=
size_of (γ[1,2,..,n]γ: rest) s.refs {}
```

```
βββ[2,...,n]βββΌβββrestββββ€
<=
βββ[1,2,...,n]βββΌβββrestββββ€
```

```
size_of' rest s.refs {} = (n,refs,seen)
```

```
n + size_of γ[2,...,n]γ refs seen
<=
n + size_of γ[1,2,..,n]γ refs seen
```

```
size_of' rest s.refs {} = (n,refs,seen)
```

```
size_of [Block 66 cons_tag [Number 2;...]] refs seen
<=
size_of [Block 67 cons_tag [Number 1;
Block 66 cons_tag [Number 2;...]]] refs seen
```

`67 β seen`

`67 β seen`

`67 β seen`

```
size_of [Block 67 ...] refs seen = 0
```

```
size_of [Block 66 ...] refs seen
<=
size_of [Block 67 ...] refs seen
```

```
size_of [Block 66 ...] refs seen <= 0
```

`67 β seen`

`67 β seen`

```
size_of [Block 67 ...] ...
= 3 + size_of [Block 66 ...] refs ({67} βͺ seen)
```

`67 β seen`

```
size_of [Block 67 ...] ...
= 3 + size_of [Block 66 ...] refs ({67} βͺ seen)
```

```
size_of [Block 66 ...] refs seen
<=
size_of [Block 66 ...] refs ({67} βͺ seen)
```

```
size_of [Block 66 ...] refs seen
<=
size_of [Block 66 ...] refs ({67} βͺ seen)
```

```
size_of [Block 66 cons_tag [Number 2;
Block 67 ...]]
refs seen
<=
size_of [Block 66 cons_tag [Number 2;
Block 67 ...]]
refs ({67} βͺ seen)
```

`size_of vals refs seen`

- Traversal-base measurement
- No algebraic properties over
`++`

- Relies on the well-formedness of its arguments

A flat reachability-based measure

`flat_size_of refs blocks roots`

Where:

- (
`roots`

) is a list of "root" values - (
`refs`

) is a mapping from numbers to values - (
`blocks`

) is a mapping from timestamps to blocks

The set of all reachable values

```
<|s.locals|> ++ <|s.stack|> ++ <|s.global|>
```

```
[
Block 71 some_tag [Number 1; Block 74 ...];
Number 57;
Word64 0xF5CA15;
Word64 0x01368E;
Word64 0xC81026;
RefPtr 45;
Block 75 some_tag [RefPtr 84; ...];
]
```

The set of all reachable values addresses

```
addr = TStamp num -- Blocks
| RStamp num -- Pointers
```

```
to_addrs [] = β
to_addrs (Block ts _ _::xs) = {TStamp ts} βͺ to_addrs xs
to_addrs (RefPtr ptr ::xs) = {RStamp ts} βͺ to_addrs xs
to_addrs (_ ::xs) = to_addrs xs
```

```
to_addrs ([
Block 71 some_tag [Number 1; Block 74 ...];
Number 57;
Word64 0xF5CA15;
Word64 0x01368E;
Word64 0xC81026;
RefPtr 45;
Block 75 some_tag [RefPtr 84; ...];
])
```

```
{TStamp 71; RStamp 45; TStamp 75}
```

```
to_addrs ([
Block 71 some_tag [Number 1; Block 74 ...];
Number 57;
Word64 0xF5CA15;
Word64 0x01368E;
Word64 0xC81026;
RefPtr 45;
Block 75 some_tag [RefPtr 84; ...];
])
```

```
TStamp 71 -> TStamp 74
TStamp 75 -> RStamp 84
RStamp 45 -> ...
```

```
next refs blocks (TStamp ts) addr =
case lookup ts blocks of
SOME (Block ts tag vs) => addr β to_addrs vs
NONE => F
next refs blocks (RStamp ptr) addr =
...
```

The set of all reachable addresses

`=`

```
reachable_addrs refs blocks roots =
{ y | βx. x β roots β§ RTC (next refs blocks) x y }
```

Now we measure!

```
flat_measure (Word64 _) = 3
flat_measure (Number i) =
if small_num i
then 0
else ... -- big num stuff
flat_measure _ = 0
```

```
addrs_measure refs blocks (TStamp ts) =
case lookup ts blocks of
| SOME (Block _ _ vs) =>
1 + LENGTH vs + SUM (MAP flat_measure vs)
| _ => 0
addrs_measure refs blocks (RStamp p) =
...
```

```
flat_measure refs blocks roots =
SUM (MAP flat_measure roots) +
πΊ (addrs_measure refs blocks)
(reachable_addrs refs blocks (to_addrs roots))
```

```
flat_size_of refs blocks
[Block 65 some_tag [Number 1];
Block 65 some_tag [Number 1]]
```

```
πΊ (addrs_measure refs blocks) {TStamp 65}
```

```
addrs_measure refs blocks (TStamp 65)
```

```
addrs_measure refs blocks (TStamp 65)
```

```
lookup 65 blocks = SOME (Block 65 some_tag [Number 1])
->
1 + LENGTH [Number 1] + flat_measure (Number 1)
```

```
1 + 1 + 0
```

```
flat_size_of (γ[2,...,n]γ: rest) s.refs s.all_blocks
<=
flat_size_of (γ[1,2,..,n]γ: rest) s.refs s.all_blocks
```

```
a β b
==>
πΊ f a <= πΊ f b
```

```
reachable_addrs refs blocks
(Block 66 ... : rest)
β
reachable_addrs refs blocks
(Block 67 cons_tag [Number 1;
Block 66 ...] : rest)
```

```
reachable_addrs refs blocks rest βͺ
{TStamp 66; ...}
β
reachable_addrs refs blocks rest βͺ
{TStamp 67;TStamp 66; ...}
```

`flat_size_of refs blocks roots`

- Precise measurements
- Handles aliasing
- Simpler reasoning