Awesome
Rewriter
Publications
- Jason Gross, Andres Erbsen, Miraya Poddar-Agrawal, Jade Philipoom, Adam Chlipala. Accelerating Verified-Compiler Development with a Verified Rewriting Engine. Proceedings of the Interactive Theorem Proving - Thirteenth International Conference (ITP'22). August 2022.
- Jason Gross, Andres Erbsen, Jade Philipoom, Rajashree Agrawal, and Adam Chlipala. Towards Towards a Scalable Proof Engine: A Performant Prototype Rewriting Primitive for Coq. Journal of Automated Reasoning, 68(3):19, Aug 2024.
Building
This repository requires Coq 8.17 or later, and requires that the version of OCaml used to build Coq be installed and accessible on the system.
Git submodules are used for some dependencies. If you did not clone with --recursive
, run
git submodule update --init --recursive
To build:
make
Usage
The entry point is src/Rewriter/Util/plugins/RewriterBuild.v
.
(Better entry point coming in the future.)
There are some examples in src/Rewriter/Rewriter/Examples.v
.
The examples from the paper are in src/Rewriter/Rewriter/Examples/PerfTesting/
.
On the branch with performance data, the perf-*.txt
files in the top-level directory contain selected performance data in two-column format, while the perf*.csv
files contain all performance data in CSV format.
The logs with raw timing data live in the subdirectories of src/Rewriter/Rewriter/Examples/PerfTesting/
, and were made with the targets perf-SuperFast
, perf-Fast
, and perf-Medium
targets.
The text and csv files were generated with make perf-csv
.
(The targets perf-Slow
and perf-VerySlow
exist but take a very, very, very long time to run.)
License
This project is distributed under the terms of the MIT License, the Apache License (Version 2.0), and the BSD 1-Clause License; users may pick which license to apply.
See COPYRIGHT
, LICENSE-MIT
, LICENSE-APACHE
, and LICENSE-BSD-1
for details.
Reading The Code
Actual Synthesis Pipeline
The entry point for clients of the PHOAS expressions we use is
Language/API.v
. Refer to comments in that file for an explanation
of the interface; the following text describes how the expressions are
generated, not how to interact with them.
The ordering of files (eliding *Proofs.v
files) is:
Language/*.v
↑
Rewriter/*.v
Within each directory, the dependency graphs (again eliding *Proofs.v
and related files) are:
Within Language/
:
PreCommon.v ←─────────── Pre.v
↑ ↑
Language.v ←── IdentifiersBasicLibrary.v ←──── IdentifiersBasicGenerate.v
↑ ↑
├────────────────┐ └────────────────────────────┐
UnderLets.v IdentifiersLibrary.v ←──────────── IdentifiersGenerate.v
↑ ↑
IdentifiersLibraryProofs.v ←─── IdentifiersGenerateProofs.v
We will come back to the Rewriter/*
files shortly.
The files contain:
-
Rewriter/*.v
: rewrite rules, rewriting. -
Inside
Language/
:-
PreCommon.v
: A few definitions which are used in writing out rewrite rules and the interpretations of PHOAS identifiers, e.g.,ident.cast
,ident.eagerly
, etc. -
Pre.v
: A few definitions which are used in writing out rewrite rules, which are not used in the parameterized rewriter, only in reifying rewrite rules. -
Language.v
: Defines parts of the PHOAS basic infrastructure parameterized over base types and identifiers including:- PHOAS
- reification
- denotation/intepretation
- utilities for inverting PHOAS exprs
- default/dummy values of PHOAS exprs
- default instantiation of generic PHOAS types
- gallina reification of ground terms
- Flat/indexed syntax trees, and conversions to and from PHOAS
Defines the passes:
- ToFlat
- FromFlat
- GeneralizeVar
-
IdentifiersBasicLibrary.v
: Defines the package type holding basic identifier definitions. -
IdentifiersBasicGenerate.v
: Defines the tactics that generate all of the identifier-list-specific definitions used by the PHOAS machinery, in addition to defining the tactics that do reification based on the generated package. -
UnderLets.v
: the UnderLets monad, a pass that does substitution of var-like things, a pass that inserts let-binders in the next-to-last line of code, substituting away var-like things (this is used to ensure that when we output C code, aliasing the input and the output arrays doesn't cause issues). Defines the passes:- SubstVar
- SubstVarLike
- SubstVarOrIdent
The following files in
Language/
are used only by the rewriter:-
IdentifiersLibrary.v
: Some definitions about identifiers and pattern identifiers and raw identifiers. Some of these definitions take generated definitions as arguments. Also defines a package record to hold all of the generated definitions. -
IdentifiersGenerate.v
: Tactics to generate definitions about untyped and pattern versions of identifiers for the rewriter. Culminates in a tactic which inhabits the package type defined inIdentifiersLibrary.v
-
IdentifiersLibraryProofs.v
: proofs about definitions in IdentifiersLibrary. Also defines a package to hold generated proofs that require destructing inductives not yet defined in this file. -
IdentifiersGenerateProofs.v
: tactics to prove lemmas to inhabit the package defined inIdentifiersLibraryProofs.v
-
The files defined in Rewriter/
are split up into the following
dependency graph (including some files from Language/
at the top):
IdentifiersLibrary.v ←───────────────────────── IdentifiersGenerate.v
↑ ↑ ↑
│ └──────────────── IdentifiersLibraryProofs.v ←──────┴─ IdentifiersGenerateProofs.v
│ ↑
│ │
│ │
│ │
│ │
Rewriter.v ←────────────────────── ProofsCommon.v ←──────────────────── ProofsCommonTactics.v
↑ ↗ ↖ ↑
Reify.v ←──────────────┐ Wf.v InterpProofs.v │
│ ↖ ↗ │
└──────────── AllTactics.v ──────────────────────────────┘
-
Rewriter.v
: Defines the rewriter machinery. In particular, all of the rewriter definitions that have non-rewrite-rule-specific proofs about them are found in this file. -
RewrierReify.v
: Defines reification of rewrite rules, continuing on fromRewriter.v
, and culminates in the tacticRewriteRules.Tactic.Build_RewriterT
and the tactic notationmake_Rewriter
which define a package of typeRewriteRules.GoalType.RewriterT
. TheBuild_*
tactic returns aconstr
, while themake_*
tactic notation refines thatconstr
in the goal. Both tactics take two arguments: first a booleaninclude_interp
which specifies whether (true
) or not (false
) to prefix the list of rewrite rules with the reduction-to-literal rewrite rules; and second a list ofbool * Prop
which is the list of rewrite rule types to reify, each paired with a boolean saying whether or not to try rewriting again in the output of the replacement for that rule. -
ProofsCommon.v
: Defines the notion of interp-goodness and wf-goodness for rewrite rules, defines tactics to prove these notions, and contains a semi-arbitrary collection of proofs and definitions that are mostly shared between the wf proofs and the interp proofs. Importantly, this file defines everything needed to state and prove that specific rewrite rules are correct. Additionally defines a packageRewriteRules.GoalType.VerifiedRewriter
which describes the type of the overall specialized rewriter together with itsWf
andInterp
proofs. (This package should perhaps move to another file?) -
ProofsCommonTactics.v
: Defines the actual tactics used to prove that specific rewrite rules are correct, and to inhabit the packages defined inProofsCommon.v
. -
Wf.v
: Proves wf-preservation of the generic rewriter, taking in wf-goodness of rewrite rules as a hypothesis. -
InterpProofs.v
: Proves interp-correctness of the generic rewriter, taking in interp-goodness of rewrite rules as a hypothesis. -
AllTactics.v
: Defines the tacticRewriteRules.Tactic.make_rewriter
(and a similar tactic notation) which build the entireVerifiedRewriter
. They take in theinclude_interp
as inRewriter.v
tactics, as well as an hlist of proofs of rewrite rules indexed over alist (bool * Prop)
of rewrite rule types. This is the primary interface for defining a rewriter from a list of rewrite rules.
Proofs files:
For Language.v
, there is a semi-arbitrary split between two files
Language.Inversion
and Language.Wf
.
-
Inversion.v
:- classifies equality of type codes and exprs
- type codes have decidable equality
- correctness of the various type-transport definitions
- correctness lemmas for the various
expr.invert_*
definitions - correctness lemmas for the various
reify_*
definitions inLanguage.v
inversion_type
, which inverts equality of type codestype_beq_to_eq
, which converts boolean equality of types to Leibniz equalityrewrite_type_transport_correct
, which rewrites with the correctness lemmas of the various type-transport definitionstype.invert_one e
which does case analysis on any inductive type indexed over type codes, in a way that preserves information about the type ofe
, and generally works even when the goal is dependently typed overe
and/or its typeident.invert
, which does case-anaylsis on idents whose type has structure (i.e., is not a var)ident.invert_match
, which does case-analysis on idents appearing as the discriminee of amatch
in the goal or in any hypothesisexpr.invert
, which does case-anaylsis on exprs whose type has structure (i.e., is not a var)expr.invert_match
, which does case-analysis on exprs appearing as the discriminee of amatch
in the goal or in any hypothesisexpr.invert_subst
, which does case-analysis on exprs which show up in hypotheses of the formexpr.invert_* _ = Some _
expr.inversion_expr
, which inverts equalities of exprs
-
Wf.v
: Depends onInversion.v
Defines:- expr.wf, expr.Wf, expr.wf3, expr.wf4, expr.Wf3, expr.Wf4
- GeneralizeVar.Flat.wf
expr.inversion_wf
(and variants), which invertwf
hypothesesexpr.wf_t
(and variants wf_unsafe_t and wf_safe_t) which make progress onwf
goals;wf_safe_t
should never turn a provable goal into an unprovable one, whilewf_unsafe_t
might.expr.interp_t
(and variants), which should make progress on equivalence-of-interp hypotheses and goals, but is not used much (mainly because I forgot I had defined it)prove_Wf
, which proves wf goals on concrete syntax trees in a more optimized way thanrepeat constructor
Proves:- funext → (type.eqv ↔ Logic.eq) (
eqv_iff_eq_of_funext
) - type.related and type.eqv are PERs
- Proper instances for
type.app_curried
,type.and_for_each_lhs_of_arrow
type.is_not_higher_order
→ Reflexive (type.and_for_each_lhs_of_arrow type.eqv)- iff between
type.related{,_hetero}
and related oftype.app_curried
- various properties of
type.and{,b_bool}for_each_lhs_of_arrow
- various properties of
type.eqv
andident.{gen_,}interp
- various properties of
ident.cast
- various properties of
expr.wf
(particularly of things defined inLanguage.v
) - interp and wf proofs for the passes to/from Flat
-
UnderLetsProofs.v
: wf and interp lemmas for the various passes defined inUnderLets.v
Extended Walkthrough
There's an extended description of much of the codebase in rewriting-extended.md
.