- Functional language
- Proven-correct compiler
- Able to bootstrap itself

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

`<->`

🤝`-{💥}`

`.asm`

Objectives

- Make space reasoning
__possible__in`CakeML`

- Produce
__concrete and tight__space bounds - Enable transportation of
__liveness__properties from source to machine code

Why do programs run out of memory?

- Runs out of heap
- Runs out of stack
- Object exceeds representation limits

` -> dataLang`

- Imperative
- Abstract values
- Stateful storage
- An explicit call-stack (unlike languages above)

```
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 []]]]
```

`is_space_safe(dataLang)`

` ==> `

`sem(CakeML)`

`<->`

`sem(machine)`

```
size_of_heap s <= s.limits.heap_limit
size_of_stack s <= s.limits.stack_limit
```

- At every allocation
- At every function call

How do we count?

`size_of_heap s`

`=`

`size_of reachabe_values s.refs {}`

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

`size_of_heap s = `

size_of_heap (run_gc s)

`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

`〘[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 []]]]
```

```
size_of [〘[1,2,3]〙;〘[1,2,3]〙] refs seen
```

```
10 + size_of [〘[1,2,3]〙] refs ({67,66,65} ∪ seen)
```

```
10 + size_of [Block 67 ...] refs ({67,66,65} ∪ seen)
```

```
10 + 0
```

`size_of vals refs seen`

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

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

🙂

You can prove it!

🙃

It's complicated...

```
size_of 〘[2,...,n]〙 refs seen
<=
size_of 〘[1,2,..,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:

- (
`refs`

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

) is a mapping from timestamps to blocks - (
`roots`

) is a list of "root" values

`blocks`

`67 |-> Block 67 ...`

`40 |-> Block 40 ...`

`10 |-> Block 10 ...`

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 -> ...
```

The set of all reachable addresses

`=`

```
reachable_addrs refs blocks roots =
{ y | ∃x. x ∈ roots ∧ 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
```

`RStamp 45`

`TStamp 50`

`RStamp 14`

`TStamp 75`

`->`

`->`

`->`

`->`

`[33,Block 43 ...]`

`[Ptr 30,Ptr 94]`

`[53,85,17]`

`[0xFF0D,0xAB0D,0xDD12]`

`RStamp 45`

`TStamp 50`

`RStamp 14`

`TStamp 75`

`->`

`->`

`->`

`->`

`3`

`2`

`6`

`12`

`Σ`

`RStamp 45`

`TStamp 50`

`RStamp 14`

`TStamp 75`

`->`

`->`

`->`

`->`

`3`

`2`

`6`

`12`

`23`

```
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_size_of refs blocks roots =
SUM (MAP flat_measure roots) +
𝚺 (addrs_measure refs blocks)
(reachable_addrs refs blocks (to_addrs roots))
```

```
flat_size_of s.refs s.all_blocks 〘[2,...,n]〙
<=
flat_size_of s.refs s.all_blocks 〘[1,2,..,n]〙
```

```
a ⊆ b
==>
𝚺 f a <= 𝚺 f b
```

```
reachable_addrs refs blocks
[Block 66 ...]
⊆
reachable_addrs refs blocks
[Block 67 cons_tag [Number 1;
Block 66 ...]]
```

```
{TStamp 66; ...}
⊆
{TStamp 67;TStamp 66; ...}
```

`flat_size_of refs blocks roots`

- Precise measurements
- Handles aliasing
- Simpler reasoning

`flat_size_of refs blocks vals`

`size_of vals refs seen`