From cdb1c27b340b168396aea79898e7303dade97b26 Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Tue, 11 Mar 2025 23:51:09 -0700 Subject: [PATCH 1/8] Add --asm-node-reveal-depth --- src/Assembly/Symbolic.v | 13 ++++++++++++- src/CLI.v | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Assembly/Symbolic.v b/src/Assembly/Symbolic.v index 9d5a7dcb74..fd1fc42f17 100644 --- a/src/Assembly/Symbolic.v +++ b/src/Assembly/Symbolic.v @@ -404,6 +404,8 @@ Module Export Options. Class rewriting_passes_opt := rewriting_passes : list rewrite_pass. (** Should we symex the assembly first, even though this may be more inefficient? *) Class debug_symex_asm_first_opt := debug_symex_asm_first : bool. + (** How deep should we reveal nodes? *) + Class node_reveal_depth_opt := node_reveal_depth : nat. Definition default_rewriting_passes {rewriting_pipeline : rewriting_pipeline_opt} {rewriting_pass_filter : rewriting_pass_filter_opt} @@ -414,12 +416,16 @@ Module Export Options. { asm_rewriting_pipeline : rewriting_pipeline_opt ; asm_rewriting_pass_filter : rewriting_pass_filter_opt ; asm_debug_symex_asm_first : debug_symex_asm_first_opt + ; asm_node_reveal_depth : node_reveal_depth_opt }. + Definition default_node_reveal_depth := 3%nat. + (* This holds the list of computed options, which are passed around between methods *) Class symbolic_options_computed_opt := { asm_rewriting_passes : rewriting_passes_opt ; asm_debug_symex_asm_first_computed : debug_symex_asm_first_opt + ; asm_node_reveal_depth_computed : node_reveal_depth_opt }. (* N.B. The default rewriting pass filter should not be changed here, but instead changed in CLI.v where it is derived from a default string *) @@ -427,6 +433,7 @@ Module Export Options. := {| asm_rewriting_pipeline := default_rewrite_pass_order ; asm_rewriting_pass_filter := fun _ => true ; asm_debug_symex_asm_first := false + ; asm_node_reveal_depth := default_node_reveal_depth |}. End Options. Module Export Hints. @@ -439,6 +446,8 @@ Module Export Hints. asm_rewriting_passes asm_debug_symex_asm_first asm_debug_symex_asm_first_computed + asm_node_reveal_depth + asm_node_reveal_depth_computed . #[global] Hint Cut [ @@ -448,6 +457,8 @@ Module Export Hints. | asm_rewriting_passes | asm_debug_symex_asm_first | asm_debug_symex_asm_first_computed + | asm_node_reveal_depth + | asm_node_reveal_depth_computed ) ( _ * ) (Build_symbolic_options_opt | Build_symbolic_options_computed_opt @@ -3808,7 +3819,7 @@ Qed. End Rewrite. Definition simplify {opts : symbolic_options_computed_opt} (dag : dag) (e : node idx) := - Rewrite.expr dag (reveal_node_at_least dag 3 e). + Rewrite.expr dag (reveal_node_at_least dag node_reveal_depth e). Lemma eval_simplify {opts : symbolic_options_computed_opt} G d n v : gensym_dag_ok G d -> eval_node G d n v -> eval G d (simplify d n) v. Proof using Type. eauto using Rewrite.eval_expr, eval_node_reveal_node_at_least. Qed. diff --git a/src/CLI.v b/src/CLI.v index fcd2ce3981..e595d28b74 100644 --- a/src/CLI.v +++ b/src/CLI.v @@ -519,6 +519,10 @@ Module ForExtraction. Arg.Custom (parse_string_and parse_callee_saved_registers) "REG", ["Either '" ++ show System_V_AMD64 ++ "' (indicating " ++ String.concat "," (List.map show system_v_amd64_assembly_callee_saved_registers) ++ "), '" ++ show Microsoft_x64 ++ "' (indicating " ++ String.concat "," (List.map show microsoft_x64_assembly_callee_saved_registers) ++ "), or a comma-separated list of registers which are callee-saved / non-volatile. Only relevant when --hints-file is specified." ; "Defaults to " ++ show default_assembly_callee_saved_registers ++ "."]). + Definition asm_node_reveal_depth_spec : named_argT + := ([Arg.long_key "asm-node-reveal-depth"], + Arg.Custom (parse_string_and parse_nat) "ℕ", + ["The depth of nodes to reveal in the assembly equivalence checker. Only relevant when --hints-file is specified. In most situations, this should not have to be changed. Defaults to " ++ show default_node_reveal_depth ++ "."]). Definition asm_stack_size_spec : named_argT := ([Arg.long_key "asm-stack-size"], Arg.Custom (parse_string_and parse_N) "ℕ", @@ -734,6 +738,7 @@ Module ForExtraction. ; asm_error_on_unique_names_mismatch_spec ; asm_rewriting_pipeline_spec ; asm_rewriting_passes_spec + ; asm_node_reveal_depth_spec ; asm_debug_symex_asm_first_spec ; doc_text_before_function_name_spec ; doc_text_before_type_name_spec @@ -793,6 +798,7 @@ Module ForExtraction. , asm_error_on_unique_names_mismatchv , asm_rewriting_pipelinev , asm_rewriting_passesv + , asm_node_reveal_depthv , asm_debug_symex_asm_firstv , doc_text_before_function_namev , doc_text_before_type_namev @@ -806,6 +812,7 @@ Module ForExtraction. let to_bool ls := (0 None @@ -820,6 +827,8 @@ Module ForExtraction. let to_assembly_callee_saved_registers_default ls default := Option.value (to_assembly_callee_saved_registers_opt ls) default in let to_N_opt ls := choose_one_of_many (to_N_list ls) in let to_N_default ls default := Option.value (to_N_opt ls) default in + let to_nat_opt ls := choose_one_of_many (to_nat_list ls) in + let to_nat_default ls default := Option.value (to_nat_opt ls) default in let to_string_opt ls := choose_one_of_many (to_string_list ls) in let to_string_default ls default := Option.value (to_string_opt ls) default in let to_capitalization_data_opt ls := choose_one_of_many (List.map (fun '(_, (_, v)) => v) ls) in @@ -882,6 +891,7 @@ Module ForExtraction. {| asm_rewriting_pipeline := to_rewriting_pipeline_list asm_rewriting_pipelinev ; asm_rewriting_pass_filter := fun p => asm_rewriting_pass_filterv (show_rewrite_pass p) ; asm_debug_symex_asm_first := to_bool asm_debug_symex_asm_firstv + ; asm_node_reveal_depth := to_nat_default asm_node_reveal_depthv default_node_reveal_depth |} |} ; ignore_unique_asm_names := negb (to_bool asm_error_on_unique_names_mismatchv) From dddfa84390d33ae569bdf40f500424ea995e09f5 Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Wed, 12 Mar 2025 22:38:12 -0700 Subject: [PATCH 2/8] Better ordering on print-report --- Makefile.examples | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile.examples b/Makefile.examples index aae64034f0..779e2b1bd3 100644 --- a/Makefile.examples +++ b/Makefile.examples @@ -355,9 +355,9 @@ include Makefile.test-amd64-files.mk .PHONY: test-amd64-files-print-report test-amd64-files-lite-print-report only-test-amd64-files-print-report only-test-amd64-files-lite-print-report .PHONY: test-amd64-files-print-report-after-make test-amd64-files-lite-print-report-after-make only-test-amd64-files-print-report-after-make only-test-amd64-files-lite-print-report-after-make -test-amd64-files-print-report:: STATUS_FILES=$(AMD64_ASM_STATUS_FILES) +test-amd64-files-print-report test-amd64-files-print-report-after-make:: STATUS_FILES=$(AMD64_ASM_STATUS_FILES) only-test-amd64-files-print-report only-test-amd64-files-print-report-after-make:: STATUS_FILES=$(AMD64_ASM_ONLY_STATUS_FILES) -test-amd64-files-lite-print-report:: STATUS_FILES=$(AMD64_ASM_SMALL_STATUS_FILES) +test-amd64-files-lite-print-report test-amd64-files-lite-print-report-after-make:: STATUS_FILES=$(AMD64_ASM_SMALL_STATUS_FILES) only-test-amd64-files-lite-print-report only-test-amd64-files-lite-print-report-after-make:: STATUS_FILES=$(AMD64_ASM_SMALL_ONLY_STATUS_FILES) test-amd64-files-status: STATUS_FILES=$(AMD64_ASM_STATUS_FILES) only-test-amd64-files-status: STATUS_FILES=$(AMD64_ASM_ONLY_STATUS_FILES) @@ -366,6 +366,8 @@ only-test-amd64-files-lite-status: STATUS_FILES=$(AMD64_ASM_SMALL_ONLY_STATUS_FI only-test-amd64-files-print-report-after-make:: | only-test-amd64-files-status only-test-amd64-files-lite-print-report-after-make:: | only-test-amd64-files-lite-status +test-amd64-files-print-report-after-make:: | test-amd64-files-status +test-amd64-files-lite-print-report-after-make:: | test-amd64-files-lite-status test-amd64-files-print-report only-test-amd64-files-print-report test-amd64-files-lite-print-report only-test-amd64-files-lite-print-report test-amd64-files-print-report-after-make only-test-amd64-files-print-report-after-make test-amd64-files-lite-print-report-after-make only-test-amd64-files-lite-print-report-after-make:: @ export passed=$$(cat $(STATUS_FILES) 2>/dev/null | grep -c '^0$$'); \ @@ -385,13 +387,11 @@ test-amd64-files-status only-test-amd64-files-status test-amd64-files-lite-statu test-amd64-files test-amd64-files-lite: $(UNSATURATED_SOLINAS) $(WORD_BY_WORD_MONTGOMERY) $(SOLINAS_REDUCTION) $(DETTMAN_MULTIPLICATION) -test-amd64-files: test-amd64-files-print-report test-amd64-files-status - -only-test-amd64-files: only-test-amd64-files-print-report-after-make only-test-amd64-files-status - -test-amd64-files-lite: test-amd64-files-lite-print-report test-amd64-files-lite-status - -only-test-amd64-files-lite: only-test-amd64-files-lite-print-report-after-make only-test-amd64-files-lite-status +test-amd64-files only-test-amd64-files test-amd64-files-lite only-test-amd64-files-lite: + +$(MAKE) $@-status; \ + RV=$$?; \ + $(MAKE) $@-print-report; \ + exit $$RV etc/tscfreq: etc/tscfreq.c $(CC) etc/tscfreq.c -s -Os -o etc/tscfreq From c05d680e3101d755cf4177a44ad6f1202edf89b9 Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Wed, 12 Mar 2025 22:38:49 -0700 Subject: [PATCH 3/8] Add proof that map is injective --- src/Util/ListUtil/Injective.v | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/Util/ListUtil/Injective.v diff --git a/src/Util/ListUtil/Injective.v b/src/Util/ListUtil/Injective.v new file mode 100644 index 0000000000..b6473dcf62 --- /dev/null +++ b/src/Util/ListUtil/Injective.v @@ -0,0 +1,13 @@ +From Coq Require Import List. +Require Export Crypto.Util.FixCoqMistakes. +Import ListNotations. +Local Open Scope list_scope. +Local Set Implicit Arguments. + +Module List. + Lemma map_inj A B f g xs ys : (forall x y, f x = g y -> x = y) -> @map A B f xs = @map A B g ys -> xs = ys. + Proof. + intro H; revert ys; induction xs as [|?? IH], ys; cbn; try reflexivity. + all: inversion 1; f_equal; eauto. + Qed. +End List. From 3888dbd21d0a5b9684d5e9b6c83e250f8f7e03df Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Wed, 12 Mar 2025 20:08:12 -0700 Subject: [PATCH 4/8] Add opcode_size for nop so we don't get errors --- src/Assembly/Syntax.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Assembly/Syntax.v b/src/Assembly/Syntax.v index a09272183f..cf81587931 100644 --- a/src/Assembly/Syntax.v +++ b/src/Assembly/Syntax.v @@ -312,7 +312,7 @@ Definition standalone_operand_size (x : ARG) : option N := Definition opcode_size (op : OpCode) := match op with | seto | setc => Some 8 - | ret => Some 64 (* irrelevant? *) + | ret | nop => Some 64 (* irrelevant? *) | clc => Some 1 (* irrelevant? *) | _ => None end%N. From f39d55cb9decc930bfe9d18c59b4fc0e77daa67c Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Wed, 12 Mar 2025 16:46:41 -0700 Subject: [PATCH 5/8] Reveal nodes on demand rather than up-front Replaces #2055 Closes #2055 Fixes #2050 --- .../fiat_curve25519_sub/clang_19_1_0_O0.asm | 54 ++++ fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm | 77 ++++++ fiat-amd64/gentest.py | 12 +- src/Assembly/Symbolic.v | 259 ++++++++++++------ src/CLI.v | 7 + 5 files changed, 315 insertions(+), 94 deletions(-) create mode 100644 fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm create mode 100644 fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm diff --git a/fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm b/fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm new file mode 100644 index 0000000000..19c2e9aaff --- /dev/null +++ b/fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm @@ -0,0 +1,54 @@ + .globl _Z14fiat_25519_subPmPKmS1_ +_Z14fiat_25519_subPmPKmS1_: + push rbp + mov rbp, rsp + mov qword ptr [rbp - 8], rdi + mov qword ptr [rbp - 16], rsi + mov qword ptr [rbp - 24], rdx + mov rcx, qword ptr [rbp - 16] + movabs rax, 4503599627370458 + add rax, qword ptr [rcx] + mov rcx, qword ptr [rbp - 24] + sub rax, qword ptr [rcx] + mov qword ptr [rbp - 32], rax + mov rcx, qword ptr [rbp - 16] + movabs rax, 4503599627370494 + add rax, qword ptr [rcx + 8] + mov rcx, qword ptr [rbp - 24] + sub rax, qword ptr [rcx + 8] + mov qword ptr [rbp - 40], rax + mov rcx, qword ptr [rbp - 16] + movabs rax, 4503599627370494 + add rax, qword ptr [rcx + 16] + mov rcx, qword ptr [rbp - 24] + sub rax, qword ptr [rcx + 16] + mov qword ptr [rbp - 48], rax + mov rcx, qword ptr [rbp - 16] + movabs rax, 4503599627370494 + add rax, qword ptr [rcx + 24] + mov rcx, qword ptr [rbp - 24] + sub rax, qword ptr [rcx + 24] + mov qword ptr [rbp - 56], rax + mov rcx, qword ptr [rbp - 16] + movabs rax, 4503599627370494 + add rax, qword ptr [rcx + 32] + mov rcx, qword ptr [rbp - 24] + sub rax, qword ptr [rcx + 32] + mov qword ptr [rbp - 64], rax + mov rcx, qword ptr [rbp - 32] + mov rax, qword ptr [rbp - 8] + mov qword ptr [rax], rcx + mov rcx, qword ptr [rbp - 40] + mov rax, qword ptr [rbp - 8] + mov qword ptr [rax + 8], rcx + mov rcx, qword ptr [rbp - 48] + mov rax, qword ptr [rbp - 8] + mov qword ptr [rax + 16], rcx + mov rcx, qword ptr [rbp - 56] + mov rax, qword ptr [rbp - 8] + mov qword ptr [rax + 24], rcx + mov rcx, qword ptr [rbp - 64] + mov rax, qword ptr [rbp - 8] + mov qword ptr [rax + 32], rcx + pop rbp + ret diff --git a/fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm b/fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm new file mode 100644 index 0000000000..69b52996cf --- /dev/null +++ b/fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm @@ -0,0 +1,77 @@ + .globl _Z14fiat_25519_subPmPKmS1_ +_Z14fiat_25519_subPmPKmS1_: + push rbp + mov rbp, rsp + mov QWORD PTR [rbp-56], rdi + mov QWORD PTR [rbp-64], rsi + mov QWORD PTR [rbp-72], rdx + mov rax, QWORD PTR [rbp-64] + mov rdx, QWORD PTR [rax] + mov rax, QWORD PTR [rbp-72] + mov rax, QWORD PTR [rax] + sub rdx, rax + movabs rax, 4503599627370458 + add rax, rdx + mov QWORD PTR [rbp-8], rax + mov rax, QWORD PTR [rbp-64] + add rax, 8 + mov rdx, QWORD PTR [rax] + mov rax, QWORD PTR [rbp-72] + add rax, 8 + mov rax, QWORD PTR [rax] + sub rdx, rax + movabs rax, 4503599627370494 + add rax, rdx + mov QWORD PTR [rbp-16], rax + mov rax, QWORD PTR [rbp-64] + add rax, 16 + mov rdx, QWORD PTR [rax] + mov rax, QWORD PTR [rbp-72] + add rax, 16 + mov rax, QWORD PTR [rax] + sub rdx, rax + movabs rax, 4503599627370494 + add rax, rdx + mov QWORD PTR [rbp-24], rax + mov rax, QWORD PTR [rbp-64] + add rax, 24 + mov rdx, QWORD PTR [rax] + mov rax, QWORD PTR [rbp-72] + add rax, 24 + mov rax, QWORD PTR [rax] + sub rdx, rax + movabs rax, 4503599627370494 + add rax, rdx + mov QWORD PTR [rbp-32], rax + mov rax, QWORD PTR [rbp-64] + add rax, 32 + mov rdx, QWORD PTR [rax] + mov rax, QWORD PTR [rbp-72] + add rax, 32 + mov rax, QWORD PTR [rax] + sub rdx, rax + movabs rax, 4503599627370494 + add rax, rdx + mov QWORD PTR [rbp-40], rax + mov rax, QWORD PTR [rbp-56] + mov rdx, QWORD PTR [rbp-8] + mov QWORD PTR [rax], rdx + mov rax, QWORD PTR [rbp-56] + lea rdx, [rax+8] + mov rax, QWORD PTR [rbp-16] + mov QWORD PTR [rdx], rax + mov rax, QWORD PTR [rbp-56] + lea rdx, [rax+16] + mov rax, QWORD PTR [rbp-24] + mov QWORD PTR [rdx], rax + mov rax, QWORD PTR [rbp-56] + lea rdx, [rax+24] + mov rax, QWORD PTR [rbp-32] + mov QWORD PTR [rdx], rax + mov rax, QWORD PTR [rbp-56] + lea rdx, [rax+32] + mov rax, QWORD PTR [rbp-40] + mov QWORD PTR [rdx], rax + nop + pop rbp + ret \ No newline at end of file diff --git a/fiat-amd64/gentest.py b/fiat-amd64/gentest.py index 088af57a3f..ceabac2cf7 100755 --- a/fiat-amd64/gentest.py +++ b/fiat-amd64/gentest.py @@ -61,10 +61,11 @@ def removeprefix(s, prefix): m = regex.match(os.path.basename(dirname)) if m: for fname in os.listdir(dirname): - groups = m.groupdict() - asm_op_names.setdefault((groups["name"], groups["op"]), []).append( - os.path.join(dirname, fname) - ) + if fname.endswith(".asm") or fname.endswith(".s"): + groups = m.groupdict() + asm_op_names.setdefault((groups["name"], groups["op"]), []).append( + os.path.join(dirname, fname) + ) def asm_op_names_key(val): @@ -221,7 +222,8 @@ def is_small(val): assert False, name if output_makefile: short_fnames = [ - removesuffix(os.path.basename(fname), ".asm") for fname in fnames + removesuffix(removesuffix(os.path.basename(fname), ".asm"), ".s") + for fname in fnames ] description = f'{name} {prime.replace(" ", "")} ({op}) ({binary_descr}) ({" ".join(short_fnames)})' output_name = f"fiat-amd64/{name}-{op}" diff --git a/src/Assembly/Symbolic.v b/src/Assembly/Symbolic.v index fd1fc42f17..62ccd5dd3a 100644 --- a/src/Assembly/Symbolic.v +++ b/src/Assembly/Symbolic.v @@ -406,6 +406,9 @@ Module Export Options. Class debug_symex_asm_first_opt := debug_symex_asm_first : bool. (** How deep should we reveal nodes? *) Class node_reveal_depth_opt := node_reveal_depth : nat. + (** How much should we reveal expressions when deriving bounds for expressions via structure? *) + Class symex_bounds_reveal_depth_opt := symex_bounds_reveal_depth : nat. + Definition default_symex_bounds_reveal_depth := 1%nat. Definition default_rewriting_passes {rewriting_pipeline : rewriting_pipeline_opt} {rewriting_pass_filter : rewriting_pass_filter_opt} @@ -417,6 +420,7 @@ Module Export Options. ; asm_rewriting_pass_filter : rewriting_pass_filter_opt ; asm_debug_symex_asm_first : debug_symex_asm_first_opt ; asm_node_reveal_depth : node_reveal_depth_opt + ; asm_symex_bounds_reveal_depth : symex_bounds_reveal_depth_opt }. Definition default_node_reveal_depth := 3%nat. @@ -426,6 +430,7 @@ Module Export Options. { asm_rewriting_passes : rewriting_passes_opt ; asm_debug_symex_asm_first_computed : debug_symex_asm_first_opt ; asm_node_reveal_depth_computed : node_reveal_depth_opt + ; asm_symex_bounds_reveal_depth_computed : symex_bounds_reveal_depth_opt }. (* N.B. The default rewriting pass filter should not be changed here, but instead changed in CLI.v where it is derived from a default string *) @@ -434,6 +439,7 @@ Module Export Options. ; asm_rewriting_pass_filter := fun _ => true ; asm_debug_symex_asm_first := false ; asm_node_reveal_depth := default_node_reveal_depth + ; asm_symex_bounds_reveal_depth := default_symex_bounds_reveal_depth |}. End Options. Module Export Hints. @@ -448,6 +454,8 @@ Module Export Hints. asm_debug_symex_asm_first_computed asm_node_reveal_depth asm_node_reveal_depth_computed + asm_symex_bounds_reveal_depth + asm_symex_bounds_reveal_depth_computed . #[global] Hint Cut [ @@ -459,6 +467,8 @@ Module Export Hints. | asm_debug_symex_asm_first_computed | asm_node_reveal_depth | asm_node_reveal_depth_computed + | asm_symex_bounds_reveal_depth + | asm_symex_bounds_reveal_depth_computed ) ( _ * ) (Build_symbolic_options_opt | Build_symbolic_options_computed_opt @@ -514,6 +524,12 @@ Definition Show_expr : Show expr fix Show_expr e := Show_expr_body Show_expr e. Global Existing Instance Show_expr. +Fixpoint expr_depth (e : expr) : nat := + match e with + | ExprRef _ => 0 + | ExprApp (_, args) => S (List.fold_right Nat.max 0 (List.map expr_depth args)) + end. + Local Notation max_powers_of_two := 5%nat (only parsing). Local Notation max_decimal := 256%Z (only parsing). @@ -1842,6 +1858,13 @@ Section WithDag. Definition reveal_node n '(op, args) := ExprApp (op, List.map (reveal n) args). + Fixpoint reveal_expr n (e : expr) {struct e} := + match e, n with + | ExprRef i, _ => reveal n i + | ExprApp (op, args), S n => ExprApp (op, List.map (reveal_expr n) args) + | _, _ => e + end. + (** given a set of indices, get the set of indices of their arguments *) Definition reveal_gather_deps_args (ls : NSet.t) : NSet.t := fold_right @@ -1890,6 +1913,13 @@ Section WithDag. Definition reveal_node_at_least n '(op, args) := ExprApp (op, List.map (reveal_at_least n) args). + Fixpoint reveal_expr_at_least n (e : expr) {struct e} : expr := + match e, n with + | ExprRef i, _ => reveal_at_least n i + | ExprApp (op, args), S n => ExprApp (op, List.map (reveal_expr_at_least n) args) + | _, _ => e + end. + Local Unset Elimination Schemes. Inductive eval : expr -> Z -> Prop := | ERef i op args args' n @@ -1969,6 +1999,19 @@ Section WithDag. eapply eval_reveal; eauto. Qed. + Lemma eval_reveal_expr : forall n e, forall v, eval e v -> + forall f, reveal_expr n e = f -> eval f v. + Proof using Type. + intros n e v H; revert e v H n. + induction 1; cbn; intros; subst; [ eapply eval_reveal; econstructor; eauto | ]. + { eapply Forall2_weaken; [ | eassumption ]; cbv beta. + intros; destruct_head'_and; eauto. } + { break_innermost_match_step; econstructor; try eassumption. + all: rewrite ?Forall2_map_l. + all: eapply Forall2_weaken; [ | eassumption ]; cbv beta. + all: intros; destruct_head'_and; eauto. } + Qed. + Lemma eval_reveal_from_deps_fueled deps : forall n i, forall v, eval (ExprRef i) v -> forall e, reveal_from_deps_fueled n deps i = e -> eval e v. Proof using Type. @@ -1997,6 +2040,19 @@ Section WithDag. eapply Forall2_weaken; try eassumption; []; cbv beta; intros. eapply eval_reveal_at_least; eauto. Qed. + + Lemma eval_reveal_expr_at_least : forall n e, forall v, eval e v -> + forall f, reveal_expr_at_least n e = f -> eval f v. + Proof using Type. + intros n e v H; revert e v H n. + induction 1; cbn; intros; subst; [ eapply eval_reveal_at_least; econstructor; eauto | ]. + { eapply Forall2_weaken; [ | eassumption ]; cbv beta. + intros; destruct_head'_and; eauto. } + { break_innermost_match_step; econstructor; try eassumption. + all: rewrite ?Forall2_map_l. + all: eapply Forall2_weaken; [ | eassumption ]; cbv beta. + all: intros; destruct_head'_and; eauto. } + Qed. End WithDag. Definition merge_node {descr : description} (n : node idx) : dag.M idx @@ -2243,44 +2299,46 @@ Section bound_expr_via_PHOAS. Local Notation interp_PHOAS_op := (ZRange.ident.option.interp true). - Fixpoint bound_expr_via_PHOAS (d : dag) (e : Symbolic.expr) : option zrange + Fixpoint bound_expr_via_PHOAS_noreveal (d : dag) (e : Symbolic.expr) : option zrange := match e with | ExprApp (o, args) => match op_to_PHOAS_bounds o with | Some o - => o (List.map (fun e 'tt => bound_expr_via_PHOAS d e) args) + => o (List.map (fun e 'tt => bound_expr_via_PHOAS_noreveal d e) args) | None => None end | ExprRef i => dag.lookup_bounds d i end. + Definition bound_expr_via_PHOAS {symex_bounds_reveal_depth:symex_bounds_reveal_depth_opt} (d : dag) (e : Symbolic.expr) : option zrange := + bound_expr_via_PHOAS_noreveal d (reveal_expr_at_least d symex_bounds_reveal_depth e). Local Coercion is_true : bool >-> Sortclass. - Fixpoint eval_bound_expr_via_PHOAS' G d (Hok : gensym_dag_ok G d) e b + Fixpoint eval_bound_expr_via_PHOAS_noreveal' G d (Hok : gensym_dag_ok G d) e b (H_dag : forall i b, dag.lookup_bounds d i = Some b -> forall v, eval G d (ExprRef i) v -> is_bounded_by_bool v b = true) {struct e} - : bound_expr_via_PHOAS d e = Some b -> + : bound_expr_via_PHOAS_noreveal d e = Some b -> forall v, eval G d e v -> is_bounded_by_bool v b = true. Proof using Type. intros H v. assert (dag.ok d) by apply Hok. - specialize (fun e v b H H_dag => eval_bound_expr_via_PHOAS' G d Hok e b H_dag H v). - destruct e as [?|[n args] ]; cbn [bound_expr_via_PHOAS] in *. - { clear eval_bound_expr_via_PHOAS'; intros; inversion_option; eauto. } - let P := lazymatch type of eval_bound_expr_via_PHOAS' with forall e v, @?P e v => P end in - assert (eval_bound_expr_via_PHOAS'_Forall : forall args', List.length args = List.length args' -> Forall2 P args args'). - { clear -eval_bound_expr_via_PHOAS'. + specialize (fun e v b H H_dag => eval_bound_expr_via_PHOAS_noreveal' G d Hok e b H_dag H v). + destruct e as [?|[n args] ]; cbn [bound_expr_via_PHOAS_noreveal] in *. + { clear eval_bound_expr_via_PHOAS_noreveal'; intros; inversion_option; eauto. } + let P := lazymatch type of eval_bound_expr_via_PHOAS_noreveal' with forall e v, @?P e v => P end in + assert (eval_bound_expr_via_PHOAS_noreveal'_Forall : forall args', List.length args = List.length args' -> Forall2 P args args'). + { clear -eval_bound_expr_via_PHOAS_noreveal'. induction args as [|arg args IH], args'; cbn [List.length]; first [ constructor; try apply IH | clear; intro; exfalso; congruence ]. - { apply eval_bound_expr_via_PHOAS'. } - { clear eval_bound_expr_via_PHOAS'; congruence. } } - clear eval_bound_expr_via_PHOAS'. + { apply eval_bound_expr_via_PHOAS_noreveal'. } + { clear eval_bound_expr_via_PHOAS_noreveal'; congruence. } } + clear eval_bound_expr_via_PHOAS_noreveal'. break_innermost_match_hyps; inversion_option. inversion 1; subst. eapply (@interp_op_op_to_PHOAS_bounds G n); try (eassumption || reflexivity). rewrite !Forall2_map_r_iff. eassert (H' : List.length _ = List.length _) by (eapply eq_length_Forall2; eassumption). - specialize (eval_bound_expr_via_PHOAS'_Forall _ H'). + specialize (eval_bound_expr_via_PHOAS_noreveal'_Forall _ H'). rewrite !Forall2_forall_iff_nth_error. intro i; repeat match goal with @@ -2291,6 +2349,16 @@ Section bound_expr_via_PHOAS. break_innermost_match; break_innermost_match_hyps; inversion_option; auto; tauto. Qed. + Lemma eval_bound_expr_via_PHOAS' {symex_bounds_reveal_depth:symex_bounds_reveal_depth_opt} G d (Hok : gensym_dag_ok G d) e b + (H_dag : forall i b, dag.lookup_bounds d i = Some b -> forall v, eval G d (ExprRef i) v -> is_bounded_by_bool v b = true) + : bound_expr_via_PHOAS d e = Some b -> + forall v, eval G d e v -> is_bounded_by_bool v b = true. + Proof using Type. + cbv [bound_expr_via_PHOAS]; intros. + eapply eval_bound_expr_via_PHOAS_noreveal'; eauto. + eapply eval_reveal_expr_at_least; eauto. + Qed. + Lemma eval_bound_expr_via_PHOAS_dag G d (Hok : gensym_dag_ok G d) : forall i b, dag.lookup_bounds d i = Some b -> forall v, eval G d (ExprRef i) v -> is_bounded_by_bool v b = true. Proof. @@ -2317,7 +2385,14 @@ Section bound_expr_via_PHOAS. eauto. Qed. - Lemma eval_bound_expr_via_PHOAS G d (Hok : gensym_dag_ok G d) e b + Lemma eval_bound_expr_via_PHOAS_noreveal G d (Hok : gensym_dag_ok G d) e b + : bound_expr_via_PHOAS_noreveal d e = Some b -> + forall v, eval G d e v -> is_bounded_by_bool v b = true. + Proof using Type. + eauto using eval_bound_expr_via_PHOAS_noreveal', eval_bound_expr_via_PHOAS_dag. + Qed. + + Lemma eval_bound_expr_via_PHOAS {symex_bounds_reveal_depth:symex_bounds_reveal_depth_opt} G d (Hok : gensym_dag_ok G d) e b : bound_expr_via_PHOAS d e = Some b -> forall v, eval G d e v -> is_bounded_by_bool v b = true. Proof using Type. @@ -2326,7 +2401,7 @@ Section bound_expr_via_PHOAS. End bound_expr_via_PHOAS. Notation bound_expr := bound_expr_via_PHOAS. -Lemma eval_bound_expr G d e b : bound_expr d e = Some b -> +Lemma eval_bound_expr {symex_bounds_reveal_depth:symex_bounds_reveal_depth_opt} G d e b : bound_expr d e = Some b -> forall v, gensym_dag_ok G d -> eval G d e v -> (ZRange.lower b <= v <= ZRange.upper b)%Z. Proof using Type. intros H v Hok He. @@ -2356,7 +2431,7 @@ Ltac t:= match goal with | _ => progress subst end. -Lemma bound_sum' G d +Lemma bound_sum' {symex_bounds_reveal_depth:symex_bounds_reveal_depth_opt} G d es (He : Forall (fun e => forall b, bound_expr d e = Some b -> forall (v : Z), eval G d e v -> (ZRange.lower b <= v <= ZRange.upper b)%Z) es) : forall @@ -2374,7 +2449,7 @@ Qed. Import Crypto.Util.ZRange. -Lemma bound_sum G d es +Lemma bound_sum {symex_bounds_reveal_depth:symex_bounds_reveal_depth_opt} G d es (Hok : gensym_dag_ok G d) bs (Hb : Option.List.lift (map (bound_expr d) es) = Some bs) vs (Hv : Forall2 (eval G d) es vs) @@ -2389,8 +2464,8 @@ Definition isCst (e : expr) := match e with ExprApp ((const _), []) => true | _ => false end. Module Rewrite. -Class Ok r := rwok : forall G d e v, gensym_dag_ok G d -> eval G d e v -> eval G d (r d e) v. -Class description_of (r : dag -> expr -> expr) := describe : string. +Class Ok r := rwok : forall {opts : symbolic_options_computed_opt} G d e v, gensym_dag_ok G d -> eval G d e v -> eval G d (r opts d e) v. +Class description_of (r : symbolic_options_computed_opt -> dag -> expr -> expr) := describe : string. #[global] Bind Scope string_scope with description_of. #[global] Typeclasses Opaque description_of. Ltac resolve_match_using_hyp := @@ -2404,6 +2479,11 @@ Ltac resolve_match_using_hyp := Create HintDb step_db discriminated. Ltac step := match goal with + | H : eval ?G ?d ?e ?v |- context[reveal_expr_at_least ?d ?n ?e] => + assert (eval G d (reveal_expr_at_least d n e) v) + by (once (eapply eval_reveal_expr_at_least; (exact H + reflexivity))); + generalize dependent (reveal_expr_at_least d n e); intros + | [ H : eval _ _ ?e _ |- _ ] => is_var e; clear e H | |- Ok ?r => cbv [Ok r]; intros | _ => solve [trivial | contradiction] | _ => resolve_match_using_hyp @@ -2457,8 +2537,8 @@ Ltac t := repeat (step || Econstructor || eauto || (progress cbn [interp0_op int #[local] Hint Rewrite Z.fold_right_land_ones_id : step_db. -Definition slice0 (d : dag) := - fun e => match e with +Definition slice0 [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (slice 0 s, [(ExprApp ((addZ|mulZ|negZ|shlZ|shrZ|andZ|orZ|xorZ) as o, args))]) => ExprApp ((match o with addZ=>add s|mulZ=>mul s|negZ=>neg s|shlZ=>shl s|shrZ => shr s|andZ => and s| orZ => or s|xorZ => xor s |_=>old 0%N 999999%N end), args) | _ => e end. @@ -2466,8 +2546,8 @@ Definition slice0 (d : dag) := := "Merges (slice 0 s) into addZ,mulZ,negZ,shlZ,shrZ,andZ,orZ,xorZ". Global Instance slice0_ok : Ok slice0. Proof using Type. t. Qed. -Definition slice01_addcarryZ (d : dag) := - fun e => match e with +Definition slice01_addcarryZ [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (slice 0 1, [(ExprApp (addcarryZ s, args))]) => ExprApp (addcarry s, args) | _ => e end. @@ -2476,8 +2556,8 @@ Definition slice01_addcarryZ (d : dag) := Global Instance slice01_addcarryZ_ok : Ok slice01_addcarryZ. Proof using Type. t; rewrite ?Z.shiftr_0_r, ?Z.land_ones, ?Z.shiftr_div_pow2; trivial; Lia.lia. Qed. -Definition slice01_subborrowZ (d : dag) := - fun e => match e with +Definition slice01_subborrowZ [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (slice 0 1, [(ExprApp (subborrowZ s, args))]) => ExprApp (subborrow s, args) | _ => e end. @@ -2486,8 +2566,8 @@ Definition slice01_subborrowZ (d : dag) := Global Instance slice01_subborrowZ_ok : Ok slice01_subborrowZ. Proof using Type. t; rewrite ?Z.shiftr_0_r, ?Z.land_ones, ?Z.shiftr_div_pow2; trivial; Lia.lia. Qed. -Definition slice_set_slice (d : dag) := - fun e => match e with +Definition slice_set_slice [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (slice 0 s1, [ExprApp (set_slice 0 s2, [_; e'])]) => if N.leb s1 s2 then ExprApp (slice 0 s1, [e']) else e | _ => e end. #[local] Instance describe_slice_set_slice : description_of Rewrite.slice_set_slice @@ -2495,8 +2575,8 @@ Definition slice_set_slice (d : dag) := Global Instance slice_set_slice_ok : Ok slice_set_slice. Proof using Type. t. f_equal. Z.bitblast. Qed. -Definition set_slice_set_slice (d : dag) := - fun e => match e with +Definition set_slice_set_slice [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (set_slice lo1 s1, [ExprApp (set_slice lo2 s2, [x; e']); y]) => if andb (N.eqb lo1 lo2) (N.leb s2 s1) then ExprApp (set_slice lo1 s1, [x; y]) else e | _ => e end. #[local] Instance describe_set_slice_set_slice : description_of Rewrite.set_slice_set_slice @@ -2504,8 +2584,8 @@ Definition set_slice_set_slice (d : dag) := Global Instance set_slice_set_slice_ok : Ok set_slice_set_slice. Proof using Type. t. f_equal. Z.bitblast. Qed. -Definition set_slice0_small (d : dag) := - fun e => match e with +Definition set_slice0_small [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 1 e with ExprApp (set_slice 0 s, [x; y]) => match bound_expr d x, bound_expr d y with Some a, Some b => if Z.leb 0 (ZRange.lower a) && Z.leb 0 (ZRange.lower b) && Z.leb (ZRange.upper a) (Z.ones (Z.of_N s)) && Z.leb (ZRange.upper b) (Z.ones (Z.of_N s)) then y @@ -2526,8 +2606,8 @@ Proof using Type. eapply Z.log2_lt_pow2; Lia.lia. Qed. -Definition truncate_small (d : dag) := - fun e => match e with +Definition truncate_small [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 1 e with ExprApp (slice 0%N s, [e']) => match bound_expr d e' with Some b => if Z.leb 0 (ZRange.lower b) && Z.leb (ZRange.upper b) (Z.ones (Z.of_N s)) @@ -2537,8 +2617,8 @@ Definition truncate_small (d : dag) := := "Simplifies slice when it's a no-op". Global Instance truncate_small_ok : Ok truncate_small. Proof using Type. t; []. cbn in *; eapply Z.land_ones_low_alt_ones; eauto. lia. Qed. -Definition addcarry_bit (d : dag) := - fun e => match e with +Definition addcarry_bit [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (addcarry s, ([ExprApp (const a, nil);b])) => if option_beq zrange_beq (bound_expr d b) (Some r[0~>1]%zrange) then match interp0_op (addcarry s) [a; 0], interp0_op (addcarry s) [a; 1] with @@ -2557,8 +2637,8 @@ Proof using Type. subst; repeat step; repeat Econstructor; cbn; congruence. Qed. -Definition addoverflow_bit (d : dag) := - fun e => match e with +Definition addoverflow_bit [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (addoverflow s, ([ExprApp (const a, nil);b])) => if option_beq zrange_beq (bound_expr d b) (Some r[0~>1]%zrange) then match interp0_op (addoverflow s) [a; 0] , interp0_op (addoverflow s) [a; 1] with @@ -2577,8 +2657,8 @@ Proof using Type. subst; repeat step; repeat Econstructor; cbn; congruence. Qed. -Definition addbyte_small (d : dag) := - fun e => match e with +Definition addbyte_small [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 1 e with ExprApp (add (8%N as s), args) => match Option.List.lift (List.map (bound_expr d) args) with | Some bounds => @@ -2597,8 +2677,8 @@ Proof using Type. replace (Z.of_N 64) with 64 in * by (vm_compute; reflexivity); lia. Qed. -Definition addcarry_small (d : dag) := - fun e => match e with +Definition addcarry_small [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 1 e with ExprApp (addcarry s, args) => match Option.List.lift (List.map (bound_expr d) args) with | Some bounds => @@ -2615,8 +2695,8 @@ Proof using Type. rewrite Z.ones_equiv in * |- ; rewrite Z.shiftr_div_pow2, Z.div_small; cbn; lia. Qed. -Definition addoverflow_small (d : dag) := - fun e => match e with +Definition addoverflow_small [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 1 e with ExprApp (addoverflow s, ([_]|[_;_]|[_;_;_]) as args) => match Option.List.lift (List.map (bound_expr d) args) with | Some bounds => @@ -2638,8 +2718,8 @@ Proof using Type. all : destruct s; cbn in * |- ; lia. Qed. -Definition constprop (d : dag) := - fun e => match interp0_expr e with +Definition constprop [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match interp0_expr (reveal_expr_at_least d 2 e) with | Some v => ExprApp (const v, nil) | _ => e end. #[local] Instance describe_constprop : description_of Rewrite.constprop @@ -2648,8 +2728,8 @@ Global Instance constprop_ok : Ok constprop. Proof using Type. t. f_equal; eauto using eval_eval. Qed. (* convert unary operations to slice *) -Definition unary_truncate (d : dag) := - fun e => match e with +Definition unary_truncate [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 1 e with ExprApp (o, [x]) => match unary_truncate_size o with | Some (-1)%Z => x @@ -2900,8 +2980,8 @@ Proof using Type. reflexivity. Qed. -Definition flatten_associative (d : dag) := - fun e => match e with +Definition flatten_associative [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (o, args) => if associative o then ExprApp (o, List.flat_map (fun e' => @@ -2918,9 +2998,11 @@ Proof using Type. let H := match goal with H : Forall2 (eval _ _) _ _ |- _ => H end in revert dependent v; induction H; cbn. { econstructor; eauto. } - intros ? H4. - pose proof H4. - eapply invert_interp_op_associative in H4; eauto. destruct H4 as (?&?&?). + intros. + let H := match goal with H : interp_op _ _ _ = _ |- _ => H end in + rename H into H'. + pose proof H'. + eapply invert_interp_op_associative in H'; eauto. destruct H' as (?&?&?). specialize (IHForall2 _ ltac:(eassumption)). inversion IHForall2; subst. let x := match goal with |- context[match x with ExprRef _ => _ | _ => _ end] => x end in @@ -2936,8 +3018,8 @@ Proof using Type. erewrite interp_op_associative_app; eauto. } Qed. -Definition flatten_bounded_associative (d : dag) := - fun e => match e with +Definition flatten_bounded_associative [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (o, args) => ExprApp (o, List.flat_map (fun e' => match e' with @@ -2955,12 +3037,10 @@ Proof. induction ls; cbn; lia. Qed. Global Instance flatten_bounded_associative_ok : Ok flatten_bounded_associative. Proof using Type. - cbv [Ok flatten_bounded_associative]. - intros G d e v Hok He. - destruct He; try solve [ t ]; []. + repeat step; []. lazymatch goal with | [ H0 : Forall2 ?P ?args ?args', H1 : interp_op _ _ ?args' = _ - |- eval _ _ (ExprApp (_, flat_map ?f args)) _ ] + |- eval _ _ (ExprApp (_, flat_map ?f ?args)) _ ] => epose [] as preargs; epose [] as preargs'; assert (Hpre : Forall2 P preargs preargs') by constructor; change (flat_map f args) with (preargs ++ flat_map f args); change args' with (preargs' ++ args') in H1; @@ -3015,8 +3095,8 @@ Proof using Type. all: try reflexivity. } Qed. -Definition consts_commutative (d : dag) := - fun e => match e with +Definition consts_commutative [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (o, args) => if commutative o then let csts_exprs := List.partition isCst args in @@ -3032,10 +3112,7 @@ Definition consts_commutative (d : dag) := Global Instance consts_commutative_ok : Ok consts_commutative. Proof using Type. - step. - destruct_one_head' @expr; trivial. - destruct_one_head' node. - destruct commutative eqn:?; trivial. + repeat (step; trivial; []). inversion_one_head' @eval. let l := match goal with l : list expr |- _ => l end in epose proof Permutation_partition l isCst. @@ -3048,7 +3125,6 @@ Proof using Type. set (fst (partition isCst l)) as csts in *; clearbody csts. set (snd (partition isCst l)) as exps in *; clearbody exps. clear dependent l. clear dependent args'. - move o at top; move Heqb0 at top; move Heqb at top. let H := match goal with H : interp0_expr _ = Some _ |- _ => H end in eapply eval_interp0_expr in H; instantiate (1:=d) in H; instantiate (1:=G) in H. @@ -3070,8 +3146,8 @@ Proof using Type. Qed. Definition neqconst i := fun a : expr => negb (option_beq Z.eqb (interp0_expr a) (Some i)). -Definition drop_identity (d : dag) := - fun e => match e with ExprApp (o, args) => +Definition drop_identity [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp (o, args) => match identity o with | Some i => let args := List.filter (neqconst i) args in @@ -3164,7 +3240,7 @@ Proof using Type. | solve [ t ] ]. Qed. -Definition fold_consts_to_and (d : dag) := +Definition fold_consts_to_and [opts : symbolic_options_computed_opt] (d : dag) := fun e => match consts_commutative d e with | ExprApp ((and _ | andZ) as o, ExprApp (const v, nil) :: args) => let v' := match o with @@ -3243,8 +3319,8 @@ Proof using Type. | t ]. Qed. -Definition xor_same (d : dag) := - fun e => match e with ExprApp (xor _,[x;y]) => +Definition xor_same [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 1 e with ExprApp (xor _,[x;y]) => if expr_beq x y then ExprApp (const 0, nil) else e | _ => e end. #[local] Instance describe_xor_same : description_of Rewrite.xor_same := "Replaces xor x x with 0". @@ -3253,8 +3329,8 @@ Proof using Type. t; cbn [fold_right]. rewrite Z.lxor_0_r, Z.lxor_nilpotent; trivial. Qed. -Definition shift_to_mul (d : dag) := - fun e => match e with +Definition shift_to_mul [opts : symbolic_options_computed_opt] (d : dag) := + fun e => match reveal_expr_at_least d 2 e with ExprApp ((shl _ | shlZ) as o, [e'; ExprApp (const v, [])]) => let o' := match o with shl bitwidth => mul bitwidth | shlZ => mulZ | _ => o (* impossible *) end in let bw := match o with shl bitwidth => Some bitwidth | shlZ => None | _ => None (* impossible *) end in @@ -3338,7 +3414,7 @@ Definition app_consts (d : dag) (o : op) (ls : list (expr * Z)) : list expr | _ => default end else default) ls. -Definition combine_consts_pre (d : dag) : expr -> expr := +Definition combine_consts_pre [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := fun e => match e with ExprApp (o, args) => if commutative o && associative o && op_always_interps o then match combines_to o with | Some o' => match identity o' with @@ -3346,14 +3422,14 @@ Definition combine_consts_pre (d : dag) : expr -> expr := ExprApp (o, app_consts d o' (compress_consts d o (group_consts d (split_consts d o' idv args)))) | None => e end | None => e end else e | _ => e end%bool. -Definition cleanup_combine_consts (d : dag) : expr -> expr := +Definition cleanup_combine_consts [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := let simp_outside := List.fold_left (fun e f => f e) [flatten_associative d] in let simp_inside := List.fold_left (fun e f => f e) [constprop d;drop_identity d;unary_truncate d;truncate_small d] in fun e => simp_outside match e with ExprApp (o, args) => ExprApp (o, List.map simp_inside args) | _ => e end. -Definition combine_consts (d : dag) : expr -> expr := fun e => cleanup_combine_consts d (combine_consts_pre d e). +Definition combine_consts [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := fun e => cleanup_combine_consts d (combine_consts_pre d (reveal_expr_at_least d 3 e)). #[local] Instance describe_combine_consts : description_of Rewrite.combine_consts := "Rewrites expressions like (x + x * 5) into (x * 6)". @@ -3715,11 +3791,13 @@ Qed. Global Instance cleanup_combine_consts_Ok : Ok cleanup_combine_consts. Proof. - repeat (step; eauto; []); cbn [fold_left]. - repeat match goal with - | [ |- eval _ _ (?r ?d ?e) _ ] - => apply (_:Ok r); try assumption - end. + repeat first [ match goal with + | [ |- eval _ _ (?r ?opts ?d ?e) _ ] + => apply (_:Ok r); try assumption + end + | progress cbn [fold_left] + | step; eauto; [] ]. + all: [ > ]. econstructor; [ | eassumption ]. rewrite Forall2_map_l. rewrite !@Forall2_forall_iff_nth_error in *; cbv [option_eq] in *. @@ -3730,8 +3808,8 @@ Proof. break_innermost_match; eauto. cbn [fold_left]. repeat lazymatch goal with - | H : eval ?c ?d ?e _ |- context[?r ?d ?e] => - let Hr := fresh in epose proof ((_:Ok r) _ _ _ _ ltac:(eassumption) H) as Hr; clear H + | H : eval ?c ?d ?e _ |- context[?r ?opts ?d ?e] => + let Hr := fresh in epose proof ((_:Ok r) _ _ _ _ _ ltac:(eassumption) H) as Hr; clear H end. assumption. Qed. @@ -3767,7 +3845,7 @@ Global Instance combine_consts_Ok : Ok combine_consts. Proof. repeat step; apply cleanup_combine_consts_Ok, combine_consts_pre_Ok; assumption. Qed. (* M-x query-replace-regex RET \(| RewritePass\.\)\([^ ]*\) => _ RET \1\2 => \2 *) -Definition named_pass (name : RewritePass.rewrite_pass) : dag -> expr -> expr +Definition named_pass (name : RewritePass.rewrite_pass) : forall [opts : symbolic_options_computed_opt], dag -> expr -> expr := match name with | RewritePass.addbyte_small => addbyte_small | RewritePass.addcarry_bit => addcarry_bit @@ -3802,17 +3880,20 @@ Proof. all: fail_if_goals_remain (). Defined. -Definition expr {rewriting_passes : rewriting_passes_opt} (d : dag) : expr -> expr := +Notation named_passf := (fun n => @named_pass n _). + +Definition expr [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := List.fold_left (fun e f => f d e) - (List.map named_pass rewriting_passes). + (List.map named_passf rewriting_passes). -Local Instance expr_Ok {rewriting_passes : rewriting_passes_opt} : Ok expr. +Local Instance expr_Ok : Ok expr. Proof. pose proof (@named_pass_Ok). - cbv [expr]; induction rewriting_passes; cbn [map fold_left]; cbv [Ok] in *; eauto. + cbv [expr]; intro opts. + induction rewriting_passes; cbn [map fold_left]; cbv [Ok] in *; eauto. Qed. -Lemma eval_expr {rewriting_passes : rewriting_passes_opt} c d e v : gensym_dag_ok c d -> eval c d e v -> eval c d (expr d e) v. +Lemma eval_expr {opts : symbolic_options_computed_opt} c d e v : gensym_dag_ok c d -> eval c d e v -> eval c d (expr d e) v. Proof. apply expr_Ok. Qed. diff --git a/src/CLI.v b/src/CLI.v index e595d28b74..8ff2021974 100644 --- a/src/CLI.v +++ b/src/CLI.v @@ -523,6 +523,10 @@ Module ForExtraction. := ([Arg.long_key "asm-node-reveal-depth"], Arg.Custom (parse_string_and parse_nat) "ℕ", ["The depth of nodes to reveal in the assembly equivalence checker. Only relevant when --hints-file is specified. In most situations, this should not have to be changed. Defaults to " ++ show default_node_reveal_depth ++ "."]). + Definition asm_symex_bounds_reveal_depth_spec : named_argT + := ([Arg.long_key "asm-symex-bounds-reveal-depth"], + Arg.Custom (parse_string_and parse_nat) "ℕ", + ["The depth of nodes to reveal in the assembly equivalence checker when deriving bounds for expressions via structure. Only relevant when --hints-file is specified. In most situations, this should not have to be changed. Defaults to " ++ show default_symex_bounds_reveal_depth ++ "."]). Definition asm_stack_size_spec : named_argT := ([Arg.long_key "asm-stack-size"], Arg.Custom (parse_string_and parse_N) "ℕ", @@ -739,6 +743,7 @@ Module ForExtraction. ; asm_rewriting_pipeline_spec ; asm_rewriting_passes_spec ; asm_node_reveal_depth_spec + ; asm_symex_bounds_reveal_depth_spec ; asm_debug_symex_asm_first_spec ; doc_text_before_function_name_spec ; doc_text_before_type_name_spec @@ -799,6 +804,7 @@ Module ForExtraction. , asm_rewriting_pipelinev , asm_rewriting_passesv , asm_node_reveal_depthv + , asm_symex_bounds_reveal_depthv , asm_debug_symex_asm_firstv , doc_text_before_function_namev , doc_text_before_type_namev @@ -892,6 +898,7 @@ Module ForExtraction. ; asm_rewriting_pass_filter := fun p => asm_rewriting_pass_filterv (show_rewrite_pass p) ; asm_debug_symex_asm_first := to_bool asm_debug_symex_asm_firstv ; asm_node_reveal_depth := to_nat_default asm_node_reveal_depthv default_node_reveal_depth + ; asm_symex_bounds_reveal_depth := to_nat_default asm_symex_bounds_reveal_depthv default_symex_bounds_reveal_depth |} |} ; ignore_unique_asm_names := negb (to_bool asm_error_on_unique_names_mismatchv) From 6e514e382aacb068d35dedd36f60f9cf0b865c5b Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Wed, 12 Mar 2025 20:34:36 -0700 Subject: [PATCH 6/8] Allow passing --asm-node-reveal-depth --- src/Assembly/Symbolic.v | 5 ++--- src/CLI.v | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Assembly/Symbolic.v b/src/Assembly/Symbolic.v index 62ccd5dd3a..3631f9d82d 100644 --- a/src/Assembly/Symbolic.v +++ b/src/Assembly/Symbolic.v @@ -404,10 +404,11 @@ Module Export Options. Class rewriting_passes_opt := rewriting_passes : list rewrite_pass. (** Should we symex the assembly first, even though this may be more inefficient? *) Class debug_symex_asm_first_opt := debug_symex_asm_first : bool. - (** How deep should we reveal nodes? *) + (** How deep should we preemptively reveal nodes? *) Class node_reveal_depth_opt := node_reveal_depth : nat. (** How much should we reveal expressions when deriving bounds for expressions via structure? *) Class symex_bounds_reveal_depth_opt := symex_bounds_reveal_depth : nat. + Definition default_node_reveal_depth := 1%nat. Definition default_symex_bounds_reveal_depth := 1%nat. Definition default_rewriting_passes {rewriting_pipeline : rewriting_pipeline_opt} @@ -423,8 +424,6 @@ Module Export Options. ; asm_symex_bounds_reveal_depth : symex_bounds_reveal_depth_opt }. - Definition default_node_reveal_depth := 3%nat. - (* This holds the list of computed options, which are passed around between methods *) Class symbolic_options_computed_opt := { asm_rewriting_passes : rewriting_passes_opt diff --git a/src/CLI.v b/src/CLI.v index 8ff2021974..a73a55d674 100644 --- a/src/CLI.v +++ b/src/CLI.v @@ -522,7 +522,7 @@ Module ForExtraction. Definition asm_node_reveal_depth_spec : named_argT := ([Arg.long_key "asm-node-reveal-depth"], Arg.Custom (parse_string_and parse_nat) "ℕ", - ["The depth of nodes to reveal in the assembly equivalence checker. Only relevant when --hints-file is specified. In most situations, this should not have to be changed. Defaults to " ++ show default_node_reveal_depth ++ "."]). + ["The depth of nodes to preemptively reveal in the assembly equivalence checker. Only relevant when --hints-file is specified. In most situations, this should not have to be changed, and, moreover, changing it to any value > 1 should not make much difference. Defaults to " ++ show default_node_reveal_depth ++ "."]). Definition asm_symex_bounds_reveal_depth_spec : named_argT := ([Arg.long_key "asm-symex-bounds-reveal-depth"], Arg.Custom (parse_string_and parse_nat) "ℕ", From af3fd807023cd5833c15d39ca50005d9d9457c34 Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Wed, 12 Mar 2025 20:35:25 -0700 Subject: [PATCH 7/8] Disable failing assembly tests --- .../{clang_19_1_0_O0.asm => clang_19_1_0_O0.asm.disabled} | 0 .../{gcc_14_1_0.asm => gcc_14_1_0.asm.disabled} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename fiat-amd64/fiat_curve25519_sub/{clang_19_1_0_O0.asm => clang_19_1_0_O0.asm.disabled} (100%) rename fiat-amd64/fiat_curve25519_sub/{gcc_14_1_0.asm => gcc_14_1_0.asm.disabled} (100%) diff --git a/fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm b/fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm.disabled similarity index 100% rename from fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm rename to fiat-amd64/fiat_curve25519_sub/clang_19_1_0_O0.asm.disabled diff --git a/fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm b/fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm.disabled similarity index 100% rename from fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm rename to fiat-amd64/fiat_curve25519_sub/gcc_14_1_0.asm.disabled From 2f6d0aaf77100ac058c77b6fce6470c777154a51 Mon Sep 17 00:00:00 2001 From: Jason Gross Date: Wed, 12 Mar 2025 22:36:49 -0700 Subject: [PATCH 8/8] WIP on equality test --- src/Assembly/Symbolic.v | 194 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 188 insertions(+), 6 deletions(-) diff --git a/src/Assembly/Symbolic.v b/src/Assembly/Symbolic.v index 3631f9d82d..abdfbb634c 100644 --- a/src/Assembly/Symbolic.v +++ b/src/Assembly/Symbolic.v @@ -45,6 +45,7 @@ Require Import Crypto.Util.ListUtil.FoldMap. Import FoldMap.List. Require Import Crypto.Util.ListUtil.IndexOf. Import IndexOf.List. Require Import Crypto.Util.ListUtil.Forall. Require Import Crypto.Util.ListUtil.Permutation. +Require Import Crypto.Util.ListUtil.Injective. Import Injective.List. Require Import Crypto.Util.ListUtil.Partition. Require Import Crypto.Util.ListUtil.Filter. Require Import Crypto.Util.ListUtil.PermutationCompat. Import ListUtil.PermutationCompat.Coq.Sorting.Permutation. @@ -1937,6 +1938,28 @@ Section WithDag. (_:interp_op ctx op args' = Some n) : eval_node (op, args) n. + Definition expr_dag_beq_fueled_step (expr_dag_beq_fueled : expr -> expr -> bool) := + fix expr_dag_beq (e1 e2 : expr) : bool := + match e1, e2 with + | ExprRef i, ExprRef j => N.eqb i j + | ExprApp (op1, args1), ExprApp (op2, args2) => + op_beq op1 op2 && list_beq _ expr_dag_beq args1 args2 + | _, _ => match reveal_expr 1 e1, reveal_expr 1 e2 with + | ExprRef i, ExprRef j => N.eqb i j + | ExprApp (op1, args1), ExprApp (op2, args2) => + op_beq op1 op2 && list_beq _ expr_dag_beq_fueled args1 args2 + | _, _ => false + end end. + + Fixpoint expr_dag_beq_fueled (fuel : nat) (e1 e2 : expr) : bool := + expr_dag_beq_fueled_step + match fuel with + | O => expr_beq + | S fuel => expr_dag_beq_fueled fuel + end + e1 e2. + + Definition expr_dag_beq e1 e2 := expr_dag_beq_fueled (S (N.to_nat (dag.size dag))) e1 e2. Section eval_ind. Context (P : expr -> Z -> Prop) @@ -2052,6 +2075,165 @@ Section WithDag. all: eapply Forall2_weaken; [ | eassumption ]; cbv beta. all: intros; destruct_head'_and; eauto. } Qed. + + Lemma reveal0 : forall e, reveal 0 e = ExprRef e. + Proof using Type. reflexivity. Qed. + + Lemma reveal_expr0 : forall e, reveal_expr 0 e = e. + Proof using Type. destruct e; cbn [reveal_expr]; break_innermost_match; now rewrite ?reveal0. Qed. + + Lemma expr_dag_beq_fueled_step_refl + expr_dag_beq_fueled (Hexpr_dag_beq_fueled : forall e, expr_dag_beq_fueled e e = true) + : forall e, expr_dag_beq_fueled_step expr_dag_beq_fueled e e = true. + Proof using Type. + induction e; cbn; break_innermost_match; rewrite ?Bool.andb_true_iff; try split. + all: try apply unreflect_bool; try reflexivity. + cbn in *. + let H := match goal with H : Forall _ _ |- _ => H end in + induction H; cbn; trivial. + rewrite Bool.andb_true_iff. + split; eauto. + Qed. + + Lemma expr_dag_beq_fueled_refl : forall fuel e, expr_dag_beq_fueled fuel e e = true. + Proof using Type. + induction fuel as [|fuel IH]; cbn. + all: apply expr_dag_beq_fueled_step_refl; eauto. + intros; apply unreflect_bool; reflexivity. + Qed. + + Lemma expr_dag_beq_fueled_step_sym_iff + expr_dag_beq_fueled (Hexpr_dag_beq_fueled : forall e1 e2, expr_dag_beq_fueled e1 e2 = true <-> expr_dag_beq_fueled e2 e1 = true) + : forall e1 e2, expr_dag_beq_fueled_step expr_dag_beq_fueled e1 e2 = true <-> expr_dag_beq_fueled_step expr_dag_beq_fueled e2 e1 = true. + Proof using Type. + clear ctx. + induction e1, e2; cbn; break_innermost_match; try tauto. + all: rewrite ?Bool.andb_true_iff. + all: try (split; intro; reflect_hyps; subst; try apply unreflect_bool; try reflexivity; assumption). + all: match goal with + | [ |- ?a /\ ?b <-> ?a' /\ ?b' ] + => cut ((a <-> a') /\ (b <-> b')); [ tauto | ] + end. + all: split; [ split; intros; reflect_hyps; subst; apply unreflect_bool; reflexivity | ]. + all: cbv [reveal_step] in *; break_innermost_match_hyps. + all: lazymatch goal with + | [ H : ExprApp _ = ExprApp _ |- _ ] => inversion H; clear H + | [ H : ExprRef _ = ExprApp _ |- _ ] => inversion H + | [ H : ExprApp _ = ExprRef _ |- _ ] => inversion H + | _ => idtac + end. + all: subst. + all: try erewrite (@map_ext _ _ (reveal_expr 0)), map_id in * by (intros; apply reveal_expr0). + all: cbn [snd] in *. + all: match goal with + | [ |- list_beq _ _ ?x ?y = true <-> list_beq _ _ ?y ?x = true ] + => generalize y; induction x as [|?? IH']; let y := fresh in intro y; destruct y + end. + all: cbn [list_beq snd]; try tauto. + all: try match goal with H : Forall _ (_ :: _) |- _ => inversion H; clear H end. + all: rewrite !Bool.andb_true_iff, IH'. + all: cbn [snd] in *. + all: try (eapply Forall_weaken; [ | eassumption ]). + all: eauto. + all: lazymatch goal with + | [ |- ?a /\ ?b <-> ?a' /\ ?b' ] + => cut ((a <-> a') /\ (b <-> b')); [ tauto | ] + end. + all: try (split; try tauto; eauto). + Qed. + + Lemma expr_dag_beq_fueled_sym_iff : forall fuel e1 e2, expr_dag_beq_fueled fuel e1 e2 = true <-> expr_dag_beq_fueled fuel e2 e1 = true. + Proof using Type. + induction fuel as [|fuel IH]; cbn. + all: apply expr_dag_beq_fueled_step_sym_iff; eauto. + intros; split; intros; reflect_hyps; apply unreflect_bool; subst; reflexivity. + Qed. + + Lemma expr_dag_beq_fueled_sym : forall fuel e1 e2, expr_dag_beq_fueled fuel e1 e2 = expr_dag_beq_fueled fuel e2 e1. + Proof using Type. + intros fuel e1 e2. + generalize (@expr_dag_beq_fueled_sym_iff fuel e1 e2). + do 2 destruct expr_dag_beq_fueled; intros; repeat split; destruct_head' iff; try tauto. + symmetry; tauto. + Qed. + + Lemma eval_expr_dag_beq_fueled_step_impl + expr_dag_beq_fueled (Hexpr_dag_beq_fueled : forall e v, eval e v -> forall e', expr_dag_beq_fueled e e' = true -> eval e' v) + + : forall e v, eval e v -> forall e', expr_dag_beq_fueled_step expr_dag_beq_fueled e e' = true -> eval e' v. + Proof using Type. + induction 1, e'; cbn; cbv [reveal_step]. + all: break_innermost_match; intros; reflect_hyps; subst. + all: try now exfalso. + all: repeat first [ progress subst + | progress inversion_option + | progress inversion_pair + | progress destruct_head'_and + | exfalso; assumption + | rewrite Bool.andb_true_iff in * + | progress cbv [reveal_step] in * + | progress reflect_hyps + | progress break_innermost_match_hyps + | erewrite (@map_ext _ _ (reveal_expr 0)), map_id in * by (intros; apply reveal_expr0) ]. + all: try solve [ econstructor; try eassumption; eapply Forall2_weaken; [ | eassumption ]; cbv beta; try tauto ]. + all: econstructor; try eassumption. + all: rewrite ?Forall2_map_l in *. + all: try match goal with H : dag.lookup _ _ = Some _ |- _ => clear H end. + all: try match goal with H : interp_op _ _ _ = Some _ |- _ => clear H end. + all: lazymatch goal with + | [ H : Forall2 _ ?x ?y |- Forall2 _ ?x' ?y' ] + => (revert dependent x' + idtac); (revert dependent y' + idtac); induction H; cbn in *; intros + end. + all: subst. + all: break_innermost_match_hyps; try congruence. + all: try solve [ constructor ]. + all: lazymatch goal with + | [ H : List.map _ ?x = [] |- _ ] => is_var x; destruct x + | [ H : List.map _ ?x = _ :: _ |- _ ] => is_var x; destruct x + | _ => idtac + end. + all: cbn in *. + all: inversion_list. + all: repeat first [ progress subst + | progress inversion_option + | progress inversion_pair + | progress destruct_head'_and + | exfalso; assumption + | rewrite Bool.andb_true_iff in * + | progress cbv [reveal_step] in * + | progress reflect_hyps + | progress break_innermost_match_hyps + | solve [ constructor; eauto ] + | erewrite (@map_ext _ _ (reveal_expr 0)), map_id in * by (intros; apply reveal_expr0) ]. + Qed. + + Lemma eval_expr_dag_beq_fueled_impl : forall fuel e v, eval e v -> forall e', expr_dag_beq_fueled fuel e e' = true -> eval e' v. + Proof using Type. + induction fuel as [|fuel IH]; cbn. + all: apply eval_expr_dag_beq_fueled_step_impl; eauto. + intros; intros; reflect_hyps; subst; eauto. + Qed. + + Lemma eval_expr_dag_beq_fueled : forall fuel e e', expr_dag_beq_fueled fuel e e' = true -> forall v, eval e v <-> eval e' v. + Proof using Type. + split; intro; eapply eval_expr_dag_beq_fueled_impl; eauto. + eapply expr_dag_beq_fueled_sym_iff; eauto. + Qed. + + Lemma expr_dag_beq_refl : forall e, expr_dag_beq e e = true. + Proof using Type. apply expr_dag_beq_fueled_refl. Qed. + + Lemma expr_dag_beq_sym_iff : forall e1 e2, expr_dag_beq e1 e2 = true <-> expr_dag_beq e2 e1 = true. + Proof using Type. apply expr_dag_beq_fueled_sym_iff. Qed. + + Lemma expr_dag_beq_sym : forall e1 e2, expr_dag_beq e1 e2 = expr_dag_beq e2 e1. + Proof using Type. apply expr_dag_beq_fueled_sym. Qed. + + Lemma eval_expr_dag_beq_impl : forall e v, eval e v -> forall e', expr_dag_beq e e' = true -> eval e' v. + Proof using Type. apply eval_expr_dag_beq_fueled_impl. Qed. + + Lemma eval_expr_dag_beq : forall e e', expr_dag_beq e e' = true -> forall v, eval e v <-> eval e' v. + Proof using Type. apply eval_expr_dag_beq_fueled. Qed. End WithDag. Definition merge_node {descr : description} (n : node idx) : dag.M idx @@ -3352,7 +3534,7 @@ Proof. t; cbn in *; rewrite ?Z.shiftl_mul_pow2, ?Z.land_0_r by lia; repeat (lia Definition split_consts (d : dag) (o : op) (i : Z) : list expr -> list (expr * Z) := List.map (fun e - => match e with + => match reveal_expr_at_least d 2 e with | ExprApp (o', args) => if op_beq o' o then @@ -3387,7 +3569,7 @@ Definition group_consts (d : dag) (ls : list (expr * Z)) : list (expr * list Z) | [] => None | (e, z) :: xs => Some (e, z :: List.map snd xs) end) - (List.groupAllBy (fun x y => expr_beq (fst x) (fst y)) ls). + (List.groupAllBy (fun x y => expr_dag_beq d (fst x) (fst y)) ls). (* o is like add *) (* spec: if interp0_op o zs is always Some _, then Forall2 (fun '(e, zs) '(e', z) => e = e' /\ interp0_op o zs = Some z) input output *) @@ -3405,7 +3587,7 @@ Definition app_consts (d : dag) (o : op) (ls : list (expr * Z)) : list expr := List.map (fun '(e, z) => let z := ExprApp (const z, []) in let default := ExprApp (o, [e; z]) in if associative o - then match e with + then match reveal_expr_at_least d 1 e with | ExprApp (o', args) => if op_beq o' o then ExprApp (o, args ++ [z]) @@ -3414,7 +3596,7 @@ Definition app_consts (d : dag) (o : op) (ls : list (expr * Z)) : list expr ls. Definition combine_consts_pre [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := - fun e => match e with ExprApp (o, args) => + fun e => match reveal_expr_at_least d 1 e with ExprApp (o, args) => if commutative o && associative o && op_always_interps o then match combines_to o with | Some o' => match identity o' with | Some idv => @@ -3424,11 +3606,11 @@ Definition combine_consts_pre [opts : symbolic_options_computed_opt] (d : dag) : Definition cleanup_combine_consts [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := let simp_outside := List.fold_left (fun e f => f e) [flatten_associative d] in let simp_inside := List.fold_left (fun e f => f e) [constprop d;drop_identity d;unary_truncate d;truncate_small d] in - fun e => simp_outside match e with ExprApp (o, args) => + fun e => simp_outside match reveal_expr_at_least d 1 e with ExprApp (o, args) => ExprApp (o, List.map simp_inside args) | _ => e end. -Definition combine_consts [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := fun e => cleanup_combine_consts d (combine_consts_pre d (reveal_expr_at_least d 3 e)). +Definition combine_consts [opts : symbolic_options_computed_opt] (d : dag) : expr -> expr := fun e => cleanup_combine_consts d (combine_consts_pre d e). #[local] Instance describe_combine_consts : description_of Rewrite.combine_consts := "Rewrites expressions like (x + x * 5) into (x * 6)".