1- (** * VCR-SEL-001 increments 1+2: Rocq obligations of the wired selector rule table
1+ (** * VCR-SEL-001 increments 1+2+3 : Rocq obligations of the wired selector rule table
22
33 One universally-quantified T1 theorem per rule in the checked-in DSL table
44 [crates/synth-synthesis/src/sel_dsl/mod.rs] (RULES), naming 1:1:
3939 generalized to universally-quantified registers
4040 ([synth_cmp_binop_proof_poly] below), with the same three manual
4141 variants (ne / lt_s / lt_u) the fixed-register proofs in
42- CorrectnessI32.v use, parameterized over registers verbatim. *)
42+ CorrectnessI32.v use, parameterized over registers verbatim.
43+
44+ INCREMENT 3 extends the DSL into the i64 register-pair family — the
45+ two-instruction pair shapes (ADDS+ADC / SUBS+SBC / ANDx2 / ORRx2 /
46+ EORx2) plus the single-instruction [I64SetCondZ] shape for i64.eqz.
47+ An i64 value lives in a (lo, hi) register pair, so each pair rule is
48+ quantified over SIX registers and its theorem proves BOTH result
49+ words. The pair shapes are where register generalization earns its
50+ keep: the low-half instruction writes [rdlo] before the high-half
51+ instruction reads [rnhi]/[rmhi], so a rule that could not state
52+ "the destination must not be clobbered before use" would be exactly
53+ how #632-class bugs happen. Three explicit aliasing hypotheses per
54+ pair rule (carried as [SideCondition::NotAlias] in the Rust table and
55+ enforced Ok-or-Err in the generated lowering):
56+
57+ - [rdhi <> rdlo] — the high write must not destroy the low result;
58+ - [rdlo <> rnhi] and [rdlo <> rmhi] — the low write must not
59+ clobber a high-half operand the second instruction still reads.
60+
61+ In-place lowering ([rdlo = rnlo], [rdhi = rnhi] — what
62+ [select_default]'s fixed R0:R1 += R2:R3 arms emit) satisfies all
63+ three, so one Qed per rule covers both selectors' assignments.
64+ Discharge: the value-level carry/borrow lemmas already proven for the
65+ fixed-register ancestors ([i64_add_via_adds_adc] /
66+ [i64_sub_via_subs_sbc] in ArmFlagLemmas.v; the halves-distribute
67+ combine lemmas in CorrectnessI64.v), applied under the generalized
68+ register bookkeeping — no new axiom. The theorem shape follows the
69+ CorrectnessI64.v ancestors: a value-level correspondence between the
70+ WASM-spec function ([I64.add] etc. on [combine_i32]-combined
71+ operands) and the ARM execution result, both halves pinned. *)
4372
4473From Stdlib Require Import List.
4574From Stdlib Require Import ZArith.
@@ -54,6 +83,10 @@ Require Import Synth.WASM.WasmSemantics.
5483Require Import Synth.Synth.Compilation.
5584Require Import Synth.Synth.Tactics.
5685Require Import Synth.ARM.ArmFlagLemmas.
86+ (* Increment 3: the halves-distribute combine lemmas
87+ ({and,or,xor}_{lo,hi}_combine) proven for the fixed-register i64
88+ bitwise ancestors live in CorrectnessI64.v — imported, not duplicated. *)
89+ Require Import Synth.Synth.CorrectnessI64.
5790Import ListNotations.
5891
5992Open Scope Z_scope.
@@ -455,3 +488,196 @@ Theorem rule_i32_ge_u_correct : forall wstate astate v1 v2 stack' rd rn rm,
455488 exec_program (rule_i32_ge_u rd rn rm) astate = Some astate' /\
456489 get_reg astate' rd = (if I32.geu v1 v2 then I32.one else I32.zero).
457490Proof . synth_cmp_binop_proof_poly flags_geu. Qed .
491+
492+ (** ** Increment 3: the i64 register-pair rule lowerings — 1:1 with
493+ sel_dsl::RULES / sel_dsl::generated. An i64 value is a (lo, hi)
494+ register pair; operand 1 is (rnlo, rnhi), operand 2 is (rmlo, rmhi),
495+ the result pair is (rdlo, rdhi). *)
496+
497+ Definition rule_i64_add (rdlo rdhi rnlo rnhi rmlo rmhi : arm_reg) : arm_program :=
498+ [ADDS rdlo rnlo (Reg rmlo); ADC rdhi rnhi (Reg rmhi)].
499+ Definition rule_i64_sub (rdlo rdhi rnlo rnhi rmlo rmhi : arm_reg) : arm_program :=
500+ [SUBS rdlo rnlo (Reg rmlo); SBC rdhi rnhi (Reg rmhi)].
501+ Definition rule_i64_and (rdlo rdhi rnlo rnhi rmlo rmhi : arm_reg) : arm_program :=
502+ [AND rdlo rnlo (Reg rmlo); AND rdhi rnhi (Reg rmhi)].
503+ Definition rule_i64_or (rdlo rdhi rnlo rnhi rmlo rmhi : arm_reg) : arm_program :=
504+ [ORR rdlo rnlo (Reg rmlo); ORR rdhi rnhi (Reg rmhi)].
505+ Definition rule_i64_xor (rdlo rdhi rnlo rnhi rmlo rmhi : arm_reg) : arm_program :=
506+ [EOR rdlo rnlo (Reg rmlo); EOR rdhi rnhi (Reg rmhi)].
507+
508+ (** i64.eqz — unary, single [I64SetCondZ] pseudo-op (the SetCondZ shape);
509+ the 0/1 result is a single i32 register, so no pair side conditions. *)
510+ Definition rule_i64_eqz (rd rnlo rnhi : arm_reg) : arm_program :=
511+ [I64SetCondZ rd rnlo rnhi].
512+
513+ (** ** Increment-3 discharge tactics.
514+
515+ [synth_i64_carry_pair_proof_poly] — the flags-coupled pair shapes
516+ (ADDS+ADC / SUBS+SBC): verbatim the stepped structure of the
517+ fixed-register [i64_add_correct] / [i64_sub_correct] proofs
518+ (CorrectnessI64.v) modulo (a) the six register binders, (b) the
519+ lowering-unfold target, and (c) the three aliasing hypotheses standing
520+ in for what was [discriminate] on concrete registers. Parameterized by
521+ the ArmFlagLemmas.v carry/borrow-propagation lemma.
522+
523+ [synth_i64_bitwise_pair_proof_poly] — the flag-free parallel-halves
524+ shapes (ANDx2/ORRx2/EORx2), parameterized by the lo/hi
525+ halves-distribute lemmas from CorrectnessI64.v. *)
526+
527+ Ltac synth_i64_carry_pair_proof_poly carry_lemma :=
528+ intros astate lo1 hi1 lo2 hi2 rdlo rdhi rnlo rnhi rmlo rmhi
529+ Hdd Hdnh Hdmh HR0 HR1 HR2 HR3;
530+ unfold rule_i64_add, rule_i64_sub;
531+ cbn [exec_program exec_instr eval_operand2];
532+ rewrite flags_set_flags_set_reg;
533+ rewrite flag_c_update_flags_arith;
534+ let Hpair := fresh "Hpair" in
535+ pose proof (carry_lemma lo1 hi1 lo2 hi2) as Hpair;
536+ let Hlo := fresh "Hlo" in
537+ let Hhi := fresh "Hhi" in
538+ destruct Hpair as [Hlo Hhi];
539+ eexists; split;
540+ [ reflexivity
541+ | split;
542+ [ (* lo word: the high-half write must not have destroyed it. *)
543+ rewrite (get_set_reg_neq _ rdhi rdlo) by exact Hdd;
544+ rewrite get_reg_set_flags;
545+ rewrite get_set_reg_eq;
546+ rewrite HR0, HR2; exact Hlo
547+ | (* hi word: the high-half instruction read its operands from the
548+ post-low-half state, where rdlo was already written. *)
549+ rewrite get_set_reg_eq;
550+ rewrite !get_reg_set_flags;
551+ rewrite (get_set_reg_neq astate rdlo rnhi) by exact Hdnh;
552+ rewrite (get_set_reg_neq astate rdlo rmhi) by exact Hdmh;
553+ rewrite HR0, HR1, HR2, HR3; exact Hhi ] ].
554+
555+ Ltac synth_i64_bitwise_pair_proof_poly lo_lemma hi_lemma :=
556+ intros astate lo1 hi1 lo2 hi2 rdlo rdhi rnlo rnhi rmlo rmhi
557+ Hdd Hdnh Hdmh HR0 HR1 HR2 HR3;
558+ unfold rule_i64_and, rule_i64_or, rule_i64_xor;
559+ cbn [exec_program exec_instr eval_operand2];
560+ eexists; split;
561+ [ reflexivity
562+ | split;
563+ [ rewrite (get_set_reg_neq _ rdhi rdlo) by exact Hdd;
564+ rewrite get_set_reg_eq;
565+ rewrite HR0, HR2; apply lo_lemma
566+ | rewrite get_set_reg_eq;
567+ rewrite (get_set_reg_neq astate rdlo rnhi) by exact Hdnh;
568+ rewrite (get_set_reg_neq astate rdlo rmhi) by exact Hdmh;
569+ rewrite HR1, HR3; apply hi_lemma ] ].
570+
571+ (** ** Increment-3 pair theorems — quantified over all SIX registers,
572+ under the three explicit aliasing hypotheses the rule table carries.
573+ Pair-result T1: BOTH words of the result are proven. *)
574+
575+ Theorem rule_i64_add_correct :
576+ forall astate lo1 hi1 lo2 hi2 rdlo rdhi rnlo rnhi rmlo rmhi,
577+ rdhi <> rdlo -> (* high write must not destroy the low result *)
578+ rdlo <> rnhi -> (* low write must not clobber operand-1's high half *)
579+ rdlo <> rmhi -> (* low write must not clobber operand-2's high half *)
580+ get_reg astate rnlo = lo1 ->
581+ get_reg astate rnhi = hi1 ->
582+ get_reg astate rmlo = lo2 ->
583+ get_reg astate rmhi = hi2 ->
584+ exists astate',
585+ exec_program (rule_i64_add rdlo rdhi rnlo rnhi rmlo rmhi) astate
586+ = Some astate' /\
587+ get_reg astate' rdlo = lo_of_i64 (I64.add (combine_i32 lo1 hi1)
588+ (combine_i32 lo2 hi2)) /\
589+ get_reg astate' rdhi = hi_of_i64 (I64.add (combine_i32 lo1 hi1)
590+ (combine_i32 lo2 hi2)).
591+ Proof . synth_i64_carry_pair_proof_poly i64_add_via_adds_adc. Qed .
592+
593+ Theorem rule_i64_sub_correct :
594+ forall astate lo1 hi1 lo2 hi2 rdlo rdhi rnlo rnhi rmlo rmhi,
595+ rdhi <> rdlo ->
596+ rdlo <> rnhi ->
597+ rdlo <> rmhi ->
598+ get_reg astate rnlo = lo1 ->
599+ get_reg astate rnhi = hi1 ->
600+ get_reg astate rmlo = lo2 ->
601+ get_reg astate rmhi = hi2 ->
602+ exists astate',
603+ exec_program (rule_i64_sub rdlo rdhi rnlo rnhi rmlo rmhi) astate
604+ = Some astate' /\
605+ get_reg astate' rdlo = lo_of_i64 (I64.sub (combine_i32 lo1 hi1)
606+ (combine_i32 lo2 hi2)) /\
607+ get_reg astate' rdhi = hi_of_i64 (I64.sub (combine_i32 lo1 hi1)
608+ (combine_i32 lo2 hi2)).
609+ Proof . synth_i64_carry_pair_proof_poly i64_sub_via_subs_sbc. Qed .
610+
611+ Theorem rule_i64_and_correct :
612+ forall astate lo1 hi1 lo2 hi2 rdlo rdhi rnlo rnhi rmlo rmhi,
613+ rdhi <> rdlo ->
614+ rdlo <> rnhi ->
615+ rdlo <> rmhi ->
616+ get_reg astate rnlo = lo1 ->
617+ get_reg astate rnhi = hi1 ->
618+ get_reg astate rmlo = lo2 ->
619+ get_reg astate rmhi = hi2 ->
620+ exists astate',
621+ exec_program (rule_i64_and rdlo rdhi rnlo rnhi rmlo rmhi) astate
622+ = Some astate' /\
623+ get_reg astate' rdlo = lo_of_i64 (I64.and (combine_i32 lo1 hi1)
624+ (combine_i32 lo2 hi2)) /\
625+ get_reg astate' rdhi = hi_of_i64 (I64.and (combine_i32 lo1 hi1)
626+ (combine_i32 lo2 hi2)).
627+ Proof . synth_i64_bitwise_pair_proof_poly and_lo_combine and_hi_combine. Qed .
628+
629+ Theorem rule_i64_or_correct :
630+ forall astate lo1 hi1 lo2 hi2 rdlo rdhi rnlo rnhi rmlo rmhi,
631+ rdhi <> rdlo ->
632+ rdlo <> rnhi ->
633+ rdlo <> rmhi ->
634+ get_reg astate rnlo = lo1 ->
635+ get_reg astate rnhi = hi1 ->
636+ get_reg astate rmlo = lo2 ->
637+ get_reg astate rmhi = hi2 ->
638+ exists astate',
639+ exec_program (rule_i64_or rdlo rdhi rnlo rnhi rmlo rmhi) astate
640+ = Some astate' /\
641+ get_reg astate' rdlo = lo_of_i64 (I64.or (combine_i32 lo1 hi1)
642+ (combine_i32 lo2 hi2)) /\
643+ get_reg astate' rdhi = hi_of_i64 (I64.or (combine_i32 lo1 hi1)
644+ (combine_i32 lo2 hi2)).
645+ Proof . synth_i64_bitwise_pair_proof_poly or_lo_combine or_hi_combine. Qed .
646+
647+ Theorem rule_i64_xor_correct :
648+ forall astate lo1 hi1 lo2 hi2 rdlo rdhi rnlo rnhi rmlo rmhi,
649+ rdhi <> rdlo ->
650+ rdlo <> rnhi ->
651+ rdlo <> rmhi ->
652+ get_reg astate rnlo = lo1 ->
653+ get_reg astate rnhi = hi1 ->
654+ get_reg astate rmlo = lo2 ->
655+ get_reg astate rmhi = hi2 ->
656+ exists astate',
657+ exec_program (rule_i64_xor rdlo rdhi rnlo rnhi rmlo rmhi) astate
658+ = Some astate' /\
659+ get_reg astate' rdlo = lo_of_i64 (I64.xor (combine_i32 lo1 hi1)
660+ (combine_i32 lo2 hi2)) /\
661+ get_reg astate' rdhi = hi_of_i64 (I64.xor (combine_i32 lo1 hi1)
662+ (combine_i32 lo2 hi2)).
663+ Proof . synth_i64_bitwise_pair_proof_poly xor_lo_combine xor_hi_combine. Qed .
664+
665+ (** i64.eqz — the SetCondZ shape. Single instruction, no pair side
666+ conditions (the pseudo-op reads both operand halves before writing
667+ [rd], so every rd/rnlo/rnhi aliasing is admitted). Value-level T1 via
668+ the [i64_setcondz_bits_spec] axiom, exactly like the fixed-register
669+ bit-manipulation ancestors (i64_clz/ctz/popcnt in CorrectnessI64.v). *)
670+ Theorem rule_i64_eqz_correct : forall astate lo hi rd rnlo rnhi,
671+ get_reg astate rnlo = lo ->
672+ get_reg astate rnhi = hi ->
673+ exists astate',
674+ exec_program (rule_i64_eqz rd rnlo rnhi) astate = Some astate' /\
675+ get_reg astate' rd =
676+ (if I64.eq (combine_i32 lo hi) I64.zero then I32.one else I32.zero).
677+ Proof .
678+ intros astate lo hi rd rnlo rnhi HR0 HR1.
679+ unfold rule_i64_eqz; simpl.
680+ rewrite HR0, HR1.
681+ rewrite i64_setcondz_bits_spec.
682+ eexists. split; [reflexivity | apply get_set_reg_eq].
683+ Qed .
0 commit comments