🗣 | Alejandro GΓ³mez-LondoΓ±o |
Magnus O. Myreen |
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
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
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
valuesrefs
) is a mapping from numbers to valuesseen
) 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
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 requiredis_safe_for_space
checks the corresponding
flag after evaluation
Proof recipe
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
++
A flat reachability-based measure
flat_size_of refs blocks roots
Where:
roots
) is a list of "root" valuesrefs
) is a mapping from numbers to valuesblocks
) is a mapping from timestamps to blocksThe 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