include "joint/joint_semantics.ma". include alias "common/Identifiers.ma". include "utilities/hide.ma". include "ASM/BitVectorTrie.ma". include "joint/TranslateUtils.ma". include "common/ExtraMonads.ma". include "common/extraGlobalenvs.ma". record hw_register_env : Type[0] ≝ { reg_env : BitVectorTrie beval 6 ; other_bit : bebit }. definition empty_hw_register_env: hw_register_env ≝ mk_hw_register_env (Stub …) BBundef. include alias "ASM/BitVectorTrie.ma". definition hwreg_retrieve: hw_register_env → Register → beval ≝ λenv,r. lookup … (bitvector_of_register r) (reg_env env) BVundef. definition hwreg_store: Register → beval → hw_register_env → hw_register_env ≝ λr,v,env.mk_hw_register_env (insert … (bitvector_of_register r) v (reg_env env)) (other_bit env). definition hwreg_set_other : bebit → hw_register_env → hw_register_env ≝ λv,env.mk_hw_register_env (reg_env env) v. definition hwreg_retrieve_sp : hw_register_env → res xpointer ≝ λenv. let spl ≝ hwreg_retrieve env RegisterSPL in let sph ≝ hwreg_retrieve env RegisterSPH in ! ptr ← pointer_of_address 〈spl, sph〉 ; match ptype ptr return λx.ptype ptr = x → res xpointer with [ XData ⇒ λprf.return «ptr, prf» | _ ⇒ λ_.Error … [MSG BadPointer] ] (refl …). definition hwreg_store_sp : hw_register_env → xpointer → hw_register_env ≝ λenv,sp. let 〈spl, sph〉 ≝ beval_pair_of_pointer sp in hwreg_store RegisterSPH sph (hwreg_store RegisterSPL spl env). (*** Store/retrieve on pseudo-registers ***) include alias "common/Identifiers.ma". record reg_sp : Type[0] ≝ { reg_sp_env :> identifier_map RegisterTag beval ; stackp : xpointer }. definition reg_store ≝ λreg,v,locals. update RegisterTag beval locals reg v. definition reg_retrieve : register_env beval → register → res beval ≝ λlocals,reg. opt_to_res … [MSG BadRegister; CTX ? reg] (lookup … locals reg). definition reg_sp_store ≝ λreg,v,locals. ! locals' ← reg_store reg v (reg_sp_env locals) ; return mk_reg_sp locals' (stackp locals). definition reg_sp_retrieve ≝ λlocals.reg_retrieve locals. (*** Store/retrieve on hardware registers ***) definition init_hw_register_env: xpointer → hw_register_env ≝ λsp.hwreg_store_sp empty_hw_register_env sp. (******************************** GRAPHS **************************************) record sem_graph_params : Type[1] ≝ { sgp_pars : unserialized_params ; sgp_sup : ∀F.sem_unserialized_params sgp_pars F }. definition sem_graph_params_to_graph_params : ∀pars : sem_graph_params.graph_params ≝ λpars.mk_graph_params (sgp_pars pars). coercion graph_params_from_sem_graph_params nocomposites : ∀pars : sem_graph_params. graph_params ≝ sem_graph_params_to_graph_params on _pars : sem_graph_params to graph_params. definition sem_graph_params_to_sem_params : ∀pars : sem_graph_params. sem_params ≝ λpars. mk_sem_params (pars : graph_params) (sgp_sup pars ?) (word_of_identifier ?) (an_identifier ?) ? (refl …). * #p % qed. coercion sem_params_from_sem_graph_params : ∀pars : sem_graph_params. sem_params ≝ sem_graph_params_to_sem_params on _pars : sem_graph_params to sem_params. (******************************** LINEAR **************************************) lemma succ_pos_of_nat_of_pos : ∀p.succ_pos_of_nat (nat_of_pos p) = succ p. @pos_elim [%] #p #IH >nat_possucc whd in ⊢ (??%?); >IH % qed. record sem_lin_params : Type[1] ≝ { slp_pars : unserialized_params ; slp_sup : ∀F.sem_unserialized_params slp_pars F }. definition sem_lin_params_to_lin_params : ∀pars : sem_lin_params.lin_params ≝ λpars.mk_lin_params (slp_pars pars). coercion lin_params_from_sem_lin_params nocomposites : ∀pars : sem_lin_params. lin_params ≝ sem_lin_params_to_lin_params on _pars : sem_lin_params to lin_params. definition sem_lin_params_to_sem_params : ∀pars : sem_lin_params. sem_params ≝ λpars. mk_sem_params (pars : lin_params) (slp_sup pars ?) succ_pos_of_nat (λp.pred (nat_of_pos p)) ??. [ #n >nat_succ_pos % | @pos_cases [%] #p >nat_possucc @succ_pos_of_nat_of_pos ] qed. coercion sem_params_from_sem_lin_params : ∀pars : sem_lin_params. sem_params ≝ sem_lin_params_to_sem_params on _pars : sem_lin_params to sem_params. lemma fetch_statement_eq : ∀p:sem_params.∀g. ∀ge : genv_t (joint_function p g).∀ptr : program_counter. ∀i,fn,s. fetch_internal_function … ge (pc_block ptr) = OK … 〈i, fn〉 → let pt ≝ point_of_pc … ptr in stmt_at … (joint_if_code … fn) pt = Some ? s → fetch_statement … ge ptr = OK … 〈i, fn, s〉. #p #g #ge #ptr #i #f #s #EQ1 #EQ2 whd in match fetch_statement; normalize nodelta >EQ1 >m_return_bind >EQ2 % qed. lemma fetch_statement_inv : ∀p:sem_params.∀g. ∀ge : genv_t (joint_function p g).∀ptr : program_counter. ∀i,fn,s. fetch_statement … ge ptr = OK … 〈i, fn, s〉 → fetch_internal_function … ge (pc_block ptr) = OK … 〈i, fn〉 ∧ let pt ≝ point_of_pc p ptr in stmt_at … (joint_if_code … fn) pt = Some ? s. #p #g #ge #ptr #i #fn #s #H elim (bind_inversion ????? H) * #i #f' * #EQ1 -H #H elim (bind_inversion ????? H) #s' * #H lapply (opt_eq_from_res ???? H) -H #EQ2 whd in ⊢ (??%%→?); #EQ destruct(EQ) %{EQ1 EQ2} qed. definition pm_P : ∀A,B.∀P : A → B → Prop.positive_map A → positive_map B → Prop ≝ λA,B,P,mA,mB. ∀p. match lookup_opt … p mA with [ Some a ⇒ match lookup_opt … p mB with [ Some b ⇒ P a b | _ ⇒ False ] | _ ⇒ match lookup_opt … p mB with [ Some _ ⇒ False | _ ⇒ True ] ]. definition match_funct_map : ∀M : matching. ∀vars.positive_map (m_A M vars) → positive_map (m_B M vars) → Prop ≝ λM,vars.pm_P … (match_fundef M ?). lemma pm_P_insert : ∀A,B,P. ∀p,a1,a2,m1,m2.P a1 a2 → pm_P A B P m1 m2 → pm_P … P (insert … p a1 m1) (insert … p a2 m2). #A #B #P #p #a1 #a2 #m1 #m2 #Pa1a2 #Pm1m2 #p' @(eqb_elim p' p) #H destruct [ >lookup_opt_insert_hit >lookup_opt_insert_hit @Pa1a2 | >(lookup_opt_insert_miss … a1 … H) >(lookup_opt_insert_miss … a2 … H) @Pm1m2 ] qed. lemma pm_P_set_None : ∀A,B,P. ∀p,m1,m2.pm_P A B P m1 m2 → pm_P … P (pm_set … p (None ?) m1) (pm_set … p (None ?) m2). #A #B #P #p #m1 #m2 #Pm1m2 #p' @(eqb_elim p' p) #H destruct [ >lookup_opt_pm_set_hit >lookup_opt_pm_set_hit % | >(lookup_opt_pm_set_miss … H) >(lookup_opt_pm_set_miss … H) @Pm1m2 ] qed. record match_genv_t (M : matching) (vars : list ident) (ge1 : genv_t (m_A M vars)) (ge2 : genv_t (m_B M vars)) : Prop ≝ { symbols_eq : symbols … ge1 = symbols … ge2 ; nextfunction_eq : nextfunction … ge1 = nextfunction … ge2 ; functions_match : pm_P … (match_fundef M ?) (functions … ge1) (functions … ge2) }. lemma add_globals_match : ∀M : matching.∀initV,initW. ∀vars1,vars2. ∀vars. (* let vars1' ≝ map … (λx.\fst (\fst x)) vars1 in let vars2' ≝ map … (λx.\fst (\fst x)) vars2 in *) ∀env_mem1 : genv_t (m_A M vars) × mem. ∀env_mem2 : genv_t (m_B M vars) × mem. All2 (ident×region×(m_V M)) (ident×region×(m_W M)) (match_var_entry M) vars1 vars2 → nextblock … (\snd env_mem1) = nextblock … (\snd env_mem2) → match_genv_t M vars (\fst env_mem1) (\fst env_mem2) → match_genv_t M vars (\fst (add_globals … initV env_mem1 vars1)) (\fst (add_globals … initW env_mem2 vars2)). #M #init1 #init2 #vars1 elim vars1 -vars1 [2: ** #id1 #r1 #v1 #tl1 #IH ] * [2,4: ** #id2 #r2 #v2 #tl2 ] #vars * #env1 #mem1 * #env2 #mem2 * [2: #_ #H @H ] #H inversion H #id' #r' #v1' #v2' #P #EQ1 #EQ2 #_ destruct -H #H #EQnxtbl #G @(IH tl2 … H) cases mem1 in EQnxtbl; -mem1 #mem1 #nbl1 #prf1 cases mem2 -mem2 #mem2 #nbl2 #prf2 #EQ destruct [ % ] normalize nodelta % whd in match add_symbol; whd in match drop_fn; normalize nodelta [ >(symbols_eq … G) % | @(nextfunction_eq … G) | >(symbols_eq … G) cases (lookup ? block ??) normalize nodelta [2: ** [2,3: #p] normalize nodelta ] try @pm_P_set_None @(functions_match … G) ] qed. lemma add_functs_match : ∀M : matching. ∀vars. ∀functs1,functs2. (* let vars1' ≝ map … (λx.\fst (\fst x)) vars1 in let vars2' ≝ map … (λx.\fst (\fst x)) vars2 in *) ∀env1 : genv_t (m_A M vars). ∀env2 : genv_t (m_B M vars). All2 … (match_funct_entry M vars vars) functs1 functs2 → match_genv_t M vars env1 env2 → match_genv_t M vars (add_functs … env1 functs1) (add_functs … env2 functs2). #M #vars #functs1 elim functs1 -functs1 [2: * #id1 #f1 #tl1 #IH ] * [2,4: * #id2 #f2 #tl2 ] #env1 #env2 * [2: #H @H ] #H cut (id1 = id2 ∧ match_fundef M … f1 f2) [ @(match_funct_entry_inv M ??????? H) #v #i #f1 #f2 #H %{H} % ] * #EQ destruct #Pf1f2 #Ptl1tl2 #G lapply (IH … Ptl1tl2 G) -G #G % whd in match (add_functs ?? (? :: tl1)); whd in match (add_functs ?? (? :: tl2)); change with (add_functs ?? tl1) in match (foldr ???? tl1); change with (add_functs ?? tl2) in match (foldr ???? tl2); whd in match drop_fn; normalize nodelta [ >(nextfunction_eq … G) >(symbols_eq … G) % | >(nextfunction_eq … G) % | >(symbols_eq … G) cases (lookup ? block ??) normalize nodelta [2: ** [2,3: #p] normalize nodelta ] >(nextfunction_eq … G) @(pm_P_insert … Pf1f2) try @pm_P_set_None @(functions_match … G) ] qed. lemma globalenv_match: ∀M:matching.∀initV,initW. (∀v,w. match_var_entry M v w → size_init_data_list (initV (\snd v)) = size_init_data_list (initW (\snd w))) → ∀p: program (m_A M) (m_V M). ∀p': program (m_B M) (m_W M). ∀MATCH:match_program … M p p'. match_genv_t M ? (globalenv … initV p) (globalenv … initW p' ⌈prog_var_names … p' ↦ prog_var_names … p⌉). [2: @sym_eq @(matching_vars … (mp_vars … MATCH)) ] * #A #B #V #W #Pf #Pv #iV #iW #init_eq * #vars1 #functs1 #main1 * #vars2 #functs2 #main2 * #Mvars #Mfns #EQmain destruct lapply (sym_eq ????) whd in match prog_var_names in functs2 Mfns ⊢ %; normalize nodelta in functs2 Mfns ⊢ %; #E lapply Mfns lapply functs2 -functs2 lapply Mvars -Mvars lapply init_eq -init_eq whd in match globalenv; whd in match globalenv_allocmem; normalize nodelta cases daemon (* TODO I hate coercions *) qed. lemma symbols_transf: ∀A,B,V,init.∀prog_in : program A V. ∀trans : ∀vars.A vars → B vars. let prog_out ≝ transform_program … prog_in trans in symbols … (globalenv … init prog_out) = symbols … (globalenv … init prog_in). #A #B #V #iV #p #tf whd lapply (transform_program_match … tf p) #MATCH >(symbols_eq … (globalenv_match … MATCH)) [2: @iV |3: #v #w * // ] lapply (sym_eq ????) whd in match (prog_var_names ???); whd in match (prog_vars B ??); #E >(K ?? E) normalize nodelta % qed. lemma functions_transf: ∀A,B,V,init.∀prog_in : program A V. ∀trans : ∀vars.A vars → B vars. let prog_out ≝ transform_program … prog_in trans in ∀p.lookup_opt … p (functions … (globalenv … init prog_out)) = option_map ?? (trans ?) (lookup_opt … p (functions … (globalenv … init prog_in))). #A #B #V #iV #p #tf #n lapply (transform_program_match … tf p) #MATCH whd in match globalenv; whd in match globalenv_allocmem; normalize nodelta lapply (functions_match … (globalenv_match … MATCH) n) [ @iV | @iV | #v #w * // ] lapply (sym_eq ????) whd in match (prog_var_names ???); whd in match (prog_vars B ??); #E >(K ?? E) normalize nodelta cases (lookup_opt ???) [2: #x ] normalize nodelta cases (lookup_opt ???) [2,4: #y ] normalize nodelta [ #EQ (symbols_transf … tf) % qed. include alias "common/Pointers.ma". lemma fetch_function_transf : ∀A,B,V,init.∀prog_in : program A V. ∀trans : ∀vars.A vars → B vars. let prog_out ≝ transform_program … prog_in trans in ∀bl. fetch_function … (globalenv … init prog_out) bl = ! 〈i, f〉 ← fetch_function … (globalenv … init prog_in) bl ; return 〈i, trans … f〉. #A #B #V #init #prog_in #trans #bl whd in match fetch_function; normalize nodelta >(symbol_for_block_transf A B V init prog_in trans) cases (symbol_for_block ???) [ % ] #id >m_return_bind >m_return_bind whd in match find_funct_ptr; normalize nodelta whd in match (block_region (pi1 ?? bl)); cases (block_id (pi1 ?? bl)) [2,3: #p] normalize nodelta try % >(functions_transf A B V init prog_in trans p) cases (lookup_opt ???) // qed. lemma fetch_internal_function_transf : ∀A,B.∀prog_in : program (λvars.fundef (A vars)) ℕ. ∀trans : ∀vars.A vars → B vars. let prog_out ≝ transform_program … prog_in (λvars.transf_fundef … (trans vars)) in ∀bl. fetch_internal_function … (globalenv_noinit … prog_out) bl = ! 〈i, f〉 ← fetch_internal_function … (globalenv_noinit … prog_in) bl ; return 〈i, trans … f〉. #A #B #prog #trans #bl whd in match fetch_internal_function; normalize nodelta >(fetch_function_transf … prog) cases (fetch_function ???) [ * #id * #f % | #e % ] qed. include alias "utilities/binary/positive.ma". lemma pm_map_add_ind : ∀A.∀P : positive_map A → Prop. (∀m.(∀p.lookup_opt A p m = None ?) → P m) → (∀m,p,v.P m → lookup_opt A p m = None ? → P (insert A p v m)) → ∀m.P m. #A #P #H1 #H2 #m lapply H2 lapply H1 lapply P -P elim m -m [ #P #H1 #_ @H1 #p % ] #o #l #r #IHl #IHr #P #H1 #H2 @IHl #l' [ #empty_l' @IHr #r' [ #empty_r' cases o [ @H1 * try #p normalize // ] #v change with (P (insert ? one v (pm_node ? (None ?) ??))) @H2 [ @H1 * try #p normalize // | % ] ] ] #p #v #H #G [ change with (P (insert ? (p1 p) v (pm_node ? ???))) | change with (P (insert ? (p0 p) v (pm_node ? ???))) ] @H2 assumption qed. include alias "basics/logic.ma". include alias "common/PositiveMap.ma". lemma swap_neg : ∀A,B : Prop.(A → B) → ¬B → ¬A. #A #B #impl * #Bf % #At @Bf @impl @At qed-. definition b_graph_transform_program_props ≝ λsrc,dst : sem_graph_params. λdata,prog_in. λinit_regs : block → list register. λf_lbls : block → label → option (list label). λf_regs : block → label → option (list register). let prog_out ≝ b_graph_transform_program src dst data prog_in in let src_genv ≝ globalenv_noinit ? prog_in in let dst_genv ≝ globalenv_noinit ? prog_out in ∀bl,def_in. find_funct_ptr … src_genv bl = return (Internal ? def_in) → ∃init_data,def_out. find_funct_ptr … dst_genv bl = return (Internal ? def_out) ∧ bind_new_instantiates … init_data (data … def_in) (init_regs bl) ∧ b_graph_translate_props src dst ? init_data (init_regs bl) def_in def_out (f_lbls bl) (f_regs bl). lemma make_b_graph_transform_program_props : ∀src,dst:sem_graph_params. ∀data : ∀globals.joint_closed_internal_function src globals → bind_new register (b_graph_translate_data src dst globals). ∀prog_in : joint_program src. let prog_out ≝ b_graph_transform_program … data prog_in in let src_genv ≝ globalenv_noinit ? prog_in in let dst_genv ≝ globalenv_noinit ? prog_out in ∃init_regs : block → list register. ∃f_lbls : block → label → option (list label). ∃f_regs : block → label → option (list register). b_graph_transform_program_props src dst data prog_in init_regs f_lbls f_regs. #src #dst #data #prog_in whd in match b_graph_transform_program_props; normalize nodelta letin prog_out ≝ (b_graph_transform_program … data prog_in) letin src_genv ≝ (globalenv_noinit ? prog_in) letin dst_genv ≝ (globalenv_noinit ? prog_out) cut (∀p.lookup_opt ? p (functions ? dst_genv) = option_map ?? (transf_fundef … (λf.b_graph_translate … (data … f) f)) (lookup_opt ? p (functions ? src_genv))) [ @functions_transf ] cases dst_genv #functs2 #nextf2 #symbols2 #H2 cases src_genv #functs1 #nextf1 #symbols1 #H1 lapply H2 lapply H1 lapply functs2 @(pm_map_add_ind … functs1) -functs1 -functs2 #functs1 [ #functs1_empty | #p #f #IH #p_not_in ] #functs2 #H1 #H2 #transf [ %{(λ_.[ ])} %{(λ_.λ_.None ?)} %{(λ_.λ_.None ?)} #bl #def_in #ABS @⊥ lapply ABS -ABS whd in match find_funct_ptr; normalize nodelta whd in match (block_region bl); cases (block_id bl) normalize nodelta [2,3: #p [2: >functs1_empty ]] normalize #ABS destruct ] cases (IH (pm_set … p (None ?) functs2) ???) [2: @hide_prf #b #G @(H1 b ?) @(swap_neg … G) -G @(eqb_elim b p) [ #EQ destruct >lookup_opt_insert_hit #ABS destruct ] #NEQ >(lookup_opt_insert_miss ? f b p … NEQ) #H @H |3: @hide_prf #b #G @(H2 b ?) @(swap_neg … G) -G @(eqb_elim b p) [ #EQ destruct >lookup_opt_pm_set_hit #_ % ] #NEQ >(lookup_opt_pm_set_miss … b p … NEQ) #H @H |4: #b @(eqb_elim b p) [ #EQ destruct >lookup_opt_pm_set_hit >p_not_in % ] #NEQ >(lookup_opt_pm_set_miss … b p … NEQ) >transf >(lookup_opt_insert_miss ? f b p … NEQ) % ] -IH cases f in H1 transf; -f #f #H1 #transf #init_regs * #f_lbls * #f_regs #props [ (* internal *) letin upd ≝ (λA : Type[0].λf : block → A. λid.λv,id'. if eq_block id id' then v else f id') cases (pi2 ?? (b_graph_translate … (data … f) f)) #loc_data * #loc_init_regs * #loc_f_lbls * #loc_f_regs * #inst_loc_data #loc_props letin bl ≝ (mk_block (neg p)) %{(upd … init_regs bl loc_init_regs)} %{(upd … f_lbls bl loc_f_lbls)} %{(upd … f_regs bl loc_f_regs)} | (* external, nothing changes *) %{init_regs} %{f_lbls} %{f_regs} ] * #p' #def_in whd in match find_funct_ptr; normalize nodelta whd in match (block_region (mk_block p')); cases p' -p' [2,3,5,6: #p' ] normalize nodelta [1,3,5,6: #ABS normalize in ABS; destruct] @(eqb_elim p' p) #pp' destruct [1,3: >lookup_opt_insert_hit whd in ⊢ (??%%→?); #EQ destruct(EQ) %{loc_data} % [2: % [ % ] [ >transf >lookup_opt_insert_hit % |*: >eq_block_b_b assumption ] |] |*: >(lookup_opt_insert_miss ???? functs1 … pp') [ @eq_block_elim [ #EQ destruct cases (absurd ?? pp') % ] #_ normalize nodelta] #EQ lapply (props (mk_block (neg p')) def_in EQ) whd in match find_funct_ptr; normalize nodelta >(lookup_opt_pm_set_miss … functs2 … pp') #H @H ] qed. lemma b_graph_transform_program_fetch_internal_function : ∀src,dst:sem_graph_params. ∀data : ∀globals.joint_closed_internal_function src globals → bind_new register (b_graph_translate_data src dst globals). ∀prog_in : joint_program src. ∀init_regs : block → list register. ∀f_lbls : block → label → option (list label). ∀f_regs : block → label → option (list register). b_graph_transform_program_props … data prog_in init_regs f_lbls f_regs → let prog_out ≝ b_graph_transform_program … data prog_in in let src_genv ≝ globalenv_noinit ? prog_in in let dst_genv ≝ globalenv_noinit ? prog_out in ∀bl,id,def_in. fetch_internal_function … src_genv bl = return 〈id, def_in〉 → ∃init_data,def_out. fetch_internal_function … dst_genv bl = return 〈id, def_out〉 ∧ bind_new_instantiates … init_data (data … def_in) (init_regs bl) ∧ b_graph_translate_props src dst ? init_data (init_regs bl) def_in def_out (f_lbls bl) (f_regs bl). #src #dst #data #prog_in #init_regs #f_lbls #f_regs #props #bl #id #def_in #H @('bind_inversion H) * #id' * #def_in' -H normalize nodelta #H whd in ⊢ (??%%→?); #EQ destruct @('bind_inversion (opt_eq_from_res … H)) -H #id' #EQ1 #H @('bind_inversion H) -H #def_in' #H whd in ⊢ (??%%→?); #EQ destruct cases (props … H) #loc_data * #def_out ** #H1 #H2 #H3 %{loc_data} %{def_out} %{H3} %{H2} whd in match fetch_internal_function; whd in match fetch_function; normalize nodelta >symbol_for_block_transf >EQ1 >m_return_bind >H1 % qed. lemma b_graph_transform_program_fetch_statement : ∀src,dst:sem_graph_params. ∀data : ∀globals.joint_closed_internal_function src globals → bind_new register (b_graph_translate_data src dst globals). ∀prog_in : joint_program src. ∀init_regs : block → list register. ∀f_lbls : block → label → option (list label). ∀f_regs : block → label → option (list register). b_graph_transform_program_props … data prog_in init_regs f_lbls f_regs → let prog_out ≝ b_graph_transform_program … data prog_in in let src_genv ≝ globalenv_noinit ? prog_in in let dst_genv ≝ globalenv_noinit ? prog_out in ∀pc,id,def_in,s. fetch_statement … src_genv pc = return 〈id, def_in, s〉 → ∃init_data,def_out. let bl ≝ pc_block pc in fetch_internal_function … dst_genv bl = return 〈id, def_out〉 ∧ bind_new_instantiates … init_data (data … def_in) (init_regs bl) ∧ let l ≝ point_of_pc dst pc in ∃lbls,regs. f_lbls bl l = Some ? lbls ∧ f_regs bl l = Some ? regs ∧ match s with [ sequential s' nxt ⇒ let block ≝ if eq_identifier … (joint_if_entry … def_in) l then append_seq_list … (f_step … init_data l s') (added_prologue … init_data) else f_step … init_data l s' in l ~❨block, l::lbls, regs❩~> nxt in joint_if_code … def_out | final s' ⇒ l ~❨f_fin … init_data l s', l::lbls, regs❩~> it in joint_if_code … def_out | FCOND abs _ _ _ ⇒ Ⓧabs ]. #src #dst #data #prog_in #init_regs #f_lbls #f_regs #props #pc #id #def_in #s #H @('bind_inversion H) * #id' #def_in' -H #EQfif #H @('bind_inversion H) -H #s #H lapply (opt_eq_from_res ???? H) -H #EQstmt_at whd in ⊢ (??%%→?); #EQ destruct cases (b_graph_transform_program_fetch_internal_function … props … EQfif) #a * #b ** #H1 #H2 #H3 %{a} %{b} % [ %{H1 H2} | @(multi_fetch_ok … H3 … EQstmt_at) ] qed.