Fil-C on Nix

Filnix wiki

the formal campaign

rocq/theories/Symbolic.v

Symbolic execution of the model's step, for proofs that a program never panics from assertions at its points (Cycle.v).

A program proof states what holds at each point of its function, as facts about the temporaries of the environment: the word a name holds, and a range for an integer. It then shows that one step from any configuration where the facts hold reaches a point where its facts hold, or ends without a failure (Panics.v's never_panics_by_invariant). That step is computed here with the environment, the memory and the fuel left abstract: the reduction stops where it needs a value it does not have, the facts supply it, and an integer comparison is decided by linear arithmetic over the facts' ranges, or split into its two answers when the facts do not decide it.

Cycle.v and Buffer.v take each step by its rule from StepRules.v (take_step): the step itself is never reduced, only its rule's premises, with the same facts, wraps and comparisons.

Decisions

DecisionWhy
integer arithmetic stays folded on an unknown (simplnomatch, set by each program file, since Rocq exports no such setting)unfolded, 65552 + x becomes a match on the bits of x as deep as 65552 is long, which neither a fact nor arithmetic can read; on numbers it still computes
the step is reduced by cbn, not cbvcbn leaves a function stuck on an unknown value folded (i mod 4294967296, lookupx e), where a fact can be rewritten; cbv unfolds it into a term no fact matches, and does not finish on a remainder of a variable
linear arithmetic decides comparisons, with remainders and quotients by constants turned into equationsthe bounds a check compares are sums and multiples of the facts' integers; lia alone does not know mod or rem
a remainder of a value proved in range is the value, and any other remainder an unknown in rangethe model wraps every result to its class; where the facts bound it, the wrap is the identity, and dropping it keeps the terms small; where they do not, as for the input, the range is all a step needs, and each wrap or comparison is then tried once
an undecided comparison is split, both answers keptthe input is unknown: the comparison with it goes either way, and the facts after it say which
From Stdlib Require Import String ZArith List Bool Lia
From Nanofil Require Import Term Syntax Values Memory Runtime Ops Semantics Panics StepRules
Import ListNotations
Open Scope Z_scope

Facts about an environment

Looking k up after put k' v answers v if k is k', and otherwise what it answered before.

39Lemma lookup_after_put
∀ A : Type k k′ v : A ps
lookupk putk′ v ps
=
name_eqbk k′
→
Somev
else
lookupk ps
Explore proof 11 tactics

Initial goal

⊢
∀ A : Type k k′ : name v : A ps : listname × A  ,
lookupk putk′ v ps
=
name_eqbk k′
→
Somev
else
lookupk ps

intros A k k′ v ps

A :
Type
k, k′ :
v :
A
ps :
listname × A
⊢
lookupk putk′ v ps
=
name_eqbk k′
→
Somev
else
lookupk ps

unfold put

A :
Type
k, k′ :
v :
A
ps :
listname × A
⊢
lookupk k′, v :: filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
name_eqbk k′
→
Somev
else
lookupk ps

simpl

A :
Type
k, k′ :
v :
A
ps :
listname × A
⊢
name_eqbk k′
→
Somev
else
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
name_eqbk k′
→
Somev
else
lookupk ps

destruct name_eqbk k′ eqn: E ; reflexivity |

A :
Type
k, k′ :
v :
A
ps :
listname × A
E :
name_eqbk k′ = false
⊢
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps

induction ps as | k0 v0 ps IH ; simpl ; reflexivity |

A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
⊢
negbname_eqbk′ k0
→
k0, v0 :: filterλp : name × A ⇒ negbname_eqbk′ fstp ps
else
filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps

destruct name_eqbk′ k0 eqn: E0 ; simpl

2 open

A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
E0 :
name_eqbk′ k0 = true
⊢
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps
focused goal
A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
E0 :
name_eqbk′ k0 = false
⊢
→
Somev0
else
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps

–apply term_eqb_eq in E0

2 open

A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
E0 :
k′ = k0
⊢
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps
deferred goal
A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
E0 :
name_eqbk′ k0 = false
⊢
→
Somev0
else
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps

subst k0

2 open

A :
Type
k, k′ :
v, v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
⊢
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
name_eqbk k′
→
Somev0
else
lookupk ps
deferred goal
A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
E0 :
name_eqbk′ k0 = false
⊢
→
Somev0
else
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps

rewrite E

2 open

A :
Type
k, k′ :
v, v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
⊢
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
deferred goal
A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
E0 :
name_eqbk′ k0 = false
⊢
→
Somev0
else
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps

exact IH

deferred goal
A :
Type
k, k′ :
v :
A
k0 :
v0 :
A
ps :
listname × A
E :
name_eqbk k′ = false
IH :
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps = lookupk ps
E0 :
name_eqbk′ k0 = false
⊢
→
Somev0
else
lookupk filterλp : name × A ⇒ negbname_eqbk′ fstp ps
=
→
Somev0
else
lookupk ps

–destruct name_eqbk k0 ; reflexivity | exact IH

No remaining goals.

Qed

✓ Proof checked and closed by Rocq.

Source script
intros A k k′ v ps
unfold put
simpl
destruct name_eqbk k′ eqn: E ; reflexivity |
induction ps as | k0 v0 ps IH ; simpl ; reflexivity |
destruct name_eqbk′ k0 eqn: E0 ; simpl
–apply term_eqb_eq in E0
subst k0
rewrite E
exact IH
–destruct name_eqbk k0 ; reflexivity | exact IH

Looking k up after put k answers its new value.

50Lemma lookup_after_put_same
∀ A : Type k v : A ps
lookupk putk v ps = Somev
Explore proof 3 tactics

Initial goal

⊢
∀A : Type k : name v : A ps : listname × A, lookupk putk v ps = Somev

intros A k v ps

A :
Type
k :
v :
A
ps :
listname × A
⊢
lookupk putk v ps = Somev

rewrite lookup_after_put , term_eqb_refl

A :
Type
k :
v :
A
ps :
listname × A
⊢
Somev = Somev

reflexivity

No remaining goals.

Qed

✓ Proof checked and closed by Rocq.

Source script
intros A k v ps
reflexivity

Looking k up after put of another name answers what it answered before.

55Lemma lookup_after_put_other
∀ A : Type k k′ v : A ps
name_eqbk k′ = false
lookupk putk′ v ps = lookupk ps
Explore proof 3 tactics

Initial goal

⊢
∀A : Type k k′ : name v : A ps : listname × A, name_eqbk k′ = false → lookupk putk′ v ps = lookupk ps

intros A k k′ v ps H

A :
Type
k, k′ :
v :
A
ps :
listname × A
H :
name_eqbk k′ = false
⊢
lookupk putk′ v ps = lookupk ps

rewrite lookup_after_put , H

A :
Type
k, k′ :
v :
A
ps :
listname × A
H :
name_eqbk k′ = false
⊢
lookupk ps = lookupk ps

reflexivity

No remaining goals.

Qed

✓ Proof checked and closed by Rocq.

Source script
intros A k k′ v ps H
rewrite lookup_after_put , H
reflexivity

A store of bytes changes no object's region.

60Lemma store_bytes_regions
∀ m a size x
Explore proof 1 tactics

Initial goal

⊢
∀m : mem a size x : Z, m_regionsstore_bytesm a size x = m_regionsm

reflexivity

No remaining goals.

Qed

✓ Proof checked and closed by Rocq.

Source script
reflexivity

The name x holds an integer word of e that P allows.

64Definition int_ate : env x : name P : Z → Prop : Prop ≔
∃v, lookupx e = SomeWIntv ∧ Pv

Any integer.

68Definition any_ : Z : Prop ≔
True

Arithmetic

The goal has a remainder or quotient in it.

73Ltac goal_divides  ≔
match goal with|- context [_ mod _]⇒idtac|- context [Z.rem_ _]⇒idtac|- context [Z.quot_ _]⇒idtac|- context [_ / _]⇒idtacend

Replace each remainder x mod m by a literal m with a new variable in [0, m), everywhere. Cheaper for lia than the division equations Z.to_euclidean_division_equations gives, and enough wherever the remainder's bounds are all an argument needs.

85Ltac mod_atoms  ≔
repeat match goal with|- context [?x mod ?m]⇒let H ≔ fresh "Hmod" in pose proof Z.mod_pos_boundx m eq_refl as H ; let r ≔ fresh "r" in set r := x mod m in * ; clearbody rH0 : context [?x mod ?m] |- _⇒let H ≔ fresh "Hmod" in pose proof Z.mod_pos_boundx m eq_refl as H ; let r ≔ fresh "r" in set r := x mod m in * ; clearbody rend

The arithmetic oracle: linear arithmetic over the integers, with the powers of two the model's classes use as numbers, and where the goal divides, with remainders and quotients by constants first turned into equations. Facts about environments and memory are set aside first; they say nothing arithmetic. So are the bodies of local definitions (a function, or a program's computed runs), which the rewriting in every hypothesis below would otherwise walk.

102Ltac arith  ≔
repeat match goal withH : context [lookup_ _] |- _⇒clear HH : context [m_regions_] |- _⇒clear HH : context [m_bytes_] |- _⇒clear HH : _ |- _⇒clearbody Hend;try change 2 ^ 31 with 2147483648 in *;try change 2 ^ 32 with 4294967296 in *;try change 2 ^ 63 with 9223372036854775808 in *;try change 2 ^ 64 with 18446744073709551616 in *;first lia | mod_atoms ; lia | goal_divides ; Z.to_euclidean_division_equations ; lia

arith without the division equations: for the side goals that may well be false (dropping a wrap, deciding a comparison), where a failing attempt should cost little.

117Ltac arith_fast  ≔
repeat match goal withH : context [lookup_ _] |- _⇒clear HH : context [m_regions_] |- _⇒clear HH : context [m_bytes_] |- _⇒clear HH : _ |- _⇒clearbody Hend;try change 2 ^ 31 with 2147483648 in *;try change 2 ^ 32 with 4294967296 in *;try change 2 ^ 63 with 9223372036854775808 in *;try change 2 ^ 64 with 18446744073709551616 in *;first lia | mod_atoms ; lia

Fails unless the term t has an if in it.

130Ltac has_if t ≔
idtac;match t withcontext [if _ then _ else _]⇒idtacend

Fails unless the term t has a remainder in it.

133Ltac has_mod t ≔
idtac;match t withcontext [_ mod _]⇒idtacend

Drop the wraps in E, innermost first, once what each wraps holds no other wrap and no undecided comparison: a remainder or quotient of a nonnegative value is Euclid's, and a wrap that arithmetic over the context, without E, proves is the identity goes. A wrap it does not, of the input say, becomes an unknown known to lie between 0 and the modulus. So each wrap is tried once.

141Ltac drop_wraps E ≔
repeat match type of E withcontext [Z.rem?x ?y]⇒assert_fails has_ifx ; assert_fails has_modx ; rewrite Z.rem_mod_nonnegx y in E by clear E ; arithcontext [Z.quot?x ?y]⇒assert_fails has_ifx ; assert_fails has_modx ; rewrite Z.quot_div_nonnegx y in E by clear E ; arithcontext [?x mod ?m]⇒assert_fails has_ifx ; assert_fails has_modx ; first rewrite Z.mod_smallx m in E by clear E ; arith | generalize Z.mod_pos_boundx m eq_refl ; revert E ; generalize x mod m ; intros ? E ?end

Decide the integer comparisons in E: true or false where arithmetic over the context proves it, and otherwise split into its two answers at once, each kept as a fact, so that no comparison is tried twice. A comparison whose operands hold another comparison waits until that one is decided.

161Ltac decide_compares E ≔
repeat match type of E withcontext [?a <? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.ltb_lta b in E by clear E ; arith | rewrite proj2Z.ltb_gea b in E by clear E ; arith | revert E ; destruct Z.ltb_speca b ; intro Econtext [?a ≤? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.leb_lea b in E by clear E ; arith | rewrite proj2Z.leb_gta b in E by clear E ; arith | revert E ; destruct Z.leb_speca b ; intro Econtext [?a =? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.eqb_eqa b in E by clear E ; arith | rewrite proj2Z.eqb_neqa b in E by clear E ; arith | revert E ; destruct Z.eqb_speca b ; intro Eend

The step

Rewrite in E every fact in the context that names a temporary of an environment, the region of an object in a memory, or a block or the return class of the function, which a proof keeps abstract (block_fact) so that its whole body is not carried through every step.

187Ltac use_facts E ≔
repeat match goal withH : lookup_ _ = Some_ |- _⇒rewrite H in EH : object_region_ m_regions_ = Some_ |- _⇒rewrite H in EH : find_block_ fn_blocks_ = _ |- _⇒rewrite H in EH : fn_ret_ = _ |- _⇒rewrite H in Eend

Reduce E as far as the facts and arithmetic allow: cbn, keeping a load's and a store's bytes folded, then the definitions cbn leaves folded (stuck on an unknown, such as an access check on the memory's regions, or building a record), the facts and the fields they supply, the wraps and the comparisons, until nothing changes.

200Ltac reduce E ≔
repeat progress cbn - [ load_bytes store_bytes ] in E ; rewrite ?store_bytes_regions in E ; try unfold sign_extension in E ; try unfold raw_address in E ; try unfold aligned_res in E ; try unfold continue_at in E ; try unfold with_point in E ; try unfold int_load_res in E ; try unfold access_res in E ; try unfold cap_access_res in E ; use_facts E ; cbn [ r_base r_end r_kind r_life ptr_addr ptr_cap ] in E ; drop_wraps E ; decide_compares E

Compute E, an equation r = step g c, to a result: reduce it, in every case of the comparisons the facts leave open.

211Ltac compute_step E ≔

Facts after a step

Take apart the facts in the context: conjunctions, existentials, and each case of a disjunction.

217Ltac facts  ≔
repeat match goal withH : True |- _⇒clear HH : _ ∧ _ |- _⇒destruct HH : ∃_, _ |- _⇒destruct HH : _ ∨ _ |- _⇒destruct Hend

Prove a fact about the configuration after a step: look each name up through the step's puts to the facts before it, each object's region through the stores, which change none, and through a new object's, and the ranges as facts or by arithmetic.

229Ltac show_facts  ≔
unfold int_at , any;repeat match goal with|- _ ∧ _⇒split|- ∃_, _⇒eexists|- True⇒exact I|- _ ∨ _⇒first left ; solve show_facts | right ; solve show_facts|- lookup_ put_ _ _ = _⇒first rewrite lookup_after_put_same | rewrite lookup_after_put_other by reflexivityH : lookup?x ?e = _ |- lookup?x ?e = _⇒rewrite H ; reflexivityH : object_region?b m_regions?m = _ |- object_region?b m_regions?m = _⇒rewrite H ; reflexivity|- context [m_regionsstore_bytes_ _ _ _]⇒rewrite store_bytes_regions|- object_region_ _ = _⇒progress cbn [ c_mem allocated m_regions object_region r_base req_extent ] ; rewrite ?Z.eqb_refl|- Some_ = Some_⇒reflexivity|- @eqZ _ _⇒reflexivity|- _ ≤ _⇒first assumption | arith|- _ < _⇒first assumption | arith|- _⇒progress cbv beta iotaend

The function, kept abstract

The block l of the function fn, computed once as b, and the fact H that the abstract f, equal to fn by Hf, has it: the step finds a block through H instead of reducing f's body.

255Ltac block_fact f fn Hf l b H ≔
pose b := find_blockl fn_blocksfn ; vm_compute in b ; assert H : find_blockl fn_blocksf = b by rewrite Hf ; reflexivity

A step by its rule

Instead of reducing step with the function and the memory inside it, take_step names the instruction at the frame's point, rewrites the step with its rule from StepRules.v, and proves the rule's premises one by one (premise). Each premise is small: a temporary's value, an op on those words, an access check, a block of the function. The words the premises compute become the rule's result, and a comparison of the input that the facts do not decide is split there (split_compares).

Rewrite in the goal every fact in the context that names a temporary of an environment or the region of an object.

272Ltac use_facts_goal  ≔
repeat match goal withH : lookup_ _ = Some_ |- _⇒rewrite HH : object_region_ m_regions_ = Some_ |- _⇒rewrite Hend

Replace each environment put k v e of the goal, e a variable, by a new variable, with what each name looks up to in it.

280Ltac gen_env  ≔
repeat match goal with|- context [put?k ?v ?e]⇒is_var e;let H ≔ fresh "Hput" inassert H : lookupk putk v e = Somev by apply lookup_after_put_same;repeat match goal withH′ : lookup?x e = Some?w |- _⇒first assert lookupx putk v e = Somew by rewrite lookup_after_put_other ; exact H′ | reflexivity | idtac ; clear H′end;let e′ ≔ fresh "e" in set e′ := putk v e in * ; clearbody e′ ; try clear eend

Drop each wrap in the goal that arithmetic over the context proves is the identity, and turn a remainder or quotient of a nonnegative value into Euclid's. A wrap it does not drop stays.

299Ltac drop_wraps_goal  ≔
repeat match goal with|- context [Z.rem?x ?y]⇒assert_fails has_ifx ; rewrite Z.rem_mod_nonnegx y by arith_fast|- context [Z.quot?x ?y]⇒assert_fails has_ifx ; rewrite Z.quot_div_nonnegx y by arith_fast|- context [?x mod ?m]⇒assert_fails has_ifx ; rewrite Z.mod_smallx m by arith_fastend

Decide each integer comparison in the goal that arithmetic over the context decides; leave the others.

308Ltac decide_compares_goal  ≔
repeat match goal with|- context [?a <? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.ltb_lta b by arith_fast | rewrite proj2Z.ltb_gea b by arith_fast|- context [?a ≤? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.leb_lea b by arith_fast | rewrite proj2Z.leb_gta b by arith_fast|- context [?a =? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.eqb_eqa b by arith_fast | rewrite proj2Z.eqb_neqa b by arith_fastend

Decide each integer comparison in the goal, splitting the goal in its two answers where the facts do not decide it, each kept as a fact. A comparison whose operands hold another waits for it. The split is a case, which looks at the goal alone, not at the definitions in the context (a program's computed runs, say).

326Ltac split_compares  ≔
repeat (match goal with|- context [?a <? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.ltb_lta b by arith_fast | rewrite proj2Z.ltb_gea b by arith_fast | case Z.ltb_speca b ; intro|- context [?a ≤? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.leb_lea b by arith_fast | rewrite proj2Z.leb_gta b by arith_fast | case Z.leb_speca b ; intro|- context [?a =? ?b]⇒assert_fails has_ifa ; assert_fails has_ifb ; first rewrite proj2Z.eqb_eqa b by arith_fast | rewrite proj2Z.eqb_neqa b by arith_fast | case Z.eqb_speca b ; introend;cbv beta iota)

Prove a premise lhs = rhs whose right side the proof leaves open: compute lhs by cbn, with the facts, the wraps and the comparisons the facts decide, then take rhs to be what it computed.

345Ltac compute_premise  ≔
repeat progress cbn - [ load_bytes store_bytes ] ; try unfold sign_extension ; use_facts_goal ; cbn [ ptr_addr ptr_cap ] ; drop_wraps_goal ; decide_compares_goal ; reflexivity

Prove that an access check passes: through the capability of an object, read-only or not, whose region a fact gives, at an address arithmetic shows in its bounds and aligned (any address is, to 1).

353Ltac access_ok  ≔
cbn [ ptr_addr ptr_cap ] ; unfold raw_address ; drop_wraps_goal;match goal with|- access_res_ _ Obj_ _ _ _ = _⇒eapply access_obj|- access_res_ _ Ro_ _ _ _ = _⇒eapply access_ro_readend;first eassumption | apply Z.mod_1_r | cbn [ r_end ] ; arith

Prove one premise of a rule from StepRules.v: by computation for the op's classes, by a fact or computation for a block (a block_fact) and the return class, by the lemma of the op for a check, a load and a store, with access_ok after it, and otherwise by compute_premise.

366Ltac premise  ≔
match goal with|- general_op_ = true⇒reflexivity|- op_arg_classes_ _ = _⇒reflexivity|- width_value_class_ = _⇒reflexivity|- find_block_ _ = _⇒first eassumption | compute_premise|- fn_ret_ = _⇒first eassumption | compute_premise|- invalidate_locals_ _ = _⇒apply invalidate_none|- _ ≠ _⇒assumption|- _ ≤ _⇒first apply Z.leb_le ; reflexivity | arith|- op_resCheck_ _ _ _ _ _ _ = _⇒apply op_check ; access_ok|- op_resRawLoadLdL _ _ _ _ = _⇒apply op_raw_load ; access_ok|- store_resWl _ _ _ _ = _⇒apply store_res_long ; access_ok|- _ = _⇒compute_premiseend

Rewrite the step in the goal, at a frame that is not doomed, by the rule of its point: entering a block (out of fuel or not, the fuel split), an allocation, any other let, a flight, a raw store, a goto, a branch, or a return of an invocation with no caller.

387Ltac step_by_rule  ≔
match goal with|- context [step_ mkConfigmkFrame_ _ _ None Enter_ _ _ _ ?fuel]⇒case Z.eq_decfuel 0 ; intro;|match goal withH : fuel = 0 |- _⇒rewrite Hend;rewrite rule_out_of_fuel|erewrite rule_enter by premise|- context [step_ mkConfigmkFrame_ _ _ None Exec_ Let_ _ Alloc_ _ :: _ _ _ _ _]⇒erewrite rule_alloc by premise|- context [step_ mkConfigmkFrame_ _ _ None Exec_ Let_ _ _ _ :: _ _ _ _ _]⇒erewrite rule_let by premise|- context [step_ mkConfigmkFrame_ _ _ None Exec_ Flight_ _ _ _ :: _ _ _ _ _]⇒erewrite rule_flight by premise|- context [step_ mkConfigmkFrame_ _ _ None Exec_ RawStore_ _ _ _ :: _ _ _ _ _]⇒erewrite rule_raw_store by premise|- context [step_ mkConfigmkFrame_ _ _ None Exec_ [ ] JGoto_ _ _ _]⇒rewrite rule_goto|- context [step_ mkConfigmkFrame_ _ _ None Exec_ [ ] JBranch_ _ _ _ _ _]⇒erewrite rule_branch by premise|- context [step_ mkConfigmkFrame_ _ _ None Exec_ [ ] JRet_ [ ] _ _]⇒erewrite rule_ret by premise ; erewrite rule_return_top by premiseend

Take the step in the goal by its rule (step_by_rule), then the jump's target, and split where the step compared the input.

411Ltac take_step  ≔