diff options
author | Timothy Bourke <tim@tbrk.org> | 2020-04-21 11:53:27 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-21 11:53:27 +0200 |
commit | e19f81cecf4a7cca67d8491fcb4c0a259c232bfb (patch) | |
tree | d046ca1d6ae2f90f119214accbabd0730a327ced | |
parent | 04b822ca7eaf59a967b9b8f700104b78e77e5c98 (diff) | |
parent | 4bda31427187fce0468004738a214830191ccda5 (diff) | |
download | compcert-kvx-e19f81cecf4a7cca67d8491fcb4c0a259c232bfb.tar.gz compcert-kvx-e19f81cecf4a7cca67d8491fcb4c0a259c232bfb.zip |
Merge pull request #1 from Lionel-Rieg/v3.7-velus
V3.7 velus
307 files changed, 28881 insertions, 7791 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..02ab53c1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Files that should be ignored by Github linguist +test/* linguist-vendored +doc/* linguist-documentation @@ -1,5 +1,7 @@ # Object files, in general *.vo +*.vok +*.vos *.glob *.o *.a @@ -40,6 +42,9 @@ /riscV/ConstpropOp.v /riscV/SelectOp.v /riscV/SelectLong.v +/aarch64/ConstpropOp.v +/aarch64/SelectOp.v +/aarch64/SelectLong.v /backend/SelectDiv.v /backend/SplitLong.v /cparser/Parser.v @@ -1,3 +1,103 @@ +Release 3.7, 2020-03-31 +======================= + +ISO C conformance: +- Functions declared `extern` then implemented `inline` remain `extern` +- The type of a wide char constant is `wchar_t`, not `int` +- Support vertical tabs and treat them as whitespace +- Define the semantics of `free(NULL)` + +Bug fixing: +- Take sign into account for conversions from 32-bit integers to 64-bit pointers +- PowerPC: more precise determination of small data accesses +- AArch64: when addressing global variables, check for correct alignment +- PowerPC, ARM: double rounding error in int64->float32 conversions + +ABI conformance: +- x86, AArch64: re-normalize values of small integer types returned by + function calls +- PowerPC: `float` arguments passed on stack are passed in 64-bit format +- RISC-V: use the new ELF psABI instead of the old ABI from ISA 2.1 + +Usability and diagnostics: +- Unknown builtin functions trigger a specific error message +- Improved error messages + +Coq formalization: +- Revised modeling of the PowerPC/EREF `isel` instruction +- Weaker `ec_readonly` condition over external calls + (permissions can be dropped on read-only locations) + +Coq and OCaml development: +- Compatibility with Coq version 8.10.1, 8.10.2, 8.11.0 +- Compatibility with OCaml 4.10 and up +- Compatibility with Menhir 20200123 and up +- Coq versions prior to 8.8.0 are no longer supported +- OCaml versions prior to 4.05.0 are no longer supported + + +Release 3.6, 2019-09-17 +======================= + +New features and optimizations: +- New port targeting the AArch64 architecture: ARMv8 in 64-bit mode. +- New optimization: if-conversion. Some `if`/`else` statements + and `a ? b : c` conditional expressions are compiled to branchless + conditional move instructions, when supported by the target processor +- New optimization flag: `-Obranchless`, to favor the generation of + branchless instruction sequences, even if probably slower than branches. +- Built-in functions can now be given a formal semantics within + CompCert, instead of being treated as I/O interactions. + Currently, `__builtin_fsqrt` and `__builtin_bswap*` have semantics. +- Extend constant propagation and CSE optimizations to built-in + functions that have known semantics. +- New "polymorphic" built-in function: `__builtin_sel(a,b,c)`. + Similar to `a ? b : c` but `b` and `c` are always evaluated, + and a branchless conditional move instruction is produced if possible. +- x86 64 bits: faster, branchless instruction sequences are produced + for conversions between `double` and `unsigned int`. +- `__builtin_bswap64` is now available for all platforms. + +Usability and diagnostics: +- Improved the DWARF debug information generated in -g mode. +- Added options -fcommon and -fno-common to control the generation + of "common" declarations for uninitialized global. +- Check for reserved keywords `_Complex` and `_Imaginary`. +- Reject function declarations with multiple `void` parameters. +- Define macros `__COMPCERT_MAJOR__`, `__COMPCERT_MINOR__`, and + `__COMPCERT_VERSION__` with CompCert's version number. (#284) +- Prepend `$(DESTDIR)` to the installation target. (#169) +- Extended inline asm: print register names according to the + types of the corresponding arguments (e.g. for x86_64, + `%eax` if int and `%rax` if long). + +Bug fixing: +- Introduce distinct scopes for iteration and selection statements, + as required by ISO C99. +- Handle dependencies in sequences of declarations + (e.g. `int * x, sz = sizeof(x);`). (#267) +- Corrected the check for overflow in integer literals. +- On x86, __builtin_fma was producing wrong code in some cases. +- `float` arguments to `__builtin_annot` and `__builtin_ais_annot` + were uselessly promoted to type `double`. + +Coq formalization and development: +- Improved C parser based on Menhir version 20190626: + fewer run-time checks, faster validation, no axioms. (#276) +- Compatibility with Coq versions 8.9.1 and 8.10.0. +- Compatibility with OCaml versions 4.08.0 and 4.08.1. +- Updated to Flocq version 3.1. +- Revised the construction of NaN payloads in processor descriptions + so as to accommodate FMA. +- Removed some definitions and lemmas from lib/Coqlib.v, using Coq's + standard library instead. + +The clightgen tool: +- Fix normalization of Clight `switch` statements. (#285) +- Add more tracing options: `-dprepro`, `-dall`. (#298) +- Fix the output of `-dclight`. (#314) + + Release 3.5, 2019-02-27 ======================= @@ -1,6 +1,6 @@ All files in this distribution are part of the CompCert verified compiler. -The CompCert verified compiler is Copyright by Institut National de +The CompCert verified compiler is Copyright by Institut National de Recherche en Informatique et en Automatique (INRIA) and AbsInt Angewandte Informatik GmbH. @@ -9,12 +9,12 @@ INRIA Non-Commercial License Agreement given below or under the terms of a Software Usage Agreement of AbsInt Angewandte Informatik GmbH. The latter is a separate contract document. -The INRIA Non-Commercial License Agreement is a non-free license that -grants you the right to use the CompCert verified compiler for -educational, research or evaluation purposes only, but prohibits +The INRIA Non-Commercial License Agreement is a non-free license that +grants you the right to use the CompCert verified compiler for +educational, research or evaluation purposes only, but prohibits any commercial use. -For commercial use you need a Software Usage Agreement from +For commercial use you need a Software Usage Agreement from AbsInt Angewandte Informatik GmbH. The following files in this distribution are dual-licensed both under @@ -38,7 +38,7 @@ option) any later version: cfrontend/Ctyping.v cfrontend/PrintClight.ml cfrontend/PrintCsyntax.ml - + backend/Cminor.v backend/PrintCminor.ml @@ -46,7 +46,7 @@ option) any later version: all files in the exportclight/ directory - the Archi.v, CBuiltins.ml, and extractionMachdep.v files + the Archi.v, CBuiltins.ml, and extractionMachdep.v files in directories arm, powerpc, riscV, x86, x86_32, x86_64 extraction/extraction.v @@ -64,11 +64,14 @@ non-commercial contexts, subject to the terms of the GNU General Public License. The files contained in the flocq/ directory and its subdirectories are -taken from the Flocq project, http://flocq.gforge.inria.fr/ -These files are Copyright 2010-2017 INRIA and distributed under the -terms of the GNU Lesser General Public Licence, either version 3 of -the licence, or (at your option) any later version. A copy of the GNU -Lesser General Public Licence version 3 is included below. +taken from the Flocq project, http://flocq.gforge.inria.fr/. The files +contained in the MenhirLib directory are taken from the Menhir +project, http://gallium.inria.fr/~fpottier/menhir/. The files from the +Flocq project and the files in the MenhirLib directory are Copyright +2010-2019 INRIA and distributed under the terms of the GNU Lesser +General Public Licence, either version 3 of the licence, or (at your +option) any later version. A copy of the GNU Lesser General Public +Licence version 3 is included below. The files contained in the runtime/ directory and its subdirectories are Copyright 2013-2017 INRIA and distributed under the terms of the BSD @@ -23,12 +23,14 @@ endif DIRS=lib common $(ARCHDIRS) backend cfrontend driver \ flocq/Core flocq/Prop flocq/Calc flocq/IEEE754 \ - exportclight cparser cparser/MenhirLib + exportclight MenhirLib cparser -RECDIRS=lib common $(ARCHDIRS) backend cfrontend driver flocq exportclight cparser +RECDIRS=lib common $(ARCHDIRS) backend cfrontend driver flocq exportclight \ + MenhirLib cparser COQINCLUDES=$(foreach d, $(RECDIRS), -R $(d) compcert.$(d)) +COQCOPTS ?= -w -undeclared-scope COQC="$(COQBIN)coqc" -q $(COQINCLUDES) $(COQCOPTS) COQDEP="$(COQBIN)coqdep" $(COQINCLUDES) COQDOC="$(COQBIN)coqdoc" @@ -53,7 +55,7 @@ FLOCQ=\ # General-purpose libraries (in lib/) VLIB=Axioms.v Coqlib.v Intv.v Maps.v Heaps.v Lattice.v Ordered.v \ - Iteration.v Integers.v Archi.v Fappli_IEEE_extra.v Floats.v \ + Iteration.v Zbits.v Integers.v Archi.v IEEE754_extra.v Floats.v \ Parmov.v UnionFind.v Wfsimpl.v \ Postorder.v FSetAVLplus.v IntvSets.v Decidableplus.v BoolEqual.v @@ -62,12 +64,12 @@ VLIB=Axioms.v Coqlib.v Intv.v Maps.v Heaps.v Lattice.v Ordered.v \ COMMON=Errors.v AST.v Linking.v \ Events.v Globalenvs.v Memdata.v Memtype.v Memory.v \ Values.v Smallstep.v Behaviors.v Switch.v Determinism.v Unityping.v \ - Separation.v + Separation.v Builtins0.v Builtins1.v Builtins.v # Back-end modules (in backend/, $(ARCH)/) BACKEND=\ - Cminor.v Op.v CminorSel.v \ + Cminor.v Cminortyping.v Op.v CminorSel.v \ SelectOp.v SelectDiv.v SplitLong.v SelectLong.v Selection.v \ SelectOpproof.v SelectDivproof.v SplitLongproof.v \ SelectLongproof.v Selectionproof.v \ @@ -103,16 +105,16 @@ CFRONTEND=Ctypes.v Cop.v Csyntax.v Csem.v Ctyping.v Cstrategy.v Cexec.v \ Cshmgen.v Cshmgenproof.v \ Csharpminor.v Cminorgen.v Cminorgenproof.v -# LR(1) parser validator - -PARSERVALIDATOR=Alphabet.v Interpreter_complete.v Interpreter.v \ - Validator_complete.v Automaton.v Interpreter_correct.v Main.v \ - Validator_safe.v Grammar.v Interpreter_safe.v Tuples.v - # Parser PARSER=Cabs.v Parser.v +# MenhirLib + +MENHIRLIB=Alphabet.v Automaton.v Grammar.v Interpreter_complete.v \ + Interpreter_correct.v Interpreter.v Main.v Validator_complete.v \ + Validator_safe.v Validator_classes.v + # Putting everything together (in driver/) DRIVER=Compopts.v Compiler.v Complements.v @@ -120,7 +122,7 @@ DRIVER=Compopts.v Compiler.v Complements.v # All source files FILES=$(VLIB) $(COMMON) $(BACKEND) $(CFRONTEND) $(DRIVER) $(FLOCQ) \ - $(PARSERVALIDATOR) $(PARSER) + $(MENHIRLIB) $(PARSER) # Generated source files @@ -141,7 +143,6 @@ ifeq ($(CLIGHTGEN),true) $(MAKE) clightgen endif - proof: $(FILES:.v=.vo) # Turn off some warnings for compiling Flocq @@ -225,7 +226,7 @@ driver/Version.ml: VERSION cparser/Parser.v: cparser/Parser.vy @rm -f $@ - $(MENHIR) $(MENHIR_FLAGS) --coq cparser/Parser.vy + $(MENHIR) --coq --coq-lib-path compcert.MenhirLib --coq-no-version-check cparser/Parser.vy @chmod a-w $@ depend: $(GENERATED) depend1 @@ -235,29 +236,29 @@ depend1: $(FILES) exportclight/Clightdefs.v @$(COQDEP) $^ > .depend install: - install -d $(BINDIR) - install -m 0755 ./ccomp $(BINDIR) - install -d $(SHAREDIR) - install -m 0644 ./compcert.ini $(SHAREDIR) - install -d $(MANDIR)/man1 - install -m 0644 ./doc/ccomp.1 $(MANDIR)/man1 + install -d $(DESTDIR)$(BINDIR) + install -m 0755 ./ccomp $(DESTDIR)$(BINDIR) + install -d $(DESTDIR)$(SHAREDIR) + install -m 0644 ./compcert.ini $(DESTDIR)$(SHAREDIR) + install -d $(DESTDIR)$(MANDIR)/man1 + install -m 0644 ./doc/ccomp.1 $(DESTDIR)$(MANDIR)/man1 $(MAKE) -C runtime install ifeq ($(CLIGHTGEN),true) - install -m 0755 ./clightgen $(BINDIR) + install -m 0755 ./clightgen $(DESTDIR)$(BINDIR) endif ifeq ($(INSTALL_COQDEV),true) - install -d $(COQDEVDIR) + install -d $(DESTDIR)$(COQDEVDIR) for d in $(DIRS); do \ - install -d $(COQDEVDIR)/$$d && \ - install -m 0644 $$d/*.vo $(COQDEVDIR)/$$d/; \ + install -d $(DESTDIR)$(COQDEVDIR)/$$d && \ + install -m 0644 $$d/*.vo $(DESTDIR)$(COQDEVDIR)/$$d/; \ done - install -m 0644 ./VERSION $(COQDEVDIR) - @(echo "To use, pass the following to coq_makefile or add the following to _CoqProject:"; echo "-R $(COQDEVDIR) compcert") > $(COQDEVDIR)/README + install -m 0644 ./VERSION $(DESTDIR)$(COQDEVDIR) + @(echo "To use, pass the following to coq_makefile or add the following to _CoqProject:"; echo "-R $(COQDEVDIR) compcert") > $(DESTDIR)$(COQDEVDIR)/README endif clean: - rm -f $(patsubst %, %/*.vo, $(DIRS)) + rm -f $(patsubst %, %/*.vo*, $(DIRS)) rm -f $(patsubst %, %/.*.aux, $(DIRS)) rm -rf doc/html doc/*.glob rm -f driver/Version.ml diff --git a/Makefile.extr b/Makefile.extr index a1c2ef7c..5948bfc6 100644 --- a/Makefile.extr +++ b/Makefile.extr @@ -50,12 +50,12 @@ INCLUDES=$(patsubst %,-I %, $(DIRS)) # Control of warnings: WARNINGS=-w +a-4-9-27 -strict-sequence -safe-string -warn-error +a #Deprication returns with ocaml 4.03 -extraction/%.cmx: WARNINGS +=-w -20-27-32..34-39-41-44..45 -extraction/%.cmo: WARNINGS +=-w -20-27-32..34-39-41-44..45 +extraction/%.cmx: WARNINGS +=-w -20-27-32..34-39-41-44..45-60-67 +extraction/%.cmo: WARNINGS +=-w -20-27-32..34-39-41-44..45-60-67 cparser/pre_parser.cmx: WARNINGS += -w -41 cparser/pre_parser.cmo: WARNINGS += -w -41 -COMPFLAGS+=-g $(INCLUDES) $(MENHIR_INCLUDES) $(WARNINGS) +COMPFLAGS+=-g $(INCLUDES) -I "$(MENHIR_DIR)" $(WARNINGS) # Using .opt compilers if available diff --git a/Makefile.menhir b/Makefile.menhir index 98bfc750..7909b2f6 100644 --- a/Makefile.menhir +++ b/Makefile.menhir @@ -41,7 +41,11 @@ MENHIR_FLAGS = -v --no-stdlib -la 1 # Using Menhir in --table mode requires MenhirLib. ifeq ($(MENHIR_TABLE),true) - MENHIR_LIBS = menhirLib.cmx + ifeq ($(wildcard $(MENHIR_DIR)/menhirLib.cmxa),) + MENHIR_LIBS = menhirLib.cmx + else + MENHIR_LIBS = menhirLib.cmxa + endif else MENHIR_LIBS = endif diff --git a/MenhirLib/Alphabet.v b/MenhirLib/Alphabet.v new file mode 100644 index 00000000..29070e3d --- /dev/null +++ b/MenhirLib/Alphabet.v @@ -0,0 +1,247 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import Omega List Syntax Relations RelationClasses. + +Local Obligation Tactic := intros. + +(** A comparable type is equiped with a [compare] function, that define an order + relation. **) +Class Comparable (A:Type) := { + compare : A -> A -> comparison; + compare_antisym : forall x y, CompOpp (compare x y) = compare y x; + compare_trans : forall x y z c, + (compare x y) = c -> (compare y z) = c -> (compare x z) = c +}. + +Theorem compare_refl {A:Type} (C: Comparable A) : + forall x, compare x x = Eq. +Proof. +intros. +pose proof (compare_antisym x x). +destruct (compare x x); intuition; try discriminate. +Qed. + +(** The corresponding order is a strict order. **) +Definition comparableLt {A:Type} (C: Comparable A) : relation A := + fun x y => compare x y = Lt. + +Instance ComparableLtStrictOrder {A:Type} (C: Comparable A) : + StrictOrder (comparableLt C). +Proof. +apply Build_StrictOrder. +unfold Irreflexive, Reflexive, complement, comparableLt. +intros. +pose proof H. +rewrite <- compare_antisym in H. +rewrite H0 in H. +discriminate H. +unfold Transitive, comparableLt. +intros x y z. +apply compare_trans. +Qed. + +(** nat is comparable. **) +Program Instance natComparable : Comparable nat := + { compare := Nat.compare }. +Next Obligation. +symmetry. +destruct (Nat.compare x y) as [] eqn:?. +rewrite Nat.compare_eq_iff in Heqc. +destruct Heqc. +rewrite Nat.compare_eq_iff. +trivial. +rewrite <- nat_compare_lt in *. +rewrite <- nat_compare_gt in *. +trivial. +rewrite <- nat_compare_lt in *. +rewrite <- nat_compare_gt in *. +trivial. +Qed. +Next Obligation. +destruct c. +rewrite Nat.compare_eq_iff in *; destruct H; assumption. +rewrite <- nat_compare_lt in *. +apply (Nat.lt_trans _ _ _ H H0). +rewrite <- nat_compare_gt in *. +apply (gt_trans _ _ _ H H0). +Qed. + +(** A pair of comparable is comparable. **) +Program Instance PairComparable {A:Type} (CA:Comparable A) {B:Type} (CB:Comparable B) : + Comparable (A*B) := + { compare := fun x y => + let (xa, xb) := x in let (ya, yb) := y in + match compare xa ya return comparison with + | Eq => compare xb yb + | x => x + end }. +Next Obligation. +destruct x, y. +rewrite <- (compare_antisym a a0). +rewrite <- (compare_antisym b b0). +destruct (compare a a0); intuition. +Qed. +Next Obligation. +destruct x, y, z. +destruct (compare a a0) as [] eqn:?, (compare a0 a1) as [] eqn:?; +try (rewrite <- H0 in H; discriminate); +try (destruct (compare a a1) as [] eqn:?; + try (rewrite <- compare_antisym in Heqc0; + rewrite CompOpp_iff in Heqc0; + rewrite (compare_trans _ _ _ _ Heqc0 Heqc2) in Heqc1; + discriminate); + try (rewrite <- compare_antisym in Heqc1; + rewrite CompOpp_iff in Heqc1; + rewrite (compare_trans _ _ _ _ Heqc2 Heqc1) in Heqc0; + discriminate); + assumption); +rewrite (compare_trans _ _ _ _ Heqc0 Heqc1); +try assumption. +apply (compare_trans _ _ _ _ H H0). +Qed. + +(** Special case of comparable, where equality is Leibniz equality. **) +Class ComparableLeibnizEq {A:Type} (C: Comparable A) := + compare_eq : forall x y, compare x y = Eq -> x = y. + +(** Boolean equality for a [Comparable]. **) +Definition compare_eqb {A:Type} {C:Comparable A} (x y:A) := + match compare x y with + | Eq => true + | _ => false + end. + +Theorem compare_eqb_iff {A:Type} {C:Comparable A} {U:ComparableLeibnizEq C} : + forall x y, compare_eqb x y = true <-> x = y. +Proof. +unfold compare_eqb. +intuition. +apply compare_eq. +destruct (compare x y); intuition; discriminate. +destruct H. +rewrite compare_refl; intuition. +Qed. + +Instance NComparableLeibnizEq : ComparableLeibnizEq natComparable := Nat.compare_eq. + +(** A pair of ComparableLeibnizEq is ComparableLeibnizEq **) +Instance PairComparableLeibnizEq + {A:Type} {CA:Comparable A} (UA:ComparableLeibnizEq CA) + {B:Type} {CB:Comparable B} (UB:ComparableLeibnizEq CB) : + ComparableLeibnizEq (PairComparable CA CB). +Proof. +intros x y; destruct x, y; simpl. +pose proof (compare_eq a a0); pose proof (compare_eq b b0). +destruct (compare a a0); try discriminate. +intuition. +destruct H2, H0. +reflexivity. +Qed. + +(** An [Finite] type is a type with the list of all elements. **) +Class Finite (A:Type) := { + all_list : list A; + all_list_forall : forall x:A, In x all_list +}. + +(** An alphabet is both [ComparableLeibnizEq] and [Finite]. **) +Class Alphabet (A:Type) := { + AlphabetComparable :> Comparable A; + AlphabetComparableLeibnizEq :> ComparableLeibnizEq AlphabetComparable; + AlphabetFinite :> Finite A +}. + +(** The [Numbered] class provides a conveniant way to build [Alphabet] instances, + with a good computationnal complexity. It is mainly a injection from it to + [positive] **) +Class Numbered (A:Type) := { + inj : A -> positive; + surj : positive -> A; + surj_inj_compat : forall x, surj (inj x) = x; + inj_bound : positive; + inj_bound_spec : forall x, (inj x < Pos.succ inj_bound)%positive +}. + +Program Instance NumberedAlphabet {A:Type} (N:Numbered A) : Alphabet A := + { AlphabetComparable := {| compare := fun x y => Pos.compare (inj x) (inj y) |}; + AlphabetFinite := + {| all_list := fst (Pos.iter + (fun '(l, p) => (surj p::l, Pos.succ p)) + ([], 1%positive) inj_bound) |} }. +Next Obligation. simpl. now rewrite <- Pos.compare_antisym. Qed. +Next Obligation. + match goal with c : comparison |- _ => destruct c end. + - rewrite Pos.compare_eq_iff in *. congruence. + - rewrite Pos.compare_lt_iff in *. eauto using Pos.lt_trans. + - rewrite Pos.compare_gt_iff in *. eauto using Pos.lt_trans. +Qed. +Next Obligation. + intros x y. unfold compare. intros Hxy. + assert (Hxy' : inj x = inj y). + (* We do not use [Pos.compare_eq_iff] directly to make sure the + proof is executable. *) + { destruct (Pos.eq_dec (inj x) (inj y)) as [|[]]; [now auto|]. + now apply Pos.compare_eq_iff. } + (* Using rewrite here leads to non-executable proofs. *) + transitivity (surj (inj x)). + { apply eq_sym, surj_inj_compat. } + transitivity (surj (inj y)); cycle 1. + { apply surj_inj_compat. } + apply f_equal, Hxy'. +Defined. +Next Obligation. + rewrite <-(surj_inj_compat x). + generalize (inj_bound_spec x). generalize (inj x). clear x. intros x. + match goal with |- ?Hx -> In ?s (fst ?p) => + assert ((Hx -> In s (fst p)) /\ snd p = Pos.succ inj_bound); [|now intuition] end. + rewrite Pos.lt_succ_r. + induction inj_bound as [|y [IH1 IH2]] using Pos.peano_ind; + (split; [intros Hx|]); simpl. + - rewrite (Pos.le_antisym _ _ Hx); auto using Pos.le_1_l. + - auto. + - rewrite Pos.iter_succ. destruct Pos.iter; simpl in *. subst. + rewrite Pos.le_lteq in Hx. destruct Hx as [?%Pos.lt_succ_r| ->]; now auto. + - rewrite Pos.iter_succ. destruct Pos.iter. simpl in IH2. subst. reflexivity. +Qed. + +(** Definitions of [FSet]/[FMap] from [Comparable] **) +Require Import OrderedTypeAlt. +Require FSetAVL. +Require FMapAVL. +Import OrderedType. + +Module Type ComparableM. + Parameter t : Type. + Declare Instance tComparable : Comparable t. +End ComparableM. + +Module OrderedTypeAlt_from_ComparableM (C:ComparableM) <: OrderedTypeAlt. + Definition t := C.t. + Definition compare : t -> t -> comparison := compare. + + Infix "?=" := compare (at level 70, no associativity). + + Lemma compare_sym x y : (y?=x) = CompOpp (x?=y). + Proof. exact (Logic.eq_sym (compare_antisym x y)). Qed. + Lemma compare_trans c x y z : + (x?=y) = c -> (y?=z) = c -> (x?=z) = c. + Proof. + apply compare_trans. + Qed. +End OrderedTypeAlt_from_ComparableM. + +Module OrderedType_from_ComparableM (C:ComparableM) <: OrderedType. + Module Alt := OrderedTypeAlt_from_ComparableM C. + Include (OrderedType_from_Alt Alt). +End OrderedType_from_ComparableM. diff --git a/cparser/MenhirLib/Automaton.v b/MenhirLib/Automaton.v index fc995298..d5a19f35 100644 --- a/cparser/MenhirLib/Automaton.v +++ b/MenhirLib/Automaton.v @@ -1,23 +1,20 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) Require Grammar. -Require Import Orders. Require Export Alphabet. -Require Export List. -Require Export Syntax. +From Coq Require Import Orders. +From Coq Require Export List Syntax. Module Type AutInit. (** The grammar of the automaton. **) @@ -102,9 +99,9 @@ Module Types(Import Init:AutInit). T term = last_symb_of_non_init_state s -> lookahead_action term | Reduce_act: production -> lookahead_action term | Fail_act: lookahead_action term. - Arguments Shift_act [term]. - Arguments Reduce_act [term]. - Arguments Fail_act [term]. + Arguments Shift_act {term}. + Arguments Reduce_act {term}. + Arguments Fail_act {term}. Inductive action := | Default_reduce_act: production -> action diff --git a/MenhirLib/Grammar.v b/MenhirLib/Grammar.v new file mode 100644 index 00000000..a371318b --- /dev/null +++ b/MenhirLib/Grammar.v @@ -0,0 +1,162 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import List Syntax Orders. +Require Import Alphabet. + +(** The terminal non-terminal alphabets of the grammar. **) +Module Type Alphs. + Parameters terminal nonterminal : Type. + Declare Instance TerminalAlph: Alphabet terminal. + Declare Instance NonTerminalAlph: Alphabet nonterminal. +End Alphs. + +(** Definition of the alphabet of symbols, given the alphabet of terminals + and the alphabet of non terminals **) +Module Symbol(Import A:Alphs). + + Inductive symbol := + | T: terminal -> symbol + | NT: nonterminal -> symbol. + + Program Instance SymbolAlph : Alphabet symbol := + { AlphabetComparable := {| compare := fun x y => + match x, y return comparison with + | T _, NT _ => Gt + | NT _, T _ => Lt + | T x, T y => compare x y + | NT x, NT y => compare x y + end |}; + AlphabetFinite := {| all_list := + map T all_list++map NT all_list |} }. + Next Obligation. + destruct x; destruct y; intuition; apply compare_antisym. + Qed. + Next Obligation. + destruct x; destruct y; destruct z; intuition; try discriminate. + apply (compare_trans _ t0); intuition. + apply (compare_trans _ n0); intuition. + Qed. + Next Obligation. + intros x y. + destruct x; destruct y; try discriminate; intros. + rewrite (compare_eq t t0); now intuition. + rewrite (compare_eq n n0); now intuition. + Defined. + Next Obligation. + rewrite in_app_iff. + destruct x; [left | right]; apply in_map; apply all_list_forall. + Qed. + +End Symbol. + +(** A curryfied function with multiple parameters **) +Definition arrows_right: Type -> list Type -> Type := + fold_right (fun A B => A -> B). + +Module Type T. + Include Alphs <+ Symbol. + + (** [symbol_semantic_type] maps a symbols to the type of its semantic + values. **) + Parameter symbol_semantic_type: symbol -> Type. + + (** The type of productions identifiers **) + Parameter production : Type. + Declare Instance ProductionAlph : Alphabet production. + + (** Accessors for productions: left hand side, right hand side, + and semantic action. The semantic actions are given in the form + of curryfied functions, that take arguments in the reverse order. **) + Parameter prod_lhs: production -> nonterminal. + (* The RHS of a production is given in reversed order, so that symbols *) + Parameter prod_rhs_rev: production -> list symbol. + Parameter prod_action: + forall p:production, + arrows_right + (symbol_semantic_type (NT (prod_lhs p))) + (map symbol_semantic_type (prod_rhs_rev p)). + + (** Tokens are the atomic elements of the input stream: they contain + a terminal and a semantic value of the type corresponding to this + terminal. *) + Parameter token : Type. + Parameter token_term : token -> terminal. + Parameter token_sem : + forall tok : token, symbol_semantic_type (T (token_term tok)). +End T. + +Module Defs(Import G:T). + + (** The semantics of a grammar is defined in two stages. First, we + define the notion of parse tree, which represents one way of + recognizing a word with a head symbol. Semantic values are stored + at the leaves. + + This notion is defined in two mutually recursive flavours: + either for a single head symbol, or for a list of head symbols. *) + Inductive parse_tree: + forall (head_symbol:symbol) (word:list token), Type := + + (** Parse tree for a terminal symbol. *) + | Terminal_pt: + forall (tok:token), parse_tree (T (token_term tok)) [tok] + + (** Parse tree for a non-terminal symbol. *) + | Non_terminal_pt: + forall (prod:production) {word:list token}, + parse_tree_list (prod_rhs_rev prod) word -> + parse_tree (NT (prod_lhs prod)) word + + (* Note : the list head_symbols_rev is reversed. *) + with parse_tree_list: + forall (head_symbols_rev:list symbol) (word:list token), Type := + + | Nil_ptl: parse_tree_list [] [] + + | Cons_ptl: + forall {head_symbolsq:list symbol} {wordq:list token}, + parse_tree_list head_symbolsq wordq -> + + forall {head_symbolt:symbol} {wordt:list token}, + parse_tree head_symbolt wordt -> + + parse_tree_list (head_symbolt::head_symbolsq) (wordq++wordt). + + (** We can now finish the definition of the semantics of a grammar, + by giving the semantic value assotiated with a parse tree. *) + Fixpoint pt_sem {head_symbol word} (tree:parse_tree head_symbol word) : + symbol_semantic_type head_symbol := + match tree with + | Terminal_pt tok => token_sem tok + | Non_terminal_pt prod ptl => ptl_sem ptl (prod_action prod) + end + with ptl_sem {A head_symbols word} (tree:parse_tree_list head_symbols word) : + arrows_right A (map symbol_semantic_type head_symbols) -> A := + match tree with + | Nil_ptl => fun act => act + | Cons_ptl q t => fun act => ptl_sem q (act (pt_sem t)) + end. + + Fixpoint pt_size {head_symbol word} (tree:parse_tree head_symbol word) := + match tree with + | Terminal_pt _ => 1 + | Non_terminal_pt _ l => S (ptl_size l) + end + with ptl_size {head_symbols word} (tree:parse_tree_list head_symbols word) := + match tree with + | Nil_ptl => 0 + | Cons_ptl q t => + pt_size t + ptl_size q + end. +End Defs. diff --git a/MenhirLib/Interpreter.v b/MenhirLib/Interpreter.v new file mode 100644 index 00000000..568597ba --- /dev/null +++ b/MenhirLib/Interpreter.v @@ -0,0 +1,453 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import List Syntax. +From Coq.ssr Require Import ssreflect. +Require Automaton. +Require Import Alphabet Grammar Validator_safe. + +Module Make(Import A:Automaton.T). +Module Import ValidSafe := Validator_safe.Make A. + +(** A few helpers for dependent types. *) + +(** Decidable propositions. *) +Class Decidable (P : Prop) := decide : {P} + {~P}. +Arguments decide _ {_}. + +(** A [Comparable] type has decidable equality. *) +Instance comparable_decidable_eq T `{ComparableLeibnizEq T} (x y : T) : + Decidable (x = y). +Proof. + unfold Decidable. + destruct (compare x y) eqn:EQ; [left; apply compare_eq; intuition | ..]; + right; intros ->; by rewrite compare_refl in EQ. +Defined. + +Instance list_decidable_eq T : + (forall x y : T, Decidable (x = y)) -> + (forall l1 l2 : list T, Decidable (l1 = l2)). +Proof. unfold Decidable. decide equality. Defined. + +Ltac subst_existT := + repeat + match goal with + | _ => progress subst + | H : @existT ?A ?P ?x ?y1 = @existT ?A ?P ?x ?y2 |- _ => + let DEC := fresh in + assert (DEC : forall u1 u2 : A, Decidable (u1 = u2)) by apply _; + apply Eqdep_dec.inj_pair2_eq_dec in H; [|by apply DEC]; + clear DEC + end. + +(** The interpreter is written using dependent types. In order to + avoid reducing proof terms while executing the parser, we thunk all + the propositions behind an arrow. + Note that thunkP is still in Prop so that it is erased by + extraction. + *) +Definition thunkP (P : Prop) : Prop := True -> P. + +(** Sometimes, we actually need a reduced proof in a program (for + example when using an equality to cast a value). In that case, + instead of reducing the proof we already have, we reprove the + assertion by using decidability. *) +Definition reprove {P} `{Decidable P} (p : thunkP P) : P := + match decide P with + | left p => p + | right np => False_ind _ (np (p I)) + end. + +(** Combination of reprove with eq_rect. *) +Definition cast {T : Type} (F : T -> Type) {x y : T} (eq : thunkP (x = y)) + {DEC : unit -> Decidable (x = y)}: + F x -> F y := + fun a => eq_rect x F a y (@reprove _ (DEC ()) eq). + +Lemma cast_eq T F (x : T) (eq : thunkP (x = x)) `{forall x y, Decidable (x = y)} a : + cast F eq a = a. +Proof. by rewrite /cast -Eqdep_dec.eq_rect_eq_dec. Qed. + +(** Input buffers and operations on them. **) +CoInductive buffer : Type := + Buf_cons { buf_head : token; buf_tail : buffer }. + +Delimit Scope buffer_scope with buf. +Bind Scope buffer_scope with buffer. + +Infix "::" := Buf_cons (at level 60, right associativity) : buffer_scope. + +(** Concatenation of a list and an input buffer **) +Fixpoint app_buf (l:list token) (buf:buffer) := + match l with + | nil => buf + | cons t q => (t :: app_buf q buf)%buf + end. +Infix "++" := app_buf (at level 60, right associativity) : buffer_scope. + +Lemma app_buf_assoc (l1 l2:list token) (buf:buffer) : + (l1 ++ (l2 ++ buf) = (l1 ++ l2) ++ buf)%buf. +Proof. induction l1 as [|?? IH]=>//=. rewrite IH //. Qed. + +(** The type of a non initial state: the type of semantic values associated + with the last symbol of this state. *) +Definition noninitstate_type state := + symbol_semantic_type (last_symb_of_non_init_state state). + +(** The stack of the automaton : it can be either nil or contains a non + initial state, a semantic value for the symbol associted with this state, + and a nested stack. **) +Definition stack := list (sigT noninitstate_type). (* eg. list {state & state_type state} *) + +Section Interpreter. + +Hypothesis safe: safe. + +(* Properties of the automaton deduced from safety validation. *) +Proposition shift_head_symbs: shift_head_symbs. +Proof. pose proof safe; unfold ValidSafe.safe in H; intuition. Qed. +Proposition goto_head_symbs: goto_head_symbs. +Proof. pose proof safe; unfold ValidSafe.safe in H; intuition. Qed. +Proposition shift_past_state: shift_past_state. +Proof. pose proof safe; unfold ValidSafe.safe in H; intuition. Qed. +Proposition goto_past_state: goto_past_state. +Proof. pose proof safe; unfold ValidSafe.safe in H; intuition. Qed. +Proposition reduce_ok: reduce_ok. +Proof. pose proof safe; unfold ValidSafe.safe in H; intuition. Qed. + +Variable init : initstate. + +(** The top state of a stack **) +Definition state_of_stack (stack:stack): state := + match stack with + | [] => init + | existT _ s _::_ => s + end. + +(** The stack of states of an automaton stack **) +Definition state_stack_of_stack (stack:stack) := + (List.map + (fun cell:sigT noninitstate_type => singleton_state_pred (projT1 cell)) + stack ++ [singleton_state_pred init])%list. + +(** The stack of symbols of an automaton stack **) +Definition symb_stack_of_stack (stack:stack) := + List.map + (fun cell:sigT noninitstate_type => last_symb_of_non_init_state (projT1 cell)) + stack. + +(** The stack invariant : it basically states that the assumptions on the + states are true. **) +Inductive stack_invariant: stack -> Prop := + | stack_invariant_constr: + forall stack, + prefix (head_symbs_of_state (state_of_stack stack)) + (symb_stack_of_stack stack) -> + prefix_pred (head_states_of_state (state_of_stack stack)) + (state_stack_of_stack stack) -> + stack_invariant_next stack -> + stack_invariant stack +with stack_invariant_next: stack -> Prop := + | stack_invariant_next_nil: + stack_invariant_next [] + | stack_invariant_next_cons: + forall state_cur st stack_rec, + stack_invariant stack_rec -> + stack_invariant_next (existT _ state_cur st::stack_rec). + +(** [pop] pops some symbols from the stack. It returns the popped semantic + values using [sem_popped] as an accumulator and discards the popped + states.**) +Fixpoint pop (symbols_to_pop:list symbol) {A:Type} (stk:stack) : + thunkP (prefix symbols_to_pop (symb_stack_of_stack stk)) -> + forall (action:arrows_right A (map symbol_semantic_type symbols_to_pop)), + stack * A. +unshelve refine + (match symbols_to_pop + return + (thunkP (prefix symbols_to_pop (symb_stack_of_stack stk))) -> + forall (action:arrows_right A (map _ symbols_to_pop)), stack * A + with + | [] => fun _ action => (stk, action) + | t::q => fun Hp action => + match stk + return thunkP (prefix (t::q) (symb_stack_of_stack stk)) -> stack * A + with + | existT _ state_cur sem::stack_rec => fun Hp => + let sem_conv := cast symbol_semantic_type _ sem in + pop q _ stack_rec _ (action sem_conv) + | [] => fun Hp => False_rect _ _ + end Hp + end). +Proof. + - simpl in Hp. clear -Hp. abstract (intros _ ; specialize (Hp I); now inversion Hp). + - clear -Hp. abstract (specialize (Hp I); now inversion Hp). + - simpl in Hp. clear -Hp. abstract (intros _ ; specialize (Hp I); now inversion Hp). +Defined. + +(* Equivalent declarative specification for pop, so that we avoid + (part of) the dependent types nightmare. *) +Inductive pop_spec {A:Type} : + forall (symbols_to_pop:list symbol) (stk : stack) + (action : arrows_right A (map symbol_semantic_type symbols_to_pop)) + (stk' : stack) (sem : A), + Prop := + | Nil_pop_spec stk sem : pop_spec [] stk sem stk sem + | Cons_pop_spec symbols_to_pop st stk action sem stk' res : + pop_spec symbols_to_pop stk (action sem) stk' res -> + pop_spec (last_symb_of_non_init_state st::symbols_to_pop) + (existT _ st sem :: stk) action stk' res. + +Lemma pop_spec_ok {A:Type} symbols_to_pop stk Hp action stk' res: + pop symbols_to_pop stk Hp action = (stk', res) <-> + pop_spec (A:=A) symbols_to_pop stk action stk' res. +Proof. + revert stk Hp action. + induction symbols_to_pop as [|t symbols_to_pop IH]=>stk Hp action /=. + - split. + + intros [= <- <-]. constructor. + + intros H. inversion H. by subst_existT. + - destruct stk as [|[st sem]]=>/=; [by destruct pop_subproof0|]. + remember (pop_subproof t symbols_to_pop stk st Hp) as EQ eqn:eq. clear eq. + generalize EQ. revert Hp action. rewrite <-(EQ I)=>Hp action ?. + rewrite cast_eq. rewrite IH. split. + + intros. by constructor. + + intros H. inversion H. by subst_existT. +Qed. + + +Lemma pop_preserves_invariant symbols_to_pop stk Hp A action : + stack_invariant stk -> + stack_invariant (fst (pop symbols_to_pop stk Hp (A:=A) action)). +Proof. + revert stk Hp A action. induction symbols_to_pop as [|t q IH]=>//=. + intros stk Hp A action Hi. + destruct Hi as [stack Hp' Hpp [|state st stk']]. + - destruct pop_subproof0. + - now apply IH. +Qed. + +Lemma pop_state_valid symbols_to_pop stk Hp A action lpred : + prefix_pred lpred (state_stack_of_stack stk) -> + let stk' := fst (pop symbols_to_pop stk Hp (A:=A) action) in + state_valid_after_pop (state_of_stack stk') symbols_to_pop lpred. +Proof. + revert stk Hp A action lpred. induction symbols_to_pop as [|t q IH]=>/=. + - intros stk Hp A a lpred Hpp. destruct lpred as [|pred lpred]; constructor. + inversion Hpp as [|? lpred' ? pred' Himpl Hpp' eq1 eq2]; subst. + specialize (Himpl (state_of_stack stk)). + destruct (pred' (state_of_stack stk)) as [] eqn:Heqpred'=>//. + destruct stk as [|[]]; simpl in *. + + inversion eq2; subst; clear eq2. + unfold singleton_state_pred in Heqpred'. + now rewrite compare_refl in Heqpred'; discriminate. + + inversion eq2; subst; clear eq2. + unfold singleton_state_pred in Heqpred'. + now rewrite compare_refl in Heqpred'; discriminate. + - intros stk Hp A a lpred Hpp. destruct stk as [|[] stk]=>//=. + + destruct pop_subproof0. + + destruct lpred as [|pred lpred]; [by constructor|]. + constructor. apply IH. by inversion Hpp. +Qed. + +(** [step_result] represents the result of one step of the automaton : it can + fail, accept or progress. [Fail_sr] means that the input is incorrect. + [Accept_sr] means that this is the last step of the automaton, and it + returns the semantic value of the input word. [Progress_sr] means that + some progress has been made, but new steps are needed in order to accept + a word. + + For [Accept_sr] and [Progress_sr], the result contains the new input buffer. + + [Fail_sr] means that the input word is rejected by the automaton. It is + different to [Err] (from the error monad), which mean that the automaton is + bogus and has perfomed a forbidden action. **) +Inductive step_result := + | Fail_sr: step_result + | Accept_sr: symbol_semantic_type (NT (start_nt init)) -> buffer -> step_result + | Progress_sr: stack -> buffer -> step_result. + +(** [reduce_step] does a reduce action : + - pops some elements from the stack + - execute the action of the production + - follows the goto for the produced non terminal symbol **) +Definition reduce_step stk prod (buffer : buffer) + (Hval : thunkP (valid_for_reduce (state_of_stack stk) prod)) + (Hi : thunkP (stack_invariant stk)) + : step_result. +refine + ((let '(stk', sem) as ss := pop (prod_rhs_rev prod) stk _ (prod_action prod) + return thunkP (state_valid_after_pop (state_of_stack (fst ss)) _ + (head_states_of_state (state_of_stack stk))) -> _ + in fun Hval' => + match goto_table (state_of_stack stk') (prod_lhs prod) as goto + return (thunkP (goto = None -> + match state_of_stack stk' with + | Init i => prod_lhs prod = start_nt i + | Ninit _ => False + end)) -> _ + with + | Some (exist _ state_new e) => fun _ => + let sem := eq_rect _ _ sem _ e in + Progress_sr (existT noninitstate_type state_new sem::stk') buffer + | None => fun Hval => + let sem := cast symbol_semantic_type _ sem in + Accept_sr sem buffer + end (fun _ => _)) + (fun _ => pop_state_valid _ _ _ _ _ _ _)). +Proof. + - clear -Hi Hval. + abstract (intros _; destruct Hi=>//; eapply prefix_trans; [by apply Hval|eassumption]). + - clear -Hval. + abstract (intros _; f_equal; specialize (Hval I eq_refl); destruct stk' as [|[]]=>//). + - simpl in Hval'. clear -Hval Hval'. + abstract (move : Hval => /(_ I) [_ /(_ _ (Hval' I))] Hval2 Hgoto; by rewrite Hgoto in Hval2). + - clear -Hi. abstract by destruct Hi. +Defined. + +Lemma reduce_step_stack_invariant_preserved stk prod buffer Hv Hi stk' buffer': + reduce_step stk prod buffer Hv Hi = Progress_sr stk' buffer' -> + stack_invariant stk'. +Proof. + unfold reduce_step. + match goal with + | |- context [pop ?symbols_to_pop stk ?Hp ?action] => + assert (Hi':=pop_preserves_invariant symbols_to_pop stk Hp _ action (Hi I)); + generalize (pop_state_valid symbols_to_pop stk Hp _ action) + end. + destruct pop as [stk0 sem]=>/=. simpl in Hi'. intros Hv'. + assert (Hgoto1:=goto_head_symbs (state_of_stack stk0) (prod_lhs prod)). + assert (Hgoto2:=goto_past_state (state_of_stack stk0) (prod_lhs prod)). + match goal with | |- context [fun _ : True => ?X] => generalize X end. + destruct goto_table as [[state_new e]|] eqn:EQgoto=>//. + intros _ [= <- <-]. constructor=>/=. + - constructor. eapply prefix_trans. apply Hgoto1. by destruct Hi'. + - unfold state_stack_of_stack; simpl; constructor. + + intros ?. by destruct singleton_state_pred. + + eapply prefix_pred_trans. apply Hgoto2. by destruct Hi'. + - by constructor. +Qed. + +(** One step of parsing. **) +Definition step stk buffer (Hi : thunkP (stack_invariant stk)): step_result := + match action_table (state_of_stack stk) as a return + thunkP + match a return Prop with + | Default_reduce_act prod => _ + | Lookahead_act awt => forall t : terminal, + match awt t with + | Reduce_act p => _ + | _ => True + end + end -> _ + with + | Default_reduce_act prod => fun Hv => + reduce_step stk prod buffer Hv Hi + | Lookahead_act awt => fun Hv => + match buf_head buffer with + | tok => + match awt (token_term tok) as a return + thunkP match a return Prop with Reduce_act p => _ | _ => _ end -> _ + with + | Shift_act state_new e => fun _ => + let sem_conv := eq_rect _ symbol_semantic_type (token_sem tok) _ e in + Progress_sr (existT noninitstate_type state_new sem_conv::stk) + (buf_tail buffer) + | Reduce_act prod => fun Hv => + reduce_step stk prod buffer Hv Hi + | Fail_act => fun _ => + Fail_sr + end (fun _ => Hv I (token_term tok)) + end + end (fun _ => reduce_ok _). + +Lemma step_stack_invariant_preserved stk buffer Hi stk' buffer': + step stk buffer Hi = Progress_sr stk' buffer' -> + stack_invariant stk'. +Proof. + unfold step. + generalize (reduce_ok (state_of_stack stk))=>Hred. + assert (Hshift1 := shift_head_symbs (state_of_stack stk)). + assert (Hshift2 := shift_past_state (state_of_stack stk)). + destruct action_table as [prod|awt]=>/=. + - eauto using reduce_step_stack_invariant_preserved. + - set (term := token_term (buf_head buffer)). + generalize (Hred term). clear Hred. intros Hred. + specialize (Hshift1 term). specialize (Hshift2 term). + destruct (awt term) as [state_new e|prod|]=>//. + + intros [= <- <-]. constructor=>/=. + * constructor. eapply prefix_trans. apply Hshift1. by destruct Hi. + * unfold state_stack_of_stack; simpl; constructor. + -- intros ?. by destruct singleton_state_pred. + -- eapply prefix_pred_trans. apply Hshift2. by destruct Hi. + * constructor; by apply Hi. + + eauto using reduce_step_stack_invariant_preserved. +Qed. + +(** The parsing use a [nat] fuel parameter [log_n_steps], so that we + do not have to prove terminaison, which is difficult. + + Note that [log_n_steps] is *not* the fuel in the conventionnal + sense: this parameter contains the logarithm (in base 2) of the + number of steps to perform. Hence, a value of, e.g., 50 will + usually be enough to ensure termination. *) +Fixpoint parse_fix stk buffer (log_n_steps : nat) (Hi : thunkP (stack_invariant stk)): + { sr : step_result | + forall stk' buffer', sr = Progress_sr stk' buffer' -> stack_invariant stk' } := + match log_n_steps with + | O => exist _ (step stk buffer Hi) + (step_stack_invariant_preserved _ _ Hi) + | S log_n_steps => + match parse_fix stk buffer log_n_steps Hi with + | exist _ (Progress_sr stk buffer) Hi' => + parse_fix stk buffer log_n_steps (fun _ => Hi' _ buffer eq_refl) + | sr => sr + end + end. + +(** The final result of a parsing is either a failure (the automaton + has rejected the input word), either a timeout (the automaton has + spent all the given [2^log_n_steps]), either a parsed semantic value + with a rest of the input buffer. + + Note that we do not make parse_result depend on start_nt for the + result type, so that this inductive is extracted without the use + of Obj.t in OCaml. **) +Inductive parse_result {A : Type} := + | Fail_pr: parse_result + | Timeout_pr: parse_result + | Parsed_pr: A -> buffer -> parse_result. +Global Arguments parse_result _ : clear implicits. + +Definition parse (buffer : buffer) (log_n_steps : nat): + parse_result (symbol_semantic_type (NT (start_nt init))). +refine (match proj1_sig (parse_fix [] buffer log_n_steps _) with + | Fail_sr => Fail_pr + | Accept_sr sem buffer' => Parsed_pr sem buffer' + | Progress_sr _ _ => Timeout_pr + end). +Proof. + abstract (repeat constructor; intros; by destruct singleton_state_pred). +Defined. + +End Interpreter. + +Arguments Fail_sr {init}. +Arguments Accept_sr {init} _ _. +Arguments Progress_sr {init} _ _. + +End Make. + +Module Type T(A:Automaton.T). + Include (Make A). +End T. diff --git a/MenhirLib/Interpreter_complete.v b/MenhirLib/Interpreter_complete.v new file mode 100644 index 00000000..ec69592b --- /dev/null +++ b/MenhirLib/Interpreter_complete.v @@ -0,0 +1,825 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import List Syntax Arith. +From Coq.ssr Require Import ssreflect. +Require Import Alphabet Grammar. +Require Automaton Interpreter Validator_complete. + +Module Make(Import A:Automaton.T) (Import Inter:Interpreter.T A). +Module Import Valid := Validator_complete.Make A. + +(** * Completeness Proof **) + +Section Completeness_Proof. + +Hypothesis safe: Inter.ValidSafe.safe. +Hypothesis complete: complete. + +(* Properties of the automaton deduced from completeness validation. *) +Proposition nullable_stable: nullable_stable. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. +Proposition first_stable: first_stable. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. +Proposition start_future: start_future. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. +Proposition terminal_shift: terminal_shift. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. +Proposition end_reduce: end_reduce. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. +Proposition start_goto: start_goto. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. +Proposition non_terminal_goto: non_terminal_goto. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. +Proposition non_terminal_closed: non_terminal_closed. +Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. + +(** If the nullable predicate has been validated, then it is correct. **) +Lemma nullable_correct head word : + word = [] -> parse_tree head word -> nullable_symb head = true +with nullable_correct_list heads word : + word = [] -> + parse_tree_list heads word -> nullable_word heads = true. +Proof. + - destruct 2=>//. assert (Hnull := nullable_stable prod). + erewrite nullable_correct_list in Hnull; eauto. + - intros Hword. destruct 1=>//=. destruct (app_eq_nil _ _ Hword). + eauto using andb_true_intro. +Qed. + +(** Auxiliary lemma for first_correct. *) +Lemma first_word_set_app t word1 word2 : + TerminalSet.In t (first_word_set (word1 ++ word2)) <-> + TerminalSet.In t (first_word_set word1) \/ + TerminalSet.In t (first_word_set word2) /\ nullable_word (rev word1) = true. +Proof. + induction word1 as [|s word1 IH]=>/=. + - split; [tauto|]. move=>[/TerminalSet.empty_1 ?|[? _]]//. + - rewrite /nullable_word forallb_app /=. destruct nullable_symb=>/=. + + rewrite Bool.andb_true_r. split. + * move=>/TerminalSet.union_1. rewrite IH. + move=>[?|[?|[??]]]; auto using TerminalSet.union_2, TerminalSet.union_3. + * destruct IH. + move=>[/TerminalSet.union_1 [?|?]|[??]]; + auto using TerminalSet.union_2, TerminalSet.union_3. + + rewrite Bool.andb_false_r. by intuition. +Qed. + +(** If the first predicate has been validated, then it is correct. **) +Lemma first_correct head word t q : + word = t::q -> + parse_tree head word -> + TerminalSet.In (token_term t) (first_symb_set head) +with first_correct_list heads word t q : + word = t::q -> + parse_tree_list heads word -> + TerminalSet.In (token_term t) (first_word_set (rev' heads)). +Proof. + - intros Hword. destruct 1=>//. + + inversion Hword. subst. apply TerminalSet.singleton_2, compare_refl. + + eapply first_stable. eauto. + - intros Hword. destruct 1 as [|symq wordq ptl symt wordt pt]=>//=. + rewrite /rev' -rev_alt /= first_word_set_app /= rev_involutive rev_alt. + destruct wordq; [right|left]. + + destruct nullable_symb; eauto using TerminalSet.union_2, nullable_correct_list. + + inversion Hword. subst. fold (rev' symq). eauto. +Qed. + +(** A PTL is compatible with a stack if the top of the stack contains + data representing to this PTL. *) +Fixpoint ptl_stack_compat {symbs word} + (stk0 : stack) (ptl : parse_tree_list symbs word) (stk : stack) : Prop := + match ptl with + | Nil_ptl => stk0 = stk + | @Cons_ptl _ _ ptl sym _ pt => + match stk with + | [] => False + | existT _ _ sem::stk => + ptl_stack_compat stk0 ptl stk /\ + exists e, + sem = eq_rect _ symbol_semantic_type (pt_sem pt) _ e + end + end. + +(** .. and when a PTL is compatible with a stack, then calling the pop + function return the semantic value of this PTL. *) +Lemma pop_stack_compat_pop_spec {A symbs word} + (ptl:parse_tree_list symbs word) (stk:stack) (stk0:stack) action : + ptl_stack_compat stk0 ptl stk -> + pop_spec symbs stk action stk0 (ptl_sem (A:=A) ptl action). +Proof. + revert stk. induction ptl=>stk /= Hstk. + - subst. constructor. + - destruct stk as [|[st sem] stk]=>//. destruct Hstk as [Hstk [??]]. subst. + simpl. constructor. eauto. +Qed. + +Variable init: initstate. + +(** In order to prove compleness, we first fix a word to be parsed + together with the content of the parser at the end of the parsing. *) +Variable full_word: list token. +Variable buffer_end: buffer. + +(** Completeness is proved by following the traversal of the parse + tree which is performed by the parser. Each step of parsing + correspond to one step of traversal. In order to represent the state + of the traversal, we define the notion of "dotted" parse tree, which + is a parse tree with one dot on one of its node. The place of the + dot represents the place of the next action to be executed. + + Such a dotted parse tree is decomposed into two part: a "regular" + parse tree, which is the parse tree placed under the dot, and a + "parse tree zipper", which is the part of the parse tree placed + above the dot. Therefore, a parse tree zipper is a parse tree with a + hole. Moreover, for easier manipulation, a parse tree zipper is + represented "upside down". That is, the root of the parse tree is + actually a leaf of the zipper, while the root of the zipper is the + hole. + *) +Inductive pt_zipper: + forall (hole_symb:symbol) (hole_word:list token), Type := +| Top_ptz: + pt_zipper (NT (start_nt init)) full_word +| Cons_ptl_ptz: + forall {head_symbolsq:list symbol} {wordq:list token}, + parse_tree_list head_symbolsq wordq -> + + forall {head_symbolt:symbol} {wordt:list token}, + + ptl_zipper (head_symbolt::head_symbolsq) (wordq++wordt) -> + pt_zipper head_symbolt wordt +with ptl_zipper: + forall (hole_symbs:list symbol) (hole_word:list token), Type := +| Non_terminal_pt_ptlz: + forall {p:production} {word:list token}, + pt_zipper (NT (prod_lhs p)) word -> + ptl_zipper (prod_rhs_rev p) word + +| Cons_ptl_ptlz: + forall {head_symbolsq:list symbol} {wordq:list token}, + + forall {head_symbolt:symbol} {wordt:list token}, + parse_tree head_symbolt wordt -> + + ptl_zipper (head_symbolt::head_symbolsq) (wordq++wordt) -> + + ptl_zipper head_symbolsq wordq. + +(** A dotted parse tree is the combination of a parse tree zipper with + a parse tree. It can be intwo flavors, depending on which is the next + action to be executed (shift or reduce). *) +Inductive pt_dot: Type := +| Reduce_ptd: forall {prod word}, + parse_tree_list (prod_rhs_rev prod) word -> + pt_zipper (NT (prod_lhs prod)) word -> + pt_dot +| Shift_ptd: forall (tok : token) {symbolsq wordq}, + parse_tree_list symbolsq wordq -> + ptl_zipper (T (token_term tok)::symbolsq) (wordq++[tok]) -> + pt_dot. + +(** We can compute the full semantic value of a parse tree when + represented as a dotted ptd. *) + +Fixpoint ptlz_sem {hole_symbs hole_word} + (ptlz:ptl_zipper hole_symbs hole_word) : + (forall A, arrows_right A (map symbol_semantic_type hole_symbs) -> A) -> + (symbol_semantic_type (NT (start_nt init))) := + match ptlz with + | @Non_terminal_pt_ptlz prod _ ptz => + fun k => ptz_sem ptz (k _ (prod_action prod)) + | Cons_ptl_ptlz pt ptlz => + fun k => ptlz_sem ptlz (fun _ f => k _ (f (pt_sem pt))) + end +with ptz_sem {hole_symb hole_word} + (ptz:pt_zipper hole_symb hole_word): + symbol_semantic_type hole_symb -> symbol_semantic_type (NT (start_nt init)) := + match ptz with + | Top_ptz => fun sem => sem + | Cons_ptl_ptz ptl ptlz => + fun sem => ptlz_sem ptlz (fun _ f => ptl_sem ptl (f sem)) + end. + +Definition ptd_sem (ptd : pt_dot) := + match ptd with + | @Reduce_ptd prod _ ptl ptz => + ptz_sem ptz (ptl_sem ptl (prod_action prod)) + | Shift_ptd tok ptl ptlz => + ptlz_sem ptlz (fun _ f => ptl_sem ptl (f (token_sem tok))) + end. + +(** The buffer associated with a dotted parse tree corresponds to the + buffer left to be read by the parser when at the state represented + by the dotted parse tree. *) +Fixpoint ptlz_buffer {hole_symbs hole_word} + (ptlz:ptl_zipper hole_symbs hole_word): buffer := + match ptlz with + | Non_terminal_pt_ptlz ptz => + ptz_buffer ptz + | @Cons_ptl_ptlz _ _ _ wordt _ ptlz' => + wordt ++ ptlz_buffer ptlz' + end +with ptz_buffer {hole_symb hole_word} + (ptz:pt_zipper hole_symb hole_word): buffer := + match ptz with + | Top_ptz => buffer_end + | Cons_ptl_ptz _ ptlz => + ptlz_buffer ptlz + end. + +Definition ptd_buffer (ptd:pt_dot) := + match ptd with + | Reduce_ptd _ ptz => ptz_buffer ptz + | @Shift_ptd tok _ wordq _ ptlz => (tok::ptlz_buffer ptlz)%buf + end. + +(** We are now ready to define the main invariant of the proof of + completeness: we need to specify when a stack is compatible with a + dotted parse tree. Informally, a stack is compatible with a dotted + parse tree when it is the concatenation stack fragments which are + compatible with each of the partially recognized productions + appearing in the parse tree zipper. Moreover, the head of each of + these stack fragment contains a state which has an item predicted by + the corresponding zipper. + + More formally, the compatibility relation first needs the following + auxiliary definitions: *) +Fixpoint ptlz_prod {hole_symbs hole_word} + (ptlz:ptl_zipper hole_symbs hole_word): production := + match ptlz with + | @Non_terminal_pt_ptlz prod _ _ => prod + | Cons_ptl_ptlz _ ptlz' => ptlz_prod ptlz' + end. + +Fixpoint ptlz_future {hole_symbs hole_word} + (ptlz:ptl_zipper hole_symbs hole_word): list symbol := + match ptlz with + | Non_terminal_pt_ptlz _ => [] + | @Cons_ptl_ptlz _ _ s _ _ ptlz' => s::ptlz_future ptlz' + end. + +Fixpoint ptlz_lookahead {hole_symbs hole_word} + (ptlz:ptl_zipper hole_symbs hole_word) : terminal := + match ptlz with + | Non_terminal_pt_ptlz ptz => token_term (buf_head (ptz_buffer ptz)) + | Cons_ptl_ptlz _ ptlz' => ptlz_lookahead ptlz' + end. + +Fixpoint ptz_stack_compat {hole_symb hole_word} + (stk : stack) (ptz : pt_zipper hole_symb hole_word) : Prop := + match ptz with + | Top_ptz => stk = [] + | Cons_ptl_ptz ptl ptlz => + exists stk0, + state_has_future (state_of_stack init stk) (ptlz_prod ptlz) + (hole_symb::ptlz_future ptlz) (ptlz_lookahead ptlz) /\ + ptl_stack_compat stk0 ptl stk /\ + ptlz_stack_compat stk0 ptlz + end +with ptlz_stack_compat {hole_symbs hole_word} + (stk : stack) (ptlz : ptl_zipper hole_symbs hole_word) : Prop := + match ptlz with + | Non_terminal_pt_ptlz ptz => ptz_stack_compat stk ptz + | Cons_ptl_ptlz _ ptlz => ptlz_stack_compat stk ptlz + end. + +Definition ptd_stack_compat (ptd:pt_dot) (stk:stack): Prop := + match ptd with + | @Reduce_ptd prod _ ptl ptz => + exists stk0, + state_has_future (state_of_stack init stk) prod [] + (token_term (buf_head (ptz_buffer ptz))) /\ + ptl_stack_compat stk0 ptl stk /\ + ptz_stack_compat stk0 ptz + | Shift_ptd tok ptl ptlz => + exists stk0, + state_has_future (state_of_stack init stk) (ptlz_prod ptlz) + (T (token_term tok) :: ptlz_future ptlz) (ptlz_lookahead ptlz) /\ + ptl_stack_compat stk0 ptl stk /\ + ptlz_stack_compat stk0 ptlz + end. + +Lemma ptz_stack_compat_cons_state_has_future {symbsq wordq symbt wordt} stk + (ptl : parse_tree_list symbsq wordq) + (ptlz : ptl_zipper (symbt :: symbsq) (wordq ++ wordt)) : + ptz_stack_compat stk (Cons_ptl_ptz ptl ptlz) -> + state_has_future (state_of_stack init stk) (ptlz_prod ptlz) + (symbt::ptlz_future ptlz) (ptlz_lookahead ptlz). +Proof. move=>[stk0 [? [? ?]]] //. Qed. + +Lemma ptlz_future_ptlz_prod hole_symbs hole_word + (ptlz:ptl_zipper hole_symbs hole_word) : + rev_append (ptlz_future ptlz) hole_symbs = prod_rhs_rev (ptlz_prod ptlz). +Proof. induction ptlz=>//=. Qed. + +Lemma ptlz_future_first {symbs word} (ptlz : ptl_zipper symbs word) : + TerminalSet.In (token_term (buf_head (ptlz_buffer ptlz))) + (first_word_set (ptlz_future ptlz)) \/ + token_term (buf_head (ptlz_buffer ptlz)) = ptlz_lookahead ptlz /\ + nullable_word (ptlz_future ptlz) = true. +Proof. + induction ptlz as [|??? [|tok] pt ptlz IH]; [by auto| |]=>/=. + - rewrite (nullable_correct _ _ eq_refl pt). + destruct IH as [|[??]]; [left|right]=>/=; auto using TerminalSet.union_3. + - left. destruct nullable_symb; eauto using TerminalSet.union_2, first_correct. +Qed. + +(** We now want to define what is the next dotted parse tree which is + to be handled after one action. Such dotted parse is built in two + steps: Not only we have to perform the action by completing the + parse tree, but we also have to prepare for the following step by + moving the dot down to place it in front of the next action to be + performed. +*) +Fixpoint build_pt_dot_from_pt {symb word} + (pt : parse_tree symb word) (ptz : pt_zipper symb word) + : pt_dot := + match pt in parse_tree symb word + return pt_zipper symb word -> pt_dot + with + | Terminal_pt tok => + fun ptz => + let X := + match ptz in pt_zipper symb word + return match symb with T term => True | NT _ => False end -> + { symbsq : list symbol & + { wordq : list token & + (parse_tree_list symbsq wordq * + ptl_zipper (symb :: symbsq) (wordq ++ word))%type } } + with + | Top_ptz => fun F => False_rect _ F + | Cons_ptl_ptz ptl ptlz => fun _ => + existT _ _ (existT _ _ (ptl, ptlz)) + end I + in + Shift_ptd tok (fst (projT2 (projT2 X))) (snd (projT2 (projT2 X))) + | Non_terminal_pt prod ptl => fun ptz => + let is_notnil := + match ptl in parse_tree_list w _ + return option (match w return Prop with [] => False | _ => True end) + with + | Nil_ptl => None + | _ => Some I + end + in + match is_notnil with + | None => Reduce_ptd ptl ptz + | Some H => build_pt_dot_from_pt_rec ptl H (Non_terminal_pt_ptlz ptz) + end + end ptz +with build_pt_dot_from_pt_rec {symbs word} + (ptl : parse_tree_list symbs word) + (Hsymbs : match symbs with [] => False | _ => True end) + (ptlz : ptl_zipper symbs word) + : pt_dot := + match ptl in parse_tree_list symbs word + return match symbs with [] => False | _ => True end -> + ptl_zipper symbs word -> + pt_dot + with + | Nil_ptl => fun Hsymbs _ => False_rect _ Hsymbs + | Cons_ptl ptl' pt => fun _ => + match ptl' in parse_tree_list symbsq wordq + return parse_tree_list symbsq wordq -> + ptl_zipper (_ :: symbsq) (wordq ++ _) -> + pt_dot + with + | Nil_ptl => fun _ ptlz => + build_pt_dot_from_pt pt (Cons_ptl_ptz Nil_ptl ptlz) + | _ => fun ptl' ptlz => + build_pt_dot_from_pt_rec ptl' I (Cons_ptl_ptlz pt ptlz) + end ptl' + end Hsymbs ptlz. + +Definition build_pt_dot_from_ptl {symbs word} + (ptl : parse_tree_list symbs word) + (ptlz : ptl_zipper symbs word) + : pt_dot := + match ptlz in ptl_zipper symbs word + return parse_tree_list symbs word -> pt_dot + with + | Non_terminal_pt_ptlz ptz => fun ptl => + Reduce_ptd ptl ptz + | Cons_ptl_ptlz pt ptlz => fun ptl => + build_pt_dot_from_pt pt (Cons_ptl_ptz ptl ptlz) + end ptl. + +Definition next_ptd (ptd:pt_dot) : option pt_dot := + match ptd with + | Shift_ptd tok ptl ptlz => + Some (build_pt_dot_from_ptl (Cons_ptl ptl (Terminal_pt tok)) ptlz) + | Reduce_ptd ptl ptz => + match ptz in pt_zipper symb word + return parse_tree symb word -> _ + with + | Top_ptz => fun _ => None + | Cons_ptl_ptz ptl' ptlz => fun pt => + Some (build_pt_dot_from_ptl (Cons_ptl ptl' pt) ptlz) + end (Non_terminal_pt _ ptl) + end. + +Fixpoint next_ptd_iter (ptd:pt_dot) (log_n_steps:nat) : option pt_dot := + match log_n_steps with + | O => next_ptd ptd + | S log_n_steps => + match next_ptd_iter ptd log_n_steps with + | None => None + | Some ptd => next_ptd_iter ptd log_n_steps + end + end. + +(** We prove that these functions behave well w.r.t. semantic values. *) +Lemma sem_build_from_pt {symb word} + (pt : parse_tree symb word) (ptz : pt_zipper symb word) : + ptz_sem ptz (pt_sem pt) + = ptd_sem (build_pt_dot_from_pt pt ptz) +with sem_build_from_pt_rec {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) + Hsymbs : + ptlz_sem ptlz (fun _ f => ptl_sem ptl f) + = ptd_sem (build_pt_dot_from_pt_rec ptl Hsymbs ptlz). +Proof. + - destruct pt as [tok|prod word ptl]=>/=. + + revert ptz. generalize [tok]. + generalize (token_sem tok). generalize I. + change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1. + generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz. + + match goal with + | |- context [match ?X with Some H => _ | None => _ end] => destruct X=>// + end. + by rewrite -sem_build_from_pt_rec. + - destruct ptl; [contradiction|]. + specialize (sem_build_from_pt_rec _ _ ptl)=>/=. destruct ptl. + + by rewrite -sem_build_from_pt. + + by rewrite -sem_build_from_pt_rec. +Qed. + +Lemma sem_build_from_ptl {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) : + ptlz_sem ptlz (fun _ f => ptl_sem ptl f) + = ptd_sem (build_pt_dot_from_ptl ptl ptlz). +Proof. destruct ptlz=>//=. by rewrite -sem_build_from_pt. Qed. + +Lemma sem_next_ptd (ptd : pt_dot) : + match next_ptd ptd with + | None => True + | Some ptd' => ptd_sem ptd = ptd_sem ptd' + end. +Proof. + destruct ptd as [prod word ptl ptz|tok symbs word ptl ptlz] =>/=. + - change (ptl_sem ptl (prod_action prod)) + with (pt_sem (Non_terminal_pt prod ptl)). + generalize (Non_terminal_pt prod ptl). clear ptl. + destruct ptz as [|?? ptl ?? ptlz]=>// pt. by rewrite -sem_build_from_ptl. + - by rewrite -sem_build_from_ptl. +Qed. + +Lemma sem_next_ptd_iter (ptd : pt_dot) (log_n_steps : nat) : + match next_ptd_iter ptd log_n_steps with + | None => True + | Some ptd' => ptd_sem ptd = ptd_sem ptd' + end. +Proof. + revert ptd. + induction log_n_steps as [|log_n_steps IH]; [by apply sem_next_ptd|]=>/= ptd. + assert (IH1 := IH ptd). destruct next_ptd_iter as [ptd'|]=>//. + specialize (IH ptd'). destruct next_ptd_iter=>//. congruence. +Qed. + +(** We prove that these functions behave well w.r.t. xxx_buffer. *) +Lemma ptd_buffer_build_from_pt {symb word} + (pt : parse_tree symb word) (ptz : pt_zipper symb word) : + (word ++ ptz_buffer ptz)%buf = ptd_buffer (build_pt_dot_from_pt pt ptz) +with ptd_buffer_build_from_pt_rec {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) + Hsymbs : + (word ++ ptlz_buffer ptlz)%buf = ptd_buffer (build_pt_dot_from_pt_rec ptl Hsymbs ptlz). +Proof. + - destruct pt as [tok|prod word ptl]=>/=. + + f_equal. revert ptz. generalize [tok]. + generalize (token_sem tok). generalize I. + change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1. + generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz. + + match goal with + | |- context [match ?X with Some H => _ | None => _ end] => destruct X eqn:EQ + end. + * by rewrite -ptd_buffer_build_from_pt_rec. + * rewrite [X in (X ++ _)%buf](_ : word = []) //. clear -EQ. by destruct ptl. + - destruct ptl as [|?? ptl ?? pt]; [contradiction|]. + specialize (ptd_buffer_build_from_pt_rec _ _ ptl). + destruct ptl. + + by rewrite /= -ptd_buffer_build_from_pt. + + by rewrite -ptd_buffer_build_from_pt_rec //= app_buf_assoc. +Qed. + +Lemma ptd_buffer_build_from_ptl {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) : + ptlz_buffer ptlz = ptd_buffer (build_pt_dot_from_ptl ptl ptlz). +Proof. + destruct ptlz as [|???? pt]=>//=. by rewrite -ptd_buffer_build_from_pt. +Qed. + +(** We prove that these functions behave well w.r.t. xxx_stack_compat. *) +Lemma ptd_stack_compat_build_from_pt {symb word} + (pt : parse_tree symb word) (ptz : pt_zipper symb word) + (stk: stack) : + ptz_stack_compat stk ptz -> + ptd_stack_compat (build_pt_dot_from_pt pt ptz) stk +with ptd_stack_compat_build_from_pt_rec {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) + (stk : stack) Hsymbs : + ptlz_stack_compat stk ptlz -> + state_has_future (state_of_stack init stk) (ptlz_prod ptlz) + (rev' (prod_rhs_rev (ptlz_prod ptlz))) (ptlz_lookahead ptlz) -> + ptd_stack_compat (build_pt_dot_from_pt_rec ptl Hsymbs ptlz) stk. +Proof. + - intros Hstk. destruct pt as [tok|prod word ptl]=>/=. + + revert ptz Hstk. generalize [tok]. generalize (token_sem tok). generalize I. + change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1. + generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz. + + assert (state_has_future (state_of_stack init stk) prod + (rev' (prod_rhs_rev prod)) (token_term (buf_head (ptz_buffer ptz)))). + { revert ptz Hstk. remember (NT (prod_lhs prod)) eqn:EQ=>ptz. + destruct ptz as [|?? ptl0 ?? ptlz0]. + - intros ->. apply start_future. congruence. + - subst. intros (stk0 & Hfut & _). apply non_terminal_closed in Hfut. + specialize (Hfut prod eq_refl). + destruct (ptlz_future_first ptlz0) as [Hfirst|[Hfirst Hnull]]. + + destruct Hfut as [_ Hfut]. auto. + + destruct Hfut as [Hfut _]. by rewrite Hnull -Hfirst in Hfut. } + match goal with + | |- context [match ?X with Some H => _ | None => _ end] => destruct X eqn:EQ + end. + * by apply ptd_stack_compat_build_from_pt_rec. + * exists stk. destruct ptl=>//. + - intros Hstk Hfut. destruct ptl as [|?? ptl ?? pt]; [contradiction|]. + specialize (ptd_stack_compat_build_from_pt_rec _ _ ptl). destruct ptl. + + eapply ptd_stack_compat_build_from_pt=>//. exists stk. + split; [|split]=>//; []. + by rewrite -ptlz_future_ptlz_prod rev_append_rev /rev' -rev_alt + rev_app_distr rev_involutive in Hfut. + + by apply ptd_stack_compat_build_from_pt_rec. +Qed. + +Lemma ptd_stack_compat_build_from_ptl {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) + (stk stk0: stack) : + ptlz_stack_compat stk0 ptlz -> + ptl_stack_compat stk0 ptl stk -> + state_has_future (state_of_stack init stk) (ptlz_prod ptlz) + (ptlz_future ptlz) (ptlz_lookahead ptlz) -> + ptd_stack_compat (build_pt_dot_from_ptl ptl ptlz) stk. +Proof. + intros Hstk0 Hstk Hfut. destruct ptlz=>/=. + - eauto. + - apply ptd_stack_compat_build_from_pt=>/=. eauto. +Qed. + +(** We can now proceed by proving that the invariant is preserved by + each step of parsing. We also prove that each step of parsing + follows next_ptd. + + We start with reduce steps: *) +Lemma reduce_step_next_ptd (prod : production) (word : list token) + (ptl : parse_tree_list (prod_rhs_rev prod) word) + (ptz : pt_zipper (NT (prod_lhs prod)) word) + (stk : stack) + Hval Hi : + ptd_stack_compat (Reduce_ptd ptl ptz) stk -> + match next_ptd (Reduce_ptd ptl ptz) with + | None => + reduce_step init stk prod (ptz_buffer ptz) Hval Hi = + Accept_sr (ptd_sem (Reduce_ptd ptl ptz)) buffer_end + | Some ptd => + exists stk', + reduce_step init stk prod (ptz_buffer ptz) Hval Hi = + Progress_sr stk' (ptd_buffer ptd) /\ + ptd_stack_compat ptd stk' + end. +Proof. + intros (stk0 & _ & Hstk & Hstk0). + apply pop_stack_compat_pop_spec with (action := prod_action prod) in Hstk. + rewrite <-pop_spec_ok with (Hp := reduce_step_subproof init stk prod Hval Hi) in Hstk. + unfold reduce_step. + match goal with + | |- context [pop_state_valid init ?A stk ?B ?C ?D ?E ?F] => + generalize (pop_state_valid init A stk B C D E F) + end. + rewrite Hstk /=. intros Hv. + generalize (reduce_step_subproof1 init stk prod Hval stk0 (fun _ : True => Hv)). + clear Hval Hstk Hi Hv stk. + assert (Hgoto := fun fut prod' => + non_terminal_goto (state_of_stack init stk0) prod' (NT (prod_lhs prod)::fut)). + simpl in Hgoto. + destruct goto_table as [[st Hst]|] eqn:Hgoto'. + - intros _. + assert (match ptz with Top_ptz => False | _ => True end). + { revert ptz Hst Hstk0 Hgoto'. + generalize (eq_refl (NT (prod_lhs prod))). + generalize (NT (prod_lhs prod)) at 1 3 5. + intros nt Hnt ptz. destruct ptz=>//. injection Hnt=> <- /= Hst -> /= Hg. + assert (Hsg := start_goto init). by rewrite Hg in Hsg. } + clear Hgoto'. + + change (ptl_sem ptl (prod_action prod)) + with (pt_sem (Non_terminal_pt prod ptl)). + generalize (Non_terminal_pt prod ptl). clear ptl. + destruct ptz as [|?? ptl ? ? ptlz]=>// pt. + + subst=>/=. eexists _. split. + + f_equal. apply ptd_buffer_build_from_ptl. + + destruct Hstk0 as (stk0' & Hfut & Hstk0' & Hstk0). + apply (ptd_stack_compat_build_from_ptl _ _ _ stk0'); auto; []. + split=>//. by exists eq_refl. + - intros Hv. generalize (reduce_step_subproof0 _ prod _ (fun _ => Hv)). + intros EQnt. clear Hv Hgoto'. + + change (ptl_sem ptl (prod_action prod)) + with (pt_sem (Non_terminal_pt prod ptl)). + generalize (Non_terminal_pt prod ptl). clear ptl. destruct ptz. + + intros pt. f_equal. by rewrite cast_eq. + + edestruct Hgoto. eapply ptz_stack_compat_cons_state_has_future, Hstk0. +Qed. + +Lemma step_next_ptd (ptd : pt_dot) (stk : stack) Hi : + ptd_stack_compat ptd stk -> + match next_ptd ptd with + | None => + step safe init stk (ptd_buffer ptd) Hi = + Accept_sr (ptd_sem ptd) buffer_end + | Some ptd' => + exists stk', + step safe init stk (ptd_buffer ptd) Hi = + Progress_sr stk' (ptd_buffer ptd') /\ + ptd_stack_compat ptd' stk' + end. +Proof. + intros Hstk. unfold step. + generalize (reduce_ok safe (state_of_stack init stk)). + destruct ptd as [prod word ptl ptz|tok symbs word ptl ptlz]. + - assert (Hfut : state_has_future (state_of_stack init stk) prod [] + (token_term (buf_head (ptz_buffer ptz)))). + { destruct Hstk as (? & ? & ?)=>//. } + assert (Hact := end_reduce _ _ _ _ Hfut). + destruct action_table as [?|awt]=>Hval /=. + + subst. by apply reduce_step_next_ptd. + + set (term := token_term (buf_head (ptz_buffer ptz))) in *. + generalize (Hval term). clear Hval. destruct (awt term)=>//. subst. + intros Hval. by apply reduce_step_next_ptd. + - destruct Hstk as (stk0 & Hfut & Hstk & Hstk0). + assert (Hact := terminal_shift _ _ _ _ Hfut). simpl in Hact. clear Hfut. + destruct action_table as [?|awt]=>//= /(_ (token_term tok)). + destruct awt as [st' EQ| |]=>// _. eexists. split. + + f_equal. rewrite -ptd_buffer_build_from_ptl //. + + apply (ptd_stack_compat_build_from_ptl _ _ _ stk0); simpl; eauto. +Qed. + +(** We prove the completeness of the parser main loop. *) +Lemma parse_fix_next_ptd_iter (ptd : pt_dot) (stk : stack) (log_n_steps : nat) Hi : + ptd_stack_compat ptd stk -> + match next_ptd_iter ptd log_n_steps with + | None => + proj1_sig (parse_fix safe init stk (ptd_buffer ptd) log_n_steps Hi) = + Accept_sr (ptd_sem ptd) buffer_end + | Some ptd' => + exists stk', + proj1_sig (parse_fix safe init stk (ptd_buffer ptd) log_n_steps Hi) = + Progress_sr stk' (ptd_buffer ptd') /\ + ptd_stack_compat ptd' stk' + end. +Proof. + revert ptd stk Hi. + induction log_n_steps as [|log_n_steps IH]; [by apply step_next_ptd|]. + move => /= ptd stk Hi Hstk. assert (IH1 := IH ptd stk Hi Hstk). + assert (EQsem := sem_next_ptd_iter ptd log_n_steps). + destruct parse_fix as [sr Hi']. simpl in IH1. + destruct next_ptd_iter as [ptd'|]. + - rewrite EQsem. destruct IH1 as (stk' & -> & Hstk'). by apply IH. + - by subst. +Qed. + +(** The parser is defined by recursion over a fuel parameter. In the + completeness proof, we need to predict how much fuel is going to be + needed in order to prove that enough fuel gives rise to a successful + parsing. + + To do so, of a dotted parse tree, which is the number of actions + left to be executed before complete parsing when the current state + is represented by the dotted parse tree. *) +Fixpoint ptlz_cost {hole_symbs hole_word} + (ptlz:ptl_zipper hole_symbs hole_word) := + match ptlz with + | Non_terminal_pt_ptlz ptz => ptz_cost ptz + | Cons_ptl_ptlz pt ptlz' => pt_size pt + ptlz_cost ptlz' + end +with ptz_cost {hole_symb hole_word} (ptz:pt_zipper hole_symb hole_word) := + match ptz with + | Top_ptz => 0 + | Cons_ptl_ptz ptl ptlz' => 1 + ptlz_cost ptlz' + end. + +Definition ptd_cost (ptd:pt_dot) := + match ptd with + | Reduce_ptd ptl ptz => ptz_cost ptz + | Shift_ptd _ ptl ptlz => 1 + ptlz_cost ptlz + end. + +Lemma ptd_cost_build_from_pt {symb word} + (pt : parse_tree symb word) (ptz : pt_zipper symb word) : + pt_size pt + ptz_cost ptz = S (ptd_cost (build_pt_dot_from_pt pt ptz)) +with ptd_cost_build_from_pt_rec {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) + Hsymbs : + ptl_size ptl + ptlz_cost ptlz = ptd_cost (build_pt_dot_from_pt_rec ptl Hsymbs ptlz). +Proof. + - destruct pt as [tok|prod word ptl']=>/=. + + revert ptz. generalize [tok]. generalize (token_sem tok). generalize I. + change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1. + generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz. + + match goal with + | |- context [match ?X with Some H => _ | None => _ end] => destruct X eqn:EQ + end. + * rewrite -ptd_cost_build_from_pt_rec /= plus_n_Sm //. + * simpl. by destruct ptl'. + - destruct ptl as [|?? ptl ?? pt]; [contradiction|]. + specialize (ptd_cost_build_from_pt_rec _ _ ptl). destruct ptl. + + apply eq_add_S. rewrite -ptd_cost_build_from_pt /=. ring. + + rewrite -ptd_cost_build_from_pt_rec //=. ring. +Qed. + +Lemma ptd_cost_build_from_ptl {symbs word} + (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) : + ptlz_cost ptlz = ptd_cost (build_pt_dot_from_ptl ptl ptlz). +Proof. + destruct ptlz=>//. apply eq_add_S. rewrite -ptd_cost_build_from_pt /=. ring. +Qed. + +Lemma next_ptd_cost ptd: + match next_ptd ptd with + | None => ptd_cost ptd = 0 + | Some ptd' => ptd_cost ptd = S (ptd_cost ptd') + end. +Proof. + destruct ptd as [prod word ptl ptz|tok symbq wordq ptl ptlz] =>/=. + - generalize (Non_terminal_pt prod ptl). clear ptl. + destruct ptz as [|?? ptl ?? ptlz]=>// pt. by rewrite -ptd_cost_build_from_ptl. + - by rewrite -ptd_cost_build_from_ptl. +Qed. + +Lemma next_ptd_iter_cost ptd log_n_steps : + match next_ptd_iter ptd log_n_steps with + | None => ptd_cost ptd < 2^log_n_steps + | Some ptd' => ptd_cost ptd = 2^log_n_steps + ptd_cost ptd' + end. +Proof. + revert ptd. induction log_n_steps as [|log_n_steps IH]=>ptd /=. + - assert (Hptd := next_ptd_cost ptd). destruct next_ptd=>//. by rewrite Hptd. + - rewrite Nat.add_0_r. assert (IH1 := IH ptd). destruct next_ptd_iter as [ptd'|]. + + specialize (IH ptd'). destruct next_ptd_iter as [ptd''|]. + * by rewrite IH1 IH -!plus_assoc. + * rewrite IH1. by apply plus_lt_compat_l. + + by apply lt_plus_trans. +Qed. + +(** We now prove the top-level parsing function. The only thing that + is left to be done is the initialization. To do so, we define the + initial dotted parse tree, depending on a full (top-level) parse tree. *) + +Variable full_pt : parse_tree (NT (start_nt init)) full_word. + +Theorem parse_complete log_n_steps: + match parse safe init (full_word ++ buffer_end) log_n_steps with + | Parsed_pr sem buff => + sem = pt_sem full_pt /\ buff = buffer_end /\ pt_size full_pt <= 2^log_n_steps + | Timeout_pr => 2^log_n_steps < pt_size full_pt + | Fail_pr => False + end. +Proof. + assert (Hstk : ptd_stack_compat (build_pt_dot_from_pt full_pt Top_ptz) []) by + by apply ptd_stack_compat_build_from_pt. + unfold parse. + assert (Hparse := parse_fix_next_ptd_iter _ _ log_n_steps (parse_subproof init) Hstk). + rewrite -ptd_buffer_build_from_pt -sem_build_from_pt /= in Hparse. + assert (Hcost := next_ptd_iter_cost (build_pt_dot_from_pt full_pt Top_ptz) log_n_steps). + destruct next_ptd_iter. + - destruct Hparse as (? & -> & ?). apply (f_equal S) in Hcost. + rewrite -ptd_cost_build_from_pt Nat.add_0_r in Hcost. rewrite Hcost. + apply le_lt_n_Sm, le_plus_l. + - rewrite Hparse. split; [|split]=>//. apply lt_le_S in Hcost. + by rewrite -ptd_cost_build_from_pt Nat.add_0_r in Hcost. +Qed. + +End Completeness_Proof. + +End Make. diff --git a/MenhirLib/Interpreter_correct.v b/MenhirLib/Interpreter_correct.v new file mode 100644 index 00000000..1325f610 --- /dev/null +++ b/MenhirLib/Interpreter_correct.v @@ -0,0 +1,175 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import List Syntax. +Require Import Alphabet. +Require Grammar Automaton Interpreter. +From Coq.ssr Require Import ssreflect. + +Module Make(Import A:Automaton.T) (Import Inter:Interpreter.T A). + +(** * Correctness of the interpreter **) + +(** We prove that, in any case, if the interpreter accepts returning a + semantic value, then this is a semantic value of the input **) + +Section Init. + +Variable init:initstate. + +(** [word_has_stack_semantics] relates a word with a stack, stating that the + word is a concatenation of words that have the semantic values stored in + the stack. **) +Inductive word_has_stack_semantics: + forall (word:list token) (stack:stack), Prop := + | Nil_stack_whss: word_has_stack_semantics [] [] + | Cons_stack_whss: + forall (wordq:list token) (stackq:stack), + word_has_stack_semantics wordq stackq -> + + forall (wordt:list token) (s:noninitstate) + (pt:parse_tree (last_symb_of_non_init_state s) wordt), + + word_has_stack_semantics + (wordq++wordt) (existT noninitstate_type s (pt_sem pt)::stackq). + +(** [pop] preserves the invariant **) +Lemma pop_spec_ptl A symbols_to_pop action word_stk stk (res : A) stk' : + pop_spec symbols_to_pop stk action stk' res -> + word_has_stack_semantics word_stk stk -> + exists word_stk' word_res (ptl:parse_tree_list symbols_to_pop word_res), + (word_stk' ++ word_res = word_stk)%list /\ + word_has_stack_semantics word_stk' stk' /\ + ptl_sem ptl action = res. +Proof. + intros Hspec. revert word_stk. + induction Hspec as [stk sem|symbols_to_pop st stk action sem stk' res Hspec IH]; + intros word_stk Hword_stk. + - exists word_stk, [], Nil_ptl. rewrite -app_nil_end. eauto. + - inversion Hword_stk. subst_existT. + edestruct IH as (word_stk' & word_res & ptl & ? & Hword_stk'' & ?); [eassumption|]. + subst. eexists word_stk', (word_res ++ _)%list, (Cons_ptl ptl _). + split; [|split]=>//. rewrite app_assoc //. +Qed. + +(** [reduce_step] preserves the invariant **) +Lemma reduce_step_invariant (stk:stack) (prod:production) Hv Hi word buffer : + word_has_stack_semantics word stk -> + match reduce_step init stk prod buffer Hv Hi with + | Accept_sr sem buffer_new => + exists pt : parse_tree (NT (start_nt init)) word, + buffer = buffer_new /\ pt_sem pt = sem + | Progress_sr stk' buffer_new => + buffer = buffer_new /\ word_has_stack_semantics word stk' + | Fail_sr => True + end. +Proof. + intros Hword_stk. unfold reduce_step. + match goal with + | |- context [pop_state_valid init ?stp stk ?x1 ?x2 ?x3 ?x4 ?x5] => + generalize (pop_state_valid init stp stk x1 x2 x3 x4 x5) + end. + destruct pop as [stk' sem] eqn:Hpop=>/= Hv'. + apply pop_spec_ok in Hpop. apply pop_spec_ptl with (word_stk := word) in Hpop=>//. + destruct Hpop as (word1 & word2 & ptl & <- & Hword1 & <-). + generalize (reduce_step_subproof1 init stk prod Hv stk' (fun _ : True => Hv')). + destruct goto_table as [[st' EQ]|]. + - intros _. split=>//. + change (ptl_sem ptl (prod_action prod)) with (pt_sem (Non_terminal_pt prod ptl)). + generalize (Non_terminal_pt prod ptl). rewrite ->EQ. intros pt. by constructor. + - intros Hstk'. destruct Hword1; [|by destruct Hstk']. + generalize (reduce_step_subproof0 init prod [] (fun _ : True => Hstk')). + simpl in Hstk'. rewrite -Hstk' // => EQ. rewrite cast_eq. + exists (Non_terminal_pt prod ptl). by split. +Qed. + +(** [step] preserves the invariant **) +Lemma step_invariant stk word buffer safe Hi : + word_has_stack_semantics word stk -> + match step safe init stk buffer Hi with + | Accept_sr sem buffer_new => + exists word_new (pt:parse_tree (NT (start_nt init)) word_new), + (word ++ buffer = word_new ++ buffer_new)%buf /\ + pt_sem pt = sem + | Progress_sr stk_new buffer_new => + exists word_new, + (word ++ buffer = word_new ++ buffer_new)%buf /\ + word_has_stack_semantics word_new stk_new + | Fail_sr => True + end. +Proof. + intros Hword_stk. unfold step. + generalize (reduce_ok safe (state_of_stack init stk)). + destruct action_table as [prod|awt]. + - intros Hv. + apply (reduce_step_invariant stk prod (fun _ => Hv) Hi word buffer) in Hword_stk. + destruct reduce_step=>//. + + destruct Hword_stk as (pt & <- & <-); eauto. + + destruct Hword_stk as [<- ?]; eauto. + - destruct buffer as [tok buffer]=>/=. + move=> /(_ (token_term tok)) Hv. destruct (awt (token_term tok)) as [st EQ|prod|]=>//. + + eexists _. split; [by apply app_buf_assoc with (l2 := [_])|]. + change (token_sem tok) with (pt_sem (Terminal_pt tok)). + generalize (Terminal_pt tok). generalize [tok]. + rewrite -> EQ=>word' pt /=. by constructor. + + apply (reduce_step_invariant stk prod (fun _ => Hv) Hi word (tok::buffer)) + in Hword_stk. + destruct reduce_step=>//. + * destruct Hword_stk as (pt & <- & <-); eauto. + * destruct Hword_stk as [<- ?]; eauto. +Qed. + +(** [step] preserves the invariant **) +Lemma parse_fix_invariant stk word buffer safe log_n_steps Hi : + word_has_stack_semantics word stk -> + match proj1_sig (parse_fix safe init stk buffer log_n_steps Hi) with + | Accept_sr sem buffer_new => + exists word_new (pt:parse_tree (NT (start_nt init)) word_new), + (word ++ buffer = word_new ++ buffer_new)%buf /\ + pt_sem pt = sem + | Progress_sr stk_new buffer_new => + exists word_new, + (word ++ buffer = word_new ++ buffer_new)%buf /\ + word_has_stack_semantics word_new stk_new + | Fail_sr => True + end. +Proof. + revert stk word buffer Hi. + induction log_n_steps as [|log_n_steps IH]=>/= stk word buffer Hi Hstk; + [by apply step_invariant|]. + assert (IH1 := IH stk word buffer Hi Hstk). + destruct parse_fix as [[] Hi']=>/=; try by apply IH1. + destruct IH1 as (word' & -> & Hstk')=>//. by apply IH. +Qed. + +(** The interpreter is correct : if it returns a semantic value, then the input + word has this semantic value. +**) +Theorem parse_correct safe buffer log_n_steps: + match parse safe init buffer log_n_steps with + | Parsed_pr sem buffer_new => + exists word_new (pt:parse_tree (NT (start_nt init)) word_new), + buffer = (word_new ++ buffer_new)%buf /\ + pt_sem pt = sem + | _ => True + end. +Proof. + unfold parse. + assert (Hparse := parse_fix_invariant [] [] buffer safe log_n_steps + (parse_subproof init)). + destruct proj1_sig=>//. apply Hparse. constructor. +Qed. + +End Init. + +End Make. diff --git a/MenhirLib/Main.v b/MenhirLib/Main.v new file mode 100644 index 00000000..f6158074 --- /dev/null +++ b/MenhirLib/Main.v @@ -0,0 +1,79 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +Require Grammar Automaton Interpreter_correct Interpreter_complete. +From Coq Require Import Syntax Arith. + +Module Make(Export Aut:Automaton.T). +Export Aut.Gram. +Export Aut.GramDefs. + +Module Import Inter := Interpreter.Make Aut. +Module Correct := Interpreter_correct.Make Aut Inter. +Module Complete := Interpreter_complete.Make Aut Inter. + +Definition complete_validator:unit->bool := Complete.Valid.is_complete. +Definition safe_validator:unit->bool := ValidSafe.is_safe. +Definition parse (safe:safe_validator ()=true) init log_n_steps buffer : + parse_result (symbol_semantic_type (NT (start_nt init))):= + parse (ValidSafe.safe_is_validator safe) init buffer log_n_steps. + +(** Correction theorem. **) +Theorem parse_correct + (safe:safe_validator ()= true) init log_n_steps buffer: + match parse safe init log_n_steps buffer with + | Parsed_pr sem buffer_new => + exists word (pt : parse_tree (NT (start_nt init)) word), + buffer = (word ++ buffer_new)%buf /\ + pt_sem pt = sem + | _ => True + end. +Proof. apply Correct.parse_correct. Qed. + +(** Completeness theorem. **) +Theorem parse_complete + (safe:safe_validator () = true) init log_n_steps word buffer_end: + complete_validator () = true -> + forall tree:parse_tree (NT (start_nt init)) word, + match parse safe init log_n_steps (word ++ buffer_end) with + | Fail_pr => False + | Parsed_pr sem_res buffer_end_res => + sem_res = pt_sem tree /\ buffer_end_res = buffer_end /\ + pt_size tree <= 2^log_n_steps + | Timeout_pr => 2^log_n_steps < pt_size tree + end. +Proof. + intros. now apply Complete.parse_complete, Complete.Valid.complete_is_validator. +Qed. + +(** Unambiguity theorem. **) +Theorem unambiguity: + safe_validator () = true -> complete_validator () = true -> inhabited token -> + forall init word, + forall (tree1 tree2:parse_tree (NT (start_nt init)) word), + pt_sem tree1 = pt_sem tree2. +Proof. + intros Hsafe Hcomp [tok] init word tree1 tree2. + pose (buf_end := cofix buf_end := (tok :: buf_end)%buf). + assert (Hcomp1 := parse_complete Hsafe init (pt_size tree1) word buf_end + Hcomp tree1). + assert (Hcomp2 := parse_complete Hsafe init (pt_size tree1) word buf_end + Hcomp tree2). + destruct parse. + - destruct Hcomp1. + - exfalso. eapply PeanoNat.Nat.lt_irrefl. etransitivity; [|apply Hcomp1]. + eapply Nat.pow_gt_lin_r. constructor. + - destruct Hcomp1 as [-> _], Hcomp2 as [-> _]. reflexivity. +Qed. + +End Make. diff --git a/MenhirLib/Validator_classes.v b/MenhirLib/Validator_classes.v new file mode 100644 index 00000000..d8063123 --- /dev/null +++ b/MenhirLib/Validator_classes.v @@ -0,0 +1,75 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import List. +From Coq.ssr Require Import ssreflect. +Require Import Alphabet. + +Class IsValidator (P : Prop) (b : bool) := + is_validator : b = true -> P. +Hint Mode IsValidator + - : typeclass_instances. + +Instance is_validator_true : IsValidator True true. +Proof. done. Qed. + +Instance is_validator_false : IsValidator False false. +Proof. done. Qed. + +Instance is_validator_eq_true b : + IsValidator (b = true) b. +Proof. done. Qed. + +Instance is_validator_and P1 b1 P2 b2 `{IsValidator P1 b1} `{IsValidator P2 b2}: + IsValidator (P1 /\ P2) (if b1 then b2 else false). +Proof. by split; destruct b1, b2; apply is_validator. Qed. + +Instance is_validator_comparable_leibniz_eq A (C:Comparable A) (x y : A) : + ComparableLeibnizEq C -> + IsValidator (x = y) (compare_eqb x y). +Proof. intros ??. by apply compare_eqb_iff. Qed. + +Instance is_validator_comparable_eq_impl A `(Comparable A) (x y : A) P b : + IsValidator P b -> + IsValidator (x = y -> P) (if compare_eqb x y then b else true). +Proof. + intros Hval Val ->. rewrite /compare_eqb compare_refl in Val. auto. +Qed. + +Lemma is_validator_forall_finite A P b `(Finite A) : + (forall (x : A), IsValidator (P x) (b x)) -> + IsValidator (forall (x : A), P x) (forallb b all_list). +Proof. + move=> ? /forallb_forall Hb ?. + apply is_validator, Hb, all_list_forall. +Qed. + +(* We do not use an instance directly here, because we need somehow to + force Coq to instantiate b with a lambda. *) +Hint Extern 2 (IsValidator (forall x : ?A, _) _) => + eapply (is_validator_forall_finite _ _ (fun (x:A) => _)) + : typeclass_instances. + +(* Hint for synthetizing pattern-matching. *) +Hint Extern 2 (IsValidator (match ?u with _ => _ end) ?b0) => + let b := fresh "b" in + unshelve notypeclasses refine (let b : bool := _ in _); + [destruct u; intros; shelve|]; (* Synthetize `match .. with` in the validator. *) + unify b b0; + unfold b; destruct u; clear b + : typeclass_instances. + +(* Hint for unfolding definitions. This is necessary because many + hints for IsValidator use [Hint Extern], which do not automatically + unfold identifiers. *) +Hint Extern 100 (IsValidator ?X _) => unfold X + : typeclass_instances. diff --git a/MenhirLib/Validator_complete.v b/MenhirLib/Validator_complete.v new file mode 100644 index 00000000..ebb74500 --- /dev/null +++ b/MenhirLib/Validator_complete.v @@ -0,0 +1,394 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import List Syntax Derive. +From Coq.ssr Require Import ssreflect. +Require Automaton. +Require Import Alphabet Validator_classes. + +Module Make(Import A:Automaton.T). + +(** We instantiate some sets/map. **) +Module TerminalComparableM <: ComparableM. + Definition t := terminal. + Instance tComparable : Comparable t := _. +End TerminalComparableM. +Module TerminalOrderedType := OrderedType_from_ComparableM TerminalComparableM. +Module StateProdPosComparableM <: ComparableM. + Definition t := (state*production*nat)%type. + Instance tComparable : Comparable t := _. +End StateProdPosComparableM. +Module StateProdPosOrderedType := + OrderedType_from_ComparableM StateProdPosComparableM. + +Module TerminalSet := FSetAVL.Make TerminalOrderedType. +Module StateProdPosMap := FMapAVL.Make StateProdPosOrderedType. + +(** Nullable predicate for symbols and list of symbols. **) +Definition nullable_symb (symbol:symbol) := + match symbol with + | NT nt => nullable_nterm nt + | _ => false + end. + +Definition nullable_word (word:list symbol) := + forallb nullable_symb word. + +(** First predicate for non terminal, symbols and list of symbols, given as FSets. **) +Definition first_nterm_set (nterm:nonterminal) := + fold_left (fun acc t => TerminalSet.add t acc) + (first_nterm nterm) TerminalSet.empty. + +Definition first_symb_set (symbol:symbol) := + match symbol with + | NT nt => first_nterm_set nt + | T t => TerminalSet.singleton t + end. + +Fixpoint first_word_set (word:list symbol) := + match word with + | [] => TerminalSet.empty + | t::q => + if nullable_symb t then + TerminalSet.union (first_symb_set t) (first_word_set q) + else + first_symb_set t + end. + +(** Small helper for finding the part of an item that is after the dot. **) +Definition future_of_prod prod dot_pos : list symbol := + (fix loop n lst := + match n with + | O => lst + | S x => match loop x lst with [] => [] | _::q => q end + end) + dot_pos (rev' (prod_rhs_rev prod)). + +(** We build a fast map to store all the items of all the states. **) +Definition items_map (_:unit): StateProdPosMap.t TerminalSet.t := + fold_left (fun acc state => + fold_left (fun acc item => + let key := (state, prod_item item, dot_pos_item item) in + let data := fold_left (fun acc t => TerminalSet.add t acc) + (lookaheads_item item) TerminalSet.empty + in + let old := + match StateProdPosMap.find key acc with + | Some x => x | None => TerminalSet.empty + end + in + StateProdPosMap.add key (TerminalSet.union data old) acc + ) (items_of_state state) acc + ) all_list (StateProdPosMap.empty TerminalSet.t). + +(** We need to avoid computing items_map each time we need it. To that + purpose, we declare a typeclass specifying that some map is equal to + items_map. *) +Class IsItemsMap m := is_items_map : m = items_map (). + +(** Accessor. **) +Definition find_items_map items_map state prod dot_pos : TerminalSet.t := + match StateProdPosMap.find (state, prod, dot_pos) items_map with + | None => TerminalSet.empty + | Some x => x + end. + +Definition state_has_future state prod (fut:list symbol) (lookahead:terminal) := + exists dot_pos:nat, + fut = future_of_prod prod dot_pos /\ + TerminalSet.In lookahead (find_items_map (items_map ()) state prod dot_pos). + +(** Iterator over items. **) +Definition forallb_items items_map (P:state -> production -> nat -> TerminalSet.t -> bool): bool:= + StateProdPosMap.fold (fun key set acc => + match key with (st, p, pos) => (acc && P st p pos set)%bool end + ) items_map true. + +(** Typeclass instances for synthetizing the validator. *) + +Instance is_validator_subset S1 S2 : + IsValidator (TerminalSet.Subset S1 S2) (TerminalSet.subset S1 S2). +Proof. intros ?. by apply TerminalSet.subset_2. Qed. + +(* While the specification of the validator always quantify over + possible lookahead tokens individually, the validator usually + handles lookahead sets directly instead, for better performances. + + For instance, the validator for [state_has_future], which speaks + about one single lookahead token is a subset operation: +*) +Lemma is_validator_state_has_future_subset st prod pos lookahead lset im fut : + TerminalSet.In lookahead lset -> + fut = future_of_prod prod pos -> + IsItemsMap im -> + IsValidator (state_has_future st prod fut lookahead) + (TerminalSet.subset lset (find_items_map im st prod pos)). +Proof. + intros ? -> -> HSS%TerminalSet.subset_2. exists pos. split=>//. by apply HSS. +Qed. +(* We do not declare this lemma as an instance, and use [Hint Extern] + instead, because the typeclass mechanism has trouble instantiating + some evars if we do not explicitely call [eassumption]. *) +Hint Extern 2 (IsValidator (state_has_future _ _ _ _) _) => + eapply is_validator_state_has_future_subset; [eassumption|eassumption || reflexivity|] +: typeclass_instances. + +(* As said previously, we manipulate lookahead terminal sets instead of + lookahead individually. Hence, when we quantify over a lookahead set + in the specification, we do not do anything in the executable + validator. + + This instance is used for [non_terminal_closed]. *) +Instance is_validator_forall_lookahead_set lset P b: + (forall lookahead, TerminalSet.In lookahead lset -> IsValidator (P lookahead) b) -> + IsValidator (forall lookahead, TerminalSet.In lookahead lset -> P lookahead) b. +Proof. unfold IsValidator. firstorder. Qed. + + +(* Dually, we sometimes still need to explicitelly iterate over a + lookahead set. This is what this lemma allows. + Used only in [end_reduce]. *) +Lemma is_validator_iterate_lset P b lookahead lset : + TerminalSet.In lookahead lset -> + IsValidator P (b lookahead) -> + IsValidator P (TerminalSet.fold (fun lookahead acc => + if acc then b lookahead else false) lset true). +Proof. + intros Hlset%TerminalSet.elements_1 Hval Val. apply Hval. + revert Val. rewrite TerminalSet.fold_1. generalize true at 1. clear -Hlset. + induction Hlset as [? l <-%compare_eq|? l ? IH]=> /= b' Val. + - destruct (b lookahead). by destruct b'. exfalso. by induction l; destruct b'. + - eauto. +Qed. +Hint Extern 100 (IsValidator _ _) => + match goal with + | H : TerminalSet.In ?lookahead ?lset |- _ => + eapply (is_validator_iterate_lset _ (fun lookahead => _) _ _ H); clear H + end +: typeclass_instances. + +(* We often quantify over all the items of all the states of the + automaton. This lemma and the accompanying [Hint Resolve] + declaration allow generating the corresponding executable + validator. + + Note that it turns out that, in all the uses of this pattern, the + first thing we do for each item is pattern-matching over the + future. This lemma also embbed this pattern-matching, which makes + it possible to get the hypothesis [fut' = future_of_prod prod (S pos)] + in the non-nil branch. + + Moreover, note, again, that while the specification quantifies over + lookahead terminals individually, the code provides lookahead sets + instead. *) +Lemma is_validator_forall_items P1 b1 P2 b2 im : + IsItemsMap im -> + + (forall st prod lookahead lset pos, + TerminalSet.In lookahead lset -> + [] = future_of_prod prod pos -> + IsValidator (P1 st prod lookahead) (b1 st prod pos lset)) -> + + (forall st prod pos lookahead lset s fut', + TerminalSet.In lookahead lset -> + fut' = future_of_prod prod (S pos) -> + IsValidator (P2 st prod lookahead s fut') (b2 st prod pos lset s fut')) -> + + IsValidator (forall st prod fut lookahead, + state_has_future st prod fut lookahead -> + match fut with + | [] => P1 st prod lookahead + | s :: fut' => P2 st prod lookahead s fut' + end) + (forallb_items im (fun st prod pos lset => + match future_of_prod prod pos with + | [] => b1 st prod pos lset + | s :: fut' => b2 st prod pos lset s fut' + end)). +Proof. + intros -> Hval1 Hval2 Val st prod fut lookahead (pos & -> & Hlookahead). + rewrite /forallb_items StateProdPosMap.fold_1 in Val. + assert (match future_of_prod prod pos with + | [] => b1 st prod pos (find_items_map (items_map ()) st prod pos) + | s :: fut' => b2 st prod pos (find_items_map (items_map ()) st prod pos) s fut' + end = true). + - unfold find_items_map in *. + assert (Hfind := @StateProdPosMap.find_2 _ (items_map ()) (st, prod, pos)). + destruct StateProdPosMap.find as [lset|]; [|by edestruct (TerminalSet.empty_1); eauto]. + specialize (Hfind _ eq_refl). apply StateProdPosMap.elements_1 in Hfind. + revert Val. generalize true at 1. + induction Hfind as [[? ?] l [?%compare_eq ?]|??? IH]=>?. + + simpl in *; subst. + match goal with |- _ -> ?X = true => destruct X end; [done|]. + rewrite Bool.andb_false_r. clear. induction l as [|[[[??]?]?] l IH]=>//. + + apply IH. + - destruct future_of_prod eqn:EQ. by eapply Hval1; eauto. + eapply Hval2 with (pos := pos); eauto; []. + revert EQ. unfold future_of_prod=>-> //. +Qed. +(* We need a hint for expplicitely instantiating b1 and b2 with lambdas. *) +Hint Extern 0 (IsValidator + (forall st prod fut lookahead, + state_has_future st prod fut lookahead -> _) + _) => + eapply (is_validator_forall_items _ (fun st prod pos lset => _) + _ (fun st prod pos lset s fut' => _)) + : typeclass_instances. + +(* Used in [start_future] only. *) +Instance is_validator_forall_state_has_future im st prod : + IsItemsMap im -> + IsValidator + (forall look, state_has_future st prod (rev' (prod_rhs_rev prod)) look) + (let lookaheads := find_items_map im st prod 0 in + forallb (fun t => TerminalSet.mem t lookaheads) all_list). +Proof. + move=> -> /forallb_forall Val look. + specialize (Val look (all_list_forall _)). exists 0. split=>//. + by apply TerminalSet.mem_2. +Qed. + +(** * Validation for completeness **) + +(** The nullable predicate is a fixpoint : it is correct. **) +Definition nullable_stable := + forall p:production, + if nullable_word (prod_rhs_rev p) then + nullable_nterm (prod_lhs p) = true + else True. + +(** The first predicate is a fixpoint : it is correct. **) +Definition first_stable:= + forall (p:production), + TerminalSet.Subset (first_word_set (rev' (prod_rhs_rev p))) + (first_nterm_set (prod_lhs p)). + +(** The initial state has all the S=>.u items, where S is the start non-terminal **) +Definition start_future := + forall (init:initstate) (p:production), + prod_lhs p = start_nt init -> + forall (t:terminal), + state_has_future init p (rev' (prod_rhs_rev p)) t. + +(** If a state contains an item of the form A->_.av[[b]], where a is a + terminal, then reading an a does a [Shift_act], to a state containing + an item of the form A->_.v[[b]]. **) +Definition terminal_shift := + forall (s1:state) prod fut lookahead, + state_has_future s1 prod fut lookahead -> + match fut with + | T t::q => + match action_table s1 with + | Lookahead_act awp => + match awp t with + | Shift_act s2 _ => + state_has_future s2 prod q lookahead + | _ => False + end + | _ => False + end + | _ => True + end. + +(** If a state contains an item of the form A->_.[[a]], then either we do a + [Default_reduce_act] of the corresponding production, either a is a + terminal (ie. there is a lookahead terminal), and reading a does a + [Reduce_act] of the corresponding production. **) +Definition end_reduce := + forall (s:state) prod fut lookahead, + state_has_future s prod fut lookahead -> + match fut with + | [] => + match action_table s with + | Default_reduce_act p => p = prod + | Lookahead_act awt => + match awt lookahead with + | Reduce_act p => p = prod + | _ => False + end + end + | _ => True + end. + +Definition is_end_reduce items_map := + forallb_items items_map (fun s prod pos lset => + match future_of_prod prod pos with + | [] => + match action_table s with + | Default_reduce_act p => compare_eqb p prod + | Lookahead_act awt => + TerminalSet.fold (fun lookahead acc => + match awt lookahead with + | Reduce_act p => (acc && compare_eqb p prod)%bool + | _ => false + end) lset true + end + | _ => true + end). + +(** If a state contains an item of the form A->_.Bv[[b]], where B is a + non terminal, then the goto table says we have to go to a state containing + an item of the form A->_.v[[b]]. **) +Definition non_terminal_goto := + forall (s1:state) prod fut lookahead, + state_has_future s1 prod fut lookahead -> + match fut with + | NT nt::q => + match goto_table s1 nt with + | Some (exist _ s2 _) => + state_has_future s2 prod q lookahead + | None => False + end + | _ => True + end. + +Definition start_goto := + forall (init:initstate), + match goto_table init (start_nt init) with + | None => True + | Some _ => False + end. + +(** Closure property of item sets : if a state contains an item of the form + A->_.Bv[[b]], then for each production B->u and each terminal a of + first(vb), the state contains an item of the form B->_.u[[a]] **) +Definition non_terminal_closed := + forall s1 prod fut lookahead, + state_has_future s1 prod fut lookahead -> + match fut with + | NT nt::q => + forall p, prod_lhs p = nt -> + (if nullable_word q then + state_has_future s1 p (future_of_prod p 0) lookahead + else True) /\ + (forall lookahead2, + TerminalSet.In lookahead2 (first_word_set q) -> + state_has_future s1 p (future_of_prod p 0) lookahead2) + | _ => True + end. + +(** The automaton is complete **) +Definition complete := + nullable_stable /\ first_stable /\ start_future /\ terminal_shift + /\ end_reduce /\ non_terminal_goto /\ start_goto /\ non_terminal_closed. + +Derive is_complete_0 +SuchThat (forall im, IsItemsMap im -> IsValidator complete (is_complete_0 im)) +As complete_0_is_validator. +Proof. intros im. subst is_complete_0. instantiate (1:=fun im => _). apply _. Qed. + +Definition is_complete (_:unit) := is_complete_0 (items_map ()). +Lemma complete_is_validator : IsValidator complete (is_complete ()). +Proof. by apply complete_0_is_validator. Qed. + +End Make. diff --git a/MenhirLib/Validator_safe.v b/MenhirLib/Validator_safe.v new file mode 100644 index 00000000..628d2009 --- /dev/null +++ b/MenhirLib/Validator_safe.v @@ -0,0 +1,234 @@ +(****************************************************************************) +(* *) +(* Menhir *) +(* *) +(* Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud *) +(* *) +(* Copyright Inria. All rights reserved. This file is distributed under *) +(* the terms of the GNU Lesser General Public License as published by the *) +(* Free Software Foundation, either version 3 of the License, or (at your *) +(* option) any later version, as described in the file LICENSE. *) +(* *) +(****************************************************************************) + +From Coq Require Import List Syntax Derive. +From Coq.ssr Require Import ssreflect. +Require Automaton. +Require Import Alphabet Validator_classes. + +Module Make(Import A:Automaton.T). + +(** The singleton predicate for states **) +Definition singleton_state_pred (state:state) := + (fun state' => match compare state state' with Eq => true |_ => false end). + +(** [past_state_of_non_init_state], extended for all states. **) +Definition past_state_of_state (state:state) := + match state with + | Init _ => [] + | Ninit nis => past_state_of_non_init_state nis + end. + +(** Concatenations of last and past **) +Definition head_symbs_of_state (state:state) := + match state with + | Init _ => [] + | Ninit s => + last_symb_of_non_init_state s::past_symb_of_non_init_state s + end. +Definition head_states_of_state (state:state) := + singleton_state_pred state::past_state_of_state state. + +(** * Validation for correctness **) + +(** Prefix predicate between two lists of symbols. **) +Inductive prefix: list symbol -> list symbol -> Prop := +| prefix_nil: forall l, prefix [] l +| prefix_cons: forall l1 l2 x, prefix l1 l2 -> prefix (x::l1) (x::l2). + +(** [prefix] is transitive **) +Lemma prefix_trans: + forall (l1 l2 l3:list symbol), prefix l1 l2 -> prefix l2 l3 -> prefix l1 l3. +Proof. + intros l1 l2 l3 H1 H2. revert l3 H2. + induction H1; [now constructor|]. inversion 1. subst. constructor. eauto. +Qed. + +Fixpoint is_prefix (l1 l2:list symbol) := + match l1, l2 with + | [], _ => true + | t1::q1, t2::q2 => (compare_eqb t1 t2 && is_prefix q1 q2)%bool + | _::_, [] => false + end. + +Instance prefix_is_validator l1 l2 : IsValidator (prefix l1 l2) (is_prefix l1 l2). +Proof. + revert l2. induction l1 as [|x1 l1 IH]=>l2 Hpref. + - constructor. + - destruct l2 as [|x2 l2]=>//. + move: Hpref=> /andb_prop [/compare_eqb_iff -> /IH ?]. by constructor. +Qed. + +(** If we shift, then the known top symbols of the destination state is + a prefix of the known top symbols of the source state, with the new + symbol added. **) +Definition shift_head_symbs := + forall s, + match action_table s with + | Lookahead_act awp => forall t, + match awp t with + | Shift_act s2 _ => + prefix (past_symb_of_non_init_state s2) (head_symbs_of_state s) + | _ => True + end + | _ => True + end. + +(** When a goto happens, then the known top symbols of the destination state + is a prefix of the known top symbols of the source state, with the new + symbol added. **) +Definition goto_head_symbs := + forall s nt, + match goto_table s nt with + | Some (exist _ s2 _) => + prefix (past_symb_of_non_init_state s2) (head_symbs_of_state s) + | None => True + end. + +(** We have to say the same kind of checks for the assumptions about the + states stack. However, theses assumptions are predicates. So we define + a notion of "prefix" over predicates lists, that means, basically, that + an assumption entails another **) +Inductive prefix_pred: list (state->bool) -> list (state->bool) -> Prop := + | prefix_pred_nil: forall l, prefix_pred [] l + | prefix_pred_cons: forall l1 l2 f1 f2, + (forall x, implb (f2 x) (f1 x) = true) -> + prefix_pred l1 l2 -> prefix_pred (f1::l1) (f2::l2). + +(** [prefix_pred] is transitive **) +Lemma prefix_pred_trans: + forall (l1 l2 l3:list (state->bool)), + prefix_pred l1 l2 -> prefix_pred l2 l3 -> prefix_pred l1 l3. +Proof. + intros l1 l2 l3 H1 H2. revert l3 H2. + induction H1 as [|l1 l2 f1 f2 Hf2f1]; [now constructor|]. + intros l3. inversion 1 as [|??? f3 Hf3f2]. subst. constructor; [|now eauto]. + intros x. specialize (Hf3f2 x). specialize (Hf2f1 x). + repeat destruct (_ x); auto. +Qed. + +Fixpoint is_prefix_pred (l1 l2:list (state->bool)) := + match l1, l2 with + | [], _ => true + | f1::q1, f2::q2 => + (forallb (fun x => implb (f2 x) (f1 x)) all_list + && is_prefix_pred q1 q2)%bool + | _::_, [] => false + end. + +Instance prefix_pred_is_validator l1 l2 : + IsValidator (prefix_pred l1 l2) (is_prefix_pred l1 l2). +Proof. + revert l2. induction l1 as [|x1 l1 IH]=>l2 Hpref. + - constructor. + - destruct l2 as [|x2 l2]=>//. + move: Hpref=> /andb_prop [/forallb_forall ? /IH ?]. + constructor; auto using all_list_forall. +Qed. + +(** The assumptions about state stack is conserved when we shift **) +Definition shift_past_state := + forall s, + match action_table s with + | Lookahead_act awp => forall t, + match awp t with + | Shift_act s2 _ => + prefix_pred (past_state_of_non_init_state s2) + (head_states_of_state s) + | _ => True + end + | _ => True + end. + +(** The assumptions about state stack is conserved when we do a goto **) +Definition goto_past_state := + forall s nt, + match goto_table s nt with + | Some (exist _ s2 _) => + prefix_pred (past_state_of_non_init_state s2) + (head_states_of_state s) + | None => True + end. + +(** What states are possible after having popped these symbols from the + stack, given the annotation of the current state ? **) +Inductive state_valid_after_pop (s:state): + list symbol -> list (state -> bool) -> Prop := + | state_valid_after_pop_nil1: + forall p pl, p s = true -> state_valid_after_pop s [] (p::pl) + | state_valid_after_pop_nil2: + forall sl, state_valid_after_pop s sl [] + | state_valid_after_pop_cons: + forall st sq p pl, state_valid_after_pop s sq pl -> + state_valid_after_pop s (st::sq) (p::pl). + +Fixpoint is_state_valid_after_pop (state:state) (to_pop:list symbol) annot := + match annot, to_pop with + | [], _ => true + | p::_, [] => p state + | p::pl, s::sl => is_state_valid_after_pop state sl pl + end. + +Instance impl_is_state_valid_after_pop_is_validator state sl pl P b : + IsValidator P b -> + IsValidator (state_valid_after_pop state sl pl -> P) + (if is_state_valid_after_pop state sl pl then b else true). +Proof. + destruct (is_state_valid_after_pop _ sl pl) eqn:EQ. + - intros ???. by eapply is_validator. + - intros _ _ Hsvap. exfalso. induction Hsvap=>//; [simpl in EQ; congruence|]. + by destruct sl. +Qed. + +(** A state is valid for reducing a production when : + - The assumptions on the state are such that we will find the right hand + side of the production on the stack. + - We will be able to do a goto after having popped the right hand side. +**) +Definition valid_for_reduce (state:state) prod := + prefix (prod_rhs_rev prod) (head_symbs_of_state state) /\ + forall state_new, + state_valid_after_pop state_new + (prod_rhs_rev prod) (head_states_of_state state) -> + match goto_table state_new (prod_lhs prod) with + | None => + match state_new with + | Init i => prod_lhs prod = start_nt i + | Ninit _ => False + end + | _ => True + end. + +(** All the states that does a reduce are valid for reduction **) +Definition reduce_ok := + forall s, + match action_table s with + | Lookahead_act awp => + forall t, match awp t with + | Reduce_act p => valid_for_reduce s p + | _ => True + end + | Default_reduce_act p => valid_for_reduce s p + end. + +(** The automaton is safe **) +Definition safe := + shift_head_symbs /\ goto_head_symbs /\ shift_past_state /\ + goto_past_state /\ reduce_ok. + +Derive is_safe +SuchThat (IsValidator safe (is_safe ())) +As safe_is_validator. +Proof. subst is_safe. instantiate (1:=fun _ => _). apply _. Qed. + +End Make. @@ -1,3 +1,3 @@ -version=3.5 +version=3.7 buildnr= tag= diff --git a/aarch64/Archi.v b/aarch64/Archi.v new file mode 100644 index 00000000..aef4ab77 --- /dev/null +++ b/aarch64/Archi.v @@ -0,0 +1,88 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Architecture-dependent parameters for AArch64 *) + +Require Import ZArith List. +(*From Flocq*) +Require Import Binary Bits. + +Definition ptr64 := true. + +Definition big_endian := false. + +Definition align_int64 := 8%Z. +Definition align_float64 := 8%Z. + +Definition splitlong := false. + +Lemma splitlong_ptr32: splitlong = true -> ptr64 = false. +Proof. + unfold splitlong, ptr64; congruence. +Qed. + +Definition default_nan_64 := (false, iter_nat 51 _ xO xH). +Definition default_nan_32 := (false, iter_nat 22 _ xO xH). + +(** Choose the first signaling NaN, if any; + otherwise choose the first NaN; + otherwise use default. *) + +Definition choose_nan (is_signaling: positive -> bool) + (default: bool * positive) + (l0: list (bool * positive)) : bool * positive := + let fix choose_snan (l1: list (bool * positive)) := + match l1 with + | nil => + match l0 with nil => default | n :: _ => n end + | ((s, p) as n) :: l1 => + if is_signaling p then n else choose_snan l1 + end + in choose_snan l0. + +Lemma choose_nan_idem: forall is_signaling default n, + choose_nan is_signaling default (n :: n :: nil) = + choose_nan is_signaling default (n :: nil). +Proof. + intros. destruct n as [s p]; unfold choose_nan; simpl. + destruct (is_signaling p); auto. +Qed. + +Definition choose_nan_64 := + choose_nan (fun p => negb (Pos.testbit p 51)) default_nan_64. + +Definition choose_nan_32 := + choose_nan (fun p => negb (Pos.testbit p 22)) default_nan_32. + +Lemma choose_nan_64_idem: forall n, + choose_nan_64 (n :: n :: nil) = choose_nan_64 (n :: nil). +Proof. intros; apply choose_nan_idem. Qed. + +Lemma choose_nan_32_idem: forall n, + choose_nan_32 (n :: n :: nil) = choose_nan_32 (n :: nil). +Proof. intros; apply choose_nan_idem. Qed. + +Definition fma_order {A: Type} (x y z: A) := (z, x, y). + +Definition fma_invalid_mul_is_nan := true. + +Definition float_of_single_preserves_sNaN := false. + +Global Opaque ptr64 big_endian splitlong + default_nan_64 choose_nan_64 + default_nan_32 choose_nan_32 + fma_order fma_invalid_mul_is_nan + float_of_single_preserves_sNaN. + +(** Whether to generate position-independent code or not *) + +Parameter pic_code: unit -> bool. diff --git a/aarch64/Asm.v b/aarch64/Asm.v new file mode 100644 index 00000000..47cd3051 --- /dev/null +++ b/aarch64/Asm.v @@ -0,0 +1,1312 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Abstract syntax and semantics for AArch64 assembly language *) + +Require Import Coqlib Zbits Maps. +Require Import AST Integers Floats. +Require Import Values Memory Events Globalenvs Smallstep. +Require Import Locations Conventions. +Require Stacklayout. + +(** * Abstract syntax *) + +(** Integer registers, floating-point registers. *) + +(** In assembly files, [Xn] denotes the full 64-bit register + and [Wn] the low 32 bits of [Xn]. *) + +Inductive ireg: Type := + | X0 | X1 | X2 | X3 | X4 | X5 | X6 | X7 + | X8 | X9 | X10 | X11 | X12 | X13 | X14 | X15 + | X16 | X17 | X18 | X19 | X20 | X21 | X22 | X23 + | X24 | X25 | X26 | X27 | X28 | X29 | X30. + +Inductive ireg0: Type := + | RR0 (r: ireg) | XZR. + +Inductive iregsp: Type := + | RR1 (r: ireg) | XSP. + +Coercion RR0: ireg >-> ireg0. +Coercion RR1: ireg >-> iregsp. + +Lemma ireg_eq: forall (x y: ireg), {x=y} + {x<>y}. +Proof. decide equality. Defined. + +(** In assembly files, [Dn] denotes the low 64-bit of a vector register, + and [Sn] the low 32 bits. *) + +Inductive freg: Type := + | D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 + | D8 | D9 | D10 | D11 | D12 | D13 | D14 | D15 + | D16 | D17 | D18 | D19 | D20 | D21 | D22 | D23 + | D24 | D25 | D26 | D27 | D28 | D29 | D30 | D31. + +Lemma freg_eq: forall (x y: freg), {x=y} + {x<>y}. +Proof. decide equality. Defined. + +(** Bits in the condition register. *) + +Inductive crbit: Type := + | CN: crbit (**r negative *) + | CZ: crbit (**r zero *) + | CC: crbit (**r carry *) + | CV: crbit. (**r overflow *) + +Lemma crbit_eq: forall (x y: crbit), {x=y} + {x<>y}. +Proof. decide equality. Defined. + +(** We model the following registers of the ARM architecture. *) + +Inductive preg: Type := + | IR: ireg -> preg (**r 64- or 32-bit integer registers *) + | FR: freg -> preg (**r double- or single-precision float registers *) + | CR: crbit -> preg (**r bits in the condition register *) + | SP: preg (**r register X31 used as stack pointer *) + | PC: preg. (**r program counter *) + +Coercion IR: ireg >-> preg. +Coercion FR: freg >-> preg. +Coercion CR: crbit >-> preg. + +Lemma preg_eq: forall (x y: preg), {x=y} + {x<>y}. +Proof. decide equality. apply ireg_eq. apply freg_eq. apply crbit_eq. Defined. + +Module PregEq. + Definition t := preg. + Definition eq := preg_eq. +End PregEq. + +Module Pregmap := EMap(PregEq). + +Definition preg_of_iregsp (r: iregsp) : preg := + match r with RR1 r => IR r | XSP => SP end. + +Coercion preg_of_iregsp: iregsp >-> preg. + +(** Conventional name for return address ([RA]) *) + +Notation "'RA'" := X30 (only parsing) : asm. + +(** The instruction set. Most instructions correspond exactly to + actual AArch64 instructions. See the ARM reference manuals for more + details. Some instructions, described below, are + pseudo-instructions: they expand to canned instruction sequences + during the printing of the assembly code. *) + +Definition label := positive. + +Inductive isize: Type := + | W (**r 32-bit integer operation *) + | X. (**r 64-bit integer operation *) + +Inductive fsize: Type := + | S (**r 32-bit, single-precision FP operation *) + | D. (**r 64-bit, double-precision FP operation *) + +Inductive testcond : Type := + | TCeq: testcond (**r equal *) + | TCne: testcond (**r not equal *) + | TChs: testcond (**r unsigned higher or same *) + | TClo: testcond (**r unsigned lower *) + | TCmi: testcond (**r negative *) + | TCpl: testcond (**r positive *) + | TChi: testcond (**r unsigned higher *) + | TCls: testcond (**r unsigned lower or same *) + | TCge: testcond (**r signed greater or equal *) + | TClt: testcond (**r signed less than *) + | TCgt: testcond (**r signed greater *) + | TCle: testcond. (**r signed less than or equal *) + +Inductive addressing: Type := + | ADimm (base: iregsp) (n: int64) (**r base plus immediate offset *) + | ADreg (base: iregsp) (r: ireg) (**r base plus reg *) + | ADlsl (base: iregsp) (r: ireg) (n: int) (**r base plus reg LSL n *) + | ADsxt (base: iregsp) (r: ireg) (n: int) (**r base plus SIGN-EXT(reg) LSL n *) + | ADuxt (base: iregsp) (r: ireg) (n: int) (**r base plus ZERO-EXT(reg) LSL n *) + | ADadr (base: iregsp) (id: ident) (ofs: ptrofs) (**r base plus low address of [id + ofs] *) + | ADpostincr (base: iregsp) (n: int64). (**r base plus offset; base is updated after *) + +Inductive shift_op: Type := + | SOnone + | SOlsl (n: int) + | SOlsr (n: int) + | SOasr (n: int) + | SOror (n: int). + +Inductive extend_op: Type := + | EOsxtb (n: int) + | EOsxth (n: int) + | EOsxtw (n: int) + | EOuxtb (n: int) + | EOuxth (n: int) + | EOuxtw (n: int) + | EOuxtx (n: int). + +Inductive instruction: Type := + (** Branches *) + | Pb (lbl: label) (**r branch *) + | Pbc (c: testcond) (lbl: label) (**r conditional branch *) + | Pbl (id: ident) (sg: signature) (**r jump to function and link *) + | Pbs (id: ident) (sg: signature) (**r jump to function *) + | Pblr (r: ireg) (sg: signature) (**r indirect jump and link *) + | Pbr (r: ireg) (sg: signature) (**r indirect jump *) + | Pret (r: ireg) (**r return *) + | Pcbnz (sz: isize) (r: ireg) (lbl: label) (**r branch if not zero *) + | Pcbz (sz: isize) (r: ireg) (lbl: label) (**r branch if zero *) + | Ptbnz (sz: isize) (r: ireg) (n: int) (lbl: label) (**r branch if bit n is not zero *) + | Ptbz (sz: isize) (r: ireg) (n: int) (lbl: label) (**r branch if bit n is zero *) + (** Memory loads and stores *) + | Pldrw (rd: ireg) (a: addressing) (**r load int32 *) + | Pldrw_a (rd: ireg) (a: addressing) (**r load int32 as any32 *) + | Pldrx (rd: ireg) (a: addressing) (**r load int64 *) + | Pldrx_a (rd: ireg) (a: addressing) (**r load int64 as any64 *) + | Pldrb (sz: isize) (rd: ireg) (a: addressing) (**r load int8, zero-extend *) + | Pldrsb (sz: isize) (rd: ireg) (a: addressing) (**r load int8, sign-extend *) + | Pldrh (sz: isize) (rd: ireg) (a: addressing) (**r load int16, zero-extend *) + | Pldrsh (sz: isize) (rd: ireg) (a: addressing) (**r load int16, sign-extend *) + | Pldrzw (rd: ireg) (a: addressing) (**r load int32, zero-extend to int64 *) + | Pldrsw (rd: ireg) (a: addressing) (**r load int32, sign-extend to int64 *) + | Pldp (rd1 rd2: ireg) (a: addressing) (**r load two int64 *) + | Pstrw (rs: ireg) (a: addressing) (**r store int32 *) + | Pstrw_a (rs: ireg) (a: addressing) (**r store int32 as any32 *) + | Pstrx (rs: ireg) (a: addressing) (**r store int64 *) + | Pstrx_a (rs: ireg) (a: addressing) (**r store int64 as any64 *) + | Pstrb (rs: ireg) (a: addressing) (**r store int8 *) + | Pstrh (rs: ireg) (a: addressing) (**r store int16 *) + | Pstp (rs1 rs2: ireg) (a: addressing) (**r store two int64 *) + (** Integer arithmetic, immediate *) + | Paddimm (sz: isize) (rd: iregsp) (r1: iregsp) (n: Z) (**r addition *) + | Psubimm (sz: isize) (rd: iregsp) (r1: iregsp) (n: Z) (**r subtraction *) + | Pcmpimm (sz: isize) (r1: ireg) (n: Z) (**r compare *) + | Pcmnimm (sz: isize) (r1: ireg) (n: Z) (**r compare negative *) + (** Move integer register *) + | Pmov (rd: iregsp) (r1: iregsp) + (** Logical, immediate *) + | Pandimm (sz: isize) (rd: ireg) (r1: ireg0) (n: Z) (**r and *) + | Peorimm (sz: isize) (rd: ireg) (r1: ireg0) (n: Z) (**r xor *) + | Porrimm (sz: isize) (rd: ireg) (r1: ireg0) (n: Z) (**r or *) + | Ptstimm (sz: isize) (r1: ireg) (n: Z) (**r and, then set flags *) + (** Move wide immediate *) + | Pmovz (sz: isize) (rd: ireg) (n: Z) (pos: Z) (**r move [n << pos] to [rd] *) + | Pmovn (sz: isize) (rd: ireg) (n: Z) (pos: Z) (**r move [NOT(n << pos)] to [rd] *) + | Pmovk (sz: isize) (rd: ireg) (n: Z) (pos: Z) (**r insert 16 bits of [n] at [pos] in rd *) + (** PC-relative addressing *) + | Padrp (rd: ireg) (id: ident) (ofs: ptrofs) (**r set [rd] to high address of [id + ofs] *) + | Paddadr (rd: ireg) (r1: ireg) (id: ident) (ofs: ptrofs) (**r add the low address of [id + ofs] *) + (** Bit-field operations *) + | Psbfiz (sz: isize) (rd: ireg) (r1: ireg) (r: int) (s: Z) (**r sign extend and shift left *) + | Psbfx (sz: isize) (rd: ireg) (r1: ireg) (r: int) (s: Z) (**r shift right and sign extend *) + | Pubfiz (sz: isize) (rd: ireg) (r1: ireg) (r: int) (s: Z) (**r zero extend and shift left *) + | Pubfx (sz: isize) (rd: ireg) (r1: ireg) (r: int) (s: Z) (**r shift right and zero extend *) + (** Integer arithmetic, shifted register *) + | Padd (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r addition *) + | Psub (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r subtraction *) + | Pcmp (sz: isize) (r1: ireg0) (r2: ireg) (s: shift_op) (**r compare *) + | Pcmn (sz: isize) (r1: ireg0) (r2: ireg) (s: shift_op) (**r compare negative *) + (** Integer arithmetic, extending register *) + | Paddext (rd: iregsp) (r1: iregsp) (r2: ireg) (x: extend_op) (**r int64-int32 add *) + | Psubext (rd: iregsp) (r1: iregsp) (r2: ireg) (x: extend_op) (**r int64-int32 sub *) + | Pcmpext (r1: ireg) (r2: ireg) (x: extend_op) (**r int64-int32 cmp *) + | Pcmnext (r1: ireg) (r2: ireg) (x: extend_op) (**r int64-int32 cmn *) + (** Logical, shifted register *) + | Pand (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r and *) + | Pbic (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r and-not *) + | Peon (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r xor-not *) + | Peor (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r xor *) + | Porr (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r or *) + | Porn (sz: isize) (rd: ireg) (r1: ireg0) (r2: ireg) (s: shift_op) (**r or-not *) + | Ptst (sz: isize) (r1: ireg0) (r2: ireg) (s: shift_op) (**r and, then set flags *) + (** Variable shifts *) + | Pasrv (sz: isize) (rd: ireg) (r1 r2: ireg) (**r arithmetic right shift *) + | Plslv (sz: isize) (rd: ireg) (r1 r2: ireg) (**r left shift *) + | Plsrv (sz: isize) (rd: ireg) (r1 r2: ireg) (**r logical right shift *) + | Prorv (sz: isize) (rd: ireg) (r1 r2: ireg) (**r rotate right *) + (** Bit operations *) + | Pcls (sz: isize) (rd r1: ireg) (**r count leading sign bits *) + | Pclz (sz: isize) (rd r1: ireg) (**r count leading zero bits *) + | Prev (sz: isize) (rd r1: ireg) (**r reverse bytes *) + | Prev16 (sz: isize) (rd r1: ireg) (**r reverse bytes in each 16-bit word *) + (** Conditional data processing *) + | Pcsel (rd: ireg) (r1 r2: ireg) (c: testcond) (**r int conditional move *) + | Pcset (rd: ireg) (c: testcond) (**r set to 1/0 if cond is true/false *) +(* + | Pcsetm (rd: ireg) (c: testcond) (**r set to -1/0 if cond is true/false *) +*) + (** Integer multiply/divide *) + | Pmadd (sz: isize) (rd: ireg) (r1 r2: ireg) (r3: ireg0) (**r multiply-add *) + | Pmsub (sz: isize) (rd: ireg) (r1 r2: ireg) (r3: ireg0) (**r multiply-sub *) + | Psmulh (rd: ireg) (r1 r2: ireg) (**r signed multiply high *) + | Pumulh (rd: ireg) (r1 r2: ireg) (**r unsigned multiply high *) + | Psdiv (sz: isize) (rd: ireg) (r1 r2: ireg) (**r signed division *) + | Pudiv (sz: isize) (rd: ireg) (r1 r2: ireg) (**r unsigned division *) + (** Floating-point loads and stores *) + | Pldrs (rd: freg) (a: addressing) (**r load float32 (single precision) *) + | Pldrd (rd: freg) (a: addressing) (**r load float64 (double precision) *) + | Pldrd_a (rd: freg) (a: addressing) (**r load float64 as any64 *) + | Pstrs (rs: freg) (a: addressing) (**r store float32 *) + | Pstrd (rs: freg) (a: addressing) (**r store float64 *) + | Pstrd_a (rs: freg) (a: addressing) (**r store float64 as any64 *) + (** Floating-point move *) + | Pfmov (rd r1: freg) + | Pfmovimms (rd: freg) (f: float32) (**r load float32 constant *) + | Pfmovimmd (rd: freg) (f: float) (**r load float64 constant *) + | Pfmovi (fsz: fsize) (rd: freg) (r1: ireg0) (**r copy int reg to FP reg *) + (** Floating-point conversions *) + | Pfcvtds (rd r1: freg) (**r convert float32 to float64 *) + | Pfcvtsd (rd r1: freg) (**r convert float64 to float32 *) + | Pfcvtzs (isz: isize) (fsz: fsize) (rd: ireg) (r1: freg) (**r convert float to signed int *) + | Pfcvtzu (isz: isize) (fsz: fsize) (rd: ireg) (r1: freg) (**r convert float to unsigned int *) + | Pscvtf (fsz: fsize) (isz: isize) (rd: freg) (r1: ireg) (**r convert signed int to float *) + | Pucvtf (fsz: fsize) (isz: isize) (rd: freg) (r1: ireg) (**r convert unsigned int to float *) + (** Floating-point arithmetic *) + | Pfabs (sz: fsize) (rd r1: freg) (**r absolute value *) + | Pfneg (sz: fsize) (rd r1: freg) (**r negation *) + | Pfsqrt (sz: fsize) (rd r1: freg) (**r square root *) + | Pfadd (sz: fsize) (rd r1 r2: freg) (**r addition *) + | Pfdiv (sz: fsize) (rd r1 r2: freg) (**r division *) + | Pfmul (sz: fsize) (rd r1 r2: freg) (**r multiplication *) + | Pfnmul (sz: fsize) (rd r1 r2: freg) (**r multiply-negate *) + | Pfsub (sz: fsize) (rd r1 r2: freg) (**r subtraction *) + | Pfmadd (sz: fsize) (rd r1 r2 r3: freg) (**r [rd = r3 + r1 * r2] *) + | Pfmsub (sz: fsize) (rd r1 r2 r3: freg) (**r [rd = r3 - r1 * r2] *) + | Pfnmadd (sz: fsize) (rd r1 r2 r3: freg) (**r [rd = - r3 - r1 * r2] *) + | Pfnmsub (sz: fsize) (rd r1 r2 r3: freg) (**r [rd = - r3 + r1 * r2] *) + (** Floating-point comparison *) + | Pfcmp (sz: fsize) (r1 r2: freg) (**r compare [r1] and [r2] *) + | Pfcmp0 (sz: fsize) (r1: freg) (**r compare [r1] and [+0.0] *) + (** Floating-point conditional select *) + | Pfsel (rd r1 r2: freg) (cond: testcond) + (** Pseudo-instructions *) + | Pallocframe (sz: Z) (linkofs: ptrofs) (**r allocate new stack frame *) + | Pfreeframe (sz: Z) (linkofs: ptrofs) (**r deallocate stack frame and restore previous frame *) + | Plabel (lbl: label) (**r define a code label *) + | Ploadsymbol (rd: ireg) (id: ident) (**r load the address of [id] *) + | Pcvtsw2x (rd: ireg) (r1: ireg) (**r sign-extend 32-bit int to 64-bit *) + | Pcvtuw2x (rd: ireg) (r1: ireg) (**r zero-extend 32-bit int to 64-bit *) + | Pcvtx2w (rd: ireg) (**r retype a 64-bit int as a 32-bit int *) + | Pbtbl (r1: ireg) (tbl: list label) (**r N-way branch through a jump table *) + | Pbuiltin (ef: external_function) + (args: list (builtin_arg preg)) (res: builtin_res preg) (**r built-in function (pseudo) *) + | Pnop (**r no operation *) + | Pcfi_adjust (ofs: int) (**r .cfi_adjust debug directive *) + | Pcfi_rel_offset (ofs: int) (**r .cfi_rel_offset debug directive *) +. + +Definition code := list instruction. +Record function : Type := mkfunction { fn_sig: signature; fn_code: code }. +Definition fundef := AST.fundef function. +Definition program := AST.program fundef unit. + +(** * Operational semantics *) + +(** The semantics operates over a single mapping from registers + (type [preg]) to values. We maintain (but do not enforce) + the convention that integer registers are mapped to values of + type [Tint], float registers to values of type [Tfloat], + and condition bits to either [Vzero] or [Vone]. *) + +Definition regset := Pregmap.t val. +Definition genv := Genv.t fundef unit. + +(** The value of an [ireg0] is either the value of the integer register, + or 0. *) + +Definition ir0w (rs: regset) (r: ireg0) : val := + match r with RR0 r => rs (IR r) | XZR => Vint Int.zero end. +Definition ir0x (rs: regset) (r: ireg0) : val := + match r with RR0 r => rs (IR r) | XZR => Vlong Int64.zero end. + +(** Concise notations for accessing and updating the values of registers. *) + +Notation "a # b" := (a b) (at level 1, only parsing) : asm. +Notation "a # b <- c" := (Pregmap.set b c a) (at level 1, b at next level) : asm. +Notation "a ## b" := (ir0w a b) (at level 1, only parsing) : asm. +Notation "a ### b" := (ir0x a b) (at level 1, only parsing) : asm. + +Open Scope asm. + +(** Undefining some registers *) + +Fixpoint undef_regs (l: list preg) (rs: regset) : regset := + match l with + | nil => rs + | r :: l' => undef_regs l' (rs#r <- Vundef) + end. + +(** Undefining the condition codes *) + +Definition undef_flags (rs: regset) : regset := + fun r => match r with CR _ => Vundef | _ => rs r end. + +(** Assigning a register pair *) + +Definition set_pair (p: rpair preg) (v: val) (rs: regset) : regset := + match p with + | One r => rs#r <- v + | Twolong rhi rlo => rs#rhi <- (Val.hiword v) #rlo <- (Val.loword v) + end. + +(** Assigning the result of a builtin *) + +Fixpoint set_res (res: builtin_res preg) (v: val) (rs: regset) : regset := + match res with + | BR r => rs#r <- v + | BR_none => rs + | BR_splitlong hi lo => set_res lo (Val.loword v) (set_res hi (Val.hiword v) rs) + end. + +(** The two functions below axiomatize how the linker processes + symbolic references [symbol + offset]. It computes the + difference between the address and the PC, and splits it into: + - 12 low bits usable as an offset in an addressing mode; + - 21 high bits usable as argument to the ADRP instruction. + + In CompCert's model, we cannot really describe PC-relative addressing, + but we can claim that the address of [symbol + offset] decomposes + as the sum of + - a low part, usable as an offset in an addressing mode; + - a high part, usable as argument to the ADRP instruction. *) + +Parameter symbol_low: genv -> ident -> ptrofs -> val. +Parameter symbol_high: genv -> ident -> ptrofs -> val. + +Axiom symbol_high_low: + forall (ge: genv) (id: ident) (ofs: ptrofs), + Val.addl (symbol_high ge id ofs) (symbol_low ge id ofs) = Genv.symbol_address ge id ofs. + +Section RELSEM. + +Variable ge: genv. + +(** Looking up instructions in a code sequence by position. *) + +Fixpoint find_instr (pos: Z) (c: code) {struct c} : option instruction := + match c with + | nil => None + | i :: il => if zeq pos 0 then Some i else find_instr (pos - 1) il + end. + +(** Position corresponding to a label *) + +Definition is_label (lbl: label) (instr: instruction) : bool := + match instr with + | Plabel lbl' => if peq lbl lbl' then true else false + | _ => false + end. + +Lemma is_label_correct: + forall lbl instr, + if is_label lbl instr then instr = Plabel lbl else instr <> Plabel lbl. +Proof. + intros. destruct instr; simpl; try discriminate. destruct (peq lbl lbl0); congruence. +Qed. + +Fixpoint label_pos (lbl: label) (pos: Z) (c: code) {struct c} : option Z := + match c with + | nil => None + | instr :: c' => + if is_label lbl instr then Some (pos + 1) else label_pos lbl (pos + 1) c' + end. + +(** The semantics is purely small-step and defined as a function + from the current state (a register set + a memory state) + to either [Next rs' m'] where [rs'] and [m'] are the updated register + set and memory state after execution of the instruction at [rs#PC], + or [Stuck] if the processor is stuck. *) + +Inductive outcome: Type := + | Next: regset -> mem -> outcome + | Stuck: outcome. + +(** Manipulations over the [PC] register: continuing with the next + instruction ([nextinstr]) or branching to a label ([goto_label]). *) + +Definition nextinstr (rs: regset) := + rs#PC <- (Val.offset_ptr rs#PC Ptrofs.one). + +Definition goto_label (f: function) (lbl: label) (rs: regset) (m: mem) := + match label_pos lbl 0 (fn_code f) with + | None => Stuck + | Some pos => + match rs#PC with + | Vptr b ofs => Next (rs#PC <- (Vptr b (Ptrofs.repr pos))) m + | _ => Stuck + end + end. + +(** Testing a condition *) + +Definition eval_testcond (c: testcond) (rs: regset) : option bool := + match c with + | TCeq => (**r equal *) + match rs#CZ with + | Vint n => Some (Int.eq n Int.one) + | _ => None + end + | TCne => (**r not equal *) + match rs#CZ with + | Vint n => Some (Int.eq n Int.zero) + | _ => None + end + | TClo => (**r unsigned less than *) + match rs#CC with + | Vint n => Some (Int.eq n Int.zero) + | _ => None + end + | TCls => (**r unsigned less or equal *) + match rs#CC, rs#CZ with + | Vint c, Vint z => Some (Int.eq c Int.zero || Int.eq z Int.one) + | _, _ => None + end + | TChs => (**r unsigned greater or equal *) + match rs#CC with + | Vint n => Some (Int.eq n Int.one) + | _ => None + end + | TChi => (**r unsigned greater *) + match rs#CC, rs#CZ with + | Vint c, Vint z => Some (Int.eq c Int.one && Int.eq z Int.zero) + | _, _ => None + end + | TClt => (**r signed less than *) + match rs#CV, rs#CN with + | Vint o, Vint s => Some (Int.eq (Int.xor o s) Int.one) + | _, _ => None + end + | TCle => (**r signed less or equal *) + match rs#CV, rs#CN, rs#CZ with + | Vint o, Vint s, Vint z => Some (Int.eq (Int.xor o s) Int.one || Int.eq z Int.one) + | _, _, _ => None + end + | TCge => (**r signed greater or equal *) + match rs#CV, rs#CN with + | Vint o, Vint s => Some (Int.eq (Int.xor o s) Int.zero) + | _, _ => None + end + | TCgt => (**r signed greater *) + match rs#CV, rs#CN, rs#CZ with + | Vint o, Vint s, Vint z => Some (Int.eq (Int.xor o s) Int.zero && Int.eq z Int.zero) + | _, _, _ => None + end + | TCpl => (**r positive *) + match rs#CN with + | Vint n => Some (Int.eq n Int.zero) + | _ => None + end + | TCmi => (**r negative *) + match rs#CN with + | Vint n => Some (Int.eq n Int.one) + | _ => None + end + end. + +(** Integer "is zero?" test *) + +Definition eval_testzero (sz: isize) (v: val) (m: mem): option bool := + match sz with + | W => Val.cmpu_bool (Mem.valid_pointer m) Ceq v (Vint Int.zero) + | X => Val.cmplu_bool (Mem.valid_pointer m) Ceq v (Vlong Int64.zero) + end. + +(** Integer "bit is set?" test *) + +Definition eval_testbit (sz: isize) (v: val) (n: int): option bool := + match sz with + | W => Val.cmp_bool Cne (Val.and v (Vint (Int.shl Int.one n))) (Vint Int.zero) + | X => Val.cmpl_bool Cne (Val.andl v (Vlong (Int64.shl' Int64.one n))) (Vlong Int64.zero) + end. + +(** Evaluating an addressing mode *) + +Definition eval_addressing (a: addressing) (rs: regset): val := + match a with + | ADimm base n => Val.addl rs#base (Vlong n) + | ADreg base r => Val.addl rs#base rs#r + | ADlsl base r n => Val.addl rs#base (Val.shll rs#r (Vint n)) + | ADsxt base r n => Val.addl rs#base (Val.shll (Val.longofint rs#r) (Vint n)) + | ADuxt base r n => Val.addl rs#base (Val.shll (Val.longofintu rs#r) (Vint n)) + | ADadr base id ofs => Val.addl rs#base (symbol_low ge id ofs) + | ADpostincr base n => Vundef (* not modeled yet *) + end. + +(** Auxiliaries for memory accesses *) + +Definition exec_load (chunk: memory_chunk) (transf: val -> val) + (a: addressing) (r: preg) (rs: regset) (m: mem) := + match Mem.loadv chunk m (eval_addressing a rs) with + | None => Stuck + | Some v => Next (nextinstr (rs#r <- (transf v))) m + end. + +Definition exec_store (chunk: memory_chunk) + (a: addressing) (v: val) + (rs: regset) (m: mem) := + match Mem.storev chunk m (eval_addressing a rs) v with + | None => Stuck + | Some m' => Next (nextinstr rs) m' + end. + +(** Comparisons *) + +Definition compare_int (rs: regset) (v1 v2: val) (m: mem) := + rs#CN <- (Val.negative (Val.sub v1 v2)) + #CZ <- (Val.cmpu (Mem.valid_pointer m) Ceq v1 v2) + #CC <- (Val.cmpu (Mem.valid_pointer m) Cge v1 v2) + #CV <- (Val.sub_overflow v1 v2). + +Definition compare_long (rs: regset) (v1 v2: val) (m: mem) := + rs#CN <- (Val.negativel (Val.subl v1 v2)) + #CZ <- (Val.maketotal (Val.cmplu (Mem.valid_pointer m) Ceq v1 v2)) + #CC <- (Val.maketotal (Val.cmplu (Mem.valid_pointer m) Cge v1 v2)) + #CV <- (Val.subl_overflow v1 v2). + +(** Semantics of [fcmp] instructions: +<< +== N=0 Z=1 C=1 V=0 +< N=1 Z=0 C=0 V=0 +> N=0 Z=0 C=1 V=0 +unord N=0 Z=0 C=1 V=1 +>> +*) + +Definition compare_float (rs: regset) (v1 v2: val) := + match v1, v2 with + | Vfloat f1, Vfloat f2 => + rs#CN <- (Val.of_bool (Float.cmp Clt f1 f2)) + #CZ <- (Val.of_bool (Float.cmp Ceq f1 f2)) + #CC <- (Val.of_bool (negb (Float.cmp Clt f1 f2))) + #CV <- (Val.of_bool (negb (Float.ordered f1 f2))) + | _, _ => + rs#CN <- Vundef + #CZ <- Vundef + #CC <- Vundef + #CV <- Vundef + end. + +Definition compare_single (rs: regset) (v1 v2: val) := + match v1, v2 with + | Vsingle f1, Vsingle f2 => + rs#CN <- (Val.of_bool (Float32.cmp Clt f1 f2)) + #CZ <- (Val.of_bool (Float32.cmp Ceq f1 f2)) + #CC <- (Val.of_bool (negb (Float32.cmp Clt f1 f2))) + #CV <- (Val.of_bool (negb (Float32.ordered f1 f2))) + | _, _ => + rs#CN <- Vundef + #CZ <- Vundef + #CC <- Vundef + #CV <- Vundef + end. + +(** Insertion of bits into an integer *) + +Definition insert_in_int (x: val) (y: Z) (pos: Z) (len: Z) : val := + match x with + | Vint n => Vint (Int.repr (Zinsert (Int.unsigned n) y pos len)) + | _ => Vundef + end. + +Definition insert_in_long (x: val) (y: Z) (pos: Z) (len: Z) : val := + match x with + | Vlong n => Vlong (Int64.repr (Zinsert (Int64.unsigned n) y pos len)) + | _ => Vundef + end. + +(** Evaluation of shifted operands *) + +Definition eval_shift_op_int (v: val) (s: shift_op): val := + match s with + | SOnone => v + | SOlsl n => Val.shl v (Vint n) + | SOlsr n => Val.shru v (Vint n) + | SOasr n => Val.shr v (Vint n) + | SOror n => Val.ror v (Vint n) + end. + +Definition eval_shift_op_long (v: val) (s: shift_op): val := + match s with + | SOnone => v + | SOlsl n => Val.shll v (Vint n) + | SOlsr n => Val.shrlu v (Vint n) + | SOasr n => Val.shrl v (Vint n) + | SOror n => Val.rorl v (Vint n) + end. + +(** Evaluation of sign- or zero- extended operands *) + +Definition eval_extend (v: val) (x: extend_op): val := + match x with + | EOsxtb n => Val.shll (Val.longofint (Val.sign_ext 8 v)) (Vint n) + | EOsxth n => Val.shll (Val.longofint (Val.sign_ext 16 v)) (Vint n) + | EOsxtw n => Val.shll (Val.longofint v) (Vint n) + | EOuxtb n => Val.shll (Val.longofintu (Val.zero_ext 8 v)) (Vint n) + | EOuxth n => Val.shll (Val.longofintu (Val.zero_ext 16 v)) (Vint n) + | EOuxtw n => Val.shll (Val.longofintu v) (Vint n) + | EOuxtx n => Val.shll v (Vint n) + end. + +(** Bit-level conversion from integers to FP numbers *) + +Definition float32_of_bits (v: val): val := + match v with + | Vint n => Vsingle (Float32.of_bits n) + | _ => Vundef + end. + +Definition float64_of_bits (v: val): val := + match v with + | Vlong n => Vfloat (Float.of_bits n) + | _ => Vundef + end. + +(** Execution of a single instruction [i] in initial state + [rs] and [m]. Return updated state. For instructions + that correspond to actual AArch64 instructions, the cases are + straightforward transliterations of the informal descriptions + given in the ARMv8 reference manuals. For pseudo-instructions, + refer to the informal descriptions given above. + + Note that we set to [Vundef] the registers used as temporaries by + the expansions of the pseudo-instructions, so that the code we + generate cannot use those registers to hold values that must + survive the execution of the pseudo-instruction. +*) + +Definition exec_instr (f: function) (i: instruction) (rs: regset) (m: mem) : outcome := + match i with + (** Branches *) + | Pb lbl => + goto_label f lbl rs m + | Pbc cond lbl => + match eval_testcond cond rs with + | Some true => goto_label f lbl rs m + | Some false => Next (nextinstr rs) m + | None => Stuck + end + | Pbl id sg => + Next (rs#RA <- (Val.offset_ptr rs#PC Ptrofs.one) #PC <- (Genv.symbol_address ge id Ptrofs.zero)) m + | Pbs id sg => + Next (rs#PC <- (Genv.symbol_address ge id Ptrofs.zero)) m + | Pblr r sg => + Next (rs#RA <- (Val.offset_ptr rs#PC Ptrofs.one) #PC <- (rs#r)) m + | Pbr r sg => + Next (rs#PC <- (rs#r)) m + | Pret r => + Next (rs#PC <- (rs#r)) m + | Pcbnz sz r lbl => + match eval_testzero sz rs#r m with + | Some true => Next (nextinstr rs) m + | Some false => goto_label f lbl rs m + | None => Stuck + end + | Pcbz sz r lbl => + match eval_testzero sz rs#r m with + | Some true => goto_label f lbl rs m + | Some false => Next (nextinstr rs) m + | None => Stuck + end + | Ptbnz sz r n lbl => + match eval_testbit sz rs#r n with + | Some true => goto_label f lbl rs m + | Some false => Next (nextinstr rs) m + | None => Stuck + end + | Ptbz sz r n lbl => + match eval_testbit sz rs#r n with + | Some true => Next (nextinstr rs) m + | Some false => goto_label f lbl rs m + | None => Stuck + end + (** Memory loads and stores *) + | Pldrw rd a => + exec_load Mint32 (fun v => v) a rd rs m + | Pldrw_a rd a => + exec_load Many32 (fun v => v) a rd rs m + | Pldrx rd a => + exec_load Mint64 (fun v => v) a rd rs m + | Pldrx_a rd a => + exec_load Many64 (fun v => v) a rd rs m + | Pldrb W rd a => + exec_load Mint8unsigned (fun v => v) a rd rs m + | Pldrb X rd a => + exec_load Mint8unsigned Val.longofintu a rd rs m + | Pldrsb W rd a => + exec_load Mint8signed (fun v => v) a rd rs m + | Pldrsb X rd a => + exec_load Mint8signed Val.longofint a rd rs m + | Pldrh W rd a => + exec_load Mint16unsigned (fun v => v) a rd rs m + | Pldrh X rd a => + exec_load Mint16unsigned Val.longofintu a rd rs m + | Pldrsh W rd a => + exec_load Mint16signed (fun v => v) a rd rs m + | Pldrsh X rd a => + exec_load Mint16signed Val.longofint a rd rs m + | Pldrzw rd a => + exec_load Mint32 Val.longofintu a rd rs m + | Pldrsw rd a => + exec_load Mint32 Val.longofint a rd rs m + | Pstrw r a => + exec_store Mint32 a rs#r rs m + | Pstrw_a r a => + exec_store Many32 a rs#r rs m + | Pstrx r a => + exec_store Mint64 a rs#r rs m + | Pstrx_a r a => + exec_store Many64 a rs#r rs m + | Pstrb r a => + exec_store Mint8unsigned a rs#r rs m + | Pstrh r a => + exec_store Mint16unsigned a rs#r rs m + (** Integer arithmetic, immediate *) + | Paddimm W rd r1 n => + Next (nextinstr (rs#rd <- (Val.add rs#r1 (Vint (Int.repr n))))) m + | Paddimm X rd r1 n => + Next (nextinstr (rs#rd <- (Val.addl rs#r1 (Vlong (Int64.repr n))))) m + | Psubimm W rd r1 n => + Next (nextinstr (rs#rd <- (Val.sub rs#r1 (Vint (Int.repr n))))) m + | Psubimm X rd r1 n => + Next (nextinstr (rs#rd <- (Val.subl rs#r1 (Vlong (Int64.repr n))))) m + | Pcmpimm W r1 n => + Next (nextinstr (compare_int rs rs#r1 (Vint (Int.repr n)) m)) m + | Pcmpimm X r1 n => + Next (nextinstr (compare_long rs rs#r1 (Vlong (Int64.repr n)) m)) m + | Pcmnimm W r1 n => + Next (nextinstr (compare_int rs rs#r1 (Vint (Int.neg (Int.repr n))) m)) m + | Pcmnimm X r1 n => + Next (nextinstr (compare_long rs rs#r1 (Vlong (Int64.neg (Int64.repr n))) m)) m + (** Move integer register *) + | Pmov rd r1 => + Next (nextinstr (rs#rd <- (rs#r1))) m + (** Logical, immediate *) + | Pandimm W rd r1 n => + Next (nextinstr (rs#rd <- (Val.and rs##r1 (Vint (Int.repr n))))) m + | Pandimm X rd r1 n => + Next (nextinstr (rs#rd <- (Val.andl rs###r1 (Vlong (Int64.repr n))))) m + | Peorimm W rd r1 n => + Next (nextinstr (rs#rd <- (Val.xor rs##r1 (Vint (Int.repr n))))) m + | Peorimm X rd r1 n => + Next (nextinstr (rs#rd <- (Val.xorl rs###r1 (Vlong (Int64.repr n))))) m + | Porrimm W rd r1 n => + Next (nextinstr (rs#rd <- (Val.or rs##r1 (Vint (Int.repr n))))) m + | Porrimm X rd r1 n => + Next (nextinstr (rs#rd <- (Val.orl rs###r1 (Vlong (Int64.repr n))))) m + | Ptstimm W r1 n => + Next (nextinstr (compare_int rs (Val.and rs#r1 (Vint (Int.repr n))) (Vint Int.zero) m)) m + | Ptstimm X r1 n => + Next (nextinstr (compare_long rs (Val.andl rs#r1 (Vlong (Int64.repr n))) (Vlong Int64.zero) m)) m + (** Move wide immediate *) + | Pmovz W rd n pos => + Next (nextinstr (rs#rd <- (Vint (Int.repr (Z.shiftl n pos))))) m + | Pmovz X rd n pos => + Next (nextinstr (rs#rd <- (Vlong (Int64.repr (Z.shiftl n pos))))) m + | Pmovn W rd n pos => + Next (nextinstr (rs#rd <- (Vint (Int.repr (Z.lnot (Z.shiftl n pos)))))) m + | Pmovn X rd n pos => + Next (nextinstr (rs#rd <- (Vlong (Int64.repr (Z.lnot (Z.shiftl n pos)))))) m + | Pmovk W rd n pos => + Next (nextinstr (rs#rd <- (insert_in_int rs#rd n pos 16))) m + | Pmovk X rd n pos => + Next (nextinstr (rs#rd <- (insert_in_long rs#rd n pos 16))) m + (** PC-relative addressing *) + | Padrp rd id ofs => + Next (nextinstr (rs#rd <- (symbol_high ge id ofs))) m + | Paddadr rd r1 id ofs => + Next (nextinstr (rs#rd <- (Val.addl rs#r1 (symbol_low ge id ofs)))) m + (** Bit-field operations *) + | Psbfiz W rd r1 r s => + Next (nextinstr (rs#rd <- (Val.shl (Val.sign_ext s rs#r1) (Vint r)))) m + | Psbfiz X rd r1 r s => + Next (nextinstr (rs#rd <- (Val.shll (Val.sign_ext_l s rs#r1) (Vint r)))) m + | Psbfx W rd r1 r s => + Next (nextinstr (rs#rd <- (Val.sign_ext s (Val.shr rs#r1 (Vint r))))) m + | Psbfx X rd r1 r s => + Next (nextinstr (rs#rd <- (Val.sign_ext_l s (Val.shrl rs#r1 (Vint r))))) m + | Pubfiz W rd r1 r s => + Next (nextinstr (rs#rd <- (Val.shl (Val.zero_ext s rs#r1) (Vint r)))) m + | Pubfiz X rd r1 r s => + Next (nextinstr (rs#rd <- (Val.shll (Val.zero_ext_l s rs#r1) (Vint r)))) m + | Pubfx W rd r1 r s => + Next (nextinstr (rs#rd <- (Val.zero_ext s (Val.shru rs#r1 (Vint r))))) m + | Pubfx X rd r1 r s => + Next (nextinstr (rs#rd <- (Val.zero_ext_l s (Val.shrlu rs#r1 (Vint r))))) m + (** Integer arithmetic, shifted register *) + | Padd W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.add rs##r1 (eval_shift_op_int rs#r2 s)))) m + | Padd X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.addl rs###r1 (eval_shift_op_long rs#r2 s)))) m + | Psub W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.sub rs##r1 (eval_shift_op_int rs#r2 s)))) m + | Psub X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.subl rs###r1 (eval_shift_op_long rs#r2 s)))) m + | Pcmp W r1 r2 s => + Next (nextinstr (compare_int rs rs##r1 (eval_shift_op_int rs#r2 s) m)) m + | Pcmp X r1 r2 s => + Next (nextinstr (compare_long rs rs###r1 (eval_shift_op_long rs#r2 s) m)) m + | Pcmn W r1 r2 s => + Next (nextinstr (compare_int rs rs##r1 (Val.neg (eval_shift_op_int rs#r2 s)) m)) m + | Pcmn X r1 r2 s => + Next (nextinstr (compare_long rs rs###r1 (Val.negl (eval_shift_op_long rs#r2 s)) m)) m + (** Integer arithmetic, extending register *) + | Paddext rd r1 r2 x => + Next (nextinstr (rs#rd <- (Val.addl rs#r1 (eval_extend rs#r2 x)))) m + | Psubext rd r1 r2 x => + Next (nextinstr (rs#rd <- (Val.subl rs#r1 (eval_extend rs#r2 x)))) m + | Pcmpext r1 r2 x => + Next (nextinstr (compare_long rs rs#r1 (eval_extend rs#r2 x) m)) m + | Pcmnext r1 r2 x => + Next (nextinstr (compare_long rs rs#r1 (Val.negl (eval_extend rs#r2 x)) m)) m + (** Logical, shifted register *) + | Pand W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.and rs##r1 (eval_shift_op_int rs#r2 s)))) m + | Pand X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.andl rs###r1 (eval_shift_op_long rs#r2 s)))) m + | Pbic W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.and rs##r1 (Val.notint (eval_shift_op_int rs#r2 s))))) m + | Pbic X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.andl rs###r1 (Val.notl (eval_shift_op_long rs#r2 s))))) m + | Peon W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.xor rs##r1 (Val.notint (eval_shift_op_int rs#r2 s))))) m + | Peon X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.xorl rs###r1 (Val.notl (eval_shift_op_long rs#r2 s))))) m + | Peor W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.xor rs##r1 (eval_shift_op_int rs#r2 s)))) m + | Peor X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.xorl rs###r1 (eval_shift_op_long rs#r2 s)))) m + | Porr W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.or rs##r1 (eval_shift_op_int rs#r2 s)))) m + | Porr X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.orl rs###r1 (eval_shift_op_long rs#r2 s)))) m + | Porn W rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.or rs##r1 (Val.notint (eval_shift_op_int rs#r2 s))))) m + | Porn X rd r1 r2 s => + Next (nextinstr (rs#rd <- (Val.orl rs###r1 (Val.notl (eval_shift_op_long rs#r2 s))))) m + | Ptst W r1 r2 s => + Next (nextinstr (compare_int rs (Val.and rs##r1 (eval_shift_op_int rs#r2 s)) (Vint Int.zero) m)) m + | Ptst X r1 r2 s => + Next (nextinstr (compare_long rs (Val.andl rs###r1 (eval_shift_op_long rs#r2 s)) (Vlong Int64.zero) m)) m + (** Variable shifts *) + | Pasrv W rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.shr rs#r1 rs#r2))) m + | Pasrv X rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.shrl rs#r1 rs#r2))) m + | Plslv W rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.shl rs#r1 rs#r2))) m + | Plslv X rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.shll rs#r1 rs#r2))) m + | Plsrv W rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.shru rs#r1 rs#r2))) m + | Plsrv X rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.shrlu rs#r1 rs#r2))) m + | Prorv W rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.ror rs#r1 rs#r2))) m + | Prorv X rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.rorl rs#r1 rs#r2))) m + (** Conditional data processing *) + | Pcsel rd r1 r2 cond => + let v := + match eval_testcond cond rs with + | Some true => rs#r1 + | Some false => rs#r2 + | None => Vundef + end in + Next (nextinstr (rs#rd <- v)) m + | Pcset rd cond => + let v := + match eval_testcond cond rs with + | Some true => Vint Int.one + | Some false => Vint Int.zero + | None => Vundef + end in + Next (nextinstr (rs#rd <- v)) m + (** Integer multiply/divide *) + | Pmadd W rd r1 r2 r3 => + Next (nextinstr (rs#rd <- (Val.add rs##r3 (Val.mul rs#r1 rs#r2)))) m + | Pmadd X rd r1 r2 r3 => + Next (nextinstr (rs#rd <- (Val.addl rs###r3 (Val.mull rs#r1 rs#r2)))) m + | Pmsub W rd r1 r2 r3 => + Next (nextinstr (rs#rd <- (Val.sub rs##r3 (Val.mul rs#r1 rs#r2)))) m + | Pmsub X rd r1 r2 r3 => + Next (nextinstr (rs#rd <- (Val.subl rs###r3 (Val.mull rs#r1 rs#r2)))) m + | Psmulh rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.mullhs rs#r1 rs#r2))) m + | Pumulh rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.mullhu rs#r1 rs#r2))) m + | Psdiv W rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.divs rs#r1 rs#r2)))) m + | Psdiv X rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.divls rs#r1 rs#r2)))) m + | Pudiv W rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.divu rs#r1 rs#r2)))) m + | Pudiv X rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.divlu rs#r1 rs#r2)))) m + (** Floating-point loads and stores *) + | Pldrs rd a => + exec_load Mfloat32 (fun v => v) a rd rs m + | Pldrd rd a => + exec_load Mfloat64 (fun v => v) a rd rs m + | Pldrd_a rd a => + exec_load Many64 (fun v => v) a rd rs m + | Pstrs r a => + exec_store Mfloat32 a rs#r rs m + | Pstrd r a => + exec_store Mfloat64 a rs#r rs m + | Pstrd_a r a => + exec_store Many64 a rs#r rs m + (** Floating-point move *) + | Pfmov rd r1 => + Next (nextinstr (rs#rd <- (rs#r1))) m + | Pfmovimms rd f => + Next (nextinstr (rs#rd <- (Vsingle f))) m + | Pfmovimmd rd f => + Next (nextinstr (rs#rd <- (Vfloat f))) m + | Pfmovi S rd r1 => + Next (nextinstr (rs#rd <- (float32_of_bits rs##r1))) m + | Pfmovi D rd r1 => + Next (nextinstr (rs#rd <- (float64_of_bits rs###r1))) m + (** Floating-point conversions *) + | Pfcvtds rd r1 => + Next (nextinstr (rs#rd <- (Val.floatofsingle rs#r1))) m + | Pfcvtsd rd r1 => + Next (nextinstr (rs#rd <- (Val.singleoffloat rs#r1))) m + | Pfcvtzs W S rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.intofsingle rs#r1)))) m + | Pfcvtzs W D rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.intoffloat rs#r1)))) m + | Pfcvtzs X S rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.longofsingle rs#r1)))) m + | Pfcvtzs X D rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.longoffloat rs#r1)))) m + | Pfcvtzu W S rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.intuofsingle rs#r1)))) m + | Pfcvtzu W D rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.intuoffloat rs#r1)))) m + | Pfcvtzu X S rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.longuofsingle rs#r1)))) m + | Pfcvtzu X D rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.longuoffloat rs#r1)))) m + | Pscvtf S W rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.singleofint rs#r1)))) m + | Pscvtf D W rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.floatofint rs#r1)))) m + | Pscvtf S X rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.singleoflong rs#r1)))) m + | Pscvtf D X rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.floatoflong rs#r1)))) m + | Pucvtf S W rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.singleofintu rs#r1)))) m + | Pucvtf D W rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.floatofintu rs#r1)))) m + | Pucvtf S X rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.singleoflongu rs#r1)))) m + | Pucvtf D X rd r1 => + Next (nextinstr (rs#rd <- (Val.maketotal (Val.floatoflongu rs#r1)))) m + (** Floating-point arithmetic *) + | Pfabs S rd r1 => + Next (nextinstr (rs#rd <- (Val.absfs rs#r1))) m + | Pfabs D rd r1 => + Next (nextinstr (rs#rd <- (Val.absf rs#r1))) m + | Pfneg S rd r1 => + Next (nextinstr (rs#rd <- (Val.negfs rs#r1))) m + | Pfneg D rd r1 => + Next (nextinstr (rs#rd <- (Val.negf rs#r1))) m + | Pfadd S rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.addfs rs#r1 rs#r2))) m + | Pfadd D rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.addf rs#r1 rs#r2))) m + | Pfdiv S rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.divfs rs#r1 rs#r2))) m + | Pfdiv D rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.divf rs#r1 rs#r2))) m + | Pfmul S rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.mulfs rs#r1 rs#r2))) m + | Pfmul D rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.mulf rs#r1 rs#r2))) m + | Pfnmul S rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.negfs (Val.mulfs rs#r1 rs#r2)))) m + | Pfnmul D rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.negf (Val.mulf rs#r1 rs#r2)))) m + | Pfsub S rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.subfs rs#r1 rs#r2))) m + | Pfsub D rd r1 r2 => + Next (nextinstr (rs#rd <- (Val.subf rs#r1 rs#r2))) m + (** Floating-point comparison *) + | Pfcmp S r1 r2 => + Next (nextinstr (compare_single rs rs#r1 rs#r2)) m + | Pfcmp D r1 r2 => + Next (nextinstr (compare_float rs rs#r1 rs#r2)) m + | Pfcmp0 S r1 => + Next (nextinstr (compare_single rs rs#r1 (Vsingle Float32.zero))) m + | Pfcmp0 D r1 => + Next (nextinstr (compare_float rs rs#r1 (Vfloat Float.zero))) m + (** Floating-point conditional select *) + | Pfsel rd r1 r2 cond => + let v := + match eval_testcond cond rs with + | Some true => rs#r1 + | Some false => rs#r2 + | None => Vundef + end in + Next (nextinstr (rs#rd <- v)) m + (** Pseudo-instructions *) + | Pallocframe sz pos => + let (m1, stk) := Mem.alloc m 0 sz in + let sp := (Vptr stk Ptrofs.zero) in + match Mem.storev Mint64 m1 (Val.offset_ptr sp pos) rs#SP with + | None => Stuck + | Some m2 => Next (nextinstr (rs #X29 <- (rs#SP) #SP <- sp #X16 <- Vundef)) m2 + end + | Pfreeframe sz pos => + match Mem.loadv Mint64 m (Val.offset_ptr rs#SP pos) with + | None => Stuck + | Some v => + match rs#SP with + | Vptr stk ofs => + match Mem.free m stk 0 sz with + | None => Stuck + | Some m' => Next (nextinstr (rs#SP <- v #X16 <- Vundef)) m' + end + | _ => Stuck + end + end + | Plabel lbl => + Next (nextinstr rs) m + | Ploadsymbol rd id => + Next (nextinstr (rs#rd <- (Genv.symbol_address ge id Ptrofs.zero))) m + | Pcvtsw2x rd r1 => + Next (nextinstr (rs#rd <- (Val.longofint rs#r1))) m + | Pcvtuw2x rd r1 => + Next (nextinstr (rs#rd <- (Val.longofintu rs#r1))) m + | Pcvtx2w rd => + Next (nextinstr (rs#rd <- (Val.loword rs#rd))) m + | Pbtbl r tbl => + match (rs#X16 <- Vundef)#r with + | Vint n => + match list_nth_z tbl (Int.unsigned n) with + | None => Stuck + | Some lbl => goto_label f lbl (rs#X16 <- Vundef #X17 <- Vundef) m + end + | _ => Stuck + end + | Pbuiltin ef args res => Stuck (**r treated specially below *) + (** The following instructions and directives are not generated directly + by Asmgen, so we do not model them. *) + | Pldp _ _ _ + | Pstp _ _ _ + | Pcls _ _ _ + | Pclz _ _ _ + | Prev _ _ _ + | Prev16 _ _ _ + | Pfsqrt _ _ _ + | Pfmadd _ _ _ _ _ + | Pfmsub _ _ _ _ _ + | Pfnmadd _ _ _ _ _ + | Pfnmsub _ _ _ _ _ + | Pnop + | Pcfi_adjust _ + | Pcfi_rel_offset _ => + Stuck + end. + +(** Translation of the LTL/Linear/Mach view of machine registers + to the AArch64 view. Note that no LTL register maps to [X16], + [X18], nor [X30]. + [X18] is reserved as the platform register and never used by the + code generated by CompCert. + [X30] is used for the return address, and can also be used as temporary. + [X16] can be used as temporary. *) + +Definition preg_of (r: mreg) : preg := + match r with + | R0 => X0 | R1 => X1 | R2 => X2 | R3 => X3 + | R4 => X4 | R5 => X5 | R6 => X6 | R7 => X7 + | R8 => X8 | R9 => X9 | R10 => X10 | R11 => X11 + | R12 => X12 | R13 => X13 | R14 => X14 | R15 => X15 + | R17 => X17 | R19 => X19 + | R20 => X20 | R21 => X21 | R22 => X22 | R23 => X23 + | R24 => X24 | R25 => X25 | R26 => X26 | R27 => X27 + | R28 => X28 | R29 => X29 + | F0 => D0 | F1 => D1 | F2 => D2 | F3 => D3 + | F4 => D4 | F5 => D5 | F6 => D6 | F7 => D7 + | F8 => D8 | F9 => D9 | F10 => D10 | F11 => D11 + | F12 => D12 | F13 => D13 | F14 => D14 | F15 => D15 + | F16 => D16 | F17 => D17 | F18 => D18 | F19 => D19 + | F20 => D20 | F21 => D21 | F22 => D22 | F23 => D23 + | F24 => D24 | F25 => D25 | F26 => D26 | F27 => D27 + | F28 => D28 | F29 => D29 | F30 => D30 | F31 => D31 + end. + +(** Undefine all registers except SP and callee-save registers *) + +Definition undef_caller_save_regs (rs: regset) : regset := + fun r => + if preg_eq r SP + || In_dec preg_eq r (List.map preg_of (List.filter is_callee_save all_mregs)) + then rs r + else Vundef. + +(** Extract the values of the arguments of an external call. + We exploit the calling conventions from module [Conventions], except that + we use AArch64 registers instead of locations. *) + +Inductive extcall_arg (rs: regset) (m: mem): loc -> val -> Prop := + | extcall_arg_reg: forall r, + extcall_arg rs m (R r) (rs (preg_of r)) + | extcall_arg_stack: forall ofs ty bofs v, + bofs = Stacklayout.fe_ofs_arg + 4 * ofs -> + Mem.loadv (chunk_of_type ty) m + (Val.offset_ptr rs#SP (Ptrofs.repr bofs)) = Some v -> + extcall_arg rs m (Locations.S Outgoing ofs ty) v. + +Inductive extcall_arg_pair (rs: regset) (m: mem): rpair loc -> val -> Prop := + | extcall_arg_one: forall l v, + extcall_arg rs m l v -> + extcall_arg_pair rs m (One l) v + | extcall_arg_twolong: forall hi lo vhi vlo, + extcall_arg rs m hi vhi -> + extcall_arg rs m lo vlo -> + extcall_arg_pair rs m (Twolong hi lo) (Val.longofwords vhi vlo). + +Definition extcall_arguments + (rs: regset) (m: mem) (sg: signature) (args: list val) : Prop := + list_forall2 (extcall_arg_pair rs m) (loc_arguments sg) args. + +Definition loc_external_result (sg: signature) : rpair preg := + map_rpair preg_of (loc_result sg). + +(** Execution of the instruction at [rs#PC]. *) + +Inductive state: Type := + | State: regset -> mem -> state. + +Inductive step: state -> trace -> state -> Prop := + | exec_step_internal: + forall b ofs f i rs m rs' m', + rs PC = Vptr b ofs -> + Genv.find_funct_ptr ge b = Some (Internal f) -> + find_instr (Ptrofs.unsigned ofs) f.(fn_code) = Some i -> + exec_instr f i rs m = Next rs' m' -> + step (State rs m) E0 (State rs' m') + | exec_step_builtin: + forall b ofs f ef args res rs m vargs t vres rs' m', + rs PC = Vptr b ofs -> + Genv.find_funct_ptr ge b = Some (Internal f) -> + find_instr (Ptrofs.unsigned ofs) f.(fn_code) = Some (Pbuiltin ef args res) -> + eval_builtin_args ge rs rs#SP m args vargs -> + external_call ef ge vargs m t vres m' -> + rs' = nextinstr + (set_res res vres + (undef_regs (map preg_of (destroyed_by_builtin ef)) rs)) -> + step (State rs m) t (State rs' m') + | exec_step_external: + forall b ef args res rs m t rs' m', + rs PC = Vptr b Ptrofs.zero -> + Genv.find_funct_ptr ge b = Some (External ef) -> + external_call ef ge args m t res m' -> + extcall_arguments rs m (ef_sig ef) args -> + rs' = (set_pair (loc_external_result (ef_sig ef)) res (undef_caller_save_regs rs)) #PC <- (rs RA) -> + step (State rs m) t (State rs' m'). + +End RELSEM. + +(** Execution of whole programs. *) + +Inductive initial_state (p: program): state -> Prop := + | initial_state_intro: forall m0, + Genv.init_mem p = Some m0 -> + let ge := Genv.globalenv p in + let rs0 := + (Pregmap.init Vundef) + # PC <- (Genv.symbol_address ge p.(prog_main) Ptrofs.zero) + # RA <- Vnullptr + # SP <- Vnullptr in + initial_state p (State rs0 m0). + +Inductive final_state: state -> int -> Prop := + | final_state_intro: forall rs m r, + rs#PC = Vnullptr -> + rs#X0 = Vint r -> + final_state (State rs m) r. + +Definition semantics (p: program) := + Semantics step (initial_state p) final_state (Genv.globalenv p). + +(** Determinacy of the [Asm] semantics. *) + +Remark extcall_arguments_determ: + forall rs m sg args1 args2, + extcall_arguments rs m sg args1 -> extcall_arguments rs m sg args2 -> args1 = args2. +Proof. + intros until m. + assert (A: forall l v1 v2, + extcall_arg rs m l v1 -> extcall_arg rs m l v2 -> v1 = v2). + { intros. inv H; inv H0; congruence. } + assert (B: forall p v1 v2, + extcall_arg_pair rs m p v1 -> extcall_arg_pair rs m p v2 -> v1 = v2). + { intros. inv H; inv H0. + eapply A; eauto. + f_equal; eapply A; eauto. } + assert (C: forall ll vl1, list_forall2 (extcall_arg_pair rs m) ll vl1 -> + forall vl2, list_forall2 (extcall_arg_pair rs m) ll vl2 -> vl1 = vl2). + { + induction 1; intros vl2 EA; inv EA. + auto. + f_equal; eauto. } + intros. eapply C; eauto. +Qed. + +Lemma semantics_determinate: forall p, determinate (semantics p). +Proof. +Ltac Equalities := + match goal with + | [ H1: ?a = ?b, H2: ?a = ?c |- _ ] => + rewrite H1 in H2; inv H2; Equalities + | _ => idtac + end. + intros; constructor; simpl; intros. +- (* determ *) + inv H; inv H0; Equalities. + split. constructor. auto. + discriminate. + discriminate. + assert (vargs0 = vargs) by (eapply eval_builtin_args_determ; eauto). subst vargs0. + exploit external_call_determ. eexact H5. eexact H11. intros [A B]. + split. auto. intros. destruct B; auto. subst. auto. + assert (args0 = args) by (eapply extcall_arguments_determ; eauto). subst args0. + exploit external_call_determ. eexact H3. eexact H8. intros [A B]. + split. auto. intros. destruct B; auto. subst. auto. +- (* trace length *) + red; intros. inv H; simpl. + omega. + eapply external_call_trace_length; eauto. + eapply external_call_trace_length; eauto. +- (* initial states *) + inv H; inv H0. f_equal. congruence. +- (* final no step *) + inv H. red; intros; red; intros. inv H; rewrite H0 in *; discriminate. +- (* final states *) + inv H; inv H0. congruence. +Qed. + +(** Classification functions for processor registers (used in Asmgenproof). *) + +Definition data_preg (r: preg) : bool := + match r with + | IR X16 => false + | IR X30 => false + | IR _ => true + | FR _ => true + | CR _ => false + | SP => true + | PC => false + end. diff --git a/aarch64/AsmToJSON.ml b/aarch64/AsmToJSON.ml new file mode 100644 index 00000000..b7cfc152 --- /dev/null +++ b/aarch64/AsmToJSON.ml @@ -0,0 +1,24 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(* Functions to serialize AArch64 Asm to JSON *) + +(* Dummy function *) + +let destination: string option ref = ref None + +let sdump_folder = ref "" + +let print_if prog sourcename = + () + +let pp_mnemonics pp = () diff --git a/aarch64/Asmexpand.ml b/aarch64/Asmexpand.ml new file mode 100644 index 00000000..471ad501 --- /dev/null +++ b/aarch64/Asmexpand.ml @@ -0,0 +1,453 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(* Expanding built-ins and some pseudo-instructions by rewriting + of the AArch64 assembly code. *) + +open Asm +open Asmexpandaux +open AST +open Camlcoq +module Ptrofs = Integers.Ptrofs + +exception Error of string + +(* Useful constants *) + +let _0 = Z.zero +let _1 = Z.one +let _2 = Z.of_sint 2 +let _4 = Z.of_sint 4 +let _8 = Z.of_sint 8 +let _16 = Z.of_sint 16 +let _m1 = Z.of_sint (-1) + +(* Emit instruction sequences that set or offset a register by a constant. *) + +let expand_loadimm32 (dst: ireg) n = + List.iter emit (Asmgen.loadimm32 dst n []) + +let expand_addimm64 (dst: iregsp) (src: iregsp) n = + List.iter emit (Asmgen.addimm64 dst src n []) + +let expand_storeptr (src: ireg) (base: iregsp) ofs = + List.iter emit (Asmgen.storeptr src base ofs []) + +(* Handling of varargs *) + +(* Determine the number of int registers, FP registers, and stack locations + used to pass the fixed parameters. *) + +let rec next_arg_locations ir fr stk = function + | [] -> + (ir, fr, stk) + | (Tint | Tlong | Tany32 | Tany64) :: l -> + if ir < 8 + then next_arg_locations (ir + 1) fr stk l + else next_arg_locations ir fr (stk + 8) l + | (Tfloat | Tsingle) :: l -> + if fr < 8 + then next_arg_locations ir (fr + 1) stk l + else next_arg_locations ir fr (stk + 8) l + +(* Allocate memory on the stack and use it to save the registers + used for parameter passing. As an optimization, do not save + the registers used to pass the fixed parameters. *) + +let int_param_regs = [| X0; X1; X2; X3; X4; X5; X6; X7 |] +let float_param_regs = [| D0; D1; D2; D3; D4; D5; D6; D7 |] +let size_save_register_area = 8*8 + 8*16 + +let save_parameter_registers ir fr = + emit (Psubimm(X, XSP, XSP, Z.of_uint size_save_register_area)); + let i = ref ir in + while !i < 8 do + let pos = 8*16 + !i*8 in + if !i land 1 = 0 then begin + emit (Pstp(int_param_regs.(!i), int_param_regs.(!i + 1), + ADimm(XSP, Z.of_uint pos))); + i := !i + 2 + end else begin + emit (Pstrx(int_param_regs.(!i), ADimm(XSP, Z.of_uint pos))); + i := !i + 1 + end + done; + for i = fr to 7 do + let pos = i*16 in + emit (Pstrd(float_param_regs.(i), ADimm(XSP, Z.of_uint pos))) + done + +(* Initialize a va_list as per va_start. + Register r points to the following struct: + + typedef struct __va_list { + void *__stack; // next stack parameter + void *__gr_top; // top of the save area for int regs + void *__vr_top; // top of the save area for float regs + int__gr_offs; // offset from gr_top to next int reg + int__vr_offs; // offset from gr_top to next FP reg + } +*) + +let current_function_stacksize = ref 0L + +let expand_builtin_va_start r = + if not (is_current_function_variadic ()) then + invalid_arg "Fatal error: va_start used in non-vararg function"; + let (ir, fr, stk) = + next_arg_locations 0 0 0 (get_current_function_args ()) in + let stack_ofs = Int64.(add !current_function_stacksize (of_int stk)) + and gr_top_ofs = !current_function_stacksize + and vr_top_ofs = Int64.(sub !current_function_stacksize 64L) + and gr_offs = - ((8 - ir) * 8) + and vr_offs = - ((8 - fr) * 16) in + (* va->__stack = sp + stack_ofs *) + expand_addimm64 (RR1 X16) XSP (coqint_of_camlint64 stack_ofs); + emit (Pstrx(X16, ADimm(RR1 r, coqint_of_camlint64 0L))); + (* va->__gr_top = sp + gr_top_ofs *) + if gr_top_ofs <> stack_ofs then + expand_addimm64 (RR1 X16) XSP (coqint_of_camlint64 gr_top_ofs); + emit (Pstrx(X16, ADimm(RR1 r, coqint_of_camlint64 8L))); + (* va->__vr_top = sp + vr_top_ofs *) + expand_addimm64 (RR1 X16) XSP (coqint_of_camlint64 vr_top_ofs); + emit (Pstrx(X16, ADimm(RR1 r, coqint_of_camlint64 16L))); + (* va->__gr_offs = gr_offs *) + expand_loadimm32 X16 (coqint_of_camlint (Int32.of_int gr_offs)); + emit (Pstrw(X16, ADimm(RR1 r, coqint_of_camlint64 24L))); + (* va->__vr_offs = vr_offs *) + expand_loadimm32 X16 (coqint_of_camlint (Int32.of_int vr_offs)); + emit (Pstrw(X16, ADimm(RR1 r, coqint_of_camlint64 28L))) + +(* Handling of annotations *) + +let expand_annot_val kind txt targ args res = + emit (Pbuiltin (EF_annot(kind,txt,[targ]), args, BR_none)); + match args, res with + | [BA(IR src)], BR(IR dst) -> + if dst <> src then emit (Pmov (RR1 dst, RR1 src)) + | [BA(FR src)], BR(FR dst) -> + if dst <> src then emit (Pfmov (dst, src)) + | _, _ -> + raise (Error "ill-formed __builtin_annot_val") + +(* Handling of memcpy *) + +(* We assume unaligned memory accesses are efficient. Hence we use + memory accesses as wide as we can, up to 16 bytes. + Temporary registers used: x15 x16 x17 x29 x30. *) + +let offset_in_range ofs = + (* The 512 upper bound comes from ldp/stp. Single-register load/store + instructions support bigger offsets. *) + let ofs = Z.to_int64 ofs in 0L <= ofs && ofs < 512L + +let memcpy_small_arg sz arg tmp = + match arg with + | BA (IR r) -> + (RR1 r, _0) + | BA_addrstack ofs -> + if offset_in_range ofs + && offset_in_range (Ptrofs.add ofs (Ptrofs.repr (Z.of_uint sz))) + then (XSP, ofs) + else begin expand_addimm64 (RR1 tmp) XSP ofs; (RR1 tmp, _0) end + | _ -> + assert false + +let expand_builtin_memcpy_small sz al src dst = + let (tsrc, tdst) = + if dst <> BA (IR X17) then (X17, X29) else (X29, X17) in + let (rsrc, osrc) = memcpy_small_arg sz src tsrc in + let (rdst, odst) = memcpy_small_arg sz dst tdst in + let rec copy osrc odst sz = + if sz >= 16 then begin + emit (Pldp(X16, X30, ADimm(rsrc, osrc))); + emit (Pstp(X16, X30, ADimm(rdst, odst))); + copy (Ptrofs.add osrc _16) (Ptrofs.add odst _16) (sz - 16) + end + else if sz >= 8 then begin + emit (Pldrx(X16, ADimm(rsrc, osrc))); + emit (Pstrx(X16, ADimm(rdst, odst))); + copy (Ptrofs.add osrc _8) (Ptrofs.add odst _8) (sz - 8) + end + else if sz >= 4 then begin + emit (Pldrw(X16, ADimm(rsrc, osrc))); + emit (Pstrw(X16, ADimm(rdst, odst))); + copy (Ptrofs.add osrc _4) (Ptrofs.add odst _4) (sz - 4) + end + else if sz >= 2 then begin + emit (Pldrh(W, X16, ADimm(rsrc, osrc))); + emit (Pstrh(X16, ADimm(rdst, odst))); + copy (Ptrofs.add osrc _2) (Ptrofs.add odst _2) (sz - 2) + end + else if sz >= 1 then begin + emit (Pldrb(W, X16, ADimm(rsrc, osrc))); + emit (Pstrb(X16, ADimm(rdst, odst))); + copy (Ptrofs.add osrc _1) (Ptrofs.add odst _1) (sz - 1) + end + in copy osrc odst sz + +let memcpy_big_arg arg tmp = + match arg with + | BA (IR r) -> emit (Pmov(RR1 tmp, RR1 r)) + | BA_addrstack ofs -> expand_addimm64 (RR1 tmp) XSP ofs + | _ -> assert false + +let expand_builtin_memcpy_big sz al src dst = + assert (sz >= 16); + memcpy_big_arg src X30; + memcpy_big_arg dst X29; + let lbl = new_label () in + expand_loadimm32 X15 (Z.of_uint (sz / 16)); + emit (Plabel lbl); + emit (Pldp(X16, X17, ADpostincr(RR1 X30, _16))); + emit (Pstp(X16, X17, ADpostincr(RR1 X29, _16))); + emit (Psubimm(W, RR1 X15, RR1 X15, _1)); + emit (Pcbnz(W, X15, lbl)); + if sz mod 16 >= 8 then begin + emit (Pldrx(X16, ADpostincr(RR1 X30, _8))); + emit (Pstrx(X16, ADpostincr(RR1 X29, _8))) + end; + if sz mod 8 >= 4 then begin + emit (Pldrw(X16, ADpostincr(RR1 X30, _4))); + emit (Pstrw(X16, ADpostincr(RR1 X29, _4))) + end; + if sz mod 4 >= 2 then begin + emit (Pldrh(W, X16, ADpostincr(RR1 X30, _2))); + emit (Pstrh(X16, ADpostincr(RR1 X29, _2))) + end; + if sz mod 2 >= 1 then begin + emit (Pldrb(W, X16, ADpostincr(RR1 X30, _1))); + emit (Pstrb(X16, ADpostincr(RR1 X29, _1))) + end + +let expand_builtin_memcpy sz al args = + let (dst, src) = + match args with [d; s] -> (d, s) | _ -> assert false in + if sz < 64 + then expand_builtin_memcpy_small sz al src dst + else expand_builtin_memcpy_big sz al src dst + +(* Handling of volatile reads and writes *) + +let expand_builtin_vload_common chunk base ofs res = + let addr = ADimm(base, ofs) in + match chunk, res with + | Mint8unsigned, BR(IR res) -> + emit (Pldrb(W, res, addr)) + | Mint8signed, BR(IR res) -> + emit (Pldrsb(W, res, addr)) + | Mint16unsigned, BR(IR res) -> + emit (Pldrh(W, res, addr)) + | Mint16signed, BR(IR res) -> + emit (Pldrsh(W, res, addr)) + | Mint32, BR(IR res) -> + emit (Pldrw(res, addr)) + | Mint64, BR(IR res) -> + emit (Pldrx(res, addr)) + | Mfloat32, BR(FR res) -> + emit (Pldrs(res, addr)) + | Mfloat64, BR(FR res) -> + emit (Pldrd(res, addr)) + | _ -> + assert false + +let expand_builtin_vload chunk args res = + match args with + | [BA(IR addr)] -> + expand_builtin_vload_common chunk (RR1 addr) _0 res + | [BA_addrstack ofs] -> + if offset_in_range (Z.add ofs (Memdata.size_chunk chunk)) then + expand_builtin_vload_common chunk XSP ofs res + else begin + expand_addimm64 (RR1 X16) XSP ofs; (* X16 <- SP + ofs *) + expand_builtin_vload_common chunk (RR1 X16) _0 res + end + | [BA_addptr(BA(IR addr), BA_long ofs)] -> + if offset_in_range (Z.add ofs (Memdata.size_chunk chunk)) then + expand_builtin_vload_common chunk (RR1 addr) ofs res + else begin + expand_addimm64 (RR1 X16) (RR1 addr) ofs; (* X16 <- addr + ofs *) + expand_builtin_vload_common chunk (RR1 X16) _0 res + end + | _ -> + assert false + +let expand_builtin_vstore_common chunk base ofs src = + let addr = ADimm(base, ofs) in + match chunk, src with + | (Mint8signed | Mint8unsigned), BA(IR src) -> + emit (Pstrb(src, addr)) + | (Mint16signed | Mint16unsigned), BA(IR src) -> + emit (Pstrh(src, addr)) + | Mint32, BA(IR src) -> + emit (Pstrw(src, addr)) + | Mint64, BA(IR src) -> + emit (Pstrx(src, addr)) + | Mfloat32, BA(FR src) -> + emit (Pstrs(src, addr)) + | Mfloat64, BA(FR src) -> + emit (Pstrd(src, addr)) + | _ -> + assert false + +let expand_builtin_vstore chunk args = + match args with + | [BA(IR addr); src] -> + expand_builtin_vstore_common chunk (RR1 addr) _0 src + | [BA_addrstack ofs; src] -> + if offset_in_range (Z.add ofs (Memdata.size_chunk chunk)) then + expand_builtin_vstore_common chunk XSP ofs src + else begin + expand_addimm64 (RR1 X16) XSP ofs; (* X16 <- SP + ofs *) + expand_builtin_vstore_common chunk (RR1 X16) _0 src + end + | [BA_addptr(BA(IR addr), BA_long ofs); src] -> + if offset_in_range (Z.add ofs (Memdata.size_chunk chunk)) then + expand_builtin_vstore_common chunk (RR1 addr) ofs src + else begin + expand_addimm64 (RR1 X16) (RR1 addr) ofs; (* X16 <- addr + ofs *) + expand_builtin_vstore_common chunk (RR1 X16) _0 src + end + | _ -> + assert false + +(* Handling of compiler-inlined builtins *) + +let expand_builtin_inline name args res = + match name, args, res with + (* Synchronization *) + | "__builtin_membar", [], _ -> + () + | "__builtin_nop", [], _ -> + emit Pnop + (* Byte swap *) + | ("__builtin_bswap" | "__builtin_bswap32"), [BA(IR a1)], BR(IR res) -> + emit (Prev(W, res, a1)) + | "__builtin_bswap64", [BA(IR a1)], BR(IR res) -> + emit (Prev(X, res, a1)) + | "__builtin_bswap16", [BA(IR a1)], BR(IR res) -> + emit (Prev16(W, res, a1)); + emit (Pandimm(W, res, RR0 res, Z.of_uint 0xFFFF)) + (* Count leading zeros and leading sign bits *) + | "__builtin_clz", [BA(IR a1)], BR(IR res) -> + emit (Pclz(W, res, a1)) + | ("__builtin_clzl" | "__builtin_clzll"), [BA(IR a1)], BR(IR res) -> + emit (Pclz(X, res, a1)) + | "__builtin_cls", [BA(IR a1)], BR(IR res) -> + emit (Pcls(W, res, a1)) + | ("__builtin_clsl" | "__builtin_clsll"), [BA(IR a1)], BR(IR res) -> + emit (Pcls(X, res, a1)) + (* Float arithmetic *) + | "__builtin_fabs", [BA(FR a1)], BR(FR res) -> + emit (Pfabs(D, res, a1)) + | "__builtin_fsqrt", [BA(FR a1)], BR(FR res) -> + emit (Pfsqrt(D, res, a1)) + | "__builtin_fmadd", [BA(FR a1); BA(FR a2); BA(FR a3)], BR(FR res) -> + emit (Pfmadd(D, res, a1, a2, a3)) + | "__builtin_fmsub", [BA(FR a1); BA(FR a2); BA(FR a3)], BR(FR res) -> + emit (Pfmsub(D, res, a1, a2, a3)) + | "__builtin_fnmadd", [BA(FR a1); BA(FR a2); BA(FR a3)], BR(FR res) -> + emit (Pfnmadd(D, res, a1, a2, a3)) + | "__builtin_fnmsub", [BA(FR a1); BA(FR a2); BA(FR a3)], BR(FR res) -> + emit (Pfnmsub(D, res, a1, a2, a3)) + (* Vararg *) + | "__builtin_va_start", [BA(IR a)], _ -> + expand_builtin_va_start a + (* Catch-all *) + | _ -> + raise (Error ("unrecognized builtin " ^ name)) + +(* Expansion of instructions *) + +let expand_instruction instr = + match instr with + | Pallocframe (sz, ofs) -> + emit (Pmov (RR1 X29, XSP)); + if is_current_function_variadic() then begin + let (ir, fr, _) = + next_arg_locations 0 0 0 (get_current_function_args ()) in + save_parameter_registers ir fr; + current_function_stacksize := + Int64.(add (Z.to_int64 sz) (of_int size_save_register_area)) + end else begin + current_function_stacksize := Z.to_int64 sz + end; + expand_addimm64 XSP XSP (Ptrofs.repr (Z.neg sz)); + expand_storeptr X29 XSP ofs + | Pfreeframe (sz, ofs) -> + expand_addimm64 XSP XSP (coqint_of_camlint64 !current_function_stacksize) + | Pcvtx2w rd -> + (* no code generated, the upper 32 bits of rd will be ignored *) + () + | Pbuiltin (ef,args,res) -> + begin match ef with + | EF_builtin (name,sg) -> + expand_builtin_inline (camlstring_of_coqstring name) args res + | EF_vload chunk -> + expand_builtin_vload chunk args res + | EF_vstore chunk -> + expand_builtin_vstore chunk args + | EF_annot_val (kind,txt,targ) -> + expand_annot_val kind txt targ args res + | EF_memcpy(sz, al) -> + expand_builtin_memcpy (Z.to_int sz) (Z.to_int al) args + | EF_annot _ | EF_debug _ | EF_inline_asm _ -> + emit instr + | _ -> + assert false + end + | _ -> + emit instr + +let int_reg_to_dwarf = function + | X0 -> 0 | X1 -> 1 | X2 -> 2 | X3 -> 3 | X4 -> 4 + | X5 -> 5 | X6 -> 6 | X7 -> 7 | X8 -> 8 | X9 -> 9 + | X10 -> 10 | X11 -> 11 | X12 -> 12 | X13 -> 13 | X14 -> 14 + | X15 -> 15 | X16 -> 16 | X17 -> 17 | X18 -> 18 | X19 -> 19 + | X20 -> 20 | X21 -> 21 | X22 -> 22 | X23 -> 23 | X24 -> 24 + | X25 -> 25 | X26 -> 26 | X27 -> 27 | X28 -> 28 | X29 -> 29 + | X30 -> 30 + +let float_reg_to_dwarf = function + | D0 -> 64 | D1 -> 65 | D2 -> 66 | D3 -> 67 | D4 -> 68 + | D5 -> 69 | D6 -> 70 | D7 -> 71 | D8 -> 72 | D9 -> 73 + | D10 -> 74 | D11 -> 75 | D12 -> 76 | D13 -> 77 | D14 -> 78 + | D15 -> 79 | D16 -> 80 | D17 -> 81 | D18 -> 82 | D19 -> 83 + | D20 -> 84 | D21 -> 85 | D22 -> 86 | D23 -> 87 | D24 -> 88 + | D25 -> 89 | D26 -> 90 | D27 -> 91 | D28 -> 92 | D29 -> 93 + | D30 -> 94 | D31 -> 95 + +let preg_to_dwarf = function + | IR r -> int_reg_to_dwarf r + | FR r -> float_reg_to_dwarf r + | SP -> 31 + | _ -> assert false + +let expand_function id fn = + try + set_current_function fn; + expand id (* sp= *) 31 preg_to_dwarf expand_instruction fn.fn_code; + Errors.OK (get_current_function ()) + with Error s -> + Errors.Error (Errors.msg (coqstring_of_camlstring s)) + +let expand_fundef id = function + | Internal f -> + begin match expand_function id f with + | Errors.OK tf -> Errors.OK (Internal tf) + | Errors.Error msg -> Errors.Error msg + end + | External ef -> + Errors.OK (External ef) + +let expand_program (p: Asm.program) : Asm.program Errors.res = + AST.transform_partial_program2 expand_fundef (fun id v -> Errors.OK v) p diff --git a/aarch64/Asmgen.v b/aarch64/Asmgen.v new file mode 100644 index 00000000..875f3fd1 --- /dev/null +++ b/aarch64/Asmgen.v @@ -0,0 +1,1156 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Translation from Mach to AArch64. *) + +Require Import Recdef Coqlib Zwf Zbits. +Require Import Errors AST Integers Floats Op. +Require Import Locations Mach Asm. + +Local Open Scope string_scope. +Local Open Scope list_scope. +Local Open Scope error_monad_scope. + +(** Alignment check for symbols *) + +Parameter symbol_is_aligned : ident -> Z -> bool. +(** [symbol_is_aligned id sz] checks whether the symbol [id] is [sz] aligned *) + +(** Extracting integer or float registers. *) + +Definition ireg_of (r: mreg) : res ireg := + match preg_of r with IR mr => OK mr | _ => Error(msg "Asmgen.ireg_of") end. + +Definition freg_of (r: mreg) : res freg := + match preg_of r with FR mr => OK mr | _ => Error(msg "Asmgen.freg_of") end. + +(** Recognition of immediate arguments for logical integer operations.*) + +(** Valid immediate arguments are repetitions of a bit pattern [B] + of length [e] = 2, 4, 8, 16, 32 or 64. + The bit pattern [B] must be of the form [0*1*0*] or [1*0*1*] + but must not be all zeros or all ones. *) + +(** The following automaton recognizes [0*1*0*|1*0*1*]. +<< + 0 1 0 + / \ / \ / \ + \ / \ / \ / + -0--> [B] --1--> [D] --0--> [F] + / + [A] + \ + -1--> [C] --0--> [E] --1--> [G] + / \ / \ / \ + \ / \ / \ / + 1 0 1 +>> +*) + +Module Automaton. + +Inductive state : Type := SA | SB | SC | SD | SE | SF | SG | Sbad. + +Definition start := SA. + +Definition next (s: state) (b: bool) := + match s, b with + | SA,false => SB | SA,true => SC + | SB,false => SB | SB,true => SD + | SC,false => SE | SC,true => SC + | SD,false => SF | SD,true => SD + | SE,false => SE | SE,true => SG + | SF,false => SF | SF,true => Sbad + | SG,false => Sbad | SG,true => SG + | Sbad,_ => Sbad + end. + +Definition accepting (s: state) := + match s with + | SA | SB | SC | SD | SE | SF | SG => true + | Sbad => false + end. + +Fixpoint run (len: nat) (s: state) (x: Z) : bool := + match len with + | Datatypes.O => accepting s + | Datatypes.S len => run len (next s (Z.odd x)) (Z.div2 x) + end. + +End Automaton. + +(** The following function determines the candidate length [e], + ensuring that [x] is a repetition [BB...B] + of a bit pattern [B] of length [e]. *) + +Definition logical_imm_length (x: Z) (sixtyfour: bool) : nat := + (** [test n] checks that the low [2n] bits of [x] are of the + form [BB], that is, two occurrences of the same [n] bits *) + let test (n: Z) : bool := + Z.eqb (Zzero_ext n x) (Zzero_ext n (Z.shiftr x n)) in + (** If [test n] fails, we know that the candidate length [e] is + at least [2n]. Hence we test with decreasing values of [n]: + 32, 16, 8, 4, 2. *) + if sixtyfour && negb (test 32) then 64%nat + else if negb (test 16) then 32%nat + else if negb (test 8) then 16%nat + else if negb (test 4) then 8%nat + else if negb (test 2) then 4%nat + else 2%nat. + +(** A valid logical immediate is +- neither [0] nor [-1]; +- composed of a repetition [BBBBB] of a bit-pattern [B] of length [e] +- the low [e] bits of the number, that is, [B], match [0*1*0*] or [1*0*1*]. +*) + +Definition is_logical_imm32 (x: int) : bool := + negb (Int.eq x Int.zero) && negb (Int.eq x Int.mone) && + Automaton.run (logical_imm_length (Int.unsigned x) false) + Automaton.start (Int.unsigned x). + +Definition is_logical_imm64 (x: int64) : bool := + negb (Int64.eq x Int64.zero) && negb (Int64.eq x Int64.mone) && + Automaton.run (logical_imm_length (Int64.unsigned x) true) + Automaton.start (Int64.unsigned x). + +(** Arithmetic immediates are 12-bit unsigned numbers, possibly shifted left 12 bits *) + +Definition is_arith_imm32 (x: int) : bool := + Int.eq x (Int.zero_ext 12 x) + || Int.eq x (Int.shl (Int.zero_ext 12 (Int.shru x (Int.repr 12))) (Int.repr 12)). + +Definition is_arith_imm64 (x: int64) : bool := + Int64.eq x (Int64.zero_ext 12 x) + || Int64.eq x (Int64.shl (Int64.zero_ext 12 (Int64.shru x (Int64.repr 12))) (Int64.repr 12)). + +(** Decompose integer literals into 16-bit fragments *) + +Fixpoint decompose_int (N: nat) (n p: Z) {struct N} : list (Z * Z) := + match N with + | Datatypes.O => nil + | Datatypes.S N => + let frag := Zzero_ext 16 (Z.shiftr n p) in + if Z.eqb frag 0 then + decompose_int N n (p + 16) + else + (frag, p) :: decompose_int N (Z.ldiff n (Z.shiftl 65535 p)) (p + 16) + end. + +Definition negate_decomposition (l: list (Z * Z)) := + List.map (fun np => (Z.lxor (fst np) 65535, snd np)) l. + +Definition loadimm_k (sz: isize) (rd: ireg) (l: list (Z * Z)) (k: code) : code := + List.fold_right (fun np k => Pmovk sz rd (fst np) (snd np) :: k) k l. + +Definition loadimm_z (sz: isize) (rd: ireg) (l: list (Z * Z)) (k: code) : code := + match l with + | nil => Pmovz sz rd 0 0 :: k + | (n1, p1) :: l => Pmovz sz rd n1 p1 :: loadimm_k sz rd l k + end. + +Definition loadimm_n (sz: isize) (rd: ireg) (l: list (Z * Z)) (k: code) : code := + match l with + | nil => Pmovn sz rd 0 0 :: k + | (n1, p1) :: l => Pmovn sz rd n1 p1 :: loadimm_k sz rd (negate_decomposition l) k + end. + +Definition loadimm (sz: isize) (rd: ireg) (n: Z) (k: code) : code := + let N := match sz with W => 2%nat | X => 4%nat end in + let dz := decompose_int N n 0 in + let dn := decompose_int N (Z.lnot n) 0 in + if Nat.leb (List.length dz) (List.length dn) + then loadimm_z sz rd dz k + else loadimm_n sz rd dn k. + +Definition loadimm32 (rd: ireg) (n: int) (k: code) : code := + if is_logical_imm32 n + then Porrimm W rd XZR (Int.unsigned n) :: k + else loadimm W rd (Int.unsigned n) k. + +Definition loadimm64 (rd: ireg) (n: int64) (k: code) : code := + if is_logical_imm64 n + then Porrimm X rd XZR (Int64.unsigned n) :: k + else loadimm X rd (Int64.unsigned n) k. + +(** Add immediate *) + +Definition addimm_aux (insn: iregsp -> iregsp -> Z -> instruction) + (rd r1: iregsp) (n: Z) (k: code) := + let nlo := Zzero_ext 12 n in + let nhi := n - nlo in + if Z.eqb nhi 0 then + insn rd r1 nlo :: k + else if Z.eqb nlo 0 then + insn rd r1 nhi :: k + else + insn rd r1 nhi :: insn rd rd nlo :: k. + +Definition addimm32 (rd r1: ireg) (n: int) (k: code) : code := + let m := Int.neg n in + if Int.eq n (Int.zero_ext 24 n) then + addimm_aux (Paddimm W) rd r1 (Int.unsigned n) k + else if Int.eq m (Int.zero_ext 24 m) then + addimm_aux (Psubimm W) rd r1 (Int.unsigned m) k + else if Int.lt n Int.zero then + loadimm32 X16 m (Psub W rd r1 X16 SOnone :: k) + else + loadimm32 X16 n (Padd W rd r1 X16 SOnone :: k). + +Definition addimm64 (rd r1: iregsp) (n: int64) (k: code) : code := + let m := Int64.neg n in + if Int64.eq n (Int64.zero_ext 24 n) then + addimm_aux (Paddimm X) rd r1 (Int64.unsigned n) k + else if Int64.eq m (Int64.zero_ext 24 m) then + addimm_aux (Psubimm X) rd r1 (Int64.unsigned m) k + else if Int64.lt n Int64.zero then + loadimm64 X16 m (Psubext rd r1 X16 (EOuxtx Int.zero) :: k) + else + loadimm64 X16 n (Paddext rd r1 X16 (EOuxtx Int.zero) :: k). + +(** Logical immediate *) + +Definition logicalimm32 + (insn1: ireg -> ireg0 -> Z -> instruction) + (insn2: ireg -> ireg0 -> ireg -> shift_op -> instruction) + (rd r1: ireg) (n: int) (k: code) : code := + if is_logical_imm32 n + then insn1 rd r1 (Int.unsigned n) :: k + else loadimm32 X16 n (insn2 rd r1 X16 SOnone :: k). + +Definition logicalimm64 + (insn1: ireg -> ireg0 -> Z -> instruction) + (insn2: ireg -> ireg0 -> ireg -> shift_op -> instruction) + (rd r1: ireg) (n: int64) (k: code) : code := + if is_logical_imm64 n + then insn1 rd r1 (Int64.unsigned n) :: k + else loadimm64 X16 n (insn2 rd r1 X16 SOnone :: k). + +(** Sign- or zero-extended arithmetic *) + +Definition transl_extension (ex: extension) (a: int) : extend_op := + match ex with Xsgn32 => EOsxtw a | Xuns32 => EOuxtw a end. + +Definition move_extended_base + (rd: ireg) (r1: ireg) (ex: extension) (k: code) : code := + match ex with + | Xsgn32 => Pcvtsw2x rd r1 :: k + | Xuns32 => Pcvtuw2x rd r1 :: k + end. + +Definition move_extended + (rd: ireg) (r1: ireg) (ex: extension) (a: int) (k: code) : code := + if Int.eq a Int.zero then + move_extended_base rd r1 ex k + else + move_extended_base rd r1 ex (Padd X rd XZR rd (SOlsl a) :: k). + +Definition arith_extended + (insnX: iregsp -> iregsp -> ireg -> extend_op -> instruction) + (insnS: ireg -> ireg0 -> ireg -> shift_op -> instruction) + (rd r1 r2: ireg) (ex: extension) (a: int) (k: code) : code := + if Int.ltu a (Int.repr 5) then + insnX rd r1 r2 (transl_extension ex a) :: k + else + move_extended_base X16 r2 ex (insnS rd r1 X16 (SOlsl a) :: k). + +(** Extended right shift *) + +Definition shrx32 (rd r1: ireg) (n: int) (k: code) : code := + if Int.eq n Int.zero then + Pmov rd r1 :: k + else + Porr W X16 XZR r1 (SOasr (Int.repr 31)) :: + Padd W X16 r1 X16 (SOlsr (Int.sub Int.iwordsize n)) :: + Porr W rd XZR X16 (SOasr n) :: k. + +Definition shrx64 (rd r1: ireg) (n: int) (k: code) : code := + if Int.eq n Int.zero then + Pmov rd r1 :: k + else + Porr X X16 XZR r1 (SOasr (Int.repr 63)) :: + Padd X X16 r1 X16 (SOlsr (Int.sub Int64.iwordsize' n)) :: + Porr X rd XZR X16 (SOasr n) :: k. + +(** Load the address [id + ofs] in [rd] *) + +Definition loadsymbol (rd: ireg) (id: ident) (ofs: ptrofs) (k: code) : code := + if Archi.pic_code tt then + if Ptrofs.eq ofs Ptrofs.zero then + Ploadsymbol rd id :: k + else + Ploadsymbol rd id :: addimm64 rd rd (Ptrofs.to_int64 ofs) k + else + Padrp rd id ofs :: Paddadr rd rd id ofs :: k. + +(** Translate a shifted operand *) + +Definition transl_shift (s: Op.shift) (a: int): Asm.shift_op := + match s with + | Slsl => SOlsl a + | Slsr => SOlsr a + | Sasr => SOasr a + | Sror => SOror a + end. + +(** Translation of a condition. Prepends to [k] the instructions + that evaluate the condition and leave its boolean result in one of + the bits of the condition register. The bit in question is + determined by the [crbit_for_cond] function. *) + +Definition transl_cond + (cond: condition) (args: list mreg) (k: code) := + match cond, args with + | (Ccomp c | Ccompu c), a1 :: a2 :: nil => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pcmp W r1 r2 SOnone :: k) + | (Ccompshift c s a | Ccompushift c s a), a1 :: a2 :: nil => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pcmp W r1 r2 (transl_shift s a) :: k) + | (Ccompimm c n | Ccompuimm c n), a1 :: nil => + do r1 <- ireg_of a1; + OK (if is_arith_imm32 n then + Pcmpimm W r1 (Int.unsigned n) :: k + else if is_arith_imm32 (Int.neg n) then + Pcmnimm W r1 (Int.unsigned (Int.neg n)) :: k + else + loadimm32 X16 n (Pcmp W r1 X16 SOnone :: k)) + | (Cmaskzero n | Cmasknotzero n), a1 :: nil => + do r1 <- ireg_of a1; + OK (if is_logical_imm32 n then + Ptstimm W r1 (Int.unsigned n) :: k + else + loadimm32 X16 n (Ptst W r1 X16 SOnone :: k)) + | (Ccompl c | Ccomplu c), a1 :: a2 :: nil => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pcmp X r1 r2 SOnone :: k) + | (Ccomplshift c s a | Ccomplushift c s a), a1 :: a2 :: nil => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pcmp X r1 r2 (transl_shift s a) :: k) + | (Ccomplimm c n | Ccompluimm c n), a1 :: nil => + do r1 <- ireg_of a1; + OK (if is_arith_imm64 n then + Pcmpimm X r1 (Int64.unsigned n) :: k + else if is_arith_imm64 (Int64.neg n) then + Pcmnimm X r1 (Int64.unsigned (Int64.neg n)) :: k + else + loadimm64 X16 n (Pcmp X r1 X16 SOnone :: k)) + | (Cmasklzero n | Cmasklnotzero n), a1 :: nil => + do r1 <- ireg_of a1; + OK (if is_logical_imm64 n then + Ptstimm X r1 (Int64.unsigned n) :: k + else + loadimm64 X16 n (Ptst X r1 X16 SOnone :: k)) + | Ccompf cmp, a1 :: a2 :: nil => + do r1 <- freg_of a1; do r2 <- freg_of a2; + OK (Pfcmp D r1 r2 :: k) + | Cnotcompf cmp, a1 :: a2 :: nil => + do r1 <- freg_of a1; do r2 <- freg_of a2; + OK (Pfcmp D r1 r2 :: k) + | Ccompfzero cmp, a1 :: nil => + do r1 <- freg_of a1; + OK (Pfcmp0 D r1 :: k) + | Cnotcompfzero cmp, a1 :: nil => + do r1 <- freg_of a1; + OK (Pfcmp0 D r1 :: k) + | Ccompfs cmp, a1 :: a2 :: nil => + do r1 <- freg_of a1; do r2 <- freg_of a2; + OK (Pfcmp S r1 r2 :: k) + | Cnotcompfs cmp, a1 :: a2 :: nil => + do r1 <- freg_of a1; do r2 <- freg_of a2; + OK (Pfcmp S r1 r2 :: k) + | Ccompfszero cmp, a1 :: nil => + do r1 <- freg_of a1; + OK (Pfcmp0 S r1 :: k) + | Cnotcompfszero cmp, a1 :: nil => + do r1 <- freg_of a1; + OK (Pfcmp0 S r1 :: k) + | _, _ => + Error(msg "Asmgen.transl_cond") + end. + +Definition cond_for_signed_cmp (cmp: comparison) := + match cmp with + | Ceq => TCeq + | Cne => TCne + | Clt => TClt + | Cle => TCle + | Cgt => TCgt + | Cge => TCge + end. + +Definition cond_for_unsigned_cmp (cmp: comparison) := + match cmp with + | Ceq => TCeq + | Cne => TCne + | Clt => TClo + | Cle => TCls + | Cgt => TChi + | Cge => TChs + end. + +Definition cond_for_float_cmp (cmp: comparison) := + match cmp with + | Ceq => TCeq + | Cne => TCne + | Clt => TCmi + | Cle => TCls + | Cgt => TCgt + | Cge => TCge + end. + +Definition cond_for_float_not_cmp (cmp: comparison) := + match cmp with + | Ceq => TCne + | Cne => TCeq + | Clt => TCpl + | Cle => TChi + | Cgt => TCle + | Cge => TClt + end. + +Definition cond_for_cond (cond: condition) := + match cond with + | Ccomp cmp => cond_for_signed_cmp cmp + | Ccompu cmp => cond_for_unsigned_cmp cmp + | Ccompshift cmp s a => cond_for_signed_cmp cmp + | Ccompushift cmp s a => cond_for_unsigned_cmp cmp + | Ccompimm cmp n => cond_for_signed_cmp cmp + | Ccompuimm cmp n => cond_for_unsigned_cmp cmp + | Cmaskzero n => TCeq + | Cmasknotzero n => TCne + | Ccompl cmp => cond_for_signed_cmp cmp + | Ccomplu cmp => cond_for_unsigned_cmp cmp + | Ccomplshift cmp s a => cond_for_signed_cmp cmp + | Ccomplushift cmp s a => cond_for_unsigned_cmp cmp + | Ccomplimm cmp n => cond_for_signed_cmp cmp + | Ccompluimm cmp n => cond_for_unsigned_cmp cmp + | Cmasklzero n => TCeq + | Cmasklnotzero n => TCne + | Ccompf cmp => cond_for_float_cmp cmp + | Cnotcompf cmp => cond_for_float_not_cmp cmp + | Ccompfzero cmp => cond_for_float_cmp cmp + | Cnotcompfzero cmp => cond_for_float_not_cmp cmp + | Ccompfs cmp => cond_for_float_cmp cmp + | Cnotcompfs cmp => cond_for_float_not_cmp cmp + | Ccompfszero cmp => cond_for_float_cmp cmp + | Cnotcompfszero cmp => cond_for_float_not_cmp cmp + end. + +(** Translation of a conditional branch. Prepends to [k] the instructions + that evaluate the condition and ranch to [lbl] if it holds. + We recognize some conditional branches that can be implemented + without setting then testing condition flags. *) + +Definition transl_cond_branch_default + (c: condition) (args: list mreg) (lbl: label) (k: code) := + transl_cond c args (Pbc (cond_for_cond c) lbl :: k). + +Definition transl_cond_branch + (c: condition) (args: list mreg) (lbl: label) (k: code) := + match args, c with + | a1 :: nil, (Ccompimm Cne n | Ccompuimm Cne n) => + if Int.eq n Int.zero + then (do r1 <- ireg_of a1; OK (Pcbnz W r1 lbl :: k)) + else transl_cond_branch_default c args lbl k + | a1 :: nil, (Ccompimm Ceq n | Ccompuimm Ceq n) => + if Int.eq n Int.zero + then (do r1 <- ireg_of a1; OK (Pcbz W r1 lbl :: k)) + else transl_cond_branch_default c args lbl k + | a1 :: nil, (Ccomplimm Cne n | Ccompluimm Cne n) => + if Int64.eq n Int64.zero + then (do r1 <- ireg_of a1; OK (Pcbnz X r1 lbl :: k)) + else transl_cond_branch_default c args lbl k + | a1 :: nil, (Ccomplimm Ceq n | Ccompluimm Ceq n) => + if Int64.eq n Int64.zero + then (do r1 <- ireg_of a1; OK (Pcbz X r1 lbl :: k)) + else transl_cond_branch_default c args lbl k + | a1 :: nil, Cmaskzero n => + match Int.is_power2 n with + | Some bit => do r1 <- ireg_of a1; OK (Ptbz W r1 bit lbl :: k) + | None => transl_cond_branch_default c args lbl k + end + | a1 :: nil, Cmasknotzero n => + match Int.is_power2 n with + | Some bit => do r1 <- ireg_of a1; OK (Ptbnz W r1 bit lbl :: k) + | None => transl_cond_branch_default c args lbl k + end + | a1 :: nil, Cmasklzero n => + match Int64.is_power2' n with + | Some bit => do r1 <- ireg_of a1; OK (Ptbz X r1 bit lbl :: k) + | None => transl_cond_branch_default c args lbl k + end + | a1 :: nil, Cmasklnotzero n => + match Int64.is_power2' n with + | Some bit => do r1 <- ireg_of a1; OK (Ptbnz X r1 bit lbl :: k) + | None => transl_cond_branch_default c args lbl k + end + | _, _ => + transl_cond_branch_default c args lbl k + end. + +(** Translation of the arithmetic operation [res <- op(args)]. + The corresponding instructions are prepended to [k]. *) + +Definition transl_op + (op: operation) (args: list mreg) (res: mreg) (k: code) := + match op, args with + | Omove, a1 :: nil => + match preg_of res, preg_of a1 with + | IR r, IR a => OK (Pmov r a :: k) + | FR r, FR a => OK (Pfmov r a :: k) + | _ , _ => Error(msg "Asmgen.Omove") + end + | Ointconst n, nil => + do rd <- ireg_of res; + OK (loadimm32 rd n k) + | Olongconst n, nil => + do rd <- ireg_of res; + OK (loadimm64 rd n k) + | Ofloatconst f, nil => + do rd <- freg_of res; + OK (if Float.eq_dec f Float.zero + then Pfmovi D rd XZR :: k + else Pfmovimmd rd f :: k) + | Osingleconst f, nil => + do rd <- freg_of res; + OK (if Float32.eq_dec f Float32.zero + then Pfmovi S rd XZR :: k + else Pfmovimms rd f :: k) + | Oaddrsymbol id ofs, nil => + do rd <- ireg_of res; + OK (loadsymbol rd id ofs k) + | Oaddrstack ofs, nil => + do rd <- ireg_of res; + OK (addimm64 rd XSP (Ptrofs.to_int64 ofs) k) +(** 32-bit integer arithmetic *) + | Oshift s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Porr W rd XZR r1 (transl_shift s a) :: k) + | Oadd, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Padd W rd r1 r2 SOnone :: k) + | Oaddshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Padd W rd r1 r2 (transl_shift s a) :: k) + | Oaddimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (addimm32 rd r1 n k) + | Oneg, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psub W rd XZR r1 SOnone :: k) + | Onegshift s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psub W rd XZR r1 (transl_shift s a) :: k) + | Osub, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Psub W rd r1 r2 SOnone :: k) + | Osubshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Psub W rd r1 r2 (transl_shift s a) :: k) + | Omul, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pmadd W rd r1 r2 XZR :: k) + | Omuladd, a1 :: a2 :: a3 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; do r3 <- ireg_of a3; + OK (Pmadd W rd r2 r3 r1 :: k) + | Omulsub, a1 :: a2 :: a3 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; do r3 <- ireg_of a3; + OK (Pmsub W rd r2 r3 r1 :: k) + | Odiv, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Psdiv W rd r1 r2 :: k) + | Odivu, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pudiv W rd r1 r2 :: k) + | Oand, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pand W rd r1 r2 SOnone :: k) + | Oandshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pand W rd r1 r2 (transl_shift s a) :: k) + | Oandimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (logicalimm32 (Pandimm W) (Pand W) rd r1 n k) + | Oor, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porr W rd r1 r2 SOnone :: k) + | Oorshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porr W rd r1 r2 (transl_shift s a) :: k) + | Oorimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (logicalimm32 (Porrimm W) (Porr W) rd r1 n k) + | Oxor, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peor W rd r1 r2 SOnone :: k) + | Oxorshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peor W rd r1 r2 (transl_shift s a) :: k) + | Oxorimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (logicalimm32 (Peorimm W) (Peor W) rd r1 n k) + | Onot, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Porn W rd XZR r1 SOnone :: k) + | Onotshift s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Porn W rd XZR r1 (transl_shift s a) :: k) + | Obic, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pbic W rd r1 r2 SOnone :: k) + | Obicshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pbic W rd r1 r2 (transl_shift s a) :: k) + | Oorn, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porn W rd r1 r2 SOnone :: k) + | Oornshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porn W rd r1 r2 (transl_shift s a) :: k) + | Oeqv, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peon W rd r1 r2 SOnone :: k) + | Oeqvshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peon W rd r1 r2 (transl_shift s a) :: k) + | Oshl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Plslv W rd r1 r2 :: k) + | Oshr, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pasrv W rd r1 r2 :: k) + | Oshru, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Plsrv W rd r1 r2 :: k) + | Oshrximm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (shrx32 rd r1 n k) + | Ozext s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Pubfiz W rd r1 Int.zero s :: k) + | Osext s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psbfiz W rd r1 Int.zero s :: k) + | Oshlzext s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Pubfiz W rd r1 a (Z.min s (Int.zwordsize - Int.unsigned a)) :: k) + | Oshlsext s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psbfiz W rd r1 a (Z.min s (Int.zwordsize - Int.unsigned a)) :: k) + | Ozextshr a s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Pubfx W rd r1 a (Z.min s (Int.zwordsize - Int.unsigned a)) :: k) + | Osextshr a s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psbfx W rd r1 a (Z.min s (Int.zwordsize - Int.unsigned a)) :: k) +(** 64-bit integer arithmetic *) + | Oshiftl s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Porr X rd XZR r1 (transl_shift s a) :: k) + | Oextend x a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (move_extended rd r1 x a k) + (* [Omakelong] and [Ohighlong] should not occur *) + | Olowlong, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + assertion (ireg_eq rd r1); + OK (Pcvtx2w rd :: k) + | Oaddl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Padd X rd r1 r2 SOnone :: k) + | Oaddlshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Padd X rd r1 r2 (transl_shift s a) :: k) + | Oaddlext x a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (arith_extended Paddext (Padd X) rd r1 r2 x a k) + | Oaddlimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (addimm64 rd r1 n k) + | Onegl, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psub X rd XZR r1 SOnone :: k) + | Oneglshift s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psub X rd XZR r1 (transl_shift s a) :: k) + | Osubl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Psub X rd r1 r2 SOnone :: k) + | Osublshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Psub X rd r1 r2 (transl_shift s a) :: k) + | Osublext x a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (arith_extended Psubext (Psub X) rd r1 r2 x a k) + | Omull, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pmadd X rd r1 r2 XZR :: k) + | Omulladd, a1 :: a2 :: a3 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; do r3 <- ireg_of a3; + OK (Pmadd X rd r2 r3 r1 :: k) + | Omullsub, a1 :: a2 :: a3 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; do r3 <- ireg_of a3; + OK (Pmsub X rd r2 r3 r1 :: k) + | Omullhs, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Psmulh rd r1 r2 :: k) + | Omullhu, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pumulh rd r1 r2 :: k) + | Odivl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Psdiv X rd r1 r2 :: k) + | Odivlu, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pudiv X rd r1 r2 :: k) + | Oandl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pand X rd r1 r2 SOnone :: k) + | Oandlshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pand X rd r1 r2 (transl_shift s a) :: k) + | Oandlimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (logicalimm64 (Pandimm X) (Pand X) rd r1 n k) + | Oorl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porr X rd r1 r2 SOnone :: k) + | Oorlshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porr X rd r1 r2 (transl_shift s a) :: k) + | Oorlimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (logicalimm64 (Porrimm X) (Porr X) rd r1 n k) + | Oxorl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peor X rd r1 r2 SOnone :: k) + | Oxorlshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peor X rd r1 r2 (transl_shift s a) :: k) + | Oxorlimm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (logicalimm64 (Peorimm X) (Peor X) rd r1 n k) + | Onotl, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Porn X rd XZR r1 SOnone :: k) + | Onotlshift s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Porn X rd XZR r1 (transl_shift s a) :: k) + | Obicl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pbic X rd r1 r2 SOnone :: k) + | Obiclshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pbic X rd r1 r2 (transl_shift s a) :: k) + | Oornl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porn X rd r1 r2 SOnone :: k) + | Oornlshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Porn X rd r1 r2 (transl_shift s a) :: k) + | Oeqvl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peon X rd r1 r2 SOnone :: k) + | Oeqvlshift s a, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Peon X rd r1 r2 (transl_shift s a) :: k) + | Oshll, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Plslv X rd r1 r2 :: k) + | Oshrl, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Pasrv X rd r1 r2 :: k) + | Oshrlu, a1 :: a2 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (Plsrv X rd r1 r2 :: k) + | Oshrlximm n, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (shrx64 rd r1 n k) + | Ozextl s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Pubfiz X rd r1 Int.zero s :: k) + | Osextl s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psbfiz X rd r1 Int.zero s :: k) + | Oshllzext s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Pubfiz X rd r1 a (Z.min s (Int64.zwordsize - Int.unsigned a)) :: k) + | Oshllsext s a, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psbfiz X rd r1 a (Z.min s (Int64.zwordsize - Int.unsigned a)) :: k) + | Ozextshrl a s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Pubfx X rd r1 a (Z.min s (Int64.zwordsize - Int.unsigned a)) :: k) + | Osextshrl a s, a1 :: nil => + do rd <- ireg_of res; do r1 <- ireg_of a1; + OK (Psbfx X rd r1 a (Z.min s (Int64.zwordsize - Int.unsigned a)) :: k) +(** 64-bit floating-point arithmetic *) + | Onegf, a1 :: nil => + do rd <- freg_of res; do rs <- freg_of a1; + OK (Pfneg D rd rs :: k) + | Oabsf, a1 :: nil => + do rd <- freg_of res; do rs <- freg_of a1; + OK (Pfabs D rd rs :: k) + | Oaddf, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfadd D rd rs1 rs2 :: k) + | Osubf, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfsub D rd rs1 rs2 :: k) + | Omulf, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfmul D rd rs1 rs2 :: k) + | Odivf, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfdiv D rd rs1 rs2 :: k) +(** 32-bit floating-point arithmetic *) + | Onegfs, a1 :: nil => + do rd <- freg_of res; do rs <- freg_of a1; + OK (Pfneg S rd rs :: k) + | Oabsfs, a1 :: nil => + do rd <- freg_of res; do rs <- freg_of a1; + OK (Pfabs S rd rs :: k) + | Oaddfs, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfadd S rd rs1 rs2 :: k) + | Osubfs, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfsub S rd rs1 rs2 :: k) + | Omulfs, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfmul S rd rs1 rs2 :: k) + | Odivfs, a1 :: a2 :: nil => + do rd <- freg_of res; do rs1 <- freg_of a1; do rs2 <- freg_of a2; + OK (Pfdiv S rd rs1 rs2 :: k) + | Osingleoffloat, a1 :: nil => + do rd <- freg_of res; do rs <- freg_of a1; + OK (Pfcvtsd rd rs :: k) + | Ofloatofsingle, a1 :: nil => + do rd <- freg_of res; do rs <- freg_of a1; + OK (Pfcvtds rd rs :: k) +(** Conversions between int and float *) + | Ointoffloat, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzs W D rd rs :: k) + | Ointuoffloat, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzu W D rd rs :: k) + | Ofloatofint, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pscvtf D W rd rs :: k) + | Ofloatofintu, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pucvtf D W rd rs :: k) + | Ointofsingle, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzs W S rd rs :: k) + | Ointuofsingle, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzu W S rd rs :: k) + | Osingleofint, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pscvtf S W rd rs :: k) + | Osingleofintu, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pucvtf S W rd rs :: k) + | Olongoffloat, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzs X D rd rs :: k) + | Olonguoffloat, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzu X D rd rs :: k) + | Ofloatoflong, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pscvtf D X rd rs :: k) + | Ofloatoflongu, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pucvtf D X rd rs :: k) + | Olongofsingle, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzs X S rd rs :: k) + | Olonguofsingle, a1 :: nil => + do rd <- ireg_of res; do rs <- freg_of a1; + OK (Pfcvtzu X S rd rs :: k) + | Osingleoflong, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pscvtf S X rd rs :: k) + | Osingleoflongu, a1 :: nil => + do rd <- freg_of res; do rs <- ireg_of a1; + OK (Pucvtf S X rd rs :: k) +(** Boolean tests *) + | Ocmp c, _ => + do rd <- ireg_of res; + transl_cond c args (Pcset rd (cond_for_cond c) :: k) +(** Conditional move *) + | Osel cmp ty, a1 :: a2 :: args => + match preg_of res with + | IR r => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + transl_cond cmp args (Pcsel r r1 r2 (cond_for_cond cmp) :: k) + | FR r => + do r1 <- freg_of a1; do r2 <- freg_of a2; + transl_cond cmp args (Pfsel r r1 r2 (cond_for_cond cmp) :: k) + | _ => + Error(msg "Asmgen.Osel") + end + | _, _ => + Error(msg "Asmgen.transl_op") + end. + +(** Translation of addressing modes *) + +Definition offset_representable (sz: Z) (ofs: int64) : bool := + let isz := Int64.repr sz in + (** either unscaled 9-bit signed *) + Int64.eq ofs (Int64.sign_ext 9 ofs) || + (** or scaled 12-bit unsigned *) + (Int64.eq (Int64.modu ofs isz) Int64.zero + && Int64.ltu ofs (Int64.shl isz (Int64.repr 12))). + +Definition transl_addressing (sz: Z) (addr: Op.addressing) (args: list mreg) + (insn: Asm.addressing -> instruction) (k: code) : res code := + match addr, args with + | Aindexed ofs, a1 :: nil => + do r1 <- ireg_of a1; + if offset_representable sz ofs then + OK (insn (ADimm r1 ofs) :: k) + else + OK (loadimm64 X16 ofs (insn (ADreg r1 X16) :: k)) + | Aindexed2, a1 :: a2 :: nil => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + OK (insn (ADreg r1 r2) :: k) + | Aindexed2shift a, a1 :: a2 :: nil => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + if Int.eq a Int.zero then + OK (insn (ADreg r1 r2) :: k) + else if Int.eq (Int.shl Int.one a) (Int.repr sz) then + OK (insn (ADlsl r1 r2 a) :: k) + else + OK (Padd X X16 r1 r2 (SOlsl a) :: insn (ADimm X16 Int64.zero) :: k) + | Aindexed2ext x a, a1 :: a2 :: nil => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + if Int.eq a Int.zero || Int.eq (Int.shl Int.one a) (Int.repr sz) then + OK (insn (match x with Xsgn32 => ADsxt r1 r2 a + | Xuns32 => ADuxt r1 r2 a end) :: k) + else + OK (arith_extended Paddext (Padd X) X16 r1 r2 x a + (insn (ADimm X16 Int64.zero) :: k)) + | Aglobal id ofs, nil => + assertion (negb (Archi.pic_code tt)); + if Ptrofs.eq (Ptrofs.modu ofs (Ptrofs.repr sz)) Ptrofs.zero && symbol_is_aligned id sz + then OK (Padrp X16 id ofs :: insn (ADadr X16 id ofs) :: k) + else OK (loadsymbol X16 id ofs (insn (ADimm X16 Int64.zero) :: k)) + | Ainstack ofs, nil => + let ofs := Ptrofs.to_int64 ofs in + if offset_representable sz ofs then + OK (insn (ADimm XSP ofs) :: k) + else + OK (loadimm64 X16 ofs (insn (ADreg XSP X16) :: k)) + | _, _ => + Error(msg "Asmgen.transl_addressing") + end. + +(** Translation of loads and stores *) + +Definition transl_load (chunk: memory_chunk) (addr: Op.addressing) + (args: list mreg) (dst: mreg) (k: code) : res code := + match chunk with + | Mint8unsigned => + do rd <- ireg_of dst; transl_addressing 1 addr args (Pldrb W rd) k + | Mint8signed => + do rd <- ireg_of dst; transl_addressing 1 addr args (Pldrsb W rd) k + | Mint16unsigned => + do rd <- ireg_of dst; transl_addressing 2 addr args (Pldrh W rd) k + | Mint16signed => + do rd <- ireg_of dst; transl_addressing 2 addr args (Pldrsh W rd) k + | Mint32 => + do rd <- ireg_of dst; transl_addressing 4 addr args (Pldrw rd) k + | Mint64 => + do rd <- ireg_of dst; transl_addressing 8 addr args (Pldrx rd) k + | Mfloat32 => + do rd <- freg_of dst; transl_addressing 4 addr args (Pldrs rd) k + | Mfloat64 => + do rd <- freg_of dst; transl_addressing 8 addr args (Pldrd rd) k + | Many32 => + do rd <- ireg_of dst; transl_addressing 4 addr args (Pldrw_a rd) k + | Many64 => + do rd <- ireg_of dst; transl_addressing 8 addr args (Pldrx_a rd) k + end. + +Definition transl_store (chunk: memory_chunk) (addr: Op.addressing) + (args: list mreg) (src: mreg) (k: code) : res code := + match chunk with + | Mint8unsigned | Mint8signed => + do r1 <- ireg_of src; transl_addressing 1 addr args (Pstrb r1) k + | Mint16unsigned | Mint16signed => + do r1 <- ireg_of src; transl_addressing 2 addr args (Pstrh r1) k + | Mint32 => + do r1 <- ireg_of src; transl_addressing 4 addr args (Pstrw r1) k + | Mint64 => + do r1 <- ireg_of src; transl_addressing 8 addr args (Pstrx r1) k + | Mfloat32 => + do r1 <- freg_of src; transl_addressing 4 addr args (Pstrs r1) k + | Mfloat64 => + do r1 <- freg_of src; transl_addressing 8 addr args (Pstrd r1) k + | Many32 => + do r1 <- ireg_of src; transl_addressing 4 addr args (Pstrw_a r1) k + | Many64 => + do r1 <- ireg_of src; transl_addressing 8 addr args (Pstrx_a r1) k + end. + +(** Register-indexed loads and stores *) + +Definition indexed_memory_access (insn: Asm.addressing -> instruction) + (sz: Z) (base: iregsp) (ofs: ptrofs) (k: code) := + let ofs := Ptrofs.to_int64 ofs in + if offset_representable sz ofs + then insn (ADimm base ofs) :: k + else loadimm64 X16 ofs (insn (ADreg base X16) :: k). + +Definition loadind (base: iregsp) (ofs: ptrofs) (ty: typ) (dst: mreg) (k: code) := + match ty, preg_of dst with + | Tint, IR rd => OK (indexed_memory_access (Pldrw rd) 4 base ofs k) + | Tlong, IR rd => OK (indexed_memory_access (Pldrx rd) 8 base ofs k) + | Tsingle, FR rd => OK (indexed_memory_access (Pldrs rd) 4 base ofs k) + | Tfloat, FR rd => OK (indexed_memory_access (Pldrd rd) 8 base ofs k) + | Tany32, IR rd => OK (indexed_memory_access (Pldrw_a rd) 4 base ofs k) + | Tany64, IR rd => OK (indexed_memory_access (Pldrx_a rd) 8 base ofs k) + | Tany64, FR rd => OK (indexed_memory_access (Pldrd_a rd) 8 base ofs k) + | _, _ => Error (msg "Asmgen.loadind") + end. + +Definition storeind (src: mreg) (base: iregsp) (ofs: ptrofs) (ty: typ) (k: code) := + match ty, preg_of src with + | Tint, IR rd => OK (indexed_memory_access (Pstrw rd) 4 base ofs k) + | Tlong, IR rd => OK (indexed_memory_access (Pstrx rd) 8 base ofs k) + | Tsingle, FR rd => OK (indexed_memory_access (Pstrs rd) 4 base ofs k) + | Tfloat, FR rd => OK (indexed_memory_access (Pstrd rd) 8 base ofs k) + | Tany32, IR rd => OK (indexed_memory_access (Pstrw_a rd) 4 base ofs k) + | Tany64, IR rd => OK (indexed_memory_access (Pstrx_a rd) 8 base ofs k) + | Tany64, FR rd => OK (indexed_memory_access (Pstrd_a rd) 8 base ofs k) + | _, _ => Error (msg "Asmgen.storeind") + end. + +Definition loadptr (base: iregsp) (ofs: ptrofs) (dst: ireg) (k: code) := + indexed_memory_access (Pldrx dst) 8 base ofs k. + +Definition storeptr (src: ireg) (base: iregsp) (ofs: ptrofs) (k: code) := + indexed_memory_access (Pstrx src) 8 base ofs k. + +(** Function epilogue *) + +Definition make_epilogue (f: Mach.function) (k: code) := + loadptr XSP f.(fn_retaddr_ofs) RA + (Pfreeframe f.(fn_stacksize) f.(fn_link_ofs) :: k). + +(** Translation of a Mach instruction. *) + +Definition transl_instr (f: Mach.function) (i: Mach.instruction) + (r29_is_parent: bool) (k: code) : res code := + match i with + | Mgetstack ofs ty dst => + loadind XSP ofs ty dst k + | Msetstack src ofs ty => + storeind src XSP ofs ty k + | Mgetparam ofs ty dst => + (* load via the frame pointer if it is valid *) + do c <- loadind X29 ofs ty dst k; + OK (if r29_is_parent then c else loadptr XSP f.(fn_link_ofs) X29 c) + | Mop op args res => + transl_op op args res k + | Mload chunk addr args dst => + transl_load chunk addr args dst k + | Mstore chunk addr args src => + transl_store chunk addr args src k + | Mcall sig (inl r) => + do r1 <- ireg_of r; OK (Pblr r1 sig :: k) + | Mcall sig (inr symb) => + OK (Pbl symb sig :: k) + | Mtailcall sig (inl r) => + do r1 <- ireg_of r; + OK (make_epilogue f (Pbr r1 sig :: k)) + | Mtailcall sig (inr symb) => + OK (make_epilogue f (Pbs symb sig :: k)) + | Mbuiltin ef args res => + OK (Pbuiltin ef (List.map (map_builtin_arg preg_of) args) (map_builtin_res preg_of res) :: k) + | Mlabel lbl => + OK (Plabel lbl :: k) + | Mgoto lbl => + OK (Pb lbl :: k) + | Mcond cond args lbl => + transl_cond_branch cond args lbl k + | Mjumptable arg tbl => + do r <- ireg_of arg; + OK (Pbtbl r tbl :: k) + | Mreturn => + OK (make_epilogue f (Pret RA :: k)) + end. + +(** Translation of a code sequence *) + +Definition it1_is_parent (before: bool) (i: Mach.instruction) : bool := + match i with + | Msetstack src ofs ty => before + | Mgetparam ofs ty dst => negb (mreg_eq dst R29) + | Mop op args res => before && negb (mreg_eq res R29) + | _ => false + end. + +(** This is the naive definition that we no longer use because it + is not tail-recursive. It is kept as specification. *) + +Fixpoint transl_code (f: Mach.function) (il: list Mach.instruction) (it1p: bool) := + match il with + | nil => OK nil + | i1 :: il' => + do k <- transl_code f il' (it1_is_parent it1p i1); + transl_instr f i1 it1p k + end. + +(** This is an equivalent definition in continuation-passing style + that runs in constant stack space. *) + +Fixpoint transl_code_rec (f: Mach.function) (il: list Mach.instruction) + (it1p: bool) (k: code -> res code) := + match il with + | nil => k nil + | i1 :: il' => + transl_code_rec f il' (it1_is_parent it1p i1) + (fun c1 => do c2 <- transl_instr f i1 it1p c1; k c2) + end. + +Definition transl_code' (f: Mach.function) (il: list Mach.instruction) (it1p: bool) := + transl_code_rec f il it1p (fun c => OK c). + +(** Translation of a whole function. Note that we must check + that the generated code contains less than [2^32] instructions, + otherwise the offset part of the [PC] code pointer could wrap + around, leading to incorrect executions. *) + +Definition transl_function (f: Mach.function) := + do c <- transl_code' f f.(Mach.fn_code) true; + OK (mkfunction f.(Mach.fn_sig) + (Pallocframe f.(fn_stacksize) f.(fn_link_ofs) :: + storeptr RA XSP f.(fn_retaddr_ofs) c)). + +Definition transf_function (f: Mach.function) : res Asm.function := + do tf <- transl_function f; + if zlt Ptrofs.max_unsigned (list_length_z tf.(fn_code)) + then Error (msg "code size exceeded") + else OK tf. + +Definition transf_fundef (f: Mach.fundef) : res Asm.fundef := + transf_partial_fundef transf_function f. + +Definition transf_program (p: Mach.program) : res Asm.program := + transform_partial_program transf_fundef p. diff --git a/aarch64/Asmgenproof.v b/aarch64/Asmgenproof.v new file mode 100644 index 00000000..eeff1956 --- /dev/null +++ b/aarch64/Asmgenproof.v @@ -0,0 +1,1026 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Correctness proof for AArch64 code generation. *) + +Require Import Coqlib Errors. +Require Import Integers Floats AST Linking. +Require Import Values Memory Events Globalenvs Smallstep. +Require Import Op Locations Mach Conventions Asm. +Require Import Asmgen Asmgenproof0 Asmgenproof1. + +Definition match_prog (p: Mach.program) (tp: Asm.program) := + match_program (fun _ f tf => transf_fundef f = OK tf) eq p tp. + +Lemma transf_program_match: + forall p tp, transf_program p = OK tp -> match_prog p tp. +Proof. + intros. eapply match_transform_partial_program; eauto. +Qed. + +Section PRESERVATION. + +Variable prog: Mach.program. +Variable tprog: Asm.program. +Hypothesis TRANSF: match_prog prog tprog. +Let ge := Genv.globalenv prog. +Let tge := Genv.globalenv tprog. + +Lemma symbols_preserved: + forall (s: ident), Genv.find_symbol tge s = Genv.find_symbol ge s. +Proof (Genv.find_symbol_match TRANSF). + +Lemma senv_preserved: + Senv.equiv ge tge. +Proof (Genv.senv_match TRANSF). + +Lemma functions_translated: + forall b f, + Genv.find_funct_ptr ge b = Some f -> + exists tf, + Genv.find_funct_ptr tge b = Some tf /\ transf_fundef f = OK tf. +Proof (Genv.find_funct_ptr_transf_partial TRANSF). + +Lemma functions_transl: + forall fb f tf, + Genv.find_funct_ptr ge fb = Some (Internal f) -> + transf_function f = OK tf -> + Genv.find_funct_ptr tge fb = Some (Internal tf). +Proof. + intros. exploit functions_translated; eauto. intros [tf' [A B]]. + monadInv B. rewrite H0 in EQ; inv EQ; auto. +Qed. + +(** * Properties of control flow *) + +Lemma transf_function_no_overflow: + forall f tf, + transf_function f = OK tf -> list_length_z tf.(fn_code) <= Ptrofs.max_unsigned. +Proof. + intros. monadInv H. destruct (zlt Ptrofs.max_unsigned (list_length_z x.(fn_code))); inv EQ0. + omega. +Qed. + +Lemma exec_straight_exec: + forall fb f c ep tf tc c' rs m rs' m', + transl_code_at_pc ge (rs PC) fb f c ep tf tc -> + exec_straight tge tf tc rs m c' rs' m' -> + plus step tge (State rs m) E0 (State rs' m'). +Proof. + intros. inv H. + eapply exec_straight_steps_1; eauto. + eapply transf_function_no_overflow; eauto. + eapply functions_transl; eauto. +Qed. + +Lemma exec_straight_at: + forall fb f c ep tf tc c' ep' tc' rs m rs' m', + transl_code_at_pc ge (rs PC) fb f c ep tf tc -> + transl_code f c' ep' = OK tc' -> + exec_straight tge tf tc rs m tc' rs' m' -> + transl_code_at_pc ge (rs' PC) fb f c' ep' tf tc'. +Proof. + intros. inv H. + exploit exec_straight_steps_2; eauto. + eapply transf_function_no_overflow; eauto. + eapply functions_transl; eauto. + intros [ofs' [PC' CT']]. + rewrite PC'. constructor; auto. +Qed. + +(** The following lemmas show that the translation from Mach to Asm + preserves labels, in the sense that the following diagram commutes: +<< + translation + Mach code ------------------------ Asm instr sequence + | | + | Mach.find_label lbl find_label lbl | + | | + v v + Mach code tail ------------------- Asm instr seq tail + translation +>> + The proof demands many boring lemmas showing that Asm constructor + functions do not introduce new labels. +*) + +Section TRANSL_LABEL. + +Remark loadimm_z_label: forall sz rd l k, tail_nolabel k (loadimm_z sz rd l k). +Proof. + intros; destruct l as [ | [n1 p1] l]; simpl; TailNoLabel. + induction l as [ | [n p] l]; simpl; TailNoLabel. +Qed. + +Remark loadimm_n_label: forall sz rd l k, tail_nolabel k (loadimm_n sz rd l k). +Proof. + intros; destruct l as [ | [n1 p1] l]; simpl; TailNoLabel. + induction l as [ | [n p] l]; simpl; TailNoLabel. +Qed. + +Remark loadimm_label: forall sz rd n k, tail_nolabel k (loadimm sz rd n k). +Proof. + unfold loadimm; intros. destruct Nat.leb; [apply loadimm_z_label|apply loadimm_n_label]. +Qed. +Hint Resolve loadimm_label: labels. + +Remark loadimm32_label: forall r n k, tail_nolabel k (loadimm32 r n k). +Proof. + unfold loadimm32; intros. destruct (is_logical_imm32 n); TailNoLabel. +Qed. +Hint Resolve loadimm32_label: labels. + +Remark loadimm64_label: forall r n k, tail_nolabel k (loadimm64 r n k). +Proof. + unfold loadimm64; intros. destruct (is_logical_imm64 n); TailNoLabel. +Qed. +Hint Resolve loadimm64_label: labels. + +Remark addimm_aux: forall insn rd r1 n k, + (forall rd r1 n, nolabel (insn rd r1 n)) -> + tail_nolabel k (addimm_aux insn rd r1 n k). +Proof. + unfold addimm_aux; intros. + destruct Z.eqb. TailNoLabel. destruct Z.eqb; TailNoLabel. +Qed. + +Remark addimm32_label: forall rd r1 n k, tail_nolabel k (addimm32 rd r1 n k). +Proof. + unfold addimm32; intros. + destruct Int.eq. apply addimm_aux; intros; red; auto. + destruct Int.eq. apply addimm_aux; intros; red; auto. + destruct Int.lt; eapply tail_nolabel_trans; TailNoLabel. +Qed. +Hint Resolve addimm32_label: labels. + +Remark addimm64_label: forall rd r1 n k, tail_nolabel k (addimm64 rd r1 n k). +Proof. + unfold addimm64; intros. + destruct Int64.eq. apply addimm_aux; intros; red; auto. + destruct Int64.eq. apply addimm_aux; intros; red; auto. + destruct Int64.lt; eapply tail_nolabel_trans; TailNoLabel. +Qed. +Hint Resolve addimm64_label: labels. + +Remark logicalimm32_label: forall insn1 insn2 rd r1 n k, + (forall rd r1 n, nolabel (insn1 rd r1 n)) -> + (forall rd r1 r2 s, nolabel (insn2 rd r1 r2 s)) -> + tail_nolabel k (logicalimm32 insn1 insn2 rd r1 n k). +Proof. + unfold logicalimm32; intros. + destruct (is_logical_imm32 n). TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +Qed. + +Remark logicalimm64_label: forall insn1 insn2 rd r1 n k, + (forall rd r1 n, nolabel (insn1 rd r1 n)) -> + (forall rd r1 r2 s, nolabel (insn2 rd r1 r2 s)) -> + tail_nolabel k (logicalimm64 insn1 insn2 rd r1 n k). +Proof. + unfold logicalimm64; intros. + destruct (is_logical_imm64 n). TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +Qed. + +Remark move_extended_label: forall rd r1 ex a k, tail_nolabel k (move_extended rd r1 ex a k). +Proof. + unfold move_extended, move_extended_base; intros. destruct Int.eq, ex; TailNoLabel. +Qed. +Hint Resolve move_extended_label: labels. + +Remark arith_extended_label: forall insnX insnS rd r1 r2 ex a k, + (forall rd r1 r2 x, nolabel (insnX rd r1 r2 x)) -> + (forall rd r1 r2 s, nolabel (insnS rd r1 r2 s)) -> + tail_nolabel k (arith_extended insnX insnS rd r1 r2 ex a k). +Proof. + unfold arith_extended; intros. destruct Int.ltu. + TailNoLabel. + destruct ex; simpl; TailNoLabel. +Qed. + +Remark loadsymbol_label: forall r id ofs k, tail_nolabel k (loadsymbol r id ofs k). +Proof. + intros; unfold loadsymbol. + destruct (Archi.pic_code tt); TailNoLabel. destruct Ptrofs.eq; TailNoLabel. +Qed. +Hint Resolve loadsymbol_label: labels. + +Remark transl_cond_label: forall cond args k c, + transl_cond cond args k = OK c -> tail_nolabel k c. +Proof. + unfold transl_cond; intros; destruct cond; TailNoLabel. +- destruct is_arith_imm32; TailNoLabel. destruct is_arith_imm32; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +- destruct is_arith_imm32; TailNoLabel. destruct is_arith_imm32; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +- destruct is_logical_imm32; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +- destruct is_logical_imm32; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +- destruct is_arith_imm64; TailNoLabel. destruct is_arith_imm64; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +- destruct is_arith_imm64; TailNoLabel. destruct is_arith_imm64; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +- destruct is_logical_imm64; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +- destruct is_logical_imm64; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. +Qed. + +Remark transl_cond_branch_default_label: forall cond args lbl k c, + transl_cond_branch_default cond args lbl k = OK c -> tail_nolabel k c. +Proof. + unfold transl_cond_branch_default; intros. + eapply tail_nolabel_trans; [eapply transl_cond_label;eauto|TailNoLabel]. +Qed. +Hint Resolve transl_cond_branch_default_label: labels. + +Remark transl_cond_branch_label: forall cond args lbl k c, + transl_cond_branch cond args lbl k = OK c -> tail_nolabel k c. +Proof. + unfold transl_cond_branch; intros; destruct args; TailNoLabel; destruct cond; TailNoLabel. +- destruct c0; TailNoLabel. +- destruct c0; TailNoLabel. +- destruct (Int.is_power2 n); TailNoLabel. +- destruct (Int.is_power2 n); TailNoLabel. +- destruct c0; TailNoLabel. +- destruct c0; TailNoLabel. +- destruct (Int64.is_power2' n); TailNoLabel. +- destruct (Int64.is_power2' n); TailNoLabel. +Qed. + +Remark transl_op_label: + forall op args r k c, + transl_op op args r k = OK c -> tail_nolabel k c. +Proof. + unfold transl_op; intros; destruct op; TailNoLabel. +- destruct (preg_of r); try discriminate; destruct (preg_of m); inv H; TailNoLabel. +- destruct (Float.eq_dec n Float.zero); TailNoLabel. +- destruct (Float32.eq_dec n Float32.zero); TailNoLabel. +- apply logicalimm32_label; unfold nolabel; auto. +- apply logicalimm32_label; unfold nolabel; auto. +- apply logicalimm32_label; unfold nolabel; auto. +- unfold shrx32. destruct Int.eq; TailNoLabel. +- apply arith_extended_label; unfold nolabel; auto. +- apply arith_extended_label; unfold nolabel; auto. +- apply logicalimm64_label; unfold nolabel; auto. +- apply logicalimm64_label; unfold nolabel; auto. +- apply logicalimm64_label; unfold nolabel; auto. +- unfold shrx64. destruct Int.eq; TailNoLabel. +- eapply tail_nolabel_trans. eapply transl_cond_label; eauto. TailNoLabel. +- destruct (preg_of r); try discriminate; TailNoLabel; + (eapply tail_nolabel_trans; [eapply transl_cond_label; eauto | TailNoLabel]). +Qed. + +Remark transl_addressing_label: + forall sz addr args insn k c, + transl_addressing sz addr args insn k = OK c -> + (forall ad, nolabel (insn ad)) -> + tail_nolabel k c. +Proof. + unfold transl_addressing; intros; destruct addr; TailNoLabel; + eapply tail_nolabel_trans; TailNoLabel. + eapply tail_nolabel_trans. apply arith_extended_label; unfold nolabel; auto. TailNoLabel. +Qed. + +Remark transl_load_label: + forall chunk addr args dst k c, + transl_load chunk addr args dst k = OK c -> tail_nolabel k c. +Proof. + unfold transl_load; intros; destruct chunk; TailNoLabel; eapply transl_addressing_label; eauto; unfold nolabel; auto. +Qed. + +Remark transl_store_label: + forall chunk addr args src k c, + transl_store chunk addr args src k = OK c -> tail_nolabel k c. +Proof. + unfold transl_store; intros; destruct chunk; TailNoLabel; eapply transl_addressing_label; eauto; unfold nolabel; auto. +Qed. + +Remark indexed_memory_access_label: + forall insn sz base ofs k, + (forall ad, nolabel (insn ad)) -> + tail_nolabel k (indexed_memory_access insn sz base ofs k). +Proof. + unfold indexed_memory_access; intros. destruct offset_representable. + TailNoLabel. + eapply tail_nolabel_trans; TailNoLabel. +Qed. + +Remark loadind_label: + forall base ofs ty dst k c, + loadind base ofs ty dst k = OK c -> tail_nolabel k c. +Proof. + unfold loadind; intros. + destruct ty, (preg_of dst); inv H; apply indexed_memory_access_label; intros; exact I. +Qed. + +Remark storeind_label: + forall src base ofs ty k c, + storeind src base ofs ty k = OK c -> tail_nolabel k c. +Proof. + unfold storeind; intros. + destruct ty, (preg_of src); inv H; apply indexed_memory_access_label; intros; exact I. +Qed. + +Remark loadptr_label: + forall base ofs dst k, tail_nolabel k (loadptr base ofs dst k). +Proof. + intros. apply indexed_memory_access_label. unfold nolabel; auto. +Qed. + +Remark storeptr_label: + forall src base ofs k, tail_nolabel k (storeptr src base ofs k). +Proof. + intros. apply indexed_memory_access_label. unfold nolabel; auto. +Qed. + +Remark make_epilogue_label: + forall f k, tail_nolabel k (make_epilogue f k). +Proof. + unfold make_epilogue; intros. eapply tail_nolabel_trans. apply loadptr_label. TailNoLabel. +Qed. + +Lemma transl_instr_label: + forall f i ep k c, + transl_instr f i ep k = OK c -> + match i with Mlabel lbl => c = Plabel lbl :: k | _ => tail_nolabel k c end. +Proof. + unfold transl_instr; intros; destruct i; TailNoLabel. +- eapply loadind_label; eauto. +- eapply storeind_label; eauto. +- destruct ep. eapply loadind_label; eauto. + eapply tail_nolabel_trans. apply loadptr_label. eapply loadind_label; eauto. +- eapply transl_op_label; eauto. +- eapply transl_load_label; eauto. +- eapply transl_store_label; eauto. +- destruct s0; monadInv H; TailNoLabel. +- destruct s0; monadInv H; (eapply tail_nolabel_trans; [eapply make_epilogue_label|TailNoLabel]). +- eapply transl_cond_branch_label; eauto. +- eapply tail_nolabel_trans; [eapply make_epilogue_label|TailNoLabel]. +Qed. + +Lemma transl_instr_label': + forall lbl f i ep k c, + transl_instr f i ep k = OK c -> + find_label lbl c = if Mach.is_label lbl i then Some k else find_label lbl k. +Proof. + intros. exploit transl_instr_label; eauto. + destruct i; try (intros [A B]; apply B). + intros. subst c. simpl. auto. +Qed. + +Lemma transl_code_label: + forall lbl f c ep tc, + transl_code f c ep = OK tc -> + match Mach.find_label lbl c with + | None => find_label lbl tc = None + | Some c' => exists tc', find_label lbl tc = Some tc' /\ transl_code f c' false = OK tc' + end. +Proof. + induction c; simpl; intros. + inv H. auto. + monadInv H. rewrite (transl_instr_label' lbl _ _ _ _ _ EQ0). + generalize (Mach.is_label_correct lbl a). + destruct (Mach.is_label lbl a); intros. + subst a. simpl in EQ. exists x; auto. + eapply IHc; eauto. +Qed. + +Lemma transl_find_label: + forall lbl f tf, + transf_function f = OK tf -> + match Mach.find_label lbl f.(Mach.fn_code) with + | None => find_label lbl tf.(fn_code) = None + | Some c => exists tc, find_label lbl tf.(fn_code) = Some tc /\ transl_code f c false = OK tc + end. +Proof. + intros. monadInv H. destruct (zlt Ptrofs.max_unsigned (list_length_z x.(fn_code))); inv EQ0. + monadInv EQ. rewrite transl_code'_transl_code in EQ0. unfold fn_code. + simpl. destruct (storeptr_label X30 XSP (fn_retaddr_ofs f) x) as [A B]; rewrite B. + eapply transl_code_label; eauto. +Qed. + +End TRANSL_LABEL. + +(** A valid branch in a piece of Mach code translates to a valid ``go to'' + transition in the generated Asm code. *) + +Lemma find_label_goto_label: + forall f tf lbl rs m c' b ofs, + Genv.find_funct_ptr ge b = Some (Internal f) -> + transf_function f = OK tf -> + rs PC = Vptr b ofs -> + Mach.find_label lbl f.(Mach.fn_code) = Some c' -> + exists tc', exists rs', + goto_label tf lbl rs m = Next rs' m + /\ transl_code_at_pc ge (rs' PC) b f c' false tf tc' + /\ forall r, r <> PC -> rs'#r = rs#r. +Proof. + intros. exploit (transl_find_label lbl f tf); eauto. rewrite H2. + intros [tc [A B]]. + exploit label_pos_code_tail; eauto. instantiate (1 := 0). + intros [pos' [P [Q R]]]. + exists tc; exists (rs#PC <- (Vptr b (Ptrofs.repr pos'))). + split. unfold goto_label. rewrite P. rewrite H1. auto. + split. rewrite Pregmap.gss. constructor; auto. + rewrite Ptrofs.unsigned_repr. replace (pos' - 0) with pos' in Q. + auto. omega. + generalize (transf_function_no_overflow _ _ H0). omega. + intros. apply Pregmap.gso; auto. +Qed. + +(** Existence of return addresses *) + +Lemma return_address_exists: + forall f sg ros c, is_tail (Mcall sg ros :: c) f.(Mach.fn_code) -> + exists ra, return_address_offset f c ra. +Proof. + intros. eapply Asmgenproof0.return_address_exists; eauto. +- intros. exploit transl_instr_label; eauto. + destruct i; try (intros [A B]; apply A). intros. subst c0. repeat constructor. +- intros. monadInv H0. + destruct (zlt Ptrofs.max_unsigned (list_length_z x.(fn_code))); inv EQ0. monadInv EQ. + rewrite transl_code'_transl_code in EQ0. + exists x; exists true; split; auto. unfold fn_code. + constructor. apply (storeptr_label X30 XSP (fn_retaddr_ofs f0) x). +- exact transf_function_no_overflow. +Qed. + +(** * Proof of semantic preservation *) + +(** Semantic preservation is proved using simulation diagrams + of the following form. +<< + st1 --------------- st2 + | | + t| *|t + | | + v v + st1'--------------- st2' +>> + The invariant is the [match_states] predicate below, which includes: +- The Asm code pointed by the PC register is the translation of + the current Mach code sequence. +- Mach register values and Asm register values agree. +*) + +Inductive match_states: Mach.state -> Asm.state -> Prop := + | match_states_intro: + forall s fb sp c ep ms m m' rs f tf tc + (STACKS: match_stack ge s) + (FIND: Genv.find_funct_ptr ge fb = Some (Internal f)) + (MEXT: Mem.extends m m') + (AT: transl_code_at_pc ge (rs PC) fb f c ep tf tc) + (AG: agree ms sp rs) + (DXP: ep = true -> rs#X29 = parent_sp s), + match_states (Mach.State s fb sp c ms m) + (Asm.State rs m') + | match_states_call: + forall s fb ms m m' rs + (STACKS: match_stack ge s) + (MEXT: Mem.extends m m') + (AG: agree ms (parent_sp s) rs) + (ATPC: rs PC = Vptr fb Ptrofs.zero) + (ATLR: rs RA = parent_ra s), + match_states (Mach.Callstate s fb ms m) + (Asm.State rs m') + | match_states_return: + forall s ms m m' rs + (STACKS: match_stack ge s) + (MEXT: Mem.extends m m') + (AG: agree ms (parent_sp s) rs) + (ATPC: rs PC = parent_ra s), + match_states (Mach.Returnstate s ms m) + (Asm.State rs m'). + +Lemma exec_straight_steps: + forall s fb f rs1 i c ep tf tc m1' m2 m2' sp ms2, + match_stack ge s -> + Mem.extends m2 m2' -> + Genv.find_funct_ptr ge fb = Some (Internal f) -> + transl_code_at_pc ge (rs1 PC) fb f (i :: c) ep tf tc -> + (forall k c (TR: transl_instr f i ep k = OK c), + exists rs2, + exec_straight tge tf c rs1 m1' k rs2 m2' + /\ agree ms2 sp rs2 + /\ (it1_is_parent ep i = true -> rs2#X29 = parent_sp s)) -> + exists st', + plus step tge (State rs1 m1') E0 st' /\ + match_states (Mach.State s fb sp c ms2 m2) st'. +Proof. + intros. inversion H2. subst. monadInv H7. + exploit H3; eauto. intros [rs2 [A [B C]]]. + exists (State rs2 m2'); split. + eapply exec_straight_exec; eauto. + econstructor; eauto. eapply exec_straight_at; eauto. +Qed. + +Lemma exec_straight_steps_goto: + forall s fb f rs1 i c ep tf tc m1' m2 m2' sp ms2 lbl c', + match_stack ge s -> + Mem.extends m2 m2' -> + Genv.find_funct_ptr ge fb = Some (Internal f) -> + Mach.find_label lbl f.(Mach.fn_code) = Some c' -> + transl_code_at_pc ge (rs1 PC) fb f (i :: c) ep tf tc -> + it1_is_parent ep i = false -> + (forall k c (TR: transl_instr f i ep k = OK c), + exists jmp, exists k', exists rs2, + exec_straight tge tf c rs1 m1' (jmp :: k') rs2 m2' + /\ agree ms2 sp rs2 + /\ exec_instr tge tf jmp rs2 m2' = goto_label tf lbl rs2 m2') -> + exists st', + plus step tge (State rs1 m1') E0 st' /\ + match_states (Mach.State s fb sp c' ms2 m2) st'. +Proof. + intros. inversion H3. subst. monadInv H9. + exploit H5; eauto. intros [jmp [k' [rs2 [A [B C]]]]]. + generalize (functions_transl _ _ _ H7 H8); intro FN. + generalize (transf_function_no_overflow _ _ H8); intro NOOV. + exploit exec_straight_steps_2; eauto. + intros [ofs' [PC2 CT2]]. + exploit find_label_goto_label; eauto. + intros [tc' [rs3 [GOTO [AT' OTH]]]]. + exists (State rs3 m2'); split. + eapply plus_right'. + eapply exec_straight_steps_1; eauto. + econstructor; eauto. + eapply find_instr_tail. eauto. + rewrite C. eexact GOTO. + traceEq. + econstructor; eauto. + apply agree_exten with rs2; auto with asmgen. + congruence. +Qed. + +Lemma exec_straight_opt_steps_goto: + forall s fb f rs1 i c ep tf tc m1' m2 m2' sp ms2 lbl c', + match_stack ge s -> + Mem.extends m2 m2' -> + Genv.find_funct_ptr ge fb = Some (Internal f) -> + Mach.find_label lbl f.(Mach.fn_code) = Some c' -> + transl_code_at_pc ge (rs1 PC) fb f (i :: c) ep tf tc -> + it1_is_parent ep i = false -> + (forall k c (TR: transl_instr f i ep k = OK c), + exists jmp, exists k', exists rs2, + exec_straight_opt tge tf c rs1 m1' (jmp :: k') rs2 m2' + /\ agree ms2 sp rs2 + /\ exec_instr tge tf jmp rs2 m2' = goto_label tf lbl rs2 m2') -> + exists st', + plus step tge (State rs1 m1') E0 st' /\ + match_states (Mach.State s fb sp c' ms2 m2) st'. +Proof. + intros. inversion H3. subst. monadInv H9. + exploit H5; eauto. intros [jmp [k' [rs2 [A [B C]]]]]. + generalize (functions_transl _ _ _ H7 H8); intro FN. + generalize (transf_function_no_overflow _ _ H8); intro NOOV. + inv A. +- exploit find_label_goto_label; eauto. + intros [tc' [rs3 [GOTO [AT' OTH]]]]. + exists (State rs3 m2'); split. + apply plus_one. econstructor; eauto. + eapply find_instr_tail. eauto. + rewrite C. eexact GOTO. + econstructor; eauto. + apply agree_exten with rs2; auto with asmgen. + congruence. +- exploit exec_straight_steps_2; eauto. + intros [ofs' [PC2 CT2]]. + exploit find_label_goto_label; eauto. + intros [tc' [rs3 [GOTO [AT' OTH]]]]. + exists (State rs3 m2'); split. + eapply plus_right'. + eapply exec_straight_steps_1; eauto. + econstructor; eauto. + eapply find_instr_tail. eauto. + rewrite C. eexact GOTO. + traceEq. + econstructor; eauto. + apply agree_exten with rs2; auto with asmgen. + congruence. +Qed. + +(** We need to show that, in the simulation diagram, we cannot + take infinitely many Mach transitions that correspond to zero + transitions on the Asm side. Actually, all Mach transitions + correspond to at least one Asm transition, except the + transition from [Machsem.Returnstate] to [Machsem.State]. + So, the following integer measure will suffice to rule out + the unwanted behaviour. *) + +Definition measure (s: Mach.state) : nat := + match s with + | Mach.State _ _ _ _ _ _ => 0%nat + | Mach.Callstate _ _ _ _ => 0%nat + | Mach.Returnstate _ _ _ => 1%nat + end. + +Remark preg_of_not_X29: forall r, negb (mreg_eq r R29) = true -> IR X29 <> preg_of r. +Proof. + intros. change (IR X29) with (preg_of R29). red; intros. + exploit preg_of_injective; eauto. intros; subst r; discriminate. +Qed. + +Lemma sp_val': forall ms sp rs, agree ms sp rs -> sp = rs XSP. +Proof. + intros. eapply sp_val; eauto. +Qed. + +(** This is the simulation diagram. We prove it by case analysis on the Mach transition. *) + +Theorem step_simulation: + forall S1 t S2, Mach.step return_address_offset ge S1 t S2 -> + forall S1' (MS: match_states S1 S1'), + (exists S2', plus step tge S1' t S2' /\ match_states S2 S2') + \/ (measure S2 < measure S1 /\ t = E0 /\ match_states S2 S1')%nat. +Proof. + induction 1; intros; inv MS. + +- (* Mlabel *) + left; eapply exec_straight_steps; eauto; intros. + monadInv TR. econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split. apply agree_nextinstr; auto. simpl; congruence. + +- (* Mgetstack *) + unfold load_stack in H. + exploit Mem.loadv_extends; eauto. intros [v' [A B]]. + rewrite (sp_val _ _ _ AG) in A. + left; eapply exec_straight_steps; eauto. intros. simpl in TR. + exploit loadind_correct; eauto with asmgen. intros [rs' [P [Q R]]]. + exists rs'; split. eauto. + split. eapply agree_set_mreg; eauto with asmgen. congruence. + simpl; congruence. + +- (* Msetstack *) + unfold store_stack in H. + assert (Val.lessdef (rs src) (rs0 (preg_of src))) by (eapply preg_val; eauto). + exploit Mem.storev_extends; eauto. intros [m2' [A B]]. + left; eapply exec_straight_steps; eauto. + rewrite (sp_val _ _ _ AG) in A. intros. simpl in TR. + exploit storeind_correct; eauto with asmgen. intros [rs' [P Q]]. + exists rs'; split. eauto. + split. eapply agree_undef_regs; eauto with asmgen. + simpl; intros. rewrite Q; auto with asmgen. + +- (* Mgetparam *) + assert (f0 = f) by congruence; subst f0. + unfold load_stack in *. + exploit Mem.loadv_extends. eauto. eexact H0. auto. + intros [parent' [A B]]. rewrite (sp_val' _ _ _ AG) in A. + exploit lessdef_parent_sp; eauto. clear B; intros B; subst parent'. + exploit Mem.loadv_extends. eauto. eexact H1. auto. + intros [v' [C D]]. +Opaque loadind. + left; eapply exec_straight_steps; eauto; intros. monadInv TR. + destruct ep. +(* X30 contains parent *) + exploit loadind_correct. eexact EQ. + instantiate (2 := rs0). simpl; rewrite DXP; eauto. simpl; congruence. + intros [rs1 [P [Q R]]]. + exists rs1; split. eauto. + split. eapply agree_set_mreg. eapply agree_set_mreg; eauto. congruence. auto with asmgen. + simpl; intros. rewrite R; auto with asmgen. + apply preg_of_not_X29; auto. +(* X30 does not contain parent *) + exploit loadptr_correct. eexact A. simpl; congruence. intros [rs1 [P [Q R]]]. + exploit loadind_correct. eexact EQ. instantiate (2 := rs1). simpl; rewrite Q. eauto. simpl; congruence. + intros [rs2 [S [T U]]]. + exists rs2; split. eapply exec_straight_trans; eauto. + split. eapply agree_set_mreg. eapply agree_set_mreg. eauto. eauto. + instantiate (1 := rs1#X29 <- (rs2#X29)). intros. + rewrite Pregmap.gso; auto with asmgen. + congruence. + intros. unfold Pregmap.set. destruct (PregEq.eq r' X29). congruence. auto with asmgen. + simpl; intros. rewrite U; auto with asmgen. + apply preg_of_not_X29; auto. + +- (* Mop *) + assert (eval_operation tge sp op (map rs args) m = Some v). + { rewrite <- H. apply eval_operation_preserved. exact symbols_preserved. } + exploit eval_operation_lessdef. eapply preg_vals; eauto. eauto. eexact H0. + intros [v' [A B]]. rewrite (sp_val _ _ _ AG) in A. + left; eapply exec_straight_steps; eauto; intros. simpl in TR. + exploit transl_op_correct; eauto. intros [rs2 [P [Q R]]]. + exists rs2; split. eauto. split. + apply agree_set_undef_mreg with rs0; auto. + apply Val.lessdef_trans with v'; auto. + simpl; intros. InvBooleans. + rewrite R; auto. apply preg_of_not_X29; auto. +Local Transparent destroyed_by_op. + destruct op; try exact I; simpl; congruence. + +- (* Mload *) + assert (Op.eval_addressing tge sp addr (map rs args) = Some a). + { rewrite <- H. apply eval_addressing_preserved. exact symbols_preserved. } + exploit eval_addressing_lessdef. eapply preg_vals; eauto. eexact H1. + intros [a' [A B]]. rewrite (sp_val _ _ _ AG) in A. + exploit Mem.loadv_extends; eauto. intros [v' [C D]]. + left; eapply exec_straight_steps; eauto; intros. simpl in TR. + exploit transl_load_correct; eauto. intros [rs2 [P [Q R]]]. + exists rs2; split. eauto. + split. eapply agree_set_undef_mreg; eauto. congruence. + simpl; congruence. + +- (* Mstore *) + assert (Op.eval_addressing tge sp addr (map rs args) = Some a). + { rewrite <- H. apply eval_addressing_preserved. exact symbols_preserved. } + exploit eval_addressing_lessdef. eapply preg_vals; eauto. eexact H1. + intros [a' [A B]]. rewrite (sp_val _ _ _ AG) in A. + assert (Val.lessdef (rs src) (rs0 (preg_of src))) by (eapply preg_val; eauto). + exploit Mem.storev_extends; eauto. intros [m2' [C D]]. + left; eapply exec_straight_steps; eauto. + intros. simpl in TR. exploit transl_store_correct; eauto. intros [rs2 [P Q]]. + exists rs2; split. eauto. + split. eapply agree_undef_regs; eauto with asmgen. + simpl; congruence. + +- (* Mcall *) + assert (f0 = f) by congruence. subst f0. + inv AT. + assert (NOOV: list_length_z tf.(fn_code) <= Ptrofs.max_unsigned). + { eapply transf_function_no_overflow; eauto. } + destruct ros as [rf|fid]; simpl in H; monadInv H5. ++ (* Indirect call *) + assert (rs rf = Vptr f' Ptrofs.zero). + { destruct (rs rf); try discriminate. + revert H; predSpec Ptrofs.eq Ptrofs.eq_spec i Ptrofs.zero; intros; congruence. } + assert (rs0 x0 = Vptr f' Ptrofs.zero). + { exploit ireg_val; eauto. rewrite H5; intros LD; inv LD; auto. } + generalize (code_tail_next_int _ _ _ _ NOOV H6). intro CT1. + assert (TCA: transl_code_at_pc ge (Vptr fb (Ptrofs.add ofs Ptrofs.one)) fb f c false tf x). + { econstructor; eauto. } + exploit return_address_offset_correct; eauto. intros; subst ra. + left; econstructor; split. + apply plus_one. eapply exec_step_internal. Simpl. rewrite <- H2; simpl; eauto. + eapply functions_transl; eauto. eapply find_instr_tail; eauto. + simpl. eauto. + econstructor; eauto. + econstructor; eauto. + eapply agree_sp_def; eauto. + simpl. eapply agree_exten; eauto. intros. Simpl. + Simpl. rewrite <- H2. auto. ++ (* Direct call *) + generalize (code_tail_next_int _ _ _ _ NOOV H6). intro CT1. + assert (TCA: transl_code_at_pc ge (Vptr fb (Ptrofs.add ofs Ptrofs.one)) fb f c false tf x). + econstructor; eauto. + exploit return_address_offset_correct; eauto. intros; subst ra. + left; econstructor; split. + apply plus_one. eapply exec_step_internal. eauto. + eapply functions_transl; eauto. eapply find_instr_tail; eauto. + simpl. unfold Genv.symbol_address. rewrite symbols_preserved. rewrite H. eauto. + econstructor; eauto. + econstructor; eauto. + eapply agree_sp_def; eauto. + simpl. eapply agree_exten; eauto. intros. Simpl. + Simpl. rewrite <- H2. auto. + +- (* Mtailcall *) + assert (f0 = f) by congruence. subst f0. + inversion AT; subst. + assert (NOOV: list_length_z tf.(fn_code) <= Ptrofs.max_unsigned). + { eapply transf_function_no_overflow; eauto. } + exploit Mem.loadv_extends. eauto. eexact H1. auto. simpl. intros [parent' [A B]]. + destruct ros as [rf|fid]; simpl in H; monadInv H7. ++ (* Indirect call *) + assert (rs rf = Vptr f' Ptrofs.zero). + { destruct (rs rf); try discriminate. + revert H; predSpec Ptrofs.eq Ptrofs.eq_spec i Ptrofs.zero; intros; congruence. } + assert (rs0 x0 = Vptr f' Ptrofs.zero). + { exploit ireg_val; eauto. rewrite H7; intros LD; inv LD; auto. } + exploit make_epilogue_correct; eauto. intros (rs1 & m1 & U & V & W & X & Y & Z). + exploit exec_straight_steps_2; eauto using functions_transl. + intros (ofs' & P & Q). + left; econstructor; split. + (* execution *) + eapply plus_right'. eapply exec_straight_exec; eauto. + econstructor. eexact P. eapply functions_transl; eauto. eapply find_instr_tail. eexact Q. + simpl. reflexivity. + traceEq. + (* match states *) + econstructor; eauto. + apply agree_set_other; auto with asmgen. + Simpl. rewrite Z by (rewrite <- (ireg_of_eq _ _ EQ1); eauto with asmgen). assumption. ++ (* Direct call *) + exploit make_epilogue_correct; eauto. intros (rs1 & m1 & U & V & W & X & Y & Z). + exploit exec_straight_steps_2; eauto using functions_transl. + intros (ofs' & P & Q). + left; econstructor; split. + (* execution *) + eapply plus_right'. eapply exec_straight_exec; eauto. + econstructor. eexact P. eapply functions_transl; eauto. eapply find_instr_tail. eexact Q. + simpl. reflexivity. + traceEq. + (* match states *) + econstructor; eauto. + apply agree_set_other; auto with asmgen. + Simpl. unfold Genv.symbol_address. rewrite symbols_preserved. rewrite H. auto. + +- (* Mbuiltin *) + inv AT. monadInv H4. + exploit functions_transl; eauto. intro FN. + generalize (transf_function_no_overflow _ _ H3); intro NOOV. + exploit builtin_args_match; eauto. intros [vargs' [P Q]]. + exploit external_call_mem_extends; eauto. + intros [vres' [m2' [A [B [C D]]]]]. + left. econstructor; split. apply plus_one. + eapply exec_step_builtin. eauto. eauto. + eapply find_instr_tail; eauto. + erewrite <- sp_val by eauto. + eapply eval_builtin_args_preserved with (ge1 := ge); eauto. exact symbols_preserved. + eapply external_call_symbols_preserved; eauto. apply senv_preserved. + eauto. + econstructor; eauto. + instantiate (2 := tf); instantiate (1 := x). + unfold nextinstr. rewrite Pregmap.gss. + rewrite set_res_other. rewrite undef_regs_other_2. + rewrite <- H1. simpl. econstructor; eauto. + eapply code_tail_next_int; eauto. + rewrite preg_notin_charact. intros. auto with asmgen. + auto with asmgen. + apply agree_nextinstr. eapply agree_set_res; auto. + eapply agree_undef_regs; eauto. intros. rewrite undef_regs_other_2; auto. + congruence. + +- (* Mgoto *) + assert (f0 = f) by congruence. subst f0. + inv AT. monadInv H4. + exploit find_label_goto_label; eauto. intros [tc' [rs' [GOTO [AT2 INV]]]]. + left; exists (State rs' m'); split. + apply plus_one. econstructor; eauto. + eapply functions_transl; eauto. + eapply find_instr_tail; eauto. + simpl; eauto. + econstructor; eauto. + eapply agree_exten; eauto with asmgen. + congruence. + +- (* Mcond true *) + assert (f0 = f) by congruence. subst f0. + exploit eval_condition_lessdef. eapply preg_vals; eauto. eauto. eauto. intros EC. + left; eapply exec_straight_opt_steps_goto; eauto. + intros. simpl in TR. + exploit transl_cond_branch_correct; eauto. intros (rs' & jmp & A & B & C). + exists jmp; exists k; exists rs'. + split. eexact A. + split. apply agree_exten with rs0; auto with asmgen. + exact B. + +- (* Mcond false *) + exploit eval_condition_lessdef. eapply preg_vals; eauto. eauto. eauto. intros EC. + left; eapply exec_straight_steps; eauto. intros. simpl in TR. + exploit transl_cond_branch_correct; eauto. intros (rs' & jmp & A & B & C). + econstructor; split. + eapply exec_straight_opt_right. eexact A. apply exec_straight_one. eexact B. auto. + split. apply agree_exten with rs0; auto. intros. Simpl. + simpl; congruence. + +- (* Mjumptable *) + assert (f0 = f) by congruence. subst f0. + inv AT. monadInv H6. + exploit functions_transl; eauto. intro FN. + generalize (transf_function_no_overflow _ _ H5); intro NOOV. + exploit find_label_goto_label. eauto. eauto. + instantiate (2 := rs0#X16 <- Vundef #X17 <- Vundef). + Simpl. eauto. + eauto. + intros [tc' [rs' [A [B C]]]]. + exploit ireg_val; eauto. rewrite H. intros LD; inv LD. + left; econstructor; split. + apply plus_one. econstructor; eauto. + eapply find_instr_tail; eauto. + simpl. Simpl. rewrite <- H9. unfold Mach.label in H0; unfold label; rewrite H0. eexact A. + econstructor; eauto. + eapply agree_undef_regs; eauto. + simpl. intros. rewrite C; auto with asmgen. Simpl. + congruence. + +- (* Mreturn *) + assert (f0 = f) by congruence. subst f0. + inversion AT; subst. simpl in H6; monadInv H6. + assert (NOOV: list_length_z tf.(fn_code) <= Ptrofs.max_unsigned). + eapply transf_function_no_overflow; eauto. + exploit make_epilogue_correct; eauto. intros (rs1 & m1 & U & V & W & X & Y & Z). + exploit exec_straight_steps_2; eauto using functions_transl. + intros (ofs' & P & Q). + left; econstructor; split. + (* execution *) + eapply plus_right'. eapply exec_straight_exec; eauto. + econstructor. eexact P. eapply functions_transl; eauto. eapply find_instr_tail. eexact Q. + simpl. reflexivity. + traceEq. + (* match states *) + econstructor; eauto. + apply agree_set_other; auto with asmgen. + +- (* internal function *) + + exploit functions_translated; eauto. intros [tf [A B]]. monadInv B. + generalize EQ; intros EQ'. monadInv EQ'. + destruct (zlt Ptrofs.max_unsigned (list_length_z x0.(fn_code))); inversion EQ1. clear EQ1. subst x0. + unfold store_stack in *. + exploit Mem.alloc_extends. eauto. eauto. apply Z.le_refl. apply Z.le_refl. + intros [m1' [C D]]. + exploit Mem.storev_extends. eexact D. eexact H1. eauto. eauto. + intros [m2' [F G]]. + simpl chunk_of_type in F. + exploit Mem.storev_extends. eexact G. eexact H2. eauto. eauto. + intros [m3' [P Q]]. + change (chunk_of_type Tptr) with Mint64 in *. + (* Execution of function prologue *) + monadInv EQ0. rewrite transl_code'_transl_code in EQ1. + set (tfbody := Pallocframe (fn_stacksize f) (fn_link_ofs f) :: + storeptr RA XSP (fn_retaddr_ofs f) x0) in *. + set (tf := {| fn_sig := Mach.fn_sig f; fn_code := tfbody |}) in *. + set (rs2 := nextinstr (rs0#X29 <- (parent_sp s) #SP <- sp #X16 <- Vundef)). + exploit (storeptr_correct tge tf XSP (fn_retaddr_ofs f) RA x0 m2' m3' rs2). + simpl preg_of_iregsp. change (rs2 X30) with (rs0 X30). rewrite ATLR. + change (rs2 X2) with sp. eexact P. + simpl; congruence. congruence. + intros (rs3 & U & V). + assert (EXEC_PROLOGUE: + exec_straight tge tf + tf.(fn_code) rs0 m' + x0 rs3 m3'). + { change (fn_code tf) with tfbody; unfold tfbody. + apply exec_straight_step with rs2 m2'. + unfold exec_instr. rewrite C. fold sp. + rewrite <- (sp_val _ _ _ AG). rewrite F. reflexivity. + reflexivity. + eexact U. } + exploit exec_straight_steps_2; eauto using functions_transl. omega. constructor. + intros (ofs' & X & Y). + left; exists (State rs3 m3'); split. + eapply exec_straight_steps_1; eauto. omega. constructor. + econstructor; eauto. + rewrite X; econstructor; eauto. + apply agree_exten with rs2; eauto with asmgen. + unfold rs2. + apply agree_nextinstr. apply agree_set_other; auto with asmgen. + apply agree_change_sp with (parent_sp s). + apply agree_undef_regs with rs0. auto. +Local Transparent destroyed_at_function_entry. simpl. + simpl; intros; Simpl. + unfold sp; congruence. + intros. rewrite V by auto with asmgen. reflexivity. + +- (* external function *) + exploit functions_translated; eauto. + intros [tf [A B]]. simpl in B. inv B. + exploit extcall_arguments_match; eauto. + intros [args' [C D]]. + exploit external_call_mem_extends; eauto. + intros [res' [m2' [P [Q [R S]]]]]. + left; econstructor; split. + apply plus_one. eapply exec_step_external; eauto. + eapply external_call_symbols_preserved; eauto. apply senv_preserved. + econstructor; eauto. + unfold loc_external_result. apply agree_set_other; auto. apply agree_set_pair; auto. + apply agree_undef_caller_save_regs; auto. + +- (* return *) + inv STACKS. simpl in *. + right. split. omega. split. auto. + rewrite <- ATPC in H5. + econstructor; eauto. congruence. +Qed. + +Lemma transf_initial_states: + forall st1, Mach.initial_state prog st1 -> + exists st2, Asm.initial_state tprog st2 /\ match_states st1 st2. +Proof. + intros. inversion H. unfold ge0 in *. + econstructor; split. + econstructor. + eapply (Genv.init_mem_transf_partial TRANSF); eauto. + replace (Genv.symbol_address (Genv.globalenv tprog) (prog_main tprog) Ptrofs.zero) + with (Vptr fb Ptrofs.zero). + econstructor; eauto. + constructor. + apply Mem.extends_refl. + split. auto. simpl. unfold Vnullptr; destruct Archi.ptr64; congruence. + intros. rewrite Regmap.gi. auto. + unfold Genv.symbol_address. + rewrite (match_program_main TRANSF). + rewrite symbols_preserved. + unfold ge; rewrite H1. auto. +Qed. + +Lemma transf_final_states: + forall st1 st2 r, + match_states st1 st2 -> Mach.final_state st1 r -> Asm.final_state st2 r. +Proof. + intros. inv H0. inv H. constructor. assumption. + compute in H1. inv H1. + generalize (preg_val _ _ _ R0 AG). rewrite H2. intros LD; inv LD. auto. +Qed. + +Theorem transf_program_correct: + forward_simulation (Mach.semantics return_address_offset prog) (Asm.semantics tprog). +Proof. + eapply forward_simulation_star with (measure := measure). + apply senv_preserved. + eexact transf_initial_states. + eexact transf_final_states. + exact step_simulation. +Qed. + +End PRESERVATION. diff --git a/aarch64/Asmgenproof1.v b/aarch64/Asmgenproof1.v new file mode 100644 index 00000000..6d44bcc8 --- /dev/null +++ b/aarch64/Asmgenproof1.v @@ -0,0 +1,1836 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Correctness proof for AArch64 code generation: auxiliary results. *) + +Require Import Recdef Coqlib Zwf Zbits. +Require Import Maps Errors AST Integers Floats Values Memory Globalenvs. +Require Import Op Locations Mach Asm Conventions. +Require Import Asmgen. +Require Import Asmgenproof0. + +Local Transparent Archi.ptr64. + +(** Properties of registers *) + +Lemma preg_of_iregsp_not_PC: forall r, preg_of_iregsp r <> PC. +Proof. + destruct r; simpl; congruence. +Qed. +Hint Resolve preg_of_iregsp_not_PC: asmgen. + +Lemma preg_of_not_X16: forall r, preg_of r <> X16. +Proof. + destruct r; simpl; congruence. +Qed. + +Lemma ireg_of_not_X16: forall r x, ireg_of r = OK x -> x <> X16. +Proof. + unfold ireg_of; intros. destruct (preg_of r) eqn:E; inv H. + red; intros; subst x. elim (preg_of_not_X16 r); auto. +Qed. + +Lemma ireg_of_not_X16': forall r x, ireg_of r = OK x -> IR x <> IR X16. +Proof. + intros. apply ireg_of_not_X16 in H. congruence. +Qed. + +Hint Resolve preg_of_not_X16 ireg_of_not_X16 ireg_of_not_X16': asmgen. + +(** Useful simplification tactic *) + + +Ltac Simplif := + ((rewrite nextinstr_inv by eauto with asmgen) + || (rewrite nextinstr_inv1 by eauto with asmgen) + || (rewrite Pregmap.gss) + || (rewrite nextinstr_pc) + || (rewrite Pregmap.gso by eauto with asmgen)); auto with asmgen. + +Ltac Simpl := repeat Simplif. + +(** * Correctness of ARM constructor functions *) + +Section CONSTRUCTORS. + +Variable ge: genv. +Variable fn: function. + +(** Decomposition of integer literals *) + +Inductive wf_decomposition: list (Z * Z) -> Prop := + | wf_decomp_nil: + wf_decomposition nil + | wf_decomp_cons: forall m n p l, + n = Zzero_ext 16 m -> 0 <= p -> wf_decomposition l -> + wf_decomposition ((n, p) :: l). + +Lemma decompose_int_wf: + forall N n p, 0 <= p -> wf_decomposition (decompose_int N n p). +Proof. +Local Opaque Zzero_ext. + induction N as [ | N]; simpl; intros. +- constructor. +- set (frag := Zzero_ext 16 (Z.shiftr n p)) in *. destruct (Z.eqb frag 0). ++ apply IHN. omega. ++ econstructor. reflexivity. omega. apply IHN; omega. +Qed. + +Fixpoint recompose_int (accu: Z) (l: list (Z * Z)) : Z := + match l with + | nil => accu + | (n, p) :: l => recompose_int (Zinsert accu n p 16) l + end. + +Lemma decompose_int_correct: + forall N n p accu, + 0 <= p -> + (forall i, p <= i -> Z.testbit accu i = false) -> + (forall i, 0 <= i < p + Z.of_nat N * 16 -> + Z.testbit (recompose_int accu (decompose_int N n p)) i = + if zlt i p then Z.testbit accu i else Z.testbit n i). +Proof. + induction N as [ | N]; intros until accu; intros PPOS ABOVE i RANGE. +- simpl. rewrite zlt_true; auto. xomega. +- rewrite inj_S in RANGE. simpl. + set (frag := Zzero_ext 16 (Z.shiftr n p)). + assert (FRAG: forall i, p <= i < p + 16 -> Z.testbit n i = Z.testbit frag (i - p)). + { unfold frag; intros. rewrite Zzero_ext_spec by omega. rewrite zlt_true by omega. + rewrite Z.shiftr_spec by omega. f_equal; omega. } + destruct (Z.eqb_spec frag 0). ++ rewrite IHN. +* destruct (zlt i p). rewrite zlt_true by omega. auto. + destruct (zlt i (p + 16)); auto. + rewrite ABOVE by omega. rewrite FRAG by omega. rewrite e, Z.testbit_0_l. auto. +* omega. +* intros; apply ABOVE; omega. +* xomega. ++ simpl. rewrite IHN. +* destruct (zlt i (p + 16)). +** rewrite Zinsert_spec by omega. unfold proj_sumbool. + rewrite zlt_true by omega. + destruct (zlt i p). + rewrite zle_false by omega. auto. + rewrite zle_true by omega. simpl. symmetry; apply FRAG; omega. +** rewrite Z.ldiff_spec, Z.shiftl_spec by omega. + change 65535 with (two_p 16 - 1). rewrite Ztestbit_two_p_m1 by omega. + rewrite zlt_false by omega. rewrite zlt_false by omega. apply andb_true_r. +* omega. +* intros. rewrite Zinsert_spec by omega. unfold proj_sumbool. + rewrite zle_true by omega. rewrite zlt_false by omega. simpl. + apply ABOVE. omega. +* xomega. +Qed. + +Corollary decompose_int_eqmod: forall N n, + eqmod (two_power_nat (N * 16)%nat) (recompose_int 0 (decompose_int N n 0)) n. +Proof. + intros; apply eqmod_same_bits; intros. + rewrite decompose_int_correct. apply zlt_false; omega. + omega. intros; apply Z.testbit_0_l. xomega. +Qed. + +Corollary decompose_notint_eqmod: forall N n, + eqmod (two_power_nat (N * 16)%nat) + (Z.lnot (recompose_int 0 (decompose_int N (Z.lnot n) 0))) n. +Proof. + intros; apply eqmod_same_bits; intros. + rewrite Z.lnot_spec, decompose_int_correct. + rewrite zlt_false by omega. rewrite Z.lnot_spec by omega. apply negb_involutive. + omega. intros; apply Z.testbit_0_l. xomega. omega. +Qed. + +Lemma negate_decomposition_wf: + forall l, wf_decomposition l -> wf_decomposition (negate_decomposition l). +Proof. + induction 1; simpl; econstructor; auto. + instantiate (1 := (Z.lnot m)). + apply equal_same_bits; intros. + rewrite H. change 65535 with (two_p 16 - 1). + rewrite Z.lxor_spec, !Zzero_ext_spec, Z.lnot_spec, Ztestbit_two_p_m1 by omega. + destruct (zlt i 16). + apply xorb_true_r. + auto. +Qed. + +Lemma Zinsert_eqmod: + forall n x1 x2 y p l, 0 <= p -> 0 <= l -> + eqmod (two_power_nat n) x1 x2 -> + eqmod (two_power_nat n) (Zinsert x1 y p l) (Zinsert x2 y p l). +Proof. + intros. apply eqmod_same_bits; intros. rewrite ! Zinsert_spec by omega. + destruct (zle p i && zlt i (p + l)); auto. + apply same_bits_eqmod with n; auto. +Qed. + +Lemma Zinsert_0_l: + forall y p l, + 0 <= p -> 0 <= l -> + Z.shiftl (Zzero_ext l y) p = Zinsert 0 (Zzero_ext l y) p l. +Proof. + intros. apply equal_same_bits; intros. + rewrite Zinsert_spec by omega. unfold proj_sumbool. + destruct (zlt i p); [rewrite zle_false by omega|rewrite zle_true by omega]; simpl. +- rewrite Z.testbit_0_l, Z.shiftl_spec_low by auto. auto. +- rewrite Z.shiftl_spec by omega. + destruct (zlt i (p + l)); auto. + rewrite Zzero_ext_spec, zlt_false, Z.testbit_0_l by omega. auto. +Qed. + +Lemma recompose_int_negated: + forall l, wf_decomposition l -> + forall accu, recompose_int (Z.lnot accu) (negate_decomposition l) = Z.lnot (recompose_int accu l). +Proof. + induction 1; intros accu; simpl. +- auto. +- rewrite <- IHwf_decomposition. f_equal. apply equal_same_bits; intros. + rewrite Z.lnot_spec, ! Zinsert_spec, Z.lxor_spec, Z.lnot_spec by omega. + unfold proj_sumbool. + destruct (zle p i); simpl; auto. + destruct (zlt i (p + 16)); simpl; auto. + change 65535 with (two_p 16 - 1). + rewrite Ztestbit_two_p_m1 by omega. rewrite zlt_true by omega. + apply xorb_true_r. +Qed. + +Lemma exec_loadimm_k_w: + forall (rd: ireg) k m l, + wf_decomposition l -> + forall (rs: regset) accu, + rs#rd = Vint (Int.repr accu) -> + exists rs', + exec_straight_opt ge fn (loadimm_k W rd l k) rs m k rs' m + /\ rs'#rd = Vint (Int.repr (recompose_int accu l)) + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + induction 1; intros rs accu ACCU; simpl. +- exists rs; split. apply exec_straight_opt_refl. auto. +- destruct (IHwf_decomposition + (nextinstr (rs#rd <- (insert_in_int rs#rd n p 16))) + (Zinsert accu n p 16)) + as (rs' & P & Q & R). + Simpl. rewrite ACCU. simpl. f_equal. apply Int.eqm_samerepr. + apply Zinsert_eqmod. auto. omega. apply Int.eqm_sym; apply Int.eqm_unsigned_repr. + exists rs'; split. + eapply exec_straight_opt_step_opt. simpl; eauto. auto. exact P. + split. exact Q. intros; Simpl. rewrite R by auto. Simpl. +Qed. + +Lemma exec_loadimm_z_w: + forall rd l k rs m, + wf_decomposition l -> + exists rs', + exec_straight ge fn (loadimm_z W rd l k) rs m k rs' m + /\ rs'#rd = Vint (Int.repr (recompose_int 0 l)) + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold loadimm_z; destruct 1. +- econstructor; split. + apply exec_straight_one. simpl; eauto. auto. + split. Simpl. + intros; Simpl. +- set (accu0 := Zinsert 0 n p 16). + set (rs1 := nextinstr (rs#rd <- (Vint (Int.repr accu0)))). + destruct (exec_loadimm_k_w rd k m l H1 rs1 accu0) as (rs2 & P & Q & R); auto. + unfold rs1; Simpl. + exists rs2; split. + eapply exec_straight_opt_step; eauto. + simpl. unfold rs1. do 5 f_equal. unfold accu0. rewrite H. apply Zinsert_0_l; omega. + reflexivity. + split. exact Q. + intros. rewrite R by auto. unfold rs1; Simpl. +Qed. + +Lemma exec_loadimm_n_w: + forall rd l k rs m, + wf_decomposition l -> + exists rs', + exec_straight ge fn (loadimm_n W rd l k) rs m k rs' m + /\ rs'#rd = Vint (Int.repr (Z.lnot (recompose_int 0 l))) + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold loadimm_n; destruct 1. +- econstructor; split. + apply exec_straight_one. simpl; eauto. auto. + split. Simpl. + intros; Simpl. +- set (accu0 := Z.lnot (Zinsert 0 n p 16)). + set (rs1 := nextinstr (rs#rd <- (Vint (Int.repr accu0)))). + destruct (exec_loadimm_k_w rd k m (negate_decomposition l) + (negate_decomposition_wf l H1) + rs1 accu0) as (rs2 & P & Q & R). + unfold rs1; Simpl. + exists rs2; split. + eapply exec_straight_opt_step; eauto. + simpl. unfold rs1. do 5 f_equal. + unfold accu0. f_equal. rewrite H. apply Zinsert_0_l; omega. + reflexivity. + split. unfold accu0 in Q; rewrite recompose_int_negated in Q by auto. exact Q. + intros. rewrite R by auto. unfold rs1; Simpl. +Qed. + +Lemma exec_loadimm32: + forall rd n k rs m, + exists rs', + exec_straight ge fn (loadimm32 rd n k) rs m k rs' m + /\ rs'#rd = Vint n + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold loadimm32, loadimm; intros. + destruct (is_logical_imm32 n). +- econstructor; split. + apply exec_straight_one. simpl; eauto. auto. + split. Simpl. rewrite Int.repr_unsigned, Int.or_zero_l; auto. + intros; Simpl. +- set (dz := decompose_int 2%nat (Int.unsigned n) 0). + set (dn := decompose_int 2%nat (Z.lnot (Int.unsigned n)) 0). + assert (A: Int.repr (recompose_int 0 dz) = n). + { transitivity (Int.repr (Int.unsigned n)). + apply Int.eqm_samerepr. apply decompose_int_eqmod. + apply Int.repr_unsigned. } + assert (B: Int.repr (Z.lnot (recompose_int 0 dn)) = n). + { transitivity (Int.repr (Int.unsigned n)). + apply Int.eqm_samerepr. apply decompose_notint_eqmod. + apply Int.repr_unsigned. } + destruct Nat.leb. ++ rewrite <- A. apply exec_loadimm_z_w. apply decompose_int_wf; omega. ++ rewrite <- B. apply exec_loadimm_n_w. apply decompose_int_wf; omega. +Qed. + +Lemma exec_loadimm_k_x: + forall (rd: ireg) k m l, + wf_decomposition l -> + forall (rs: regset) accu, + rs#rd = Vlong (Int64.repr accu) -> + exists rs', + exec_straight_opt ge fn (loadimm_k X rd l k) rs m k rs' m + /\ rs'#rd = Vlong (Int64.repr (recompose_int accu l)) + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + induction 1; intros rs accu ACCU; simpl. +- exists rs; split. apply exec_straight_opt_refl. auto. +- destruct (IHwf_decomposition + (nextinstr (rs#rd <- (insert_in_long rs#rd n p 16))) + (Zinsert accu n p 16)) + as (rs' & P & Q & R). + Simpl. rewrite ACCU. simpl. f_equal. apply Int64.eqm_samerepr. + apply Zinsert_eqmod. auto. omega. apply Int64.eqm_sym; apply Int64.eqm_unsigned_repr. + exists rs'; split. + eapply exec_straight_opt_step_opt. simpl; eauto. auto. exact P. + split. exact Q. intros; Simpl. rewrite R by auto. Simpl. +Qed. + +Lemma exec_loadimm_z_x: + forall rd l k rs m, + wf_decomposition l -> + exists rs', + exec_straight ge fn (loadimm_z X rd l k) rs m k rs' m + /\ rs'#rd = Vlong (Int64.repr (recompose_int 0 l)) + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold loadimm_z; destruct 1. +- econstructor; split. + apply exec_straight_one. simpl; eauto. auto. + split. Simpl. + intros; Simpl. +- set (accu0 := Zinsert 0 n p 16). + set (rs1 := nextinstr (rs#rd <- (Vlong (Int64.repr accu0)))). + destruct (exec_loadimm_k_x rd k m l H1 rs1 accu0) as (rs2 & P & Q & R); auto. + unfold rs1; Simpl. + exists rs2; split. + eapply exec_straight_opt_step; eauto. + simpl. unfold rs1. do 5 f_equal. unfold accu0. rewrite H. apply Zinsert_0_l; omega. + reflexivity. + split. exact Q. + intros. rewrite R by auto. unfold rs1; Simpl. +Qed. + +Lemma exec_loadimm_n_x: + forall rd l k rs m, + wf_decomposition l -> + exists rs', + exec_straight ge fn (loadimm_n X rd l k) rs m k rs' m + /\ rs'#rd = Vlong (Int64.repr (Z.lnot (recompose_int 0 l))) + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold loadimm_n; destruct 1. +- econstructor; split. + apply exec_straight_one. simpl; eauto. auto. + split. Simpl. + intros; Simpl. +- set (accu0 := Z.lnot (Zinsert 0 n p 16)). + set (rs1 := nextinstr (rs#rd <- (Vlong (Int64.repr accu0)))). + destruct (exec_loadimm_k_x rd k m (negate_decomposition l) + (negate_decomposition_wf l H1) + rs1 accu0) as (rs2 & P & Q & R). + unfold rs1; Simpl. + exists rs2; split. + eapply exec_straight_opt_step; eauto. + simpl. unfold rs1. do 5 f_equal. + unfold accu0. f_equal. rewrite H. apply Zinsert_0_l; omega. + reflexivity. + split. unfold accu0 in Q; rewrite recompose_int_negated in Q by auto. exact Q. + intros. rewrite R by auto. unfold rs1; Simpl. +Qed. + +Lemma exec_loadimm64: + forall rd n k rs m, + exists rs', + exec_straight ge fn (loadimm64 rd n k) rs m k rs' m + /\ rs'#rd = Vlong n + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold loadimm64, loadimm; intros. + destruct (is_logical_imm64 n). +- econstructor; split. + apply exec_straight_one. simpl; eauto. auto. + split. Simpl. rewrite Int64.repr_unsigned, Int64.or_zero_l; auto. + intros; Simpl. +- set (dz := decompose_int 4%nat (Int64.unsigned n) 0). + set (dn := decompose_int 4%nat (Z.lnot (Int64.unsigned n)) 0). + assert (A: Int64.repr (recompose_int 0 dz) = n). + { transitivity (Int64.repr (Int64.unsigned n)). + apply Int64.eqm_samerepr. apply decompose_int_eqmod. + apply Int64.repr_unsigned. } + assert (B: Int64.repr (Z.lnot (recompose_int 0 dn)) = n). + { transitivity (Int64.repr (Int64.unsigned n)). + apply Int64.eqm_samerepr. apply decompose_notint_eqmod. + apply Int64.repr_unsigned. } + destruct Nat.leb. ++ rewrite <- A. apply exec_loadimm_z_x. apply decompose_int_wf; omega. ++ rewrite <- B. apply exec_loadimm_n_x. apply decompose_int_wf; omega. +Qed. + +(** Add immediate *) + +Lemma exec_addimm_aux_32: + forall (insn: iregsp -> iregsp -> Z -> instruction) (sem: val -> val -> val), + (forall rd r1 n rs m, + exec_instr ge fn (insn rd r1 n) rs m = + Next (nextinstr (rs#rd <- (sem rs#r1 (Vint (Int.repr n))))) m) -> + (forall v n1 n2, sem (sem v (Vint n1)) (Vint n2) = sem v (Vint (Int.add n1 n2))) -> + forall rd r1 n k rs m, + exists rs', + exec_straight ge fn (addimm_aux insn rd r1 (Int.unsigned n) k) rs m k rs' m + /\ rs'#rd = sem rs#r1 (Vint n) + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros insn sem SEM ASSOC; intros. unfold addimm_aux. + set (nlo := Zzero_ext 12 (Int.unsigned n)). set (nhi := Int.unsigned n - nlo). + assert (E: Int.unsigned n = nhi + nlo) by (unfold nhi; omega). + rewrite <- (Int.repr_unsigned n). + destruct (Z.eqb_spec nhi 0); [|destruct (Z.eqb_spec nlo 0)]. +- econstructor; split. apply exec_straight_one. apply SEM. Simpl. + split. Simpl. do 3 f_equal; omega. + intros; Simpl. +- econstructor; split. apply exec_straight_one. apply SEM. Simpl. + split. Simpl. do 3 f_equal; omega. + intros; Simpl. +- econstructor; split. eapply exec_straight_two. + apply SEM. apply SEM. Simpl. Simpl. + split. Simpl. rewrite ASSOC. do 2 f_equal. apply Int.eqm_samerepr. + rewrite E. auto with ints. + intros; Simpl. +Qed. + +Lemma exec_addimm32: + forall rd r1 n k rs m, + r1 <> X16 -> + exists rs', + exec_straight ge fn (addimm32 rd r1 n k) rs m k rs' m + /\ rs'#rd = Val.add rs#r1 (Vint n) + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros. unfold addimm32. set (nn := Int.neg n). + destruct (Int.eq n (Int.zero_ext 24 n)); [| destruct (Int.eq nn (Int.zero_ext 24 nn))]. +- apply exec_addimm_aux_32 with (sem := Val.add). auto. intros; apply Val.add_assoc. +- rewrite <- Val.sub_opp_add. + apply exec_addimm_aux_32 with (sem := Val.sub). auto. + intros. rewrite ! Val.sub_add_opp, Val.add_assoc. rewrite Int.neg_add_distr. auto. +- destruct (Int.lt n Int.zero). ++ rewrite <- Val.sub_opp_add; fold nn. + edestruct (exec_loadimm32 X16 nn) as (rs1 & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. eapply exec_straight_one. simpl; eauto. auto. + split. Simpl. rewrite B, C; eauto with asmgen. + intros; Simpl. ++ edestruct (exec_loadimm32 X16 n) as (rs1 & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. eapply exec_straight_one. simpl; eauto. auto. + split. Simpl. rewrite B, C; eauto with asmgen. + intros; Simpl. +Qed. + +Lemma exec_addimm_aux_64: + forall (insn: iregsp -> iregsp -> Z -> instruction) (sem: val -> val -> val), + (forall rd r1 n rs m, + exec_instr ge fn (insn rd r1 n) rs m = + Next (nextinstr (rs#rd <- (sem rs#r1 (Vlong (Int64.repr n))))) m) -> + (forall v n1 n2, sem (sem v (Vlong n1)) (Vlong n2) = sem v (Vlong (Int64.add n1 n2))) -> + forall rd r1 n k rs m, + exists rs', + exec_straight ge fn (addimm_aux insn rd r1 (Int64.unsigned n) k) rs m k rs' m + /\ rs'#rd = sem rs#r1 (Vlong n) + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros insn sem SEM ASSOC; intros. unfold addimm_aux. + set (nlo := Zzero_ext 12 (Int64.unsigned n)). set (nhi := Int64.unsigned n - nlo). + assert (E: Int64.unsigned n = nhi + nlo) by (unfold nhi; omega). + rewrite <- (Int64.repr_unsigned n). + destruct (Z.eqb_spec nhi 0); [|destruct (Z.eqb_spec nlo 0)]. +- econstructor; split. apply exec_straight_one. apply SEM. Simpl. + split. Simpl. do 3 f_equal; omega. + intros; Simpl. +- econstructor; split. apply exec_straight_one. apply SEM. Simpl. + split. Simpl. do 3 f_equal; omega. + intros; Simpl. +- econstructor; split. eapply exec_straight_two. + apply SEM. apply SEM. Simpl. Simpl. + split. Simpl. rewrite ASSOC. do 2 f_equal. apply Int64.eqm_samerepr. + rewrite E. auto with ints. + intros; Simpl. +Qed. + +Lemma exec_addimm64: + forall rd r1 n k rs m, + preg_of_iregsp r1 <> X16 -> + exists rs', + exec_straight ge fn (addimm64 rd r1 n k) rs m k rs' m + /\ rs'#rd = Val.addl rs#r1 (Vlong n) + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros. + unfold addimm64. set (nn := Int64.neg n). + destruct (Int64.eq n (Int64.zero_ext 24 n)); [| destruct (Int64.eq nn (Int64.zero_ext 24 nn))]. +- apply exec_addimm_aux_64 with (sem := Val.addl). auto. intros; apply Val.addl_assoc. +- rewrite <- Val.subl_opp_addl. + apply exec_addimm_aux_64 with (sem := Val.subl). auto. + intros. rewrite ! Val.subl_addl_opp, Val.addl_assoc. rewrite Int64.neg_add_distr. auto. +- destruct (Int64.lt n Int64.zero). ++ rewrite <- Val.subl_opp_addl; fold nn. + edestruct (exec_loadimm64 X16 nn) as (rs1 & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. eapply exec_straight_one. simpl; eauto. Simpl. + split. Simpl. rewrite B, C; eauto with asmgen. simpl. rewrite Int64.shl'_zero. auto. + intros; Simpl. ++ edestruct (exec_loadimm64 X16 n) as (rs1 & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. eapply exec_straight_one. simpl; eauto. Simpl. + split. Simpl. rewrite B, C; eauto with asmgen. simpl. rewrite Int64.shl'_zero. auto. + intros; Simpl. +Qed. + +(** Logical immediate *) + +Lemma exec_logicalimm32: + forall (insn1: ireg -> ireg0 -> Z -> instruction) + (insn2: ireg -> ireg0 -> ireg -> shift_op -> instruction) + (sem: val -> val -> val), + (forall rd r1 n rs m, + exec_instr ge fn (insn1 rd r1 n) rs m = + Next (nextinstr (rs#rd <- (sem rs##r1 (Vint (Int.repr n))))) m) -> + (forall rd r1 r2 s rs m, + exec_instr ge fn (insn2 rd r1 r2 s) rs m = + Next (nextinstr (rs#rd <- (sem rs##r1 (eval_shift_op_int rs#r2 s)))) m) -> + forall rd r1 n k rs m, + r1 <> X16 -> + exists rs', + exec_straight ge fn (logicalimm32 insn1 insn2 rd r1 n k) rs m k rs' m + /\ rs'#rd = sem rs#r1 (Vint n) + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros until sem; intros SEM1 SEM2; intros. unfold logicalimm32. + destruct (is_logical_imm32 n). +- econstructor; split. + apply exec_straight_one. apply SEM1. reflexivity. + split. Simpl. rewrite Int.repr_unsigned; auto. intros; Simpl. +- edestruct (exec_loadimm32 X16 n) as (rs1 & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. + apply exec_straight_one. apply SEM2. reflexivity. + split. Simpl. f_equal; auto. apply C; auto with asmgen. + intros; Simpl. +Qed. + +Lemma exec_logicalimm64: + forall (insn1: ireg -> ireg0 -> Z -> instruction) + (insn2: ireg -> ireg0 -> ireg -> shift_op -> instruction) + (sem: val -> val -> val), + (forall rd r1 n rs m, + exec_instr ge fn (insn1 rd r1 n) rs m = + Next (nextinstr (rs#rd <- (sem rs###r1 (Vlong (Int64.repr n))))) m) -> + (forall rd r1 r2 s rs m, + exec_instr ge fn (insn2 rd r1 r2 s) rs m = + Next (nextinstr (rs#rd <- (sem rs###r1 (eval_shift_op_long rs#r2 s)))) m) -> + forall rd r1 n k rs m, + r1 <> X16 -> + exists rs', + exec_straight ge fn (logicalimm64 insn1 insn2 rd r1 n k) rs m k rs' m + /\ rs'#rd = sem rs#r1 (Vlong n) + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros until sem; intros SEM1 SEM2; intros. unfold logicalimm64. + destruct (is_logical_imm64 n). +- econstructor; split. + apply exec_straight_one. apply SEM1. reflexivity. + split. Simpl. rewrite Int64.repr_unsigned. auto. intros; Simpl. +- edestruct (exec_loadimm64 X16 n) as (rs1 & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. + apply exec_straight_one. apply SEM2. reflexivity. + split. Simpl. f_equal; auto. apply C; auto with asmgen. + intros; Simpl. +Qed. + +(** Load address of symbol *) + +Lemma exec_loadsymbol: forall rd s ofs k rs m, + rd <> X16 \/ Archi.pic_code tt = false -> + exists rs', + exec_straight ge fn (loadsymbol rd s ofs k) rs m k rs' m + /\ rs'#rd = Genv.symbol_address ge s ofs + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + unfold loadsymbol; intros. destruct (Archi.pic_code tt). +- predSpec Ptrofs.eq Ptrofs.eq_spec ofs Ptrofs.zero. ++ subst ofs. econstructor; split. + apply exec_straight_one; [simpl; eauto | reflexivity]. + split. Simpl. intros; Simpl. ++ exploit exec_addimm64. instantiate (1 := rd). simpl. destruct H; congruence. + intros (rs1 & A & B & C). + econstructor; split. + econstructor. simpl; eauto. auto. eexact A. + split. simpl in B; rewrite B. Simpl. + rewrite <- Genv.shift_symbol_address_64 by auto. + rewrite Ptrofs.add_zero_l, Ptrofs.of_int64_to_int64 by auto. auto. + intros. rewrite C by auto. Simpl. +- econstructor; split. + eapply exec_straight_two. simpl; eauto. simpl; eauto. auto. auto. + split. Simpl. rewrite symbol_high_low; auto. + intros; Simpl. +Qed. + +(** Shifted operands *) + +Remark transl_shift_not_none: + forall s a, transl_shift s a <> SOnone. +Proof. + destruct s; intros; simpl; congruence. +Qed. + +Remark or_zero_eval_shift_op_int: + forall v s, s <> SOnone -> Val.or (Vint Int.zero) (eval_shift_op_int v s) = eval_shift_op_int v s. +Proof. + intros; destruct s; try congruence; destruct v; auto; simpl; + destruct (Int.ltu n Int.iwordsize); auto; rewrite Int.or_zero_l; auto. +Qed. + +Remark or_zero_eval_shift_op_long: + forall v s, s <> SOnone -> Val.orl (Vlong Int64.zero) (eval_shift_op_long v s) = eval_shift_op_long v s. +Proof. + intros; destruct s; try congruence; destruct v; auto; simpl; + destruct (Int.ltu n Int64.iwordsize'); auto; rewrite Int64.or_zero_l; auto. +Qed. + +Remark add_zero_eval_shift_op_long: + forall v s, s <> SOnone -> Val.addl (Vlong Int64.zero) (eval_shift_op_long v s) = eval_shift_op_long v s. +Proof. + intros; destruct s; try congruence; destruct v; auto; simpl; + destruct (Int.ltu n Int64.iwordsize'); auto; rewrite Int64.add_zero_l; auto. +Qed. + +Lemma transl_eval_shift: forall s v (a: amount32), + eval_shift_op_int v (transl_shift s a) = eval_shift s v a. +Proof. + intros. destruct s; simpl; auto. +Qed. + +Lemma transl_eval_shift': forall s v (a: amount32), + Val.or (Vint Int.zero) (eval_shift_op_int v (transl_shift s a)) = eval_shift s v a. +Proof. + intros. rewrite or_zero_eval_shift_op_int by (apply transl_shift_not_none). + apply transl_eval_shift. +Qed. + +Lemma transl_eval_shiftl: forall s v (a: amount64), + eval_shift_op_long v (transl_shift s a) = eval_shiftl s v a. +Proof. + intros. destruct s; simpl; auto. +Qed. + +Lemma transl_eval_shiftl': forall s v (a: amount64), + Val.orl (Vlong Int64.zero) (eval_shift_op_long v (transl_shift s a)) = eval_shiftl s v a. +Proof. + intros. rewrite or_zero_eval_shift_op_long by (apply transl_shift_not_none). + apply transl_eval_shiftl. +Qed. + +Lemma transl_eval_shiftl'': forall s v (a: amount64), + Val.addl (Vlong Int64.zero) (eval_shift_op_long v (transl_shift s a)) = eval_shiftl s v a. +Proof. + intros. rewrite add_zero_eval_shift_op_long by (apply transl_shift_not_none). + apply transl_eval_shiftl. +Qed. + +(** Zero- and Sign- extensions *) + +Lemma exec_move_extended_base: forall rd r1 ex k rs m, + exists rs', + exec_straight ge fn (move_extended_base rd r1 ex k) rs m k rs' m + /\ rs' rd = match ex with Xsgn32 => Val.longofint rs#r1 | Xuns32 => Val.longofintu rs#r1 end + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold move_extended_base; destruct ex; econstructor; + (split; [apply exec_straight_one; [simpl;eauto|auto] | split; [Simpl|intros;Simpl]]). +Qed. + +Lemma exec_move_extended: forall rd r1 ex (a: amount64) k rs m, + exists rs', + exec_straight ge fn (move_extended rd r1 ex a k) rs m k rs' m + /\ rs' rd = Op.eval_extend ex rs#r1 a + /\ forall r, r <> PC -> r <> rd -> rs'#r = rs#r. +Proof. + unfold move_extended; intros. predSpec Int.eq Int.eq_spec a Int.zero. +- exploit (exec_move_extended_base rd r1 ex). intros (rs' & A & B & C). + exists rs'; split. eexact A. split. unfold Op.eval_extend. rewrite H. rewrite B. + destruct ex, (rs r1); simpl; auto; rewrite Int64.shl'_zero; auto. + auto. +- Local Opaque Val.addl. + exploit (exec_move_extended_base rd r1 ex). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. + unfold exec_instr. change (SOlsl a) with (transl_shift Slsl a). rewrite transl_eval_shiftl''. eauto. auto. + split. Simpl. rewrite B. auto. + intros; Simpl. +Qed. + +Lemma exec_arith_extended: + forall (sem: val -> val -> val) + (insnX: iregsp -> iregsp -> ireg -> extend_op -> instruction) + (insnS: ireg -> ireg0 -> ireg -> shift_op -> instruction), + (forall rd r1 r2 x rs m, + exec_instr ge fn (insnX rd r1 r2 x) rs m = + Next (nextinstr (rs#rd <- (sem rs#r1 (eval_extend rs#r2 x)))) m) -> + (forall rd r1 r2 s rs m, + exec_instr ge fn (insnS rd r1 r2 s) rs m = + Next (nextinstr (rs#rd <- (sem rs###r1 (eval_shift_op_long rs#r2 s)))) m) -> + forall (rd r1 r2: ireg) (ex: extension) (a: amount64) (k: code) rs m, + r1 <> X16 -> + exists rs', + exec_straight ge fn (arith_extended insnX insnS rd r1 r2 ex a k) rs m k rs' m + /\ rs'#rd = sem rs#r1 (Op.eval_extend ex rs#r2 a) + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros sem insnX insnS EX ES; intros. unfold arith_extended. destruct (Int.ltu a (Int.repr 5)). +- econstructor; split. + apply exec_straight_one. rewrite EX; eauto. auto. + split. Simpl. f_equal. destruct ex; auto. + intros; Simpl. +- exploit (exec_move_extended_base X16 r2 ex). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. + rewrite ES. eauto. auto. + split. Simpl. unfold ir0x. rewrite C by eauto with asmgen. f_equal. + rewrite B. destruct ex; auto. + intros; Simpl. +Qed. + +(** Extended right shift *) + +Lemma exec_shrx32: forall (rd r1: ireg) (n: int) k v (rs: regset) m, + Val.shrx rs#r1 (Vint n) = Some v -> + r1 <> X16 -> + exists rs', + exec_straight ge fn (shrx32 rd r1 n k) rs m k rs' m + /\ rs'#rd = v + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + unfold shrx32; intros. apply Val.shrx_shr_2 in H. + destruct (Int.eq n Int.zero) eqn:E. +- econstructor; split. apply exec_straight_one; [simpl;eauto|auto]. + split. Simpl. subst v; auto. intros; Simpl. +- econstructor; split. eapply exec_straight_three. + unfold exec_instr. rewrite or_zero_eval_shift_op_int by congruence. eauto. + simpl; eauto. + unfold exec_instr. rewrite or_zero_eval_shift_op_int by congruence. eauto. + auto. auto. auto. + split. subst v; Simpl. intros; Simpl. +Qed. + +Lemma exec_shrx64: forall (rd r1: ireg) (n: int) k v (rs: regset) m, + Val.shrxl rs#r1 (Vint n) = Some v -> + r1 <> X16 -> + exists rs', + exec_straight ge fn (shrx64 rd r1 n k) rs m k rs' m + /\ rs'#rd = v + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + unfold shrx64; intros. apply Val.shrxl_shrl_2 in H. + destruct (Int.eq n Int.zero) eqn:E. +- econstructor; split. apply exec_straight_one; [simpl;eauto|auto]. + split. Simpl. subst v; auto. intros; Simpl. +- econstructor; split. eapply exec_straight_three. + unfold exec_instr. rewrite or_zero_eval_shift_op_long by congruence. eauto. + simpl; eauto. + unfold exec_instr. rewrite or_zero_eval_shift_op_long by congruence. eauto. + auto. auto. auto. + split. subst v; Simpl. intros; Simpl. +Qed. + +(** Condition bits *) + +Lemma compare_int_spec: forall rs v1 v2 m, + let rs' := compare_int rs v1 v2 m in + rs'#CN = (Val.negative (Val.sub v1 v2)) + /\ rs'#CZ = (Val.cmpu (Mem.valid_pointer m) Ceq v1 v2) + /\ rs'#CC = (Val.cmpu (Mem.valid_pointer m) Cge v1 v2) + /\ rs'#CV = (Val.sub_overflow v1 v2). +Proof. + intros; unfold rs'; auto. +Qed. + +Lemma eval_testcond_compare_sint: forall c v1 v2 b rs m, + Val.cmp_bool c v1 v2 = Some b -> + eval_testcond (cond_for_signed_cmp c) (compare_int rs v1 v2 m) = Some b. +Proof. + intros. generalize (compare_int_spec rs v1 v2 m). + set (rs' := compare_int rs v1 v2 m). intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E. + destruct v1; try discriminate; destruct v2; try discriminate. + simpl in H; inv H. + unfold Val.cmpu; simpl. destruct c; simpl. +- destruct (Int.eq i i0); auto. +- destruct (Int.eq i i0); auto. +- rewrite Int.lt_sub_overflow. destruct (Int.lt i i0); auto. +- rewrite Int.lt_sub_overflow, Int.not_lt. + destruct (Int.eq i i0), (Int.lt i i0); auto. +- rewrite Int.lt_sub_overflow, (Int.lt_not i). + destruct (Int.eq i i0), (Int.lt i i0); auto. +- rewrite Int.lt_sub_overflow. destruct (Int.lt i i0); auto. +Qed. + +Lemma eval_testcond_compare_uint: forall c v1 v2 b rs m, + Val.cmpu_bool (Mem.valid_pointer m) c v1 v2 = Some b -> + eval_testcond (cond_for_unsigned_cmp c) (compare_int rs v1 v2 m) = Some b. +Proof. + intros. generalize (compare_int_spec rs v1 v2 m). + set (rs' := compare_int rs v1 v2 m). intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E. + destruct v1; try discriminate; destruct v2; try discriminate. + simpl in H; inv H. + unfold Val.cmpu; simpl. destruct c; simpl. +- destruct (Int.eq i i0); auto. +- destruct (Int.eq i i0); auto. +- destruct (Int.ltu i i0); auto. +- rewrite (Int.not_ltu i). destruct (Int.eq i i0), (Int.ltu i i0); auto. +- rewrite (Int.ltu_not i). destruct (Int.eq i i0), (Int.ltu i i0); auto. +- destruct (Int.ltu i i0); auto. +Qed. + +Lemma compare_long_spec: forall rs v1 v2 m, + let rs' := compare_long rs v1 v2 m in + rs'#CN = (Val.negativel (Val.subl v1 v2)) + /\ rs'#CZ = (Val.maketotal (Val.cmplu (Mem.valid_pointer m) Ceq v1 v2)) + /\ rs'#CC = (Val.maketotal (Val.cmplu (Mem.valid_pointer m) Cge v1 v2)) + /\ rs'#CV = (Val.subl_overflow v1 v2). +Proof. + intros; unfold rs'; auto. +Qed. + +Remark int64_sub_overflow: + forall x y, + Int.xor (Int.repr (Int64.unsigned (Int64.sub_overflow x y Int64.zero))) + (Int.repr (Int64.unsigned (Int64.negative (Int64.sub x y)))) = + (if Int64.lt x y then Int.one else Int.zero). +Proof. + intros. + transitivity (Int.repr (Int64.unsigned (if Int64.lt x y then Int64.one else Int64.zero))). + rewrite <- (Int64.lt_sub_overflow x y). + unfold Int64.sub_overflow, Int64.negative. + set (s := Int64.signed x - Int64.signed y - Int64.signed Int64.zero). + destruct (zle Int64.min_signed s && zle s Int64.max_signed); + destruct (Int64.lt (Int64.sub x y) Int64.zero); + auto. + destruct (Int64.lt x y); auto. +Qed. + +Lemma eval_testcond_compare_slong: forall c v1 v2 b rs m, + Val.cmpl_bool c v1 v2 = Some b -> + eval_testcond (cond_for_signed_cmp c) (compare_long rs v1 v2 m) = Some b. +Proof. + intros. generalize (compare_long_spec rs v1 v2 m). + set (rs' := compare_long rs v1 v2 m). intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E. + destruct v1; try discriminate; destruct v2; try discriminate. + simpl in H; inv H. + unfold Val.cmplu; simpl. destruct c; simpl. +- destruct (Int64.eq i i0); auto. +- destruct (Int64.eq i i0); auto. +- rewrite int64_sub_overflow. destruct (Int64.lt i i0); auto. +- rewrite int64_sub_overflow, Int64.not_lt. + destruct (Int64.eq i i0), (Int64.lt i i0); auto. +- rewrite int64_sub_overflow, (Int64.lt_not i). + destruct (Int64.eq i i0), (Int64.lt i i0); auto. +- rewrite int64_sub_overflow. destruct (Int64.lt i i0); auto. +Qed. + +Lemma eval_testcond_compare_ulong: forall c v1 v2 b rs m, + Val.cmplu_bool (Mem.valid_pointer m) c v1 v2 = Some b -> + eval_testcond (cond_for_unsigned_cmp c) (compare_long rs v1 v2 m) = Some b. +Proof. + intros. generalize (compare_long_spec rs v1 v2 m). + set (rs' := compare_long rs v1 v2 m). intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E; unfold Val.cmplu. + destruct v1; try discriminate; destruct v2; try discriminate; simpl in H. +- (* int-int *) + inv H. destruct c; simpl. ++ destruct (Int64.eq i i0); auto. ++ destruct (Int64.eq i i0); auto. ++ destruct (Int64.ltu i i0); auto. ++ rewrite (Int64.not_ltu i). destruct (Int64.eq i i0), (Int64.ltu i i0); auto. ++ rewrite (Int64.ltu_not i). destruct (Int64.eq i i0), (Int64.ltu i i0); auto. ++ destruct (Int64.ltu i i0); auto. +- (* int-ptr *) + simpl. + destruct (Int64.eq i Int64.zero && + (Mem.valid_pointer m b0 (Ptrofs.unsigned i0) + || Mem.valid_pointer m b0 (Ptrofs.unsigned i0 - 1))); try discriminate. + destruct c; simpl in H; inv H; reflexivity. +- (* ptr-int *) + simpl. + destruct (Int64.eq i0 Int64.zero && + (Mem.valid_pointer m b0 (Ptrofs.unsigned i) + || Mem.valid_pointer m b0 (Ptrofs.unsigned i - 1))); try discriminate. + destruct c; simpl in H; inv H; reflexivity. +- (* ptr-ptr *) + simpl. + destruct (eq_block b0 b1). ++ destruct ((Mem.valid_pointer m b0 (Ptrofs.unsigned i) + || Mem.valid_pointer m b0 (Ptrofs.unsigned i - 1)) && + (Mem.valid_pointer m b1 (Ptrofs.unsigned i0) + || Mem.valid_pointer m b1 (Ptrofs.unsigned i0 - 1))); + inv H. + destruct c; simpl. +* destruct (Ptrofs.eq i i0); auto. +* destruct (Ptrofs.eq i i0); auto. +* destruct (Ptrofs.ltu i i0); auto. +* rewrite (Ptrofs.not_ltu i). destruct (Ptrofs.eq i i0), (Ptrofs.ltu i i0); auto. +* rewrite (Ptrofs.ltu_not i). destruct (Ptrofs.eq i i0), (Ptrofs.ltu i i0); auto. +* destruct (Ptrofs.ltu i i0); auto. ++ destruct (Mem.valid_pointer m b0 (Ptrofs.unsigned i) && + Mem.valid_pointer m b1 (Ptrofs.unsigned i0)); try discriminate. + destruct c; simpl in H; inv H; reflexivity. +Qed. + +Lemma compare_float_spec: forall rs f1 f2, + let rs' := compare_float rs (Vfloat f1) (Vfloat f2) in + rs'#CN = (Val.of_bool (Float.cmp Clt f1 f2)) + /\ rs'#CZ = (Val.of_bool (Float.cmp Ceq f1 f2)) + /\ rs'#CC = (Val.of_bool (negb (Float.cmp Clt f1 f2))) + /\ rs'#CV = (Val.of_bool (negb (Float.ordered f1 f2))). +Proof. + intros; auto. +Qed. + +Lemma eval_testcond_compare_float: forall c v1 v2 b rs, + Val.cmpf_bool c v1 v2 = Some b -> + eval_testcond (cond_for_float_cmp c) (compare_float rs v1 v2) = Some b. +Proof. + intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H. + generalize (compare_float_spec rs f f0). + set (rs' := compare_float rs (Vfloat f) (Vfloat f0)). + intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E. +Local Transparent Float.cmp Float.ordered. + unfold Float.cmp, Float.ordered; + destruct c; destruct (Float.compare f f0) as [[]|]; reflexivity. +Qed. + +Lemma eval_testcond_compare_not_float: forall c v1 v2 b rs, + option_map negb (Val.cmpf_bool c v1 v2) = Some b -> + eval_testcond (cond_for_float_not_cmp c) (compare_float rs v1 v2) = Some b. +Proof. + intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H. + generalize (compare_float_spec rs f f0). + set (rs' := compare_float rs (Vfloat f) (Vfloat f0)). + intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E. +Local Transparent Float.cmp Float.ordered. + unfold Float.cmp, Float.ordered; + destruct c; destruct (Float.compare f f0) as [[]|]; reflexivity. +Qed. + +Lemma compare_single_spec: forall rs f1 f2, + let rs' := compare_single rs (Vsingle f1) (Vsingle f2) in + rs'#CN = (Val.of_bool (Float32.cmp Clt f1 f2)) + /\ rs'#CZ = (Val.of_bool (Float32.cmp Ceq f1 f2)) + /\ rs'#CC = (Val.of_bool (negb (Float32.cmp Clt f1 f2))) + /\ rs'#CV = (Val.of_bool (negb (Float32.ordered f1 f2))). +Proof. + intros; auto. +Qed. + +Lemma eval_testcond_compare_single: forall c v1 v2 b rs, + Val.cmpfs_bool c v1 v2 = Some b -> + eval_testcond (cond_for_float_cmp c) (compare_single rs v1 v2) = Some b. +Proof. + intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H. + generalize (compare_single_spec rs f f0). + set (rs' := compare_single rs (Vsingle f) (Vsingle f0)). + intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E. +Local Transparent Float32.cmp Float32.ordered. + unfold Float32.cmp, Float32.ordered; + destruct c; destruct (Float32.compare f f0) as [[]|]; reflexivity. +Qed. + +Lemma eval_testcond_compare_not_single: forall c v1 v2 b rs, + option_map negb (Val.cmpfs_bool c v1 v2) = Some b -> + eval_testcond (cond_for_float_not_cmp c) (compare_single rs v1 v2) = Some b. +Proof. + intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H. + generalize (compare_single_spec rs f f0). + set (rs' := compare_single rs (Vsingle f) (Vsingle f0)). + intros (B & C & D & E). + unfold eval_testcond; rewrite B, C, D, E. +Local Transparent Float32.cmp Float32.ordered. + unfold Float32.cmp, Float32.ordered; + destruct c; destruct (Float32.compare f f0) as [[]|]; reflexivity. +Qed. + +Remark compare_float_inv: forall rs v1 v2 r, + match r with CR _ => False | _ => True end -> + (nextinstr (compare_float rs v1 v2))#r = (nextinstr rs)#r. +Proof. + intros; unfold compare_float. + destruct r; try contradiction; destruct v1; auto; destruct v2; auto. +Qed. + +Remark compare_single_inv: forall rs v1 v2 r, + match r with CR _ => False | _ => True end -> + (nextinstr (compare_single rs v1 v2))#r = (nextinstr rs)#r. +Proof. + intros; unfold compare_single. + destruct r; try contradiction; destruct v1; auto; destruct v2; auto. +Qed. + +(** Translation of conditionals *) + +Ltac ArgsInv := + repeat (match goal with + | [ H: Error _ = OK _ |- _ ] => discriminate + | [ H: match ?args with nil => _ | _ :: _ => _ end = OK _ |- _ ] => destruct args + | [ H: bind _ _ = OK _ |- _ ] => monadInv H + | [ H: match _ with left _ => _ | right _ => assertion_failed end = OK _ |- _ ] => monadInv H; ArgsInv + | [ H: match _ with true => _ | false => assertion_failed end = OK _ |- _ ] => monadInv H; ArgsInv + end); + subst; + repeat (match goal with + | [ H: ireg_of _ = OK _ |- _ ] => simpl in *; rewrite (ireg_of_eq _ _ H) in * + | [ H: freg_of _ = OK _ |- _ ] => simpl in *; rewrite (freg_of_eq _ _ H) in * + end). + +Lemma transl_cond_correct: + forall cond args k c rs m, + transl_cond cond args k = OK c -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ (forall b, + eval_condition cond (map rs (map preg_of args)) m = Some b -> + eval_testcond (cond_for_cond cond) rs' = Some b) + /\ forall r, data_preg r = true -> rs'#r = rs#r. +Proof. + intros until m; intros TR. destruct cond; simpl in TR; ArgsInv. +- (* Ccomp *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. apply eval_testcond_compare_sint; auto. + destruct r; reflexivity || discriminate. +- (* Ccompu *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. apply eval_testcond_compare_uint; auto. + destruct r; reflexivity || discriminate. +- (* Ccompimm *) + destruct (is_arith_imm32 n); [|destruct (is_arith_imm32 (Int.neg n))]. ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int.repr_unsigned. apply eval_testcond_compare_sint; auto. + destruct r; reflexivity || discriminate. ++ econstructor; split. + apply exec_straight_one. simpl. rewrite Int.repr_unsigned, Int.neg_involutive. eauto. auto. + split; intros. apply eval_testcond_compare_sint; auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm32 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. + simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply eval_testcond_compare_sint; auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Ccompuimm *) + destruct (is_arith_imm32 n); [|destruct (is_arith_imm32 (Int.neg n))]. ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int.repr_unsigned. apply eval_testcond_compare_uint; auto. + destruct r; reflexivity || discriminate. ++ econstructor; split. + apply exec_straight_one. simpl. rewrite Int.repr_unsigned, Int.neg_involutive. eauto. auto. + split; intros. apply eval_testcond_compare_uint; auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm32 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. + simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply eval_testcond_compare_uint; auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Ccompshift *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite transl_eval_shift. apply eval_testcond_compare_sint; auto. + destruct r; reflexivity || discriminate. +- (* Ccompushift *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite transl_eval_shift. apply eval_testcond_compare_uint; auto. + destruct r; reflexivity || discriminate. +- (* Cmaskzero *) + destruct (is_logical_imm32 n). ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int.repr_unsigned. apply (eval_testcond_compare_sint Ceq); auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm32 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. + apply exec_straight_one. simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply (eval_testcond_compare_sint Ceq); auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Cmasknotzero *) + destruct (is_logical_imm32 n). ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int.repr_unsigned. apply (eval_testcond_compare_sint Cne); auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm32 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. + apply exec_straight_one. simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply (eval_testcond_compare_sint Cne); auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Ccompl *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. apply eval_testcond_compare_slong; auto. + destruct r; reflexivity || discriminate. +- (* Ccomplu *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. apply eval_testcond_compare_ulong; auto. + destruct r; reflexivity || discriminate. +- (* Ccomplimm *) + destruct (is_arith_imm64 n); [|destruct (is_arith_imm64 (Int64.neg n))]. ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int64.repr_unsigned. apply eval_testcond_compare_slong; auto. + destruct r; reflexivity || discriminate. ++ econstructor; split. + apply exec_straight_one. simpl. rewrite Int64.repr_unsigned, Int64.neg_involutive. eauto. auto. + split; intros. apply eval_testcond_compare_slong; auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm64 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. + simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply eval_testcond_compare_slong; auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Ccompluimm *) + destruct (is_arith_imm64 n); [|destruct (is_arith_imm64 (Int64.neg n))]. ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int64.repr_unsigned. apply eval_testcond_compare_ulong; auto. + destruct r; reflexivity || discriminate. ++ econstructor; split. + apply exec_straight_one. simpl. rewrite Int64.repr_unsigned, Int64.neg_involutive. eauto. auto. + split; intros. apply eval_testcond_compare_ulong; auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm64 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. + simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply eval_testcond_compare_ulong; auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Ccomplshift *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite transl_eval_shiftl. apply eval_testcond_compare_slong; auto. + destruct r; reflexivity || discriminate. +- (* Ccomplushift *) + econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite transl_eval_shiftl. apply eval_testcond_compare_ulong; auto. + destruct r; reflexivity || discriminate. +- (* Cmasklzero *) + destruct (is_logical_imm64 n). ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int64.repr_unsigned. apply (eval_testcond_compare_slong Ceq); auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm64 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. + apply exec_straight_one. simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply (eval_testcond_compare_slong Ceq); auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Cmasknotzero *) + destruct (is_logical_imm64 n). ++ econstructor; split. apply exec_straight_one. simpl; eauto. auto. + split; intros. rewrite Int64.repr_unsigned. apply (eval_testcond_compare_slong Cne); auto. + destruct r; reflexivity || discriminate. ++ exploit (exec_loadimm64 X16 n). intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. + apply exec_straight_one. simpl. rewrite B, C by eauto with asmgen. eauto. auto. + split; intros. apply (eval_testcond_compare_slong Cne); auto. + transitivity (rs' r). destruct r; reflexivity || discriminate. auto with asmgen. +- (* Ccompf *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_float_inv; auto. + split; intros. apply eval_testcond_compare_float; auto. + destruct r; discriminate || rewrite compare_float_inv; auto. +- (* Cnotcompf *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_float_inv; auto. + split; intros. apply eval_testcond_compare_not_float; auto. + destruct r; discriminate || rewrite compare_float_inv; auto. +- (* Ccompfzero *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_float_inv; auto. + split; intros. apply eval_testcond_compare_float; auto. + destruct r; discriminate || rewrite compare_float_inv; auto. +- (* Cnotcompfzero *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_float_inv; auto. + split; intros. apply eval_testcond_compare_not_float; auto. + destruct r; discriminate || rewrite compare_float_inv; auto. +- (* Ccompfs *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_single_inv; auto. + split; intros. apply eval_testcond_compare_single; auto. + destruct r; discriminate || rewrite compare_single_inv; auto. +- (* Cnotcompfs *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_single_inv; auto. + split; intros. apply eval_testcond_compare_not_single; auto. + destruct r; discriminate || rewrite compare_single_inv; auto. +- (* Ccompfszero *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_single_inv; auto. + split; intros. apply eval_testcond_compare_single; auto. + destruct r; discriminate || rewrite compare_single_inv; auto. +- (* Cnotcompfszero *) + econstructor; split. apply exec_straight_one. simpl; eauto. + rewrite compare_single_inv; auto. + split; intros. apply eval_testcond_compare_not_single; auto. + destruct r; discriminate || rewrite compare_single_inv; auto. +Qed. + +(** Translation of conditional branches *) + +Lemma transl_cond_branch_correct: + forall cond args lbl k c rs m b, + transl_cond_branch cond args lbl k = OK c -> + eval_condition cond (map rs (map preg_of args)) m = Some b -> + exists rs' insn, + exec_straight_opt ge fn c rs m (insn :: k) rs' m + /\ exec_instr ge fn insn rs' m = + (if b then goto_label fn lbl rs' m else Next (nextinstr rs') m) + /\ forall r, data_preg r = true -> rs'#r = rs#r. +Proof. + intros until b; intros TR EV. + assert (DFL: + transl_cond_branch_default cond args lbl k = OK c -> + exists rs' insn, + exec_straight_opt ge fn c rs m (insn :: k) rs' m + /\ exec_instr ge fn insn rs' m = + (if b then goto_label fn lbl rs' m else Next (nextinstr rs') m) + /\ forall r, data_preg r = true -> rs'#r = rs#r). + { + unfold transl_cond_branch_default; intros. + exploit transl_cond_correct; eauto. intros (rs' & A & B & C). + exists rs', (Pbc (cond_for_cond cond) lbl); split. + apply exec_straight_opt_intro. eexact A. + split; auto. simpl. rewrite (B b) by auto. auto. + } +Local Opaque transl_cond transl_cond_branch_default. + destruct args as [ | a1 args]; simpl in TR; auto. + destruct args as [ | a2 args]; simpl in TR; auto. + destruct cond; simpl in TR; auto. +- (* Ccompimm *) + destruct c0; auto; destruct (Int.eq n Int.zero) eqn:N0; auto; + apply Int.same_if_eq in N0; subst n; ArgsInv. ++ (* Ccompimm Cne 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. destruct (rs x); simpl in EV; inv EV. simpl. auto. ++ (* Ccompimm Ceq 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. destruct (rs x); simpl in EV; inv EV. simpl. destruct (Int.eq i Int.zero); auto. +- (* Ccompuimm *) + destruct c0; auto; destruct (Int.eq n Int.zero) eqn:N0; auto; + apply Int.same_if_eq in N0; subst n; ArgsInv. ++ (* Ccompuimm Cne 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. rewrite EV. auto. ++ (* Ccompuimm Ceq 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. rewrite (Val.negate_cmpu_bool (Mem.valid_pointer m) Cne), EV. destruct b; auto. +- (* Cmaskzero *) + destruct (Int.is_power2 n) as [bit|] eqn:P2; auto. ArgsInv. + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. + erewrite <- Int.mul_pow2, Int.mul_commut, Int.mul_one by eauto. + rewrite (Val.negate_cmp_bool Ceq), EV. destruct b; auto. +- (* Cmasknotzero *) + destruct (Int.is_power2 n) as [bit|] eqn:P2; auto. ArgsInv. + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. + erewrite <- Int.mul_pow2, Int.mul_commut, Int.mul_one by eauto. + rewrite EV. auto. +- (* Ccomplimm *) + destruct c0; auto; destruct (Int64.eq n Int64.zero) eqn:N0; auto; + apply Int64.same_if_eq in N0; subst n; ArgsInv. ++ (* Ccomplimm Cne 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. destruct (rs x); simpl in EV; inv EV. simpl. auto. ++ (* Ccomplimm Ceq 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. destruct (rs x); simpl in EV; inv EV. simpl. destruct (Int64.eq i Int64.zero); auto. +- (* Ccompluimm *) + destruct c0; auto; destruct (Int64.eq n Int64.zero) eqn:N0; auto; + apply Int64.same_if_eq in N0; subst n; ArgsInv. ++ (* Ccompluimm Cne 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. rewrite EV. auto. ++ (* Ccompluimm Ceq 0 *) + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. rewrite (Val.negate_cmplu_bool (Mem.valid_pointer m) Cne), EV. destruct b; auto. +- (* Cmasklzero *) + destruct (Int64.is_power2' n) as [bit|] eqn:P2; auto. ArgsInv. + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. + erewrite <- Int64.mul_pow2', Int64.mul_commut, Int64.mul_one by eauto. + rewrite (Val.negate_cmpl_bool Ceq), EV. destruct b; auto. +- (* Cmasklnotzero *) + destruct (Int64.is_power2' n) as [bit|] eqn:P2; auto. ArgsInv. + do 2 econstructor; split. + apply exec_straight_opt_refl. + split; auto. simpl. + erewrite <- Int64.mul_pow2', Int64.mul_commut, Int64.mul_one by eauto. + rewrite EV. auto. +Qed. + +(** Translation of arithmetic operations *) + +Ltac SimplEval H := + match type of H with + | Some _ = None _ => discriminate + | Some _ = Some _ => inv H + | ?a = Some ?b => let A := fresh in assert (A: Val.maketotal a = b) by (rewrite H; reflexivity) +end. + +Ltac TranslOpSimpl := + econstructor; split; + [ apply exec_straight_one; [simpl; eauto | reflexivity] + | split; [ rewrite ? transl_eval_shift, ? transl_eval_shiftl; + apply Val.lessdef_same; Simpl; fail + | intros; Simpl; fail ] ]. + +Ltac TranslOpBase := + econstructor; split; + [ apply exec_straight_one; [simpl; eauto | reflexivity] + | split; [ rewrite ? transl_eval_shift, ? transl_eval_shiftl; Simpl + | intros; Simpl; fail ] ]. + +Lemma transl_op_correct: + forall op args res k (rs: regset) m v c, + transl_op op args res k = OK c -> + eval_operation ge (rs#SP) op (map rs (map preg_of args)) m = Some v -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ Val.lessdef v rs'#(preg_of res) + /\ forall r, data_preg r = true -> r <> preg_of res -> preg_notin r (destroyed_by_op op) -> rs' r = rs r. +Proof. +Local Opaque Int.eq Int64.eq Val.add Val.addl Int.zwordsize Int64.zwordsize. + intros until c; intros TR EV. + unfold transl_op in TR; destruct op; ArgsInv; simpl in EV; SimplEval EV; try TranslOpSimpl. +- (* move *) + destruct (preg_of res) eqn:RR; try discriminate; destruct (preg_of m0) eqn:R1; inv TR. ++ TranslOpSimpl. ++ TranslOpSimpl. +- (* intconst *) + exploit exec_loadimm32. intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. intros; auto with asmgen. +- (* longconst *) + exploit exec_loadimm64. intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. intros; auto with asmgen. +- (* floatconst *) + destruct (Float.eq_dec n Float.zero). ++ subst n. TranslOpSimpl. ++ TranslOpSimpl. +- (* singleconst *) + destruct (Float32.eq_dec n Float32.zero). ++ subst n. TranslOpSimpl. ++ TranslOpSimpl. +- (* loadsymbol *) + exploit (exec_loadsymbol x id ofs). eauto with asmgen. intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* addrstack *) + exploit (exec_addimm64 x XSP (Ptrofs.to_int64 ofs)). simpl; eauto with asmgen. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. simpl in B; rewrite B. +Local Transparent Val.addl. + destruct (rs SP); simpl; auto. rewrite Ptrofs.of_int64_to_int64 by auto. auto. + auto. +- (* shift *) + rewrite <- transl_eval_shift'. TranslOpSimpl. +- (* addimm *) + exploit (exec_addimm32 x x0 n). eauto with asmgen. intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* mul *) + TranslOpBase. +Local Transparent Val.add. + destruct (rs x0); auto; destruct (rs x1); auto. simpl. rewrite Int.add_zero_l; auto. +- (* andimm *) + exploit (exec_logicalimm32 (Pandimm W) (Pand W)). + intros; reflexivity. intros; reflexivity. instantiate (1 := x0). eauto with asmgen. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* orimm *) + exploit (exec_logicalimm32 (Porrimm W) (Porr W)). + intros; reflexivity. intros; reflexivity. instantiate (1 := x0). eauto with asmgen. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* xorimm *) + exploit (exec_logicalimm32 (Peorimm W) (Peor W)). + intros; reflexivity. intros; reflexivity. instantiate (1 := x0). eauto with asmgen. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* not *) + TranslOpBase. + destruct (rs x0); auto. simpl. rewrite Int.or_zero_l; auto. +- (* notshift *) + TranslOpBase. + destruct (eval_shift s (rs x0) a); auto. simpl. rewrite Int.or_zero_l; auto. +- (* shrx *) + exploit (exec_shrx32 x x0 n); eauto with asmgen. intros (rs' & A & B & C). + econstructor; split. eexact A. split. rewrite B; auto. auto. +- (* zero-ext *) + TranslOpBase. + destruct (rs x0); auto; simpl. rewrite Int.shl_zero. auto. +- (* sign-ext *) + TranslOpBase. + destruct (rs x0); auto; simpl. rewrite Int.shl_zero. auto. +- (* shlzext *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite <- Int.shl_zero_ext_min; auto using a32_range. +- (* shlsext *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite <- Int.shl_sign_ext_min; auto using a32_range. +- (* zextshr *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite ! a32_range; simpl. rewrite <- Int.zero_ext_shru_min; auto using a32_range. +- (* sextshr *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite ! a32_range; simpl. rewrite <- Int.sign_ext_shr_min; auto using a32_range. +- (* shiftl *) + rewrite <- transl_eval_shiftl'. TranslOpSimpl. +- (* extend *) + exploit (exec_move_extended x0 x1 x a k). intros (rs' & A & B & C). + econstructor; split. eexact A. + split. rewrite B; auto. eauto with asmgen. +- (* addext *) + exploit (exec_arith_extended Val.addl Paddext (Padd X)). + auto. auto. instantiate (1 := x1). eauto with asmgen. intros (rs' & A & B & C). + econstructor; split. eexact A. split. rewrite B; auto. auto. +- (* addlimm *) + exploit (exec_addimm64 x x0 n). simpl. generalize (ireg_of_not_X16 _ _ EQ1). congruence. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. simpl in B; rewrite B; auto. auto. +- (* subext *) + exploit (exec_arith_extended Val.subl Psubext (Psub X)). + auto. auto. instantiate (1 := x1). eauto with asmgen. intros (rs' & A & B & C). + econstructor; split. eexact A. split. rewrite B; auto. auto. +- (* mull *) + TranslOpBase. + destruct (rs x0); auto; destruct (rs x1); auto. simpl. rewrite Int64.add_zero_l; auto. +- (* andlimm *) + exploit (exec_logicalimm64 (Pandimm X) (Pand X)). + intros; reflexivity. intros; reflexivity. instantiate (1 := x0). eauto with asmgen. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* orlimm *) + exploit (exec_logicalimm64 (Porrimm X) (Porr X)). + intros; reflexivity. intros; reflexivity. instantiate (1 := x0). eauto with asmgen. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* xorlimm *) + exploit (exec_logicalimm64 (Peorimm X) (Peor X)). + intros; reflexivity. intros; reflexivity. instantiate (1 := x0). eauto with asmgen. + intros (rs' & A & B & C). + exists rs'; split. eexact A. split. rewrite B; auto. auto. +- (* notl *) + TranslOpBase. + destruct (rs x0); auto. simpl. rewrite Int64.or_zero_l; auto. +- (* notlshift *) + TranslOpBase. + destruct (eval_shiftl s (rs x0) a); auto. simpl. rewrite Int64.or_zero_l; auto. +- (* shrx *) + exploit (exec_shrx64 x x0 n); eauto with asmgen. intros (rs' & A & B & C). + econstructor; split. eexact A. split. rewrite B; auto. auto. +- (* zero-ext-l *) + TranslOpBase. + destruct (rs x0); auto; simpl. rewrite Int64.shl'_zero. auto. +- (* sign-ext-l *) + TranslOpBase. + destruct (rs x0); auto; simpl. rewrite Int64.shl'_zero. auto. +- (* shllzext *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite <- Int64.shl'_zero_ext_min; auto using a64_range. +- (* shllsext *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite <- Int64.shl'_sign_ext_min; auto using a64_range. +- (* zextshrl *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite ! a64_range; simpl. rewrite <- Int64.zero_ext_shru'_min; auto using a64_range. +- (* sextshrl *) + TranslOpBase. + destruct (rs x0); simpl; auto. rewrite ! a64_range; simpl. rewrite <- Int64.sign_ext_shr'_min; auto using a64_range. +- (* condition *) + exploit (transl_cond_correct cond args); eauto. intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto. + split. Simpl. destruct (eval_condition cond (map rs (map preg_of args)) m) as [b|]; simpl in *. + rewrite (B b) by auto. auto. + auto. + intros; Simpl. +- (* select *) + destruct (preg_of res) eqn:RES; monadInv TR. + + (* integer *) + generalize (ireg_of_eq _ _ EQ) (ireg_of_eq _ _ EQ1); intros E1 E2; rewrite E1, E2. + exploit (transl_cond_correct cond args); eauto. intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto. + split. Simpl. destruct (eval_condition cond (map rs (map preg_of args)) m) as [b|]; simpl in *. + rewrite (B b) by auto. rewrite !C. apply Val.lessdef_normalize. + rewrite <- E2; auto with asmgen. rewrite <- E1; auto with asmgen. + auto. + intros; Simpl. + + (* FP *) + generalize (freg_of_eq _ _ EQ) (freg_of_eq _ _ EQ1); intros E1 E2; rewrite E1, E2. + exploit (transl_cond_correct cond args); eauto. intros (rs' & A & B & C). + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto. + split. Simpl. destruct (eval_condition cond (map rs (map preg_of args)) m) as [b|]; simpl in *. + rewrite (B b) by auto. rewrite !C. apply Val.lessdef_normalize. + rewrite <- E2; auto with asmgen. rewrite <- E1; auto with asmgen. + auto. + intros; Simpl. +Qed. + +(** Translation of addressing modes, loads, stores *) + +Lemma transl_addressing_correct: + forall sz addr args (insn: Asm.addressing -> instruction) k (rs: regset) m c b o, + transl_addressing sz addr args insn k = OK c -> + Op.eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some (Vptr b o) -> + exists ad rs', + exec_straight_opt ge fn c rs m (insn ad :: k) rs' m + /\ Asm.eval_addressing ge ad rs' = Vptr b o + /\ forall r, data_preg r = true -> rs' r = rs r. +Proof. + intros until o; intros TR EV. + unfold transl_addressing in TR; destruct addr; ArgsInv; SimplEval EV. +- (* Aindexed *) + destruct (offset_representable sz ofs); inv EQ0. ++ econstructor; econstructor; split. apply exec_straight_opt_refl. + auto. ++ exploit (exec_loadimm64 X16 ofs). intros (rs' & A & B & C). + econstructor; exists rs'; split. apply exec_straight_opt_intro; eexact A. + split. simpl. rewrite B, C by eauto with asmgen. auto. + eauto with asmgen. +- (* Aindexed2 *) + econstructor; econstructor; split. apply exec_straight_opt_refl. + auto. +- (* Aindexed2shift *) + destruct (Int.eq a Int.zero) eqn:E; [|destruct (Int.eq (Int.shl Int.one a) (Int.repr sz))]; inv EQ2. ++ apply Int.same_if_eq in E. rewrite E. + econstructor; econstructor; split. apply exec_straight_opt_refl. + split; auto. simpl. + rewrite Val.addl_commut in H0. destruct (rs x0); try discriminate. + unfold Val.shll. rewrite Int64.shl'_zero. auto. ++ econstructor; econstructor; split. apply exec_straight_opt_refl. + auto. ++ econstructor; econstructor; split. + apply exec_straight_opt_intro. apply exec_straight_one. simpl; eauto. auto. + split. simpl. Simpl. rewrite H0. simpl. rewrite Ptrofs.add_zero. auto. + intros; Simpl. +- (* Aindexed2ext *) + destruct (Int.eq a Int.zero || Int.eq (Int.shl Int.one a) (Int.repr sz)); inv EQ2. ++ econstructor; econstructor; split. apply exec_straight_opt_refl. + split; auto. destruct x; auto. ++ exploit (exec_arith_extended Val.addl Paddext (Padd X)); auto. + instantiate (1 := x0). eauto with asmgen. + intros (rs' & A & B & C). + econstructor; exists rs'; split. + apply exec_straight_opt_intro. eexact A. + split. simpl. rewrite B. rewrite Val.addl_assoc. f_equal. + unfold Op.eval_extend; destruct x, (rs x1); simpl; auto; rewrite ! a64_range; + simpl; rewrite Int64.add_zero; auto. + intros. apply C; eauto with asmgen. +- (* Aglobal *) + destruct (Ptrofs.eq (Ptrofs.modu ofs (Ptrofs.repr sz)) Ptrofs.zero && symbol_is_aligned id sz); inv TR. ++ econstructor; econstructor; split. + apply exec_straight_opt_intro. apply exec_straight_one. simpl; eauto. auto. + split. simpl. Simpl. rewrite symbol_high_low. simpl in EV. congruence. + intros; Simpl. ++ exploit (exec_loadsymbol X16 id ofs). auto. intros (rs' & A & B & C). + econstructor; exists rs'; split. + apply exec_straight_opt_intro. eexact A. + split. simpl. + rewrite B. rewrite <- Genv.shift_symbol_address_64, Ptrofs.add_zero by auto. + simpl in EV. congruence. + auto with asmgen. +- (* Ainstrack *) + assert (E: Val.addl (rs SP) (Vlong (Ptrofs.to_int64 ofs)) = Vptr b o). + { simpl in EV. inv EV. destruct (rs SP); simpl in H1; inv H1. simpl. + rewrite Ptrofs.of_int64_to_int64 by auto. auto. } + destruct (offset_representable sz (Ptrofs.to_int64 ofs)); inv TR. ++ econstructor; econstructor; split. apply exec_straight_opt_refl. + auto. ++ exploit (exec_loadimm64 X16 (Ptrofs.to_int64 ofs)). intros (rs' & A & B & C). + econstructor; exists rs'; split. + apply exec_straight_opt_intro. eexact A. + split. simpl. rewrite B, C by eauto with asmgen. auto. + auto with asmgen. +Qed. + +Lemma transl_load_correct: + forall chunk addr args dst k c (rs: regset) m vaddr v, + transl_load chunk addr args dst k = OK c -> + Op.eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some vaddr -> + Mem.loadv chunk m vaddr = Some v -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ rs'#(preg_of dst) = v + /\ forall r, data_preg r = true -> r <> preg_of dst -> rs' r = rs r. +Proof. + intros. destruct vaddr; try discriminate. + assert (A: exists sz insn, + transl_addressing sz addr args insn k = OK c + /\ (forall ad rs', exec_instr ge fn (insn ad) rs' m = + exec_load ge chunk (fun v => v) ad (preg_of dst) rs' m)). + { + destruct chunk; monadInv H; + try rewrite (ireg_of_eq _ _ EQ); try rewrite (freg_of_eq _ _ EQ); + do 2 econstructor; (split; [eassumption|auto]). + } + destruct A as (sz & insn & B & C). + exploit transl_addressing_correct. eexact B. eexact H0. intros (ad & rs' & P & Q & R). + assert (X: exec_load ge chunk (fun v => v) ad (preg_of dst) rs' m = + Next (nextinstr (rs'#(preg_of dst) <- v)) m). + { unfold exec_load. rewrite Q, H1. auto. } + econstructor; split. + eapply exec_straight_opt_right. eexact P. + apply exec_straight_one. rewrite C, X; eauto. Simpl. + split. Simpl. intros; Simpl. +Qed. + +Lemma transl_store_correct: + forall chunk addr args src k c (rs: regset) m vaddr m', + transl_store chunk addr args src k = OK c -> + Op.eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some vaddr -> + Mem.storev chunk m vaddr rs#(preg_of src) = Some m' -> + exists rs', + exec_straight ge fn c rs m k rs' m' + /\ forall r, data_preg r = true -> rs' r = rs r. +Proof. + intros. destruct vaddr; try discriminate. + set (chunk' := match chunk with Mint8signed => Mint8unsigned + | Mint16signed => Mint16unsigned + | _ => chunk end). + assert (A: exists sz insn, + transl_addressing sz addr args insn k = OK c + /\ (forall ad rs', exec_instr ge fn (insn ad) rs' m = + exec_store ge chunk' ad rs'#(preg_of src) rs' m)). + { + unfold chunk'; destruct chunk; monadInv H; + try rewrite (ireg_of_eq _ _ EQ); try rewrite (freg_of_eq _ _ EQ); + do 2 econstructor; (split; [eassumption|auto]). + } + destruct A as (sz & insn & B & C). + exploit transl_addressing_correct. eexact B. eexact H0. intros (ad & rs' & P & Q & R). + assert (X: Mem.storev chunk' m (Vptr b i) rs#(preg_of src) = Some m'). + { rewrite <- H1. unfold chunk'. destruct chunk; auto; simpl; symmetry. + apply Mem.store_signed_unsigned_8. + apply Mem.store_signed_unsigned_16. } + assert (Y: exec_store ge chunk' ad rs'#(preg_of src) rs' m = + Next (nextinstr rs') m'). + { unfold exec_store. rewrite Q, R, X by auto with asmgen. auto. } + econstructor; split. + eapply exec_straight_opt_right. eexact P. + apply exec_straight_one. rewrite C, Y; eauto. Simpl. + intros; Simpl. +Qed. + +(** Translation of indexed memory accesses *) + +Lemma indexed_memory_access_correct: forall insn sz (base: iregsp) ofs k (rs: regset) m b i, + preg_of_iregsp base <> IR X16 -> + Val.offset_ptr rs#base ofs = Vptr b i -> + exists ad rs', + exec_straight_opt ge fn (indexed_memory_access insn sz base ofs k) rs m (insn ad :: k) rs' m + /\ Asm.eval_addressing ge ad rs' = Vptr b i + /\ forall r, r <> PC -> r <> X16 -> rs' r = rs r. +Proof. + unfold indexed_memory_access; intros. + assert (Val.addl rs#base (Vlong (Ptrofs.to_int64 ofs)) = Vptr b i). + { destruct (rs base); try discriminate. simpl in *. rewrite Ptrofs.of_int64_to_int64 by auto. auto. } + destruct offset_representable. +- econstructor; econstructor; split. apply exec_straight_opt_refl. auto. +- exploit (exec_loadimm64 X16); eauto. intros (rs' & A & B & C). + econstructor; econstructor; split. apply exec_straight_opt_intro; eexact A. + split. simpl. rewrite B, C by eauto with asmgen. auto. auto. +Qed. + +Lemma loadptr_correct: forall (base: iregsp) ofs dst k m v (rs: regset), + Mem.loadv Mint64 m (Val.offset_ptr rs#base ofs) = Some v -> + preg_of_iregsp base <> IR X16 -> + exists rs', + exec_straight ge fn (loadptr base ofs dst k) rs m k rs' m + /\ rs'#dst = v + /\ forall r, r <> PC -> r <> X16 -> r <> dst -> rs' r = rs r. +Proof. + intros. + destruct (Val.offset_ptr rs#base ofs) eqn:V; try discriminate. + exploit indexed_memory_access_correct; eauto. intros (ad & rs' & A & B & C). + econstructor; split. + eapply exec_straight_opt_right. eexact A. + apply exec_straight_one. simpl. unfold exec_load. rewrite B, H. eauto. auto. + split. Simpl. intros; Simpl. +Qed. + +Lemma storeptr_correct: forall (base: iregsp) ofs (src: ireg) k m m' (rs: regset), + Mem.storev Mint64 m (Val.offset_ptr rs#base ofs) rs#src = Some m' -> + preg_of_iregsp base <> IR X16 -> + src <> X16 -> + exists rs', + exec_straight ge fn (storeptr src base ofs k) rs m k rs' m' + /\ forall r, r <> PC -> r <> X16 -> rs' r = rs r. +Proof. + intros. + destruct (Val.offset_ptr rs#base ofs) eqn:V; try discriminate. + exploit indexed_memory_access_correct; eauto. intros (ad & rs' & A & B & C). + econstructor; split. + eapply exec_straight_opt_right. eexact A. + apply exec_straight_one. simpl. unfold exec_store. rewrite B, C, H by eauto with asmgen. eauto. auto. + intros; Simpl. +Qed. + +Lemma loadind_correct: forall (base: iregsp) ofs ty dst k c (rs: regset) m v, + loadind base ofs ty dst k = OK c -> + Mem.loadv (chunk_of_type ty) m (Val.offset_ptr rs#base ofs) = Some v -> + preg_of_iregsp base <> IR X16 -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ rs'#(preg_of dst) = v + /\ forall r, data_preg r = true -> r <> preg_of dst -> rs' r = rs r. +Proof. + intros. + destruct (Val.offset_ptr rs#base ofs) eqn:V; try discriminate. + assert (X: exists sz insn, + c = indexed_memory_access insn sz base ofs k + /\ (forall ad rs', exec_instr ge fn (insn ad) rs' m = + exec_load ge (chunk_of_type ty) (fun v => v) ad (preg_of dst) rs' m)). + { + unfold loadind in H; destruct ty; destruct (preg_of dst); inv H; do 2 econstructor; eauto. + } + destruct X as (sz & insn & EQ & SEM). subst c. + exploit indexed_memory_access_correct; eauto. intros (ad & rs' & A & B & C). + econstructor; split. + eapply exec_straight_opt_right. eexact A. + apply exec_straight_one. rewrite SEM. unfold exec_load. rewrite B, H0. eauto. Simpl. + split. Simpl. intros; Simpl. +Qed. + +Lemma storeind_correct: forall (base: iregsp) ofs ty src k c (rs: regset) m m', + storeind src base ofs ty k = OK c -> + Mem.storev (chunk_of_type ty) m (Val.offset_ptr rs#base ofs) rs#(preg_of src) = Some m' -> + preg_of_iregsp base <> IR X16 -> + exists rs', + exec_straight ge fn c rs m k rs' m' + /\ forall r, data_preg r = true -> rs' r = rs r. +Proof. + intros. + destruct (Val.offset_ptr rs#base ofs) eqn:V; try discriminate. + assert (X: exists sz insn, + c = indexed_memory_access insn sz base ofs k + /\ (forall ad rs', exec_instr ge fn (insn ad) rs' m = + exec_store ge (chunk_of_type ty) ad rs'#(preg_of src) rs' m)). + { + unfold storeind in H; destruct ty; destruct (preg_of src); inv H; do 2 econstructor; eauto. + } + destruct X as (sz & insn & EQ & SEM). subst c. + exploit indexed_memory_access_correct; eauto. intros (ad & rs' & A & B & C). + econstructor; split. + eapply exec_straight_opt_right. eexact A. + apply exec_straight_one. rewrite SEM. + unfold exec_store. rewrite B, C, H0 by eauto with asmgen. eauto. + Simpl. + intros; Simpl. +Qed. + +Lemma make_epilogue_correct: + forall ge0 f m stk soff cs m' ms rs k tm, + load_stack m (Vptr stk soff) Tptr f.(fn_link_ofs) = Some (parent_sp cs) -> + load_stack m (Vptr stk soff) Tptr f.(fn_retaddr_ofs) = Some (parent_ra cs) -> + Mem.free m stk 0 f.(fn_stacksize) = Some m' -> + agree ms (Vptr stk soff) rs -> + Mem.extends m tm -> + match_stack ge0 cs -> + exists rs', exists tm', + exec_straight ge fn (make_epilogue f k) rs tm k rs' tm' + /\ agree ms (parent_sp cs) rs' + /\ Mem.extends m' tm' + /\ rs'#RA = parent_ra cs + /\ rs'#SP = parent_sp cs + /\ (forall r, r <> PC -> r <> SP -> r <> X30 -> r <> X16 -> rs'#r = rs#r). +Proof. + intros until tm; intros LP LRA FREE AG MEXT MCS. + exploit Mem.loadv_extends. eauto. eexact LP. auto. simpl. intros (parent' & LP' & LDP'). + exploit Mem.loadv_extends. eauto. eexact LRA. auto. simpl. intros (ra' & LRA' & LDRA'). + exploit lessdef_parent_sp; eauto. intros EQ; subst parent'; clear LDP'. + exploit lessdef_parent_ra; eauto. intros EQ; subst ra'; clear LDRA'. + exploit Mem.free_parallel_extends; eauto. intros (tm' & FREE' & MEXT'). + unfold make_epilogue. + exploit (loadptr_correct XSP (fn_retaddr_ofs f)). + instantiate (2 := rs). simpl. rewrite <- (sp_val _ _ _ AG). simpl. eexact LRA'. simpl; congruence. + intros (rs1 & A1 & B1 & C1). + econstructor; econstructor; split. + eapply exec_straight_trans. eexact A1. apply exec_straight_one. simpl. + simpl; rewrite (C1 SP) by auto with asmgen. rewrite <- (sp_val _ _ _ AG). simpl; rewrite LP'. + rewrite FREE'. eauto. auto. + split. apply agree_nextinstr. apply agree_set_other; auto. + apply agree_change_sp with (Vptr stk soff). + apply agree_exten with rs; auto. intros; apply C1; auto with asmgen. + eapply parent_sp_def; eauto. + split. auto. + split. Simpl. + split. Simpl. + intros. Simpl. +Qed. + +End CONSTRUCTORS.
\ No newline at end of file diff --git a/cparser/Builtins.mli b/aarch64/Builtins1.v index 7f9d78a9..53c83d7e 100644 --- a/cparser/Builtins.mli +++ b/aarch64/Builtins1.v @@ -2,7 +2,7 @@ (* *) (* The Compcert verified compiler *) (* *) -(* Xavier Leroy, INRIA Paris-Rocquencourt *) +(* Xavier Leroy, Collège de France and Inria Paris *) (* *) (* Copyright Institut National de Recherche en Informatique et en *) (* Automatique. All rights reserved. This file is distributed *) @@ -13,13 +13,21 @@ (* *) (* *********************************************************************) -val environment: unit -> Env.t -val identifiers: unit -> C.ident list -val declarations: unit -> C.globdecl list +(** Platform-specific built-in functions *) -type t = { - typedefs: (string * C.typ) list; - functions: (string * (C.typ * C.typ list * bool)) list -} +Require Import String Coqlib. +Require Import AST Integers Floats Values. +Require Import Builtins0. -val set: t -> unit +Inductive platform_builtin : Type := . + +Local Open Scope string_scope. + +Definition platform_builtin_table : list (string * platform_builtin) := + nil. + +Definition platform_builtin_sig (b: platform_builtin) : signature := + match b with end. + +Definition platform_builtin_sem (b: platform_builtin) : builtin_sem (sig_res (platform_builtin_sig b)) := + match b with end. diff --git a/aarch64/CBuiltins.ml b/aarch64/CBuiltins.ml new file mode 100644 index 00000000..fdc1372d --- /dev/null +++ b/aarch64/CBuiltins.ml @@ -0,0 +1,72 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(* Processor-dependent builtin C functions *) + +open C + +(* va_list is a struct of size 32 and alignment 8, passed by reference *) + +let va_list_type = TArray(TInt(IULong, []), Some 4L, []) +let size_va_list = 32 +let va_list_scalar = false + +let builtins = { + builtin_typedefs = [ + "__builtin_va_list", va_list_type + ]; + builtin_functions = [ + (* Synchronization *) + "__builtin_fence", + (TVoid [], [], false); + (* Integer arithmetic *) + "__builtin_bswap64", + (TInt(IULongLong, []), [TInt(IULongLong, [])], false); + "__builtin_clz", + (TInt(IInt, []), [TInt(IUInt, [])], false); + "__builtin_clzl", + (TInt(IInt, []), [TInt(IULong, [])], false); + "__builtin_clzll", + (TInt(IInt, []), [TInt(IULongLong, [])], false); + "__builtin_cls", + (TInt(IInt, []), [TInt(IInt, [])], false); + "__builtin_clsl", + (TInt(IInt, []), [TInt(ILong, [])], false); + "__builtin_clsll", + (TInt(IInt, []), [TInt(ILongLong, [])], false); + (* Float arithmetic *) + "__builtin_fmadd", + (TFloat(FDouble, []), + [TFloat(FDouble, []); TFloat(FDouble, []); TFloat(FDouble, [])], + false); + "__builtin_fmsub", + (TFloat(FDouble, []), + [TFloat(FDouble, []); TFloat(FDouble, []); TFloat(FDouble, [])], + false); + "__builtin_fnmadd", + (TFloat(FDouble, []), + [TFloat(FDouble, []); TFloat(FDouble, []); TFloat(FDouble, [])], + false); + "__builtin_fnmsub", + (TFloat(FDouble, []), + [TFloat(FDouble, []); TFloat(FDouble, []); TFloat(FDouble, [])], + false); + "__builtin_fmax", + (TFloat(FDouble, []), [TFloat(FDouble, []); TFloat(FDouble, [])], false); + "__builtin_fmin", + (TFloat(FDouble, []), [TFloat(FDouble, []); TFloat(FDouble, [])], false); + ] +} + +(* Expand memory references inside extended asm statements. Used in C2C. *) + +let asm_mem_argument arg = Printf.sprintf "[%s]" arg diff --git a/aarch64/CombineOp.v b/aarch64/CombineOp.v new file mode 100644 index 00000000..4d78c9a0 --- /dev/null +++ b/aarch64/CombineOp.v @@ -0,0 +1,137 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Recognition of combined operations, addressing modes and conditions + during the [CSE] phase. *) + +Require Import Coqlib. +Require Import AST Integers. +Require Import Op. +Require Import CSEdomain. + +Section COMBINE. + +Variable get: valnum -> option rhs. + +Function combine_compimm_ne_0 (x: valnum) : option(condition * list valnum) := + match get x with + | Some(Op (Ocmp c) ys) => Some (c, ys) + | _ => None + end. + +Function combine_compimm_eq_0 (x: valnum) : option(condition * list valnum) := + match get x with + | Some(Op (Ocmp c) ys) => Some (negate_condition c, ys) + | _ => None + end. + +Function combine_compimm_eq_1 (x: valnum) : option(condition * list valnum) := + match get x with + | Some(Op (Ocmp c) ys) => Some (c, ys) + | _ => None + end. + +Function combine_compimm_ne_1 (x: valnum) : option(condition * list valnum) := + match get x with + | Some(Op (Ocmp c) ys) => Some (negate_condition c, ys) + | _ => None + end. + +Function combine_cond (cond: condition) (args: list valnum) : option(condition * list valnum) := + match cond, args with + | Ccompimm Cne n, x::nil => + if Int.eq_dec n Int.zero then combine_compimm_ne_0 x + else if Int.eq_dec n Int.one then combine_compimm_ne_1 x + else None + | Ccompimm Ceq n, x::nil => + if Int.eq_dec n Int.zero then combine_compimm_eq_0 x + else if Int.eq_dec n Int.one then combine_compimm_eq_1 x + else None + | Ccompuimm Cne n, x::nil => + if Int.eq_dec n Int.zero then combine_compimm_ne_0 x + else if Int.eq_dec n Int.one then combine_compimm_ne_1 x + else None + | Ccompuimm Ceq n, x::nil => + if Int.eq_dec n Int.zero then combine_compimm_eq_0 x + else if Int.eq_dec n Int.one then combine_compimm_eq_1 x + else None + | _, _ => None + end. + +Function combine_addr (addr: addressing) (args: list valnum) : option(addressing * list valnum) := + match addr, args with + | Aindexed n, x::nil => + match get x with + | Some(Op (Oaddlimm m) ys) => + Some(Aindexed (Int64.add m n), ys) + | _ => None + end + | _, _ => None + end. + +Function combine_op (op: operation) (args: list valnum) : option(operation * list valnum) := + match op, args with + | Oaddimm n, x :: nil => + match get x with + | Some(Op (Oaddimm m) ys) => Some(Oaddimm (Int.add m n), ys) + | _ => None + end + | Oandimm n, x :: nil => + match get x with + | Some(Op (Oandimm m) ys) => + Some(let p := Int.and m n in + if Int.eq p m then (Omove, x :: nil) else (Oandimm p, ys)) + | _ => None + end + | Oorimm n, x :: nil => + match get x with + | Some(Op (Oorimm m) ys) => Some(Oorimm (Int.or m n), ys) + | _ => None + end + | Oxorimm n, x :: nil => + match get x with + | Some(Op (Oxorimm m) ys) => Some(Oxorimm (Int.xor m n), ys) + | _ => None + end + | Oaddlimm n, x :: nil => + match get x with + | Some(Op (Oaddlimm m) ys) => Some(Oaddlimm (Int64.add m n), ys) + | _ => None + end + | Oandlimm n, x :: nil => + match get x with + | Some(Op (Oandlimm m) ys) => + Some(let p := Int64.and m n in + if Int64.eq p m then (Omove, x :: nil) else (Oandlimm p, ys)) + | _ => None + end + | Oorlimm n, x :: nil => + match get x with + | Some(Op (Oorlimm m) ys) => Some(Oorlimm (Int64.or m n), ys) + | _ => None + end + | Oxorlimm n, x :: nil => + match get x with + | Some(Op (Oxorlimm m) ys) => Some(Oxorlimm (Int64.xor m n), ys) + | _ => None + end + | Ocmp cond, _ => + match combine_cond cond args with + | Some(cond', args') => Some(Ocmp cond', args') + | None => None + end + | _, _ => None + end. + +End COMBINE. + + diff --git a/aarch64/CombineOpproof.v b/aarch64/CombineOpproof.v new file mode 100644 index 00000000..7d13b964 --- /dev/null +++ b/aarch64/CombineOpproof.v @@ -0,0 +1,161 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +Require Import FunInd. +Require Import Coqlib. +Require Import AST Integers Values Memory. +Require Import Op Registers RTL. +Require Import CSEdomain. +Require Import CombineOp. + +Section COMBINE. + +Variable ge: genv. +Variable sp: val. +Variable m: mem. +Variable get: valnum -> option rhs. +Variable valu: valnum -> val. +Hypothesis get_sound: forall v rhs, get v = Some rhs -> rhs_eval_to valu ge sp m rhs (valu v). + +Lemma get_op_sound: + forall v op vl, get v = Some (Op op vl) -> eval_operation ge sp op (map valu vl) m = Some (valu v). +Proof. + intros. exploit get_sound; eauto. intros REV; inv REV; auto. +Qed. + +Ltac UseGetSound := + match goal with + | [ H: get _ = Some _ |- _ ] => + let x := fresh "EQ" in (generalize (get_op_sound _ _ _ H); intros x; simpl in x; FuncInv) + end. + +Lemma combine_compimm_ne_0_sound: + forall x cond args, + combine_compimm_ne_0 get x = Some(cond, args) -> + eval_condition cond (map valu args) m = Val.cmp_bool Cne (valu x) (Vint Int.zero) /\ + eval_condition cond (map valu args) m = Val.cmpu_bool (Mem.valid_pointer m) Cne (valu x) (Vint Int.zero). +Proof. + intros until args. functional induction (combine_compimm_ne_0 get x); intros EQ; inv EQ. + (* of cmp *) + UseGetSound. rewrite <- H. + destruct (eval_condition cond (map valu args) m); simpl; auto. destruct b; auto. +Qed. + +Lemma combine_compimm_eq_0_sound: + forall x cond args, + combine_compimm_eq_0 get x = Some(cond, args) -> + eval_condition cond (map valu args) m = Val.cmp_bool Ceq (valu x) (Vint Int.zero) /\ + eval_condition cond (map valu args) m = Val.cmpu_bool (Mem.valid_pointer m) Ceq (valu x) (Vint Int.zero). +Proof. + intros until args. functional induction (combine_compimm_eq_0 get x); intros EQ; inv EQ. + (* of cmp *) + UseGetSound. rewrite <- H. + rewrite eval_negate_condition. + destruct (eval_condition c (map valu args) m); simpl; auto. destruct b; auto. +Qed. + +Lemma combine_compimm_eq_1_sound: + forall x cond args, + combine_compimm_eq_1 get x = Some(cond, args) -> + eval_condition cond (map valu args) m = Val.cmp_bool Ceq (valu x) (Vint Int.one) /\ + eval_condition cond (map valu args) m = Val.cmpu_bool (Mem.valid_pointer m) Ceq (valu x) (Vint Int.one). +Proof. + intros until args. functional induction (combine_compimm_eq_1 get x); intros EQ; inv EQ. + (* of cmp *) + UseGetSound. rewrite <- H. + destruct (eval_condition cond (map valu args) m); simpl; auto. destruct b; auto. +Qed. + +Lemma combine_compimm_ne_1_sound: + forall x cond args, + combine_compimm_ne_1 get x = Some(cond, args) -> + eval_condition cond (map valu args) m = Val.cmp_bool Cne (valu x) (Vint Int.one) /\ + eval_condition cond (map valu args) m = Val.cmpu_bool (Mem.valid_pointer m) Cne (valu x) (Vint Int.one). +Proof. + intros until args. functional induction (combine_compimm_ne_1 get x); intros EQ; inv EQ. + (* of cmp *) + UseGetSound. rewrite <- H. + rewrite eval_negate_condition. + destruct (eval_condition c (map valu args) m); simpl; auto. destruct b; auto. +Qed. + +Theorem combine_cond_sound: + forall cond args cond' args', + combine_cond get cond args = Some(cond', args') -> + eval_condition cond' (map valu args') m = eval_condition cond (map valu args) m. +Proof. + intros. functional inversion H; subst. + (* compimm ne zero *) + - simpl; eapply combine_compimm_ne_0_sound; eauto. + (* compimm ne one *) + - simpl; eapply combine_compimm_ne_1_sound; eauto. + (* compimm eq zero *) + - simpl; eapply combine_compimm_eq_0_sound; eauto. + (* compimm eq one *) + - simpl; eapply combine_compimm_eq_1_sound; eauto. + (* compuimm ne zero *) + - simpl; eapply combine_compimm_ne_0_sound; eauto. + (* compuimm ne one *) + - simpl; eapply combine_compimm_ne_1_sound; eauto. + (* compuimm eq zero *) + - simpl; eapply combine_compimm_eq_0_sound; eauto. + (* compuimm eq one *) + - simpl; eapply combine_compimm_eq_1_sound; eauto. +Qed. + +Theorem combine_addr_sound: + forall addr args addr' args', + combine_addr get addr args = Some(addr', args') -> + eval_addressing ge sp addr' (map valu args') = eval_addressing ge sp addr (map valu args). +Proof. + intros. functional inversion H; subst. +- (* indexed - addimml *) + UseGetSound. simpl. rewrite <- H0. rewrite Val.addl_assoc. auto. +Qed. + +Theorem combine_op_sound: + forall op args op' args', + combine_op get op args = Some(op', args') -> + eval_operation ge sp op' (map valu args') m = eval_operation ge sp op (map valu args) m. +Proof. + intros. functional inversion H; subst. + (* addimm - addimm *) + - UseGetSound. FuncInv. simpl. + rewrite <- H0. rewrite Val.add_assoc. auto. + (* andimm - andimm *) + - UseGetSound; simpl. + generalize (Int.eq_spec p m0); rewrite H7; intros. + rewrite <- H0. rewrite Val.and_assoc. simpl. fold p. rewrite H1. auto. + - UseGetSound; simpl. + rewrite <- H0. rewrite Val.and_assoc. auto. + (* orimm - orimm *) + - UseGetSound. simpl. rewrite <- H0. rewrite Val.or_assoc. auto. + (* xorimm - xorimm *) + - UseGetSound. simpl. rewrite <- H0. rewrite Val.xor_assoc. auto. + (* addlimm - addlimm *) + - UseGetSound. FuncInv. simpl. + rewrite <- H0. rewrite Val.addl_assoc. auto. + (* andlimm - andlimm *) + - UseGetSound; simpl. + generalize (Int64.eq_spec p m0); rewrite H7; intros. + rewrite <- H0. rewrite Val.andl_assoc. simpl. fold p. rewrite H1. auto. + - UseGetSound; simpl. + rewrite <- H0. rewrite Val.andl_assoc. auto. + (* orlimm - orlimm *) + - UseGetSound. simpl. rewrite <- H0. rewrite Val.orl_assoc. auto. + (* xorlimm - xorlimm *) + - UseGetSound. simpl. rewrite <- H0. rewrite Val.xorl_assoc. auto. + (* cmp *) + - simpl. decEq; decEq. eapply combine_cond_sound; eauto. +Qed. + +End COMBINE. diff --git a/aarch64/ConstpropOp.vp b/aarch64/ConstpropOp.vp new file mode 100644 index 00000000..c0a2c6bf --- /dev/null +++ b/aarch64/ConstpropOp.vp @@ -0,0 +1,401 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Strength reduction for operators and conditions. + This is the machine-dependent part of [Constprop]. *) + +Require Archi. +Require Import Coqlib Compopts. +Require Import AST Integers Floats. +Require Import Op Registers. +Require Import ValueDomain ValueAOp. + +(** * Converting known values to constants *) + +Definition const_for_result (a: aval) : option operation := + match a with + | I n => Some(Ointconst n) + | L n => Some(Olongconst n) + | F n => if Compopts.generate_float_constants tt then Some(Ofloatconst n) else None + | FS n => if Compopts.generate_float_constants tt then Some(Osingleconst n) else None + | Ptr(Gl id ofs) => Some(Oaddrsymbol id ofs) + | Ptr(Stk ofs) => Some(Oaddrstack ofs) + | _ => None + end. + +(** * Operator strength reduction *) + +Definition eval_static_shift (s: shift) (v: int) (n: amount32) : int := + match s with + | Slsl => Int.shl v n + | Slsr => Int.shru v n + | Sasr => Int.shr v n + | Sror => Int.ror v n + end. + +Definition eval_static_shiftl (s: shift) (v: int64) (n: amount64) : int64 := + match s with + | Slsl => Int64.shl' v n + | Slsr => Int64.shru' v n + | Sasr => Int64.shr' v n + | Sror => Int64.ror v (Int64.repr (Int.unsigned n)) + end. + +Definition eval_static_extend (x: extension) (v: int) (n: amount64) : int64 := + Int64.shl' (match x with Xsgn32 => Int64.repr (Int.signed v) + | Xuns32 => Int64.repr (Int.unsigned v) end) + n. + +Nondetfunction cond_strength_reduction + (cond: condition) (args: list reg) (vl: list aval) := + match cond, args, vl with + | Ccomp c, r1 :: r2 :: nil, I n1 :: v2 :: nil => + (Ccompimm (swap_comparison c) n1, r2 :: nil) + | Ccomp c, r1 :: r2 :: nil, v1 :: I n2 :: nil => + (Ccompimm c n2, r1 :: nil) + | Ccompu c, r1 :: r2 :: nil, I n1 :: v2 :: nil => + (Ccompuimm (swap_comparison c) n1, r2 :: nil) + | Ccompu c, r1 :: r2 :: nil, v1 :: I n2 :: nil => + (Ccompuimm c n2, r1 :: nil) + | Ccompshift c s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => + (Ccompimm c (eval_static_shift s n2 a), r1 :: nil) + | Ccompushift c s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => + (Ccompuimm c (eval_static_shift s n2 a), r1 :: nil) + | Ccompl c, r1 :: r2 :: nil, L n1 :: v2 :: nil => + (Ccomplimm (swap_comparison c) n1, r2 :: nil) + | Ccompl c, r1 :: r2 :: nil, v1 :: L n2 :: nil => + (Ccomplimm c n2, r1 :: nil) + | Ccomplu c, r1 :: r2 :: nil, L n1 :: v2 :: nil => + (Ccompluimm (swap_comparison c) n1, r2 :: nil) + | Ccomplu c, r1 :: r2 :: nil, v1 :: L n2 :: nil => + (Ccompluimm c n2, r1 :: nil) + | Ccomplshift c s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => + (Ccomplimm c (eval_static_shiftl s n2 a), r1 :: nil) + | Ccomplushift c s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => + (Ccompluimm c (eval_static_shiftl s n2 a), r1 :: nil) + | Ccompf c, r1 :: r2 :: nil, F n1 :: v2 :: nil => + if Float.eq_dec n1 Float.zero + then (Ccompfzero (swap_comparison c), r2 :: nil) + else (cond, args) + | Ccompf c, r1 :: r2 :: nil, v1 :: F n2 :: nil => + if Float.eq_dec n2 Float.zero + then (Ccompfzero c, r1 :: nil) + else (cond, args) + | Cnotcompf c, r1 :: r2 :: nil, F n1 :: v2 :: nil => + if Float.eq_dec n1 Float.zero + then (Cnotcompfzero (swap_comparison c), r2 :: nil) + else (cond, args) + | Cnotcompf c, r1 :: r2 :: nil, v1 :: F n2 :: nil => + if Float.eq_dec n2 Float.zero + then (Cnotcompfzero c, r1 :: nil) + else (cond, args) + | Ccompfs c, r1 :: r2 :: nil, FS n1 :: v2 :: nil => + if Float32.eq_dec n1 Float32.zero + then (Ccompfszero (swap_comparison c), r2 :: nil) + else (cond, args) + | Ccompfs c, r1 :: r2 :: nil, v1 :: FS n2 :: nil => + if Float32.eq_dec n2 Float32.zero + then (Ccompfszero c, r1 :: nil) + else (cond, args) + | Cnotcompfs c, r1 :: r2 :: nil, FS n1 :: v2 :: nil => + if Float32.eq_dec n1 Float32.zero + then (Cnotcompfszero (swap_comparison c), r2 :: nil) + else (cond, args) + | Cnotcompfs c, r1 :: r2 :: nil, v1 :: FS n2 :: nil => + if Float32.eq_dec n2 Float32.zero + then (Cnotcompfszero c, r1 :: nil) + else (cond, args) + | _, _, _ => + (cond, args) + end. + +Definition make_cmp_base (c: condition) (args: list reg) (vl: list aval) := + let (c', args') := cond_strength_reduction c args vl in (Ocmp c', args'). + +Definition make_cmp_imm_eq (c: condition) (args: list reg) (vl: list aval) + (n: int) (r1: reg) (v1: aval) := + if Int.eq_dec n Int.one && vincl v1 (Uns Ptop 1) then (Omove, r1 :: nil) + else if Int.eq_dec n Int.zero && vincl v1 (Uns Ptop 1) then (Oxorimm Int.one, r1 :: nil) + else make_cmp_base c args vl. + +Definition make_cmp_imm_ne (c: condition) (args: list reg) (vl: list aval) + (n: int) (r1: reg) (v1: aval) := + if Int.eq_dec n Int.zero && vincl v1 (Uns Ptop 1) then (Omove, r1 :: nil) + else if Int.eq_dec n Int.one && vincl v1 (Uns Ptop 1) then (Oxorimm Int.one, r1 :: nil) + else make_cmp_base c args vl. + +Nondetfunction make_cmp (c: condition) (args: list reg) (vl: list aval) := + match c, args, vl with + | Ccompimm Ceq n, r1 :: nil, v1 :: nil => + make_cmp_imm_eq c args vl n r1 v1 + | Ccompimm Cne n, r1 :: nil, v1 :: nil => + make_cmp_imm_ne c args vl n r1 v1 + | Ccompuimm Ceq n, r1 :: nil, v1 :: nil => + make_cmp_imm_eq c args vl n r1 v1 + | Ccompuimm Cne n, r1 :: nil, v1 :: nil => + make_cmp_imm_ne c args vl n r1 v1 + | _, _, _ => + make_cmp_base c args vl + end. + +Definition make_select (c: condition) (ty: typ) + (r1 r2: reg) (args: list reg) (vl: list aval) := + match resolve_branch (eval_static_condition c vl) with + | Some b => (Omove, (if b then r1 else r2) :: nil) + | None => + let (c', args') := cond_strength_reduction c args vl in + (Osel c' ty, r1 :: r2 :: args') + end. + +Definition make_addimm (n: int) (r: reg) := + if Int.eq n Int.zero + then (Omove, r :: nil) + else (Oaddimm n, r :: nil). + +Definition make_shlimm (n: int) (r1 r2: reg) := + if Int.eq n Int.zero then (Omove, r1 :: nil) + else if Int.ltu n Int.iwordsize then (Oshift Slsl (mk_amount32 n), r1 :: nil) + else (Oshl, r1 :: r2 :: nil). + +Definition make_shrimm (n: int) (r1 r2: reg) := + if Int.eq n Int.zero then (Omove, r1 :: nil) + else if Int.ltu n Int.iwordsize then (Oshift Sasr (mk_amount32 n), r1 :: nil) + else (Oshr, r1 :: r2 :: nil). + +Definition make_shruimm (n: int) (r1 r2: reg) := + if Int.eq n Int.zero then (Omove, r1 :: nil) + else if Int.ltu n Int.iwordsize then (Oshift Slsr (mk_amount32 n), r1 :: nil) + else (Oshru, r1 :: r2 :: nil). + +Definition make_mulimm (n: int) (r1 r2: reg) := + if Int.eq n Int.zero then + (Ointconst Int.zero, nil) + else if Int.eq n Int.one then + (Omove, r1 :: nil) + else + match Int.is_power2 n with + | Some l => (Oshift Slsl (mk_amount32 l), r1 :: nil) + | None => (Omul, r1 :: r2 :: nil) + end. + +Definition make_andimm (n: int) (r: reg) (a: aval) := + if Int.eq n Int.zero then (Ointconst Int.zero, nil) + else if Int.eq n Int.mone then (Omove, r :: nil) + else if match a with Uns _ m => Int.eq (Int.zero_ext m (Int.not n)) Int.zero + | _ => false end + then (Omove, r :: nil) + else (Oandimm n, r :: nil). + +Definition make_orimm (n: int) (r: reg) := + if Int.eq n Int.zero then (Omove, r :: nil) + else if Int.eq n Int.mone then (Ointconst Int.mone, nil) + else (Oorimm n, r :: nil). + +Definition make_xorimm (n: int) (r: reg) := + if Int.eq n Int.zero then (Omove, r :: nil) + else (Oxorimm n, r :: nil). + +Definition make_divimm n (r1 r2: reg) := + if Int.eq n Int.one then + (Omove, r1 :: nil) + else + match Int.is_power2 n with + | Some l => if Int.ltu l (Int.repr 31) + then (Oshrximm l, r1 :: nil) + else (Odiv, r1 :: r2 :: nil) + | None => (Odiv, r1 :: r2 :: nil) + end. + +Definition make_divuimm n (r1 r2: reg) := + if Int.eq n Int.one then + (Omove, r1 :: nil) + else + match Int.is_power2 n with + | Some l => (Oshift Slsr (mk_amount32 l), r1 :: nil) + | None => (Odivu, r1 :: r2 :: nil) + end. + +Definition make_addlimm (n: int64) (r: reg) := + if Int64.eq n Int64.zero + then (Omove, r :: nil) + else (Oaddlimm n, r :: nil). + +Definition make_shllimm (n: int) (r1 r2: reg) := + if Int.eq n Int.zero then (Omove, r1 :: nil) + else if Int.ltu n Int64.iwordsize' then (Oshiftl Slsl (mk_amount64 n), r1 :: nil) + else (Oshll, r1 :: r2 :: nil). + +Definition make_shrlimm (n: int) (r1 r2: reg) := + if Int.eq n Int.zero then (Omove, r1 :: nil) + else if Int.ltu n Int64.iwordsize' then (Oshiftl Sasr (mk_amount64 n), r1 :: nil) + else (Oshrl, r1 :: r2 :: nil). + +Definition make_shrluimm (n: int) (r1 r2: reg) := + if Int.eq n Int.zero then (Omove, r1 :: nil) + else if Int.ltu n Int64.iwordsize' then (Oshiftl Slsr (mk_amount64 n), r1 :: nil) + else (Oshrlu, r1 :: r2 :: nil). + +Definition make_mullimm (n: int64) (r1 r2: reg) := + if Int64.eq n Int64.zero then + (Olongconst Int64.zero, nil) + else if Int64.eq n Int64.one then + (Omove, r1 :: nil) + else + match Int64.is_power2' n with + | Some l => (Oshiftl Slsl (mk_amount64 l), r1 :: nil) + | None => (Omull, r1 :: r2 :: nil) + end. + +Definition make_andlimm (n: int64) (r: reg) (a: aval) := + if Int64.eq n Int64.zero then (Olongconst Int64.zero, nil) + else if Int64.eq n Int64.mone then (Omove, r :: nil) + else (Oandlimm n, r :: nil). + +Definition make_orlimm (n: int64) (r: reg) := + if Int64.eq n Int64.zero then (Omove, r :: nil) + else if Int64.eq n Int64.mone then (Olongconst Int64.mone, nil) + else (Oorlimm n, r :: nil). + +Definition make_xorlimm (n: int64) (r: reg) := + if Int64.eq n Int64.zero then (Omove, r :: nil) + else (Oxorlimm n, r :: nil). + +Definition make_divlimm n (r1 r2: reg) := + match Int64.is_power2' n with + | Some l => if Int.ltu l (Int.repr 63) + then (Oshrlximm l, r1 :: nil) + else (Odivl, r1 :: r2 :: nil) + | None => (Odivl, r1 :: r2 :: nil) + end. + +Definition make_divluimm n (r1 r2: reg) := + match Int64.is_power2' n with + | Some l => (Oshiftl Slsr (mk_amount64 l), r1 :: nil) + | None => (Odivlu, r1 :: r2 :: nil) + end. + +Definition make_mulfimm (n: float) (r r1 r2: reg) := + if Float.eq_dec n (Float.of_int (Int.repr 2)) + then (Oaddf, r :: r :: nil) + else (Omulf, r1 :: r2 :: nil). + +Definition make_mulfsimm (n: float32) (r r1 r2: reg) := + if Float32.eq_dec n (Float32.of_int (Int.repr 2)) + then (Oaddfs, r :: r :: nil) + else (Omulfs, r1 :: r2 :: nil). + +Definition make_zext (s: Z) (r: reg) (a: aval) := + if vincl a (Uns Ptop s) then (Omove, r :: nil) else (Ozext s, r :: nil). + +Definition make_sext (s: Z) (r: reg) (a: aval) := + if vincl a (Sgn Ptop s) then (Omove, r :: nil) else (Osext s, r :: nil). + +Nondetfunction op_strength_reduction + (op: operation) (args: list reg) (vl: list aval) := + match op, args, vl with + | Oadd, r1 :: r2 :: nil, I n1 :: v2 :: nil => make_addimm n1 r2 + | Oadd, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_addimm n2 r1 + | Oaddshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_addimm (eval_static_shift s n2 a) r1 + | Osub, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_addimm (Int.neg n2) r1 + | Osubshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_addimm (Int.neg (eval_static_shift s n2 a)) r1 + | Omul, r1 :: r2 :: nil, I n1 :: v2 :: nil => make_mulimm n1 r2 r1 + | Omul, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_mulimm n2 r1 r2 + | Odiv, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_divimm n2 r1 r2 + | Odivu, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_divuimm n2 r1 r2 + | Oand, r1 :: r2 :: nil, I n1 :: v2 :: nil => make_andimm n1 r2 v2 + | Oand, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_andimm n2 r1 v1 + | Oandshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_andimm (eval_static_shift s n2 a) r1 v1 + | Oandimm n, r1 :: nil, v1 :: nil => make_andimm n r1 v1 + | Oor, r1 :: r2 :: nil, I n1 :: v2 :: nil => make_orimm n1 r2 + | Oor, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_orimm n2 r1 + | Oorshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_orimm (eval_static_shift s n2 a) r1 + | Oxor, r1 :: r2 :: nil, I n1 :: v2 :: nil => make_xorimm n1 r2 + | Oxor, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_xorimm n2 r1 + | Oxorshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_xorimm (eval_static_shift s n2 a) r1 + | Obic, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_andimm (Int.not n2) r1 v1 + | Obicshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_andimm (Int.not (eval_static_shift s n2 a)) r1 v1 + | Oorn, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_orimm (Int.not n2) r1 + | Oornshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_orimm (Int.not (eval_static_shift s n2 a)) r1 + | Oeqv, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_xorimm (Int.not n2) r1 + | Oeqvshift s a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_xorimm (Int.not (eval_static_shift s n2 a)) r1 + | Oshl, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shlimm n2 r1 r2 + | Oshr, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shrimm n2 r1 r2 + | Oshru, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shruimm n2 r1 r2 + | Ozext s, r1 :: nil, v1 :: nil => make_zext s r1 v1 + | Osext s, r1 :: nil, v1 :: nil => make_sext s r1 v1 + + | Oaddl, r1 :: r2 :: nil, L n1 :: v2 :: nil => make_addlimm n1 r2 + | Oaddl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_addlimm n2 r1 + | Oaddlshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_addlimm (eval_static_shiftl s n2 a) r1 + | Oaddlext x a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_addlimm (eval_static_extend x n2 a) r1 + | Osubl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_addlimm (Int64.neg n2) r1 + | Osublshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_addlimm (Int64.neg (eval_static_shiftl s n2 a)) r1 + | Osublext x a, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_addlimm (Int64.neg (eval_static_extend x n2 a)) r1 + | Omull, r1 :: r2 :: nil, L n1 :: v2 :: nil => make_mullimm n1 r2 r1 + | Omull, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_mullimm n2 r1 r2 + | Odivl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_divlimm n2 r1 r2 + | Odivlu, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_divluimm n2 r1 r2 + | Oandl, r1 :: r2 :: nil, L n1 :: v2 :: nil => make_andlimm n1 r2 v2 + | Oandl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_andlimm n2 r1 v1 + | Oandlshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_andlimm (eval_static_shiftl s n2 a) r1 v1 + | Oandlimm n, r1 :: nil, v1 :: nil => make_andlimm n r1 v1 + | Oorl, r1 :: r2 :: nil, L n1 :: v2 :: nil => make_orlimm n1 r2 + | Oorl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_orlimm n2 r1 + | Oorlshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_orlimm (eval_static_shiftl s n2 a) r1 + | Oxorl, r1 :: r2 :: nil, L n1 :: v2 :: nil => make_xorlimm n1 r2 + | Oxorl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_xorlimm n2 r1 + | Oxorlshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_xorlimm (eval_static_shiftl s n2 a) r1 + | Obicl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_andlimm (Int64.not n2) r1 v1 + | Obiclshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_andlimm (Int64.not (eval_static_shiftl s n2 a)) r1 v1 + | Oornl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_orlimm (Int64.not n2) r1 + | Oornlshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_orlimm (Int64.not (eval_static_shiftl s n2 a)) r1 + | Oeqvl, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_xorlimm (Int64.not n2) r1 + | Oeqvlshift s a, r1 :: r2 :: nil, v1 :: L n2 :: nil => make_xorlimm (Int64.not (eval_static_shiftl s n2 a)) r1 + | Oshll, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shllimm n2 r1 r2 + | Oshrl, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shrlimm n2 r1 r2 + | Oshrlu, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shrluimm n2 r1 r2 + | Ocmp c, args, vl => make_cmp c args vl + | Osel c ty, r1 :: r2 :: args, v1 :: v2 :: vl => make_select c ty r1 r2 args vl + | Omulf, r1 :: r2 :: nil, v1 :: F n2 :: nil => make_mulfimm n2 r1 r1 r2 + | Omulf, r1 :: r2 :: nil, F n1 :: v2 :: nil => make_mulfimm n1 r2 r1 r2 + | Omulfs, r1 :: r2 :: nil, v1 :: FS n2 :: nil => make_mulfsimm n2 r1 r1 r2 + | Omulfs, r1 :: r2 :: nil, FS n1 :: v2 :: nil => make_mulfsimm n1 r2 r1 r2 + | _, _, _ => (op, args) + end. + +Nondetfunction addr_strength_reduction + (addr: addressing) (args: list reg) (vl: list aval) := + match addr, args, vl with + | Aindexed n, r1 :: nil, Ptr(Gl symb n1) :: nil => + (Aglobal symb (Ptrofs.add n1 (Ptrofs.of_int64 n)), nil) + | Aindexed n, r1 :: nil, Ptr(Stk n1) :: nil => + (Ainstack (Ptrofs.add n1 (Ptrofs.of_int64 n)), nil) + | Aindexed2, r1 :: r2 :: nil, Ptr(Stk n1) :: L n2 :: nil => + (Ainstack (Ptrofs.add n1 (Ptrofs.of_int64 n2)), nil) + | Aindexed2, r1 :: r2 :: nil, L n1 :: Ptr(Stk n2) :: nil => + (Ainstack (Ptrofs.add (Ptrofs.of_int64 n1) n2), nil) + | Aindexed2, r1 :: r2 :: nil, L n1 :: v2 :: nil => + (Aindexed n1, r2 :: nil) + | Aindexed2, r1 :: r2 :: nil, v1 :: L n2 :: nil => + (Aindexed n2, r1 :: nil) + | Aindexed2shift a, r1 :: r2 :: nil, Ptr(Stk n1) :: L n2 :: nil => + (Ainstack (Ptrofs.add n1 (Ptrofs.of_int64 (Int64.shl' n2 a))), nil) + | Aindexed2shift a, r1 :: r2 :: nil, v1 :: L n2 :: nil => + (Aindexed (Int64.shl' n2 a), r1 :: nil) + | Aindexed2ext x a, r1 :: r2 :: nil, Ptr(Stk n1) :: I n2 :: nil => + (Ainstack (Ptrofs.add n1 (Ptrofs.of_int64 (eval_static_extend x n2 a))), nil) + | Aindexed2ext x a, r1 :: r2 :: nil, v1 :: I n2 :: nil => + (Aindexed (eval_static_extend x n2 a), r1 :: nil) + | _, _, _ => + (addr, args) + end. + diff --git a/aarch64/ConstpropOpproof.v b/aarch64/ConstpropOpproof.v new file mode 100644 index 00000000..deab7cd4 --- /dev/null +++ b/aarch64/ConstpropOpproof.v @@ -0,0 +1,838 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Correctness proof for operator strength reduction. *) + +Require Import Coqlib Compopts. +Require Import Integers Floats Values Memory Globalenvs Events. +Require Import Op Registers RTL ValueDomain ValueAOp ValueAnalysis. +Require Import ConstpropOp. + +Local Transparent Archi.ptr64. + +Section STRENGTH_REDUCTION. + +Variable bc: block_classification. +Variable ge: genv. +Hypothesis GENV: genv_match bc ge. +Variable sp: block. +Hypothesis STACK: bc sp = BCstack. +Variable ae: AE.t. +Variable e: regset. +Variable m: mem. +Hypothesis MATCH: ematch bc e ae. + +Lemma match_G: + forall r id ofs, + AE.get r ae = Ptr(Gl id ofs) -> Val.lessdef e#r (Genv.symbol_address ge id ofs). +Proof. + intros. apply vmatch_ptr_gl with bc; auto. rewrite <- H. apply MATCH. +Qed. + +Lemma match_S: + forall r ofs, + AE.get r ae = Ptr(Stk ofs) -> Val.lessdef e#r (Vptr sp ofs). +Proof. + intros. apply vmatch_ptr_stk with bc; auto. rewrite <- H. apply MATCH. +Qed. + +Ltac InvApproxRegs := + match goal with + | [ H: _ :: _ = _ :: _ |- _ ] => + injection H; clear H; intros; InvApproxRegs + | [ H: ?v = AE.get ?r ae |- _ ] => + generalize (MATCH r); rewrite <- H; clear H; intro; InvApproxRegs + | _ => idtac + end. + +Ltac SimplVM := + match goal with + | [ H: vmatch _ ?v (I ?n) |- _ ] => + let E := fresh in + assert (E: v = Vint n) by (inversion H; auto); + rewrite E in *; clear H; SimplVM + | [ H: vmatch _ ?v (L ?n) |- _ ] => + let E := fresh in + assert (E: v = Vlong n) by (inversion H; auto); + rewrite E in *; clear H; SimplVM + | [ H: vmatch _ ?v (F ?n) |- _ ] => + let E := fresh in + assert (E: v = Vfloat n) by (inversion H; auto); + rewrite E in *; clear H; SimplVM + | [ H: vmatch _ ?v (FS ?n) |- _ ] => + let E := fresh in + assert (E: v = Vsingle n) by (inversion H; auto); + rewrite E in *; clear H; SimplVM + | [ H: vmatch _ ?v (Ptr(Gl ?id ?ofs)) |- _ ] => + let E := fresh in + assert (E: Val.lessdef v (Genv.symbol_address ge id ofs)) by (eapply vmatch_ptr_gl; eauto); + clear H; SimplVM + | [ H: vmatch _ ?v (Ptr(Stk ?ofs)) |- _ ] => + let E := fresh in + assert (E: Val.lessdef v (Vptr sp ofs)) by (eapply vmatch_ptr_stk; eauto); + clear H; SimplVM + | _ => idtac + end. + +Lemma const_for_result_correct: + forall a op v, + const_for_result a = Some op -> + vmatch bc v a -> + exists v', eval_operation ge (Vptr sp Ptrofs.zero) op nil m = Some v' /\ Val.lessdef v v'. +Proof. + unfold const_for_result; intros; destruct a; inv H; SimplVM. +- (* integer *) + exists (Vint n); auto. +- (* long *) + exists (Vlong n); auto. +- (* float *) + destruct (Compopts.generate_float_constants tt); inv H2. exists (Vfloat f); auto. +- (* single *) + destruct (Compopts.generate_float_constants tt); inv H2. exists (Vsingle f); auto. +- (* pointer *) + destruct p; try discriminate; SimplVM. + + (* global *) + inv H2. exists (Genv.symbol_address ge id ofs); auto. + + (* stack *) + inv H2. exists (Vptr sp ofs); split; auto. simpl. rewrite Ptrofs.add_zero_l; auto. +Qed. + +Lemma eval_static_shift_correct: forall s v a, + eval_shift s (Vint v) a = Vint (eval_static_shift s v a). +Proof. + intros; destruct s; simpl; rewrite ? a32_range; auto. +Qed. + +Lemma eval_static_shiftl_correct: forall s v a, + eval_shiftl s (Vlong v) a = Vlong (eval_static_shiftl s v a). +Proof. + intros; destruct s; simpl; rewrite ? a64_range; auto. +Qed. + +Lemma eval_static_extend_correct: forall x v a, + eval_extend x (Vint v) a = Vlong (eval_static_extend x v a). +Proof. + unfold eval_extend, eval_static_extend; intros; destruct x; simpl; rewrite ? a64_range; auto. +Qed. + +Lemma cond_strength_reduction_correct: + forall cond args vl, + vl = map (fun r => AE.get r ae) args -> + let (cond', args') := cond_strength_reduction cond args vl in + eval_condition cond' e##args' m = eval_condition cond e##args m. +Proof. + intros until vl. unfold cond_strength_reduction. + case (cond_strength_reduction_match cond args vl); simpl; intros; InvApproxRegs; SimplVM. +- apply Val.swap_cmp_bool. +- auto. +- apply Val.swap_cmpu_bool. +- auto. +- rewrite eval_static_shift_correct; auto. +- rewrite eval_static_shift_correct; auto. +- apply Val.swap_cmpl_bool. +- auto. +- apply Val.swap_cmplu_bool. +- auto. +- rewrite eval_static_shiftl_correct; auto. +- rewrite eval_static_shiftl_correct; auto. +- destruct (Float.eq_dec n1 Float.zero). + subst n1. simpl. destruct (e#r2); simpl; auto. rewrite Float.cmp_swap. auto. + simpl. rewrite H1; auto. +- destruct (Float.eq_dec n2 Float.zero). + subst n2. simpl. auto. + simpl. rewrite H1; auto. +- destruct (Float.eq_dec n1 Float.zero). + subst n1. simpl. destruct (e#r2); simpl; auto. rewrite Float.cmp_swap. auto. + simpl. rewrite H1; auto. +- destruct (Float.eq_dec n2 Float.zero); simpl; auto. + subst n2; auto. + rewrite H1; auto. +- destruct (Float32.eq_dec n1 Float32.zero). + subst n1. simpl. destruct (e#r2); simpl; auto. rewrite Float32.cmp_swap. auto. + simpl. rewrite H1; auto. +- destruct (Float32.eq_dec n2 Float32.zero). + subst n2. simpl. auto. + simpl. rewrite H1; auto. +- destruct (Float32.eq_dec n1 Float32.zero). + subst n1. simpl. destruct (e#r2); simpl; auto. rewrite Float32.cmp_swap. auto. + simpl. rewrite H1; auto. +- destruct (Float32.eq_dec n2 Float32.zero); simpl; auto. + subst n2; auto. + rewrite H1; auto. +- auto. +Qed. + +Lemma make_cmp_base_correct: + forall c args vl, + vl = map (fun r => AE.get r ae) args -> + let (op', args') := make_cmp_base c args vl in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op' e##args' m = Some v + /\ Val.lessdef (Val.of_optbool (eval_condition c e##args m)) v. +Proof. + intros. unfold make_cmp_base. + generalize (cond_strength_reduction_correct c args vl H). + destruct (cond_strength_reduction c args vl) as [c' args']. intros EQ. + econstructor; split. simpl; eauto. rewrite EQ. auto. +Qed. + +Lemma make_cmp_correct: + forall c args vl, + vl = map (fun r => AE.get r ae) args -> + let (op', args') := make_cmp c args vl in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op' e##args' m = Some v + /\ Val.lessdef (Val.of_optbool (eval_condition c e##args m)) v. +Proof. + intros c args vl. + assert (Y: forall r, vincl (AE.get r ae) (Uns Ptop 1) = true -> + e#r = Vundef \/ e#r = Vint Int.zero \/ e#r = Vint Int.one). + { intros. apply vmatch_Uns_1 with bc Ptop. eapply vmatch_ge. eapply vincl_ge; eauto. apply MATCH. } + unfold make_cmp. case (make_cmp_match c args vl); intros. +- unfold make_cmp_imm_eq. + destruct (Int.eq_dec n Int.one && vincl v1 (Uns Ptop 1)) eqn:E1. ++ simpl in H; inv H. InvBooleans. subst n. + exists (e#r1); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. ++ destruct (Int.eq_dec n Int.zero && vincl v1 (Uns Ptop 1)) eqn:E0. +* simpl in H; inv H. InvBooleans. subst n. + exists (Val.xor e#r1 (Vint Int.one)); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. +* apply make_cmp_base_correct; auto. +- unfold make_cmp_imm_ne. + destruct (Int.eq_dec n Int.zero && vincl v1 (Uns Ptop 1)) eqn:E0. ++ simpl in H; inv H. InvBooleans. subst n. + exists (e#r1); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. ++ destruct (Int.eq_dec n Int.one && vincl v1 (Uns Ptop 1)) eqn:E1. +* simpl in H; inv H. InvBooleans. subst n. + exists (Val.xor e#r1 (Vint Int.one)); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. +* apply make_cmp_base_correct; auto. +- unfold make_cmp_imm_eq. + destruct (Int.eq_dec n Int.one && vincl v1 (Uns Ptop 1)) eqn:E1. ++ simpl in H; inv H. InvBooleans. subst n. + exists (e#r1); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. ++ destruct (Int.eq_dec n Int.zero && vincl v1 (Uns Ptop 1)) eqn:E0. +* simpl in H; inv H. InvBooleans. subst n. + exists (Val.xor e#r1 (Vint Int.one)); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. +* apply make_cmp_base_correct; auto. +- unfold make_cmp_imm_ne. + destruct (Int.eq_dec n Int.zero && vincl v1 (Uns Ptop 1)) eqn:E0. ++ simpl in H; inv H. InvBooleans. subst n. + exists (e#r1); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. ++ destruct (Int.eq_dec n Int.one && vincl v1 (Uns Ptop 1)) eqn:E1. +* simpl in H; inv H. InvBooleans. subst n. + exists (Val.xor e#r1 (Vint Int.one)); split; auto. simpl. + exploit Y; eauto. intros [A | [A | A]]; rewrite A; simpl; auto. +* apply make_cmp_base_correct; auto. +- apply make_cmp_base_correct; auto. +Qed. + +Lemma make_select_correct: + forall c ty r1 r2 args vl, + vl = map (fun r => AE.get r ae) args -> + let (op', args') := make_select c ty r1 r2 args vl in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op' e##args' m = Some v + /\ Val.lessdef (Val.select (eval_condition c e##args m) e#r1 e#r2 ty) v. +Proof. + unfold make_select; intros. + destruct (resolve_branch (eval_static_condition c vl)) as [b|] eqn:RB. +- exists (if b then e#r1 else e#r2); split. ++ simpl. destruct b; auto. ++ destruct (eval_condition c e##args m) as [b'|] eqn:EC; simpl; auto. + assert (b = b'). + { eapply resolve_branch_sound; eauto. + rewrite <- EC. apply eval_static_condition_sound with bc. + subst vl. exact (aregs_sound _ _ _ args MATCH). } + subst b'. apply Val.lessdef_normalize. +- generalize (cond_strength_reduction_correct c args vl H). + destruct (cond_strength_reduction c args vl) as [cond' args']; intros EQ. + econstructor; split. simpl; eauto. rewrite EQ; auto. +Qed. + +Lemma make_addimm_correct: + forall n r, + let (op, args) := make_addimm n r in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.add e#r (Vint n)) v. +Proof. + intros. unfold make_addimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. + subst. exists (e#r); split; auto. + destruct (e#r); simpl; rewrite ?Int.add_zero; auto. + exists (Val.add e#r (Vint n)); split; auto. +Qed. + +Lemma make_shlimm_correct: + forall n r1 r2, + e#r2 = Vint n -> + let (op, args) := make_shlimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.shl e#r1 (Vint n)) v. +Proof. +Local Opaque mk_amount32. + intros; unfold make_shlimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. rewrite Int.shl_zero. auto. + destruct (Int.ltu n Int.iwordsize) eqn:L. + econstructor; split. simpl. eauto. rewrite mk_amount32_eq; auto. + econstructor; split. simpl. eauto. rewrite H; auto. +Qed. + +Lemma make_shrimm_correct: + forall n r1 r2, + e#r2 = Vint n -> + let (op, args) := make_shrimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.shr e#r1 (Vint n)) v. +Proof. + intros; unfold make_shrimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. rewrite Int.shr_zero. auto. + destruct (Int.ltu n Int.iwordsize) eqn:L. + econstructor; split. simpl. eauto. rewrite mk_amount32_eq; auto. + econstructor; split. simpl. eauto. rewrite H; auto. +Qed. + +Lemma make_shruimm_correct: + forall n r1 r2, + e#r2 = Vint n -> + let (op, args) := make_shruimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.shru e#r1 (Vint n)) v. +Proof. + intros; unfold make_shruimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. rewrite Int.shru_zero. auto. + destruct (Int.ltu n Int.iwordsize) eqn:L. + econstructor; split. simpl. eauto. rewrite mk_amount32_eq; auto. + econstructor; split. simpl. eauto. rewrite H; auto. +Qed. + +Lemma make_mulimm_correct: + forall n r1 r2, + e#r2 = Vint n -> + let (op, args) := make_mulimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.mul e#r1 (Vint n)) v. +Proof. + intros; unfold make_mulimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. subst. + exists (Vint Int.zero); split; auto. destruct (e#r1); simpl; auto. rewrite Int.mul_zero; auto. + predSpec Int.eq Int.eq_spec n Int.one; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. rewrite Int.mul_one; auto. + destruct (Int.is_power2 n) eqn:?; intros. + rewrite (Val.mul_pow2 e#r1 _ _ Heqo). econstructor; split. simpl; eauto. + rewrite mk_amount32_eq; auto. eapply Int.is_power2_range; eauto. + econstructor; split; eauto. simpl. rewrite H; auto. +Qed. + +Lemma make_divimm_correct: + forall n r1 r2 v, + Val.divs e#r1 e#r2 = Some v -> + e#r2 = Vint n -> + let (op, args) := make_divimm n r1 r2 in + exists w, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some w /\ Val.lessdef v w. +Proof. + intros; unfold make_divimm. + predSpec Int.eq Int.eq_spec n Int.one; intros. subst. rewrite H0 in H. + destruct (e#r1) eqn:?; + try (rewrite Val.divs_one in H; exists (Vint i); split; simpl; try rewrite Heqv0; auto); + inv H; auto. + destruct (Int.is_power2 n) eqn:?. + destruct (Int.ltu i (Int.repr 31)) eqn:?. + exists v; split; auto. simpl. eapply Val.divs_pow2; eauto. congruence. + exists v; auto. + exists v; auto. +Qed. + +Lemma make_divuimm_correct: + forall n r1 r2 v, + Val.divu e#r1 e#r2 = Some v -> + e#r2 = Vint n -> + let (op, args) := make_divuimm n r1 r2 in + exists w, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some w /\ Val.lessdef v w. +Proof. + intros; unfold make_divuimm. + predSpec Int.eq Int.eq_spec n Int.one; intros. subst. rewrite H0 in H. + destruct (e#r1) eqn:?; + try (rewrite Val.divu_one in H; exists (Vint i); split; simpl; try rewrite Heqv0; auto); + inv H; auto. + destruct (Int.is_power2 n) eqn:?. + econstructor; split. simpl; eauto. + rewrite mk_amount32_eq by (eapply Int.is_power2_range; eauto). + rewrite H0 in H. erewrite Val.divu_pow2 by eauto. auto. + exists v; auto. +Qed. + +Lemma make_andimm_correct: + forall n r x, + vmatch bc e#r x -> + let (op, args) := make_andimm n r x in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.and e#r (Vint n)) v. +Proof. + intros; unfold make_andimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. + subst n. exists (Vint Int.zero); split; auto. destruct (e#r); simpl; auto. rewrite Int.and_zero; auto. + predSpec Int.eq Int.eq_spec n Int.mone; intros. + subst n. exists (e#r); split; auto. destruct (e#r); simpl; auto. rewrite Int.and_mone; auto. + destruct (match x with Uns _ k => Int.eq (Int.zero_ext k (Int.not n)) Int.zero + | _ => false end) eqn:UNS. + destruct x; try congruence. + exists (e#r); split; auto. + inv H; auto. simpl. replace (Int.and i n) with i; auto. + generalize (Int.eq_spec (Int.zero_ext n0 (Int.not n)) Int.zero); rewrite UNS; intro EQ. + Int.bit_solve. destruct (zlt i0 n0). + replace (Int.testbit n i0) with (negb (Int.testbit Int.zero i0)). + rewrite Int.bits_zero. simpl. rewrite andb_true_r. auto. + rewrite <- EQ. rewrite Int.bits_zero_ext by omega. rewrite zlt_true by auto. + rewrite Int.bits_not by auto. apply negb_involutive. + rewrite H6 by auto. auto. + econstructor; split; eauto. auto. +Qed. + +Lemma make_orimm_correct: + forall n r, + let (op, args) := make_orimm n r in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.or e#r (Vint n)) v. +Proof. + intros; unfold make_orimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. + subst n. exists (e#r); split; auto. destruct (e#r); simpl; auto. rewrite Int.or_zero; auto. + predSpec Int.eq Int.eq_spec n Int.mone; intros. + subst n. exists (Vint Int.mone); split; auto. destruct (e#r); simpl; auto. rewrite Int.or_mone; auto. + econstructor; split; eauto. auto. +Qed. + +Lemma make_xorimm_correct: + forall n r, + let (op, args) := make_xorimm n r in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.xor e#r (Vint n)) v. +Proof. + intros; unfold make_xorimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. + subst n. exists (e#r); split; auto. destruct (e#r); simpl; auto. rewrite Int.xor_zero; auto. + predSpec Int.eq Int.eq_spec n Int.mone; intros. + subst n. exists (Val.notint e#r); split; auto. + econstructor; split; eauto. auto. +Qed. + +Lemma make_addlimm_correct: + forall n r, + let (op, args) := make_addlimm n r in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.addl e#r (Vlong n)) v. +Proof. + intros. unfold make_addlimm. + predSpec Int64.eq Int64.eq_spec n Int64.zero; intros. + subst. exists (e#r); split; auto. + destruct (e#r); simpl; auto; rewrite ? Int64.add_zero, ? Ptrofs.add_zero; auto. + exists (Val.addl e#r (Vlong n)); split; auto. +Qed. + +Lemma make_shllimm_correct: + forall n r1 r2, + e#r2 = Vint n -> + let (op, args) := make_shllimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.shll e#r1 (Vint n)) v. +Proof. +Local Opaque mk_amount64. + intros; unfold make_shllimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. + unfold Int64.shl'. rewrite Z.shiftl_0_r, Int64.repr_unsigned. auto. + destruct (Int.ltu n Int64.iwordsize') eqn:L. + econstructor; split. simpl. eauto. rewrite mk_amount64_eq; auto. + econstructor; split. simpl. eauto. rewrite H; auto. +Qed. + +Lemma make_shrlimm_correct: + forall n r1 r2, + e#r2 = Vint n -> + let (op, args) := make_shrlimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.shrl e#r1 (Vint n)) v. +Proof. + intros; unfold make_shrlimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. + unfold Int64.shr'. rewrite Z.shiftr_0_r, Int64.repr_signed. auto. + destruct (Int.ltu n Int64.iwordsize') eqn:L. + econstructor; split. simpl. eauto. rewrite mk_amount64_eq; auto. + econstructor; split. simpl. eauto. rewrite H; auto. +Qed. + +Lemma make_shrluimm_correct: + forall n r1 r2, + e#r2 = Vint n -> + let (op, args) := make_shrluimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.shrlu e#r1 (Vint n)) v. +Proof. + intros; unfold make_shrluimm. + predSpec Int.eq Int.eq_spec n Int.zero; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. + unfold Int64.shru'. rewrite Z.shiftr_0_r, Int64.repr_unsigned. auto. + destruct (Int.ltu n Int64.iwordsize') eqn:L. + econstructor; split. simpl. eauto. rewrite mk_amount64_eq; auto. + econstructor; split. simpl. eauto. rewrite H; auto. +Qed. + +Lemma make_mullimm_correct: + forall n r1 r2, + e#r2 = Vlong n -> + let (op, args) := make_mullimm n r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.mull e#r1 (Vlong n)) v. +Proof. + intros; unfold make_mullimm. + predSpec Int64.eq Int64.eq_spec n Int64.zero; intros. subst. + exists (Vlong Int64.zero); split; auto. destruct (e#r1); simpl; auto. rewrite Int64.mul_zero; auto. + predSpec Int64.eq Int64.eq_spec n Int64.one; intros. subst. + exists (e#r1); split; auto. destruct (e#r1); simpl; auto. rewrite Int64.mul_one; auto. + destruct (Int64.is_power2' n) eqn:?; intros. + econstructor; split. simpl; eauto. + rewrite mk_amount64_eq by (eapply Int64.is_power2'_range; eauto). + destruct (e#r1); simpl; auto. + erewrite Int64.is_power2'_range by eauto. + erewrite Int64.mul_pow2' by eauto. auto. + econstructor; split; eauto. simpl; rewrite H; auto. +Qed. + +Lemma make_divlimm_correct: + forall n r1 r2 v, + Val.divls e#r1 e#r2 = Some v -> + e#r2 = Vlong n -> + let (op, args) := make_divlimm n r1 r2 in + exists w, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some w /\ Val.lessdef v w. +Proof. + intros; unfold make_divlimm. + destruct (Int64.is_power2' n) eqn:?. destruct (Int.ltu i (Int.repr 63)) eqn:?. + rewrite H0 in H. econstructor; split. simpl; eauto. eapply Val.divls_pow2; eauto. auto. + exists v; auto. + exists v; auto. +Qed. + +Lemma make_divluimm_correct: + forall n r1 r2 v, + Val.divlu e#r1 e#r2 = Some v -> + e#r2 = Vlong n -> + let (op, args) := make_divluimm n r1 r2 in + exists w, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some w /\ Val.lessdef v w. +Proof. + intros; unfold make_divluimm. + destruct (Int64.is_power2' n) eqn:?. + econstructor; split. simpl; eauto. + rewrite mk_amount64_eq by (eapply Int64.is_power2'_range; eauto). + rewrite H0 in H. destruct (e#r1); inv H. destruct (Int64.eq n Int64.zero); inv H2. + simpl. + erewrite Int64.is_power2'_range by eauto. + erewrite Int64.divu_pow2' by eauto. auto. + exists v; auto. +Qed. + +Lemma make_andlimm_correct: + forall n r x, + let (op, args) := make_andlimm n r x in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.andl e#r (Vlong n)) v. +Proof. + intros; unfold make_andlimm. + predSpec Int64.eq Int64.eq_spec n Int64.zero; intros. + subst n. exists (Vlong Int64.zero); split; auto. destruct (e#r); simpl; auto. rewrite Int64.and_zero; auto. + predSpec Int64.eq Int64.eq_spec n Int64.mone; intros. + subst n. exists (e#r); split; auto. destruct (e#r); simpl; auto. rewrite Int64.and_mone; auto. + econstructor; split; eauto. auto. +Qed. + +Lemma make_orlimm_correct: + forall n r, + let (op, args) := make_orlimm n r in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.orl e#r (Vlong n)) v. +Proof. + intros; unfold make_orlimm. + predSpec Int64.eq Int64.eq_spec n Int64.zero; intros. + subst n. exists (e#r); split; auto. destruct (e#r); simpl; auto. rewrite Int64.or_zero; auto. + predSpec Int64.eq Int64.eq_spec n Int64.mone; intros. + subst n. exists (Vlong Int64.mone); split; auto. destruct (e#r); simpl; auto. rewrite Int64.or_mone; auto. + econstructor; split; eauto. auto. +Qed. + +Lemma make_xorlimm_correct: + forall n r, + let (op, args) := make_xorlimm n r in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.xorl e#r (Vlong n)) v. +Proof. + intros; unfold make_xorlimm. + predSpec Int64.eq Int64.eq_spec n Int64.zero; intros. + subst n. exists (e#r); split; auto. destruct (e#r); simpl; auto. rewrite Int64.xor_zero; auto. + predSpec Int64.eq Int64.eq_spec n Int64.mone; intros. + subst n. exists (Val.notl e#r); split; auto. + econstructor; split; eauto. auto. +Qed. + +Lemma make_mulfimm_correct: + forall n r1 r2, + e#r2 = Vfloat n -> + let (op, args) := make_mulfimm n r1 r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.mulf e#r1 e#r2) v. +Proof. + intros; unfold make_mulfimm. + destruct (Float.eq_dec n (Float.of_int (Int.repr 2))); intros. + simpl. econstructor; split. eauto. rewrite H; subst n. + destruct (e#r1); simpl; auto. rewrite Float.mul2_add; auto. + simpl. econstructor; split; eauto. +Qed. + +Lemma make_mulfimm_correct_2: + forall n r1 r2, + e#r1 = Vfloat n -> + let (op, args) := make_mulfimm n r2 r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.mulf e#r1 e#r2) v. +Proof. + intros; unfold make_mulfimm. + destruct (Float.eq_dec n (Float.of_int (Int.repr 2))); intros. + simpl. econstructor; split. eauto. rewrite H; subst n. + destruct (e#r2); simpl; auto. rewrite Float.mul2_add; auto. + rewrite Float.mul_commut; auto. + simpl. econstructor; split; eauto. +Qed. + +Lemma make_mulfsimm_correct: + forall n r1 r2, + e#r2 = Vsingle n -> + let (op, args) := make_mulfsimm n r1 r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.mulfs e#r1 e#r2) v. +Proof. + intros; unfold make_mulfsimm. + destruct (Float32.eq_dec n (Float32.of_int (Int.repr 2))); intros. + simpl. econstructor; split. eauto. rewrite H; subst n. + destruct (e#r1); simpl; auto. rewrite Float32.mul2_add; auto. + simpl. econstructor; split; eauto. +Qed. + +Lemma make_mulfsimm_correct_2: + forall n r1 r2, + e#r1 = Vsingle n -> + let (op, args) := make_mulfsimm n r2 r1 r2 in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.mulfs e#r1 e#r2) v. +Proof. + intros; unfold make_mulfsimm. + destruct (Float32.eq_dec n (Float32.of_int (Int.repr 2))); intros. + simpl. econstructor; split. eauto. rewrite H; subst n. + destruct (e#r2); simpl; auto. rewrite Float32.mul2_add; auto. + rewrite Float32.mul_commut; auto. + simpl. econstructor; split; eauto. +Qed. + +Lemma make_zext_correct: + forall s r x, + vmatch bc e#r x -> + let (op, args) := make_zext s r x in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.zero_ext s e#r) v. +Proof. + intros; unfold make_zext. destruct (vincl x (Uns Ptop s)) eqn:INCL. +- exists e#r; split; auto. + assert (V: vmatch bc e#r (Uns Ptop s)). + { eapply vmatch_ge; eauto. apply vincl_ge; auto. } + inv V; simpl; auto. rewrite is_uns_zero_ext in H4 by auto. rewrite H4; auto. +- econstructor; split; simpl; eauto. +Qed. + +Lemma make_sext_correct: + forall s r x, + vmatch bc e#r x -> + let (op, args) := make_sext s r x in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v /\ Val.lessdef (Val.sign_ext s e#r) v. +Proof. + intros; unfold make_sext. destruct (vincl x (Sgn Ptop s)) eqn:INCL. +- exists e#r; split; auto. + assert (V: vmatch bc e#r (Sgn Ptop s)). + { eapply vmatch_ge; eauto. apply vincl_ge; auto. } + inv V; simpl; auto. rewrite is_sgn_sign_ext in H4 by auto. rewrite H4; auto. +- econstructor; split; simpl; eauto. +Qed. + +Lemma op_strength_reduction_correct: + forall op args vl v, + vl = map (fun r => AE.get r ae) args -> + eval_operation ge (Vptr sp Ptrofs.zero) op e##args m = Some v -> + let (op', args') := op_strength_reduction op args vl in + exists w, eval_operation ge (Vptr sp Ptrofs.zero) op' e##args' m = Some w /\ Val.lessdef v w. +Proof. + intros until v; unfold op_strength_reduction; + case (op_strength_reduction_match op args vl); simpl; intros. +- (* add 1 *) + rewrite Val.add_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_addimm_correct; auto. +- (* add 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_addimm_correct; auto. +- (* addshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct. apply make_addimm_correct; auto. +- (* sub *) + InvApproxRegs; SimplVM; inv H0. rewrite Val.sub_add_opp. apply make_addimm_correct; auto. +- (* subshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct, Val.sub_add_opp. apply make_addimm_correct; auto. +- (* mul 1 *) + rewrite Val.mul_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_mulimm_correct; auto. +- (* mul 2*) + InvApproxRegs; SimplVM; inv H0. apply make_mulimm_correct; auto. +- (* divs *) + assert (e#r2 = Vint n2). clear H0. InvApproxRegs; SimplVM; auto. + apply make_divimm_correct; auto. +- (* divu *) + assert (e#r2 = Vint n2). clear H0. InvApproxRegs; SimplVM; auto. + apply make_divuimm_correct; auto. +- (* and 1 *) + rewrite Val.and_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_andimm_correct; auto. +- (* and 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_andimm_correct; auto. +- (* andshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct. apply make_andimm_correct; auto. +- (* andimm *) + inv H; inv H0. apply make_andimm_correct; auto. +- (* or 1 *) + rewrite Val.or_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_orimm_correct; auto. +- (* or 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_orimm_correct; auto. +- (* orshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct. apply make_orimm_correct; auto. +- (* xor 1 *) + rewrite Val.xor_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_xorimm_correct; auto. +- (* xor 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_xorimm_correct; auto. +- (* xorshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct. apply make_xorimm_correct; auto. +- (* bic *) + InvApproxRegs; SimplVM; inv H0. apply make_andimm_correct; auto. +- (* bicshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct. apply make_andimm_correct; auto. +- (* orn *) + InvApproxRegs; SimplVM; inv H0. apply make_orimm_correct; auto. +- (* ornshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct. apply make_orimm_correct; auto. +- (* eor *) + InvApproxRegs; SimplVM; inv H0. apply make_xorimm_correct; auto. +- (* eorshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shift_correct. apply make_xorimm_correct; auto. +- (* shl *) + InvApproxRegs; SimplVM; inv H0. apply make_shlimm_correct; auto. +- (* shr *) + InvApproxRegs; SimplVM; inv H0. apply make_shrimm_correct; auto. +- (* shru *) + InvApproxRegs; SimplVM; inv H0. apply make_shruimm_correct; auto. +- (* zext *) + InvApproxRegs; SimplVM; inv H0. apply make_zext_correct; auto. +- (* sext *) + InvApproxRegs; SimplVM; inv H0. apply make_sext_correct; auto. +- (* addl 1 *) + rewrite Val.addl_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_addlimm_correct; auto. +- (* addl 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_addlimm_correct; auto. +- (* addshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct. apply make_addlimm_correct; auto. +- (* addext *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_extend_correct. apply make_addlimm_correct; auto. +- (* subl *) + InvApproxRegs; SimplVM; inv H0. rewrite Val.subl_addl_opp. apply make_addlimm_correct; auto. +- (* sublshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct, Val.subl_addl_opp. apply make_addlimm_correct; auto. +- (* sublextend *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_extend_correct, Val.subl_addl_opp. apply make_addlimm_correct; auto. +- (* mull 1 *) + rewrite Val.mull_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_mullimm_correct; auto. +- (* mull 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_mullimm_correct; auto. +- (* divl *) + assert (e#r2 = Vlong n2). clear H0. InvApproxRegs; SimplVM; auto. + apply make_divlimm_correct; auto. +- (* divlu *) + assert (e#r2 = Vlong n2). clear H0. InvApproxRegs; SimplVM; auto. + apply make_divluimm_correct; auto. +- (* andl 1 *) + rewrite Val.andl_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_andlimm_correct; auto. +- (* andl 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_andlimm_correct; auto. +- (* andlshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct. apply make_andlimm_correct; auto. +- (* andlimm *) + inv H; inv H0. apply make_andlimm_correct; auto. +- (* orl 1 *) + rewrite Val.orl_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_orlimm_correct; auto. +- (* orl 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_orlimm_correct; auto. +- (* orlshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct. apply make_orlimm_correct; auto. +- (* xorl 1 *) + rewrite Val.xorl_commut in H0. InvApproxRegs; SimplVM; inv H0. apply make_xorlimm_correct; auto. +- (* xorl 2 *) + InvApproxRegs; SimplVM; inv H0. apply make_xorlimm_correct; auto. +- (* xorlshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct. apply make_xorlimm_correct; auto. +- (* bicl *) + InvApproxRegs; SimplVM; inv H0. apply make_andlimm_correct; auto. +- (* biclshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct. apply make_andlimm_correct; auto. +- (* ornl *) + InvApproxRegs; SimplVM; inv H0. apply make_orlimm_correct; auto. +- (* ornlshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct. apply make_orlimm_correct; auto. +- (* eorl *) + InvApproxRegs; SimplVM; inv H0. apply make_xorlimm_correct; auto. +- (* eorlshift *) + InvApproxRegs; SimplVM; inv H0. rewrite eval_static_shiftl_correct. apply make_xorlimm_correct; auto. +- (* shll *) + InvApproxRegs; SimplVM; inv H0. apply make_shllimm_correct; auto. +- (* shrl *) + InvApproxRegs; SimplVM; inv H0. apply make_shrlimm_correct; auto. +- (* shrlu *) + InvApproxRegs; SimplVM; inv H0. apply make_shrluimm_correct; auto. +- (* cond *) + inv H0. apply make_cmp_correct; auto. +- (* select *) + inv H0. apply make_select_correct; congruence. +- (* mulf 1 *) + InvApproxRegs; SimplVM; inv H0. rewrite <- H2. apply make_mulfimm_correct; auto. +- (* mulf 2 *) + InvApproxRegs; SimplVM; inv H0. fold (Val.mulf (Vfloat n1) e#r2). + rewrite <- H2. apply make_mulfimm_correct_2; auto. +- (* mulfs 1 *) + InvApproxRegs; SimplVM; inv H0. rewrite <- H2. apply make_mulfsimm_correct; auto. +- (* mulfs 2 *) + InvApproxRegs; SimplVM; inv H0. fold (Val.mulfs (Vsingle n1) e#r2). + rewrite <- H2. apply make_mulfsimm_correct_2; auto. +- (* default *) + exists v; auto. +Qed. + +Lemma addr_strength_reduction_correct: + forall addr args vl res, + vl = map (fun r => AE.get r ae) args -> + eval_addressing ge (Vptr sp Ptrofs.zero) addr e##args = Some res -> + let (addr', args') := addr_strength_reduction addr args vl in + exists res', eval_addressing ge (Vptr sp Ptrofs.zero) addr' e##args' = Some res' /\ Val.lessdef res res'. +Proof. + intros until res. unfold addr_strength_reduction. + destruct (addr_strength_reduction_match addr args vl); simpl; + intros VL EA; InvApproxRegs; SimplVM; try (inv EA). +- econstructor; split; eauto. inv H0; simpl; auto. rewrite H2. + unfold Genv.symbol_address. destruct (Genv.find_symbol ge symb); auto. +- rewrite Ptrofs.add_zero_l. econstructor; split; eauto. + inv H0; auto. rewrite H2; auto. +- rewrite Ptrofs.add_zero_l. econstructor; split; eauto. + inv H; auto. rewrite H3; auto. +- rewrite Ptrofs.add_zero_l. econstructor; split; eauto. + inv H0; auto. rewrite H3. rewrite Ptrofs.add_commut; auto. +- econstructor; split; eauto. rewrite Val.addl_commut. auto. +- econstructor; split; eauto. +- rewrite Ptrofs.add_zero_l. rewrite a64_range. econstructor; split; eauto. + inv H; auto. rewrite H3; auto. +- rewrite a64_range. econstructor; split; eauto. +- rewrite Ptrofs.add_zero_l, eval_static_extend_correct. + econstructor; split; eauto. inv H; auto. rewrite H3; auto. +- rewrite eval_static_extend_correct. + econstructor; split; eauto. +- exists res; auto. +Qed. + +End STRENGTH_REDUCTION. diff --git a/aarch64/Conventions1.v b/aarch64/Conventions1.v new file mode 100644 index 00000000..efda835d --- /dev/null +++ b/aarch64/Conventions1.v @@ -0,0 +1,285 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Function calling conventions and other conventions regarding the use of + machine registers and stack slots. *) + +Require Import Coqlib Decidableplus. +Require Import AST Events Locations. +Require Archi. + +(** * Classification of machine registers *) + +(** Machine registers (type [mreg] in module [Locations]) are divided in: +- Callee-save registers, whose value is preserved across a function call. +- Caller-save registers that can be modified during a function call. + + We follow the Procedure Call Standard for the ARM 64-bit Architecture + (AArch64) document: R19-R28 and F8-F15 are callee-save. *) + +Definition is_callee_save (r: mreg): bool := + match r with + | R0 | R1 | R2 | R3 | R4 | R5 | R6 | R7 => false + | R8 | R9 | R10 | R11 | R12 | R13 | R14 | R15 => false + | R17 => false + | R19 | R20 | R21 | R22 | R23 => true + | R24 | R25 | R26 | R27 | R28 => true + | R29 => false + | F0 | F1 | F2 | F3 | F4 | F5 | F6 | F7 => false + | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15 => true + | F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 => false + | F24 | F25 | F26 | F27 | F28 | F29 | F30 | F31 => false + end. + +Definition int_caller_save_regs := + R0 :: R1 :: R2 :: R3 :: R4 :: R5 :: R6 :: R7 + :: R8 :: R9 :: R10 :: R11 :: R12 :: R13 :: R14 :: R15 + :: R17 :: R29 :: nil. + +Definition float_caller_save_regs := + F0 :: F1 :: F2 :: F3 :: F4 :: F5 :: F6 :: F7 + :: F16 :: F17 :: F18 :: F19 :: F20 :: F21 :: F22 :: F23 + :: F24 :: F25 :: F26 :: F27 :: F28 :: F29 :: F30 :: F31 :: nil. + +Definition int_callee_save_regs := + R19 :: R20 :: R21 :: R22 :: R23 + :: R24 :: R25 :: R26 :: R27 :: R28 :: nil. + +Definition float_callee_save_regs := + F8 :: F9 :: F10 :: F11 :: F12 :: F13 :: F14 :: F15 :: nil. + +Definition destroyed_at_call := + List.filter (fun r => negb (is_callee_save r)) all_mregs. + +Definition dummy_int_reg := R0. (**r Used in [Coloring]. *) +Definition dummy_float_reg := F0. (**r Used in [Coloring]. *) + +Definition callee_save_type := mreg_type. + +Definition is_float_reg (r: mreg): bool := + match r with + | R0 | R1 | R2 | R3 | R4 | R5 | R6 | R7 + | R8 | R9 | R10 | R11 | R12 | R13 | R14 | R15 + | R17 | R19 | R20 | R21 | R22 | R23 + | R24 | R25 | R26 | R27 | R28 + | R29 => false + | F0 | F1 | F2 | F3 | F4 | F5 | F6 | F7 + | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15 + | F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 + | F24 | F25 | F26 | F27 | F28 | F29 | F30 | F31 => true + end. + +(** * Function calling conventions *) + +(** The functions in this section determine the locations (machine registers + and stack slots) used to communicate arguments and results between the + caller and the callee during function calls. These locations are functions + of the signature of the function and of the call instruction. + Agreement between the caller and the callee on the locations to use + is guaranteed by our dynamic semantics for Cminor and RTL, which demand + that the signature of the call instruction is identical to that of the + called function. + + Calling conventions are largely arbitrary: they must respect the properties + proved in this section (such as no overlapping between the locations + of function arguments), but this leaves much liberty in choosing actual + locations. *) + +(** ** Location of function result *) + +(** The result value of a function is passed back to the caller in + registers [R0] or [F0], depending on the type of the + returned value. We treat a function without result as a function + with one integer result. *) + +Definition loc_result (s: signature) : rpair mreg := + match proj_sig_res s with + | Tint | Tlong | Tany32 | Tany64 => One R0 + | Tfloat | Tsingle => One F0 + end. + +(** The result registers have types compatible with that given in the signature. *) + +Lemma loc_result_type: + forall sig, + subtype (proj_sig_res sig) (typ_rpair mreg_type (loc_result sig)) = true. +Proof. + intros. unfold loc_result. destruct (proj_sig_res sig); auto. +Qed. + +(** The result locations are caller-save registers *) + +Lemma loc_result_caller_save: + forall (s: signature), + forall_rpair (fun r => is_callee_save r = false) (loc_result s). +Proof. + intros. + unfold loc_result. destruct (proj_sig_res s); simpl; auto. +Qed. + +(** If the result is in a pair of registers, those registers are distinct and have type [Tint] at least. *) + +Lemma loc_result_pair: + forall sg, + match loc_result sg with + | One _ => True + | Twolong r1 r2 => + r1 <> r2 /\ proj_sig_res sg = Tlong + /\ subtype Tint (mreg_type r1) = true /\ subtype Tint (mreg_type r2) = true + /\ Archi.ptr64 = false + end. +Proof. + intros; unfold loc_result; destruct (proj_sig_res sg); exact I. +Qed. + +(** The location of the result depends only on the result part of the signature *) + +Lemma loc_result_exten: + forall s1 s2, s1.(sig_res) = s2.(sig_res) -> loc_result s1 = loc_result s2. +Proof. + intros. unfold loc_result, proj_sig_res. rewrite H; auto. +Qed. + +(** ** Location of function arguments *) + +(** +- The first 8 integer arguments are passed in registers [R0...R7]. +- The first 8 FP arguments are passed in registers [F0...F7]. +- Extra arguments are passed on the stack, in [Outgoing] slots of size + 64 bits (2 words), consecutively assigned, starting at word offset 0. +**) + +Definition int_param_regs := + R0 :: R1 :: R2 :: R3 :: R4 :: R5 :: R6 :: R7 :: nil. + +Definition float_param_regs := + F0 :: F1 :: F2 :: F3 :: F4 :: F5 :: F6 :: F7 :: nil. + +Fixpoint loc_arguments_rec + (tyl: list typ) (ir fr ofs: Z) {struct tyl} : list (rpair loc) := + match tyl with + | nil => nil + | (Tint | Tlong | Tany32 | Tany64) as ty :: tys => + match list_nth_z int_param_regs ir with + | None => + One (S Outgoing ofs ty) :: loc_arguments_rec tys ir fr (ofs + 2) + | Some ireg => + One (R ireg) :: loc_arguments_rec tys (ir + 1) fr ofs + end + | (Tfloat | Tsingle) as ty :: tys => + match list_nth_z float_param_regs fr with + | None => + One (S Outgoing ofs ty) :: loc_arguments_rec tys ir fr (ofs + 2) + | Some freg => + One (R freg) :: loc_arguments_rec tys ir (fr + 1) ofs + end + end. + +(** [loc_arguments s] returns the list of locations where to store arguments + when calling a function with signature [s]. *) + +Definition loc_arguments (s: signature) : list (rpair loc) := + loc_arguments_rec s.(sig_args) 0 0 0. + +(** Argument locations are either caller-save registers or [Outgoing] + stack slots at nonnegative offsets. *) + +Definition loc_argument_acceptable (l: loc) : Prop := + match l with + | R r => is_callee_save r = false + | S Outgoing ofs ty => ofs >= 0 /\ (typealign ty | ofs) + | _ => False + end. + +Definition loc_argument_charact (ofs: Z) (l: loc) : Prop := + match l with + | R r => In r int_param_regs \/ In r float_param_regs + | S Outgoing ofs' ty => ofs' >= ofs /\ (2 | ofs') + | _ => False + end. + +Remark loc_arguments_rec_charact: + forall tyl ir fr ofs p, + In p (loc_arguments_rec tyl ir fr ofs) -> (2 | ofs) -> forall_rpair (loc_argument_charact ofs) p. +Proof. + assert (X: forall ofs1 ofs2 l, loc_argument_charact ofs2 l -> ofs1 <= ofs2 -> loc_argument_charact ofs1 l). + { destruct l; simpl; intros; auto. destruct sl; auto. intuition omega. } + assert (Y: forall ofs1 ofs2 p, forall_rpair (loc_argument_charact ofs2) p -> ofs1 <= ofs2 -> forall_rpair (loc_argument_charact ofs1) p). + { destruct p; simpl; intuition eauto. } + assert (Z: forall ofs, (2 | ofs) -> (2 | ofs + 2)). + { intros. apply Z.divide_add_r; auto. apply Z.divide_refl. } +Opaque list_nth_z. + induction tyl; simpl loc_arguments_rec; intros. +- contradiction. +- assert (A: forall ty, In p + match list_nth_z int_param_regs ir with + | Some ireg => One (R ireg) :: loc_arguments_rec tyl (ir + 1) fr ofs + | None => One (S Outgoing ofs ty) :: loc_arguments_rec tyl ir fr (ofs + 2) + end -> + forall_rpair (loc_argument_charact ofs) p). + { intros. destruct (list_nth_z int_param_regs ir) as [r|] eqn:E; destruct H1. + subst. left. eapply list_nth_z_in; eauto. + eapply IHtyl; eauto. + subst. split. omega. assumption. + eapply Y; eauto. omega. } + assert (B: forall ty, In p + match list_nth_z float_param_regs fr with + | Some ireg => One (R ireg) :: loc_arguments_rec tyl ir (fr + 1) ofs + | None => One (S Outgoing ofs ty) :: loc_arguments_rec tyl ir fr (ofs + 2) + end -> + forall_rpair (loc_argument_charact ofs) p). + { intros. destruct (list_nth_z float_param_regs fr) as [r|] eqn:E; destruct H1. + subst. right. eapply list_nth_z_in; eauto. + eapply IHtyl; eauto. + subst. split. omega. assumption. + eapply Y; eauto. omega. } + destruct a; eauto. +Qed. + +Lemma loc_arguments_acceptable: + forall (s: signature) (p: rpair loc), + In p (loc_arguments s) -> forall_rpair loc_argument_acceptable p. +Proof. + unfold loc_arguments; intros. + assert (A: forall r, In r int_param_regs -> is_callee_save r = false) by decide_goal. + assert (B: forall r, In r float_param_regs -> is_callee_save r = false) by decide_goal. + assert (X: forall l, loc_argument_charact 0 l -> loc_argument_acceptable l). + { unfold loc_argument_charact, loc_argument_acceptable. + destruct l as [r | [] ofs ty]; auto. intros [C|C]; auto. + intros [C D]. split; auto. apply Z.divide_trans with 2; auto. + exists (2 / typealign ty); destruct ty; reflexivity. + } + exploit loc_arguments_rec_charact; eauto using Z.divide_0_r. + unfold forall_rpair; destruct p; intuition auto. +Qed. + +Hint Resolve loc_arguments_acceptable: locs. + +Lemma loc_arguments_main: + loc_arguments signature_main = nil. +Proof. + unfold loc_arguments; reflexivity. +Qed. + +(** ** Normalization of function results *) + +(** According to the AAPCS64 ABI specification, "padding bits" in the return + value of a function have unpredictable values and must be ignored. + Consequently, we force normalization of return values of small integer + types (8- and 16-bit integers), so that the top bits (the "padding bits") + are proper sign- or zero-extensions of the small integer value. *) + +Definition return_value_needs_normalization (t: rettype) : bool := + match t with + | Tint8signed | Tint8unsigned | Tint16signed | Tint16unsigned => true + | _ => false + end. diff --git a/aarch64/Machregs.v b/aarch64/Machregs.v new file mode 100644 index 00000000..b2a2308e --- /dev/null +++ b/aarch64/Machregs.v @@ -0,0 +1,210 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +Require Import String. +Require Import Coqlib Decidableplus Maps. +Require Import AST Op. + +(** ** Machine registers *) + +(** Integer register 16 is reserved as temporary and for call veeners. + Integer register 18 is reserved as the platform register. + Integer register 30 is reserved for the return address. *) + +Inductive mreg: Type := + (** Allocatable integer regs *) + | R0 | R1 | R2 | R3 | R4 | R5 | R6 | R7 + | R8 | R9 | R10 | R11 | R12 | R13 | R14 | R15 + | R17 | R19 | R20 | R21 | R22 | R23 + | R24 | R25 | R26 | R27 | R28 | R29 + (** Allocatable floating-point regs *) + | F0 | F1 | F2 | F3 | F4 | F5 | F6 | F7 + | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15 + | F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 + | F24 | F25 | F26 | F27 | F28 | F29 | F30 | F31. + +Lemma mreg_eq: forall (r1 r2: mreg), {r1 = r2} + {r1 <> r2}. +Proof. decide equality. Defined. +Global Opaque mreg_eq. + +Definition all_mregs := + R0 :: R1 :: R2 :: R3 :: R4 :: R5 :: R6 :: R7 + :: R8 :: R9 :: R10 :: R11 :: R12 :: R13 :: R14 :: R15 + :: R17 :: R19 :: R20 :: R21 :: R22 :: R23 + :: R24 :: R25 :: R26 :: R27 :: R28 :: R29 + :: F0 :: F1 :: F2 :: F3 :: F4 :: F5 :: F6 :: F7 + :: F8 :: F9 :: F10 :: F11 :: F12 :: F13 :: F14 :: F15 + :: F16 :: F17 :: F18 :: F19 :: F20 :: F21 :: F22 :: F23 + :: F24 :: F25 :: F26 :: F27 :: F28 :: F29 :: F30 :: F31 + :: nil. + +Lemma all_mregs_complete: + forall (r: mreg), In r all_mregs. +Proof. + assert (forall r, proj_sumbool (In_dec mreg_eq r all_mregs) = true) by (destruct r; reflexivity). + intros. specialize (H r). InvBooleans. auto. +Qed. + +Instance Decidable_eq_mreg : forall (x y: mreg), Decidable (eq x y) := Decidable_eq mreg_eq. + +Instance Finite_mreg : Finite mreg := { + Finite_elements := all_mregs; + Finite_elements_spec := all_mregs_complete +}. + +Definition mreg_type (r: mreg): typ := Tany64. + +Open Scope positive_scope. + +Module IndexedMreg <: INDEXED_TYPE. + Definition t := mreg. + Definition eq := mreg_eq. + Definition index (r: mreg): positive := + match r with + | R0 => 1 | R1 => 2 | R2 => 3 | R3 => 4 + | R4 => 5 | R5 => 6 | R6 => 7 | R7 => 8 + | R8 => 9 | R9 => 10 | R10 => 11 | R11 => 12 + | R12 => 13 | R13 => 14 | R14 => 15 | R15 => 16 + | R17 => 17 | R19 => 19 + | R20 => 20 | R21 => 21 | R22 => 22 | R23 => 23 + | R24 => 24 | R25 => 25 | R26 => 26 | R27 => 27 + | R28 => 28 | R29 => 29 + | F0 => 32 | F1 => 33 | F2 => 34 | F3 => 35 + | F4 => 36 | F5 => 37 | F6 => 38 | F7 => 39 + | F8 => 40 | F9 => 41 | F10 => 42 | F11 => 43 + | F12 => 44 | F13 => 45 | F14 => 46 | F15 => 47 + | F16 => 48 | F17 => 49 | F18 => 50 | F19 => 51 + | F20 => 52 | F21 => 53 | F22 => 54 | F23 => 55 + | F24 => 56 | F25 => 57 | F26 => 58 | F27 => 59 + | F28 => 60 | F29 => 61 | F30 => 62 | F31 => 63 + end. + Lemma index_inj: + forall r1 r2, index r1 = index r2 -> r1 = r2. + Proof. + decide_goal. + Qed. +End IndexedMreg. + +Definition is_stack_reg (r: mreg) : bool := false. + +(** ** Names of registers *) + +Local Open Scope string_scope. + +Definition register_names := + ("X0", R0) :: ("X1", R1) :: ("X2", R2) :: ("X3", R3) + :: ("X4", R4) :: ("X5", R5) :: ("X6", R6) :: ("X7", R7) + :: ("X8", R8) :: ("X9", R9) :: ("X10", R10) :: ("X11", R11) + :: ("X12", R12) :: ("X13", R13) :: ("X14", R14) :: ("X15", R15) + :: ("X17", R17) :: ("X19", R19) + :: ("X20", R20) :: ("X21", R21) :: ("X22", R22) :: ("X23", R23) + :: ("X24", R24) :: ("X25", R25) :: ("X26", R26) :: ("X27", R27) + :: ("X28", R28) :: ("X29", R29) + :: ("D0", F0) :: ("D1", F1) :: ("D2", F2) :: ("D3", F3) + :: ("D4", F4) :: ("D5", F5) :: ("D6", F6) :: ("D7", F7) + :: ("D8", F8) :: ("D9", F9) :: ("D10", F10) :: ("D11", F11) + :: ("D12", F12) :: ("D13", F13) :: ("D14", F14) :: ("D15", F15) + :: ("D16", F16) :: ("D17", F17) :: ("D18", F18) :: ("D19", F19) + :: ("D20", F20) :: ("D21", F21) :: ("D22", F22) :: ("D23", F23) + :: ("D24", F24) :: ("D25", F25) :: ("D26", F26) :: ("D27", F27) + :: ("D28", F28) :: ("D29", F29) :: ("D30", F30) :: ("D31", F31) + :: nil. + +Definition register_by_name (s: string) : option mreg := + let fix assoc (l: list (string * mreg)) : option mreg := + match l with + | nil => None + | (s1, r1) :: l' => if string_dec s s1 then Some r1 else assoc l' + end + in assoc register_names. + +(** ** Destroyed registers, preferred registers *) + +Definition destroyed_by_op (op: operation): list mreg := + match op with + | Oshrximm _ | Oshrlximm _ => R17 :: nil + | _ => nil + end. + +Definition destroyed_by_load (chunk: memory_chunk) (addr: addressing): list mreg := + nil. + +Definition destroyed_by_store (chunk: memory_chunk) (addr: addressing): list mreg := nil. + +Definition destroyed_by_cond (cond: condition): list mreg := nil. + +Definition destroyed_by_jumptable: list mreg := R17 :: nil. + +Fixpoint destroyed_by_clobber (cl: list string): list mreg := + match cl with + | nil => nil + | c1 :: cl => + match register_by_name c1 with + | Some r => r :: destroyed_by_clobber cl + | None => destroyed_by_clobber cl + end + end. + +Definition destroyed_by_builtin (ef: external_function): list mreg := + match ef with + | EF_memcpy sz al => R15 :: R17 :: R29 :: nil + | EF_inline_asm txt sg clob => destroyed_by_clobber clob + | _ => nil + end. + +Definition destroyed_by_setstack (ty: typ): list mreg := nil. + +Definition destroyed_at_function_entry: list mreg := R29 :: nil. + +Definition destroyed_at_indirect_call: list mreg := nil. + +Definition temp_for_parent_frame: mreg := R29. + +Definition mregs_for_operation (op: operation): list (option mreg) * option mreg := + (nil, None). + +Definition mregs_for_builtin (ef: external_function): list (option mreg) * list(option mreg) := + (nil, nil). + +Global Opaque + destroyed_by_op destroyed_by_load destroyed_by_store + destroyed_by_cond destroyed_by_jumptable destroyed_by_builtin + destroyed_by_setstack destroyed_at_function_entry temp_for_parent_frame + destroyed_at_indirect_call + mregs_for_operation mregs_for_builtin. + +(** Two-address operations. Return [true] if the first argument and + the result must be in the same location *and* are unconstrained + by [mregs_for_operation]. There is one for AArch64: [Olowlong], + which is actually a no-operation in the generated asm code. *) + +Definition two_address_op (op: operation) : bool := + match op with + | Olowlong => true + | _ => false + end. + +Global Opaque two_address_op. + +(* Constraints on constant propagation for builtins *) + +Definition builtin_constraints (ef: external_function) : + list builtin_arg_constraint := + match ef with + | EF_vload _ => OK_addressing :: nil + | EF_vstore _ => OK_addressing :: OK_default :: nil + | EF_memcpy _ _ => OK_addrstack :: OK_addrstack :: nil + | EF_annot kind txt targs => map (fun _ => OK_all) targs + | EF_debug kind txt targs => map (fun _ => OK_all) targs + | _ => nil + end. + diff --git a/aarch64/Machregsaux.ml b/aarch64/Machregsaux.ml new file mode 100644 index 00000000..d7f10b9b --- /dev/null +++ b/aarch64/Machregsaux.ml @@ -0,0 +1,35 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Auxiliary functions on machine registers *) + +open Camlcoq +open Machregs + +let register_names : (mreg, string) Hashtbl.t = Hashtbl.create 31 + +let _ = + List.iter + (fun (s, r) -> Hashtbl.add register_names r (camlstring_of_coqstring s)) + Machregs.register_names + +let is_scratch_register s = + s = "X16" || s = "x16" || s = "X30" || s = "x30" + + +let name_of_register r = + try Some (Hashtbl.find register_names r) with Not_found -> None + +let register_by_name s = + Machregs.register_by_name (coqstring_uppercase_ascii_of_camlstring s) + +let can_reserve_register r = Conventions1.is_callee_save r diff --git a/aarch64/NeedOp.v b/aarch64/NeedOp.v new file mode 100644 index 00000000..8fcab9e1 --- /dev/null +++ b/aarch64/NeedOp.v @@ -0,0 +1,253 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +Require Import Coqlib. +Require Import AST Integers Floats. +Require Import Values Memory Globalenvs. +Require Import Op RTL. +Require Import NeedDomain. + +(** Neededness analysis for AArch64 operators *) + +Definition needs_of_shift (s: shift) (a: amount32) (nv: nval) := + match s with + | Slsl => shlimm nv a + | Sasr => shrimm nv a + | Slsr => shruimm nv a + | Sror => ror nv a + end. + +Definition zero_ext' (s: Z) (nv: nval) := + if zle 0 s then zero_ext s nv else default nv. +Definition sign_ext' (s: Z) (nv: nval) := + if zlt 0 s then sign_ext s nv else default nv. + +Definition op1 (nv: nval) := nv :: nil. +Definition op2 (nv: nval) := nv :: nv :: nil. +Definition op1shift (s: shift) (a: amount32) (nv: nval) := + needs_of_shift s a nv :: nil. +Definition op2shift (s: shift) (a: amount32) (nv: nval) := + nv :: needs_of_shift s a nv :: nil. + +Definition needs_of_condition (cond: condition): list nval := nil. + +Definition needs_of_operation (op: operation) (nv: nval): list nval := + match op with + | Omove => nv :: nil + | Ointconst _ => nil + | Olongconst _ => nil + | Ofloatconst _ => nil + | Osingleconst _ => nil + | Oaddrsymbol _ _ => nil + | Oaddrstack _ => nil + | Oshift s a => op1shift s a nv + | Oadd | Osub | Omul => op2 (modarith nv) + | Oaddshift s a | Osubshift s a => op2shift s a (modarith nv) + | Oaddimm _ => op1 (modarith nv) + | Oneg => op1 (modarith nv) + | Onegshift s a => op1shift s a (modarith nv) + | Omuladd | Omulsub => + let n := modarith nv in n :: n :: n :: nil + | Odiv | Odivu => op2 (default nv) + | Oand | Oor | Oxor => op2 (bitwise nv) + | Oandshift s a | Oorshift s a | Oxorshift s a => op2shift s a (bitwise nv) + | Oandimm n => op1 (andimm nv n) + | Oorimm n => op1 (orimm nv n) + | Oxorimm n => op1 (bitwise nv) + | Onot => op1 (bitwise nv) + | Onotshift s a => needs_of_shift s a (bitwise nv) :: nil + | Obic | Oorn | Oeqv => + let n := bitwise nv in n :: bitwise n :: nil + | Obicshift s a | Oornshift s a | Oeqvshift s a => + let n := bitwise nv in n :: needs_of_shift s a (bitwise n) :: nil + | Oshl | Oshr | Oshru => op2 (default nv) + | Oshrximm _ => op1 (default nv) + | Ozext s => op1 (zero_ext' s nv) + | Osext s => op1 (sign_ext' s nv) + | Oshlzext s a => op1 (zero_ext' s (shlimm nv a)) + | Oshlsext s a => op1 (sign_ext' s (shlimm nv a)) + | Ozextshr a s => op1 (shruimm (zero_ext' s nv) a) + | Osextshr a s => op1 (shrimm (sign_ext' s nv) a) + + | Oshiftl _ _ | Oextend _ _ => op1 (default nv) + | Omakelong | Olowlong | Ohighlong => op1 (default nv) + | Oaddl | Osubl | Omull => op2 (default nv) + | Oaddlshift _ _ | Oaddlext _ _ | Osublshift _ _ | Osublext _ _ => op2 (default nv) + | Oaddlimm _ => op1 (default nv) + | Onegl => op1 (default nv) + | Oneglshift _ _ => op1 (default nv) + | Omulladd | Omullsub => let n := default nv in n :: n :: n :: nil + | Omullhs | Omullhu | Odivl | Odivlu => op2 (default nv) + | Oandl | Oorl | Oxorl | Obicl | Oornl | Oeqvl => op2 (default nv) + | Oandlshift _ _ | Oorlshift _ _ | Oxorlshift _ _ + | Obiclshift _ _ | Oornlshift _ _ | Oeqvlshift _ _ => op2 (default nv) + | Oandlimm _ | Oorlimm _ | Oxorlimm _ => op1 (default nv) + | Onotl => op1 (default nv) + | Onotlshift _ _ => op1 (default nv) + | Oshll | Oshrl | Oshrlu => op2 (default nv) + | Oshrlximm _ => op1 (default nv) + | Ozextl _ | Osextl _ + | Oshllzext _ _ | Oshllsext _ _ | Ozextshrl _ _ | Osextshrl _ _ => op1 (default nv) + | Onegf | Oabsf => op1 (default nv) + | Oaddf | Osubf | Omulf | Odivf => op2 (default nv) + | Onegfs | Oabsfs => op1 (default nv) + | Oaddfs | Osubfs | Omulfs | Odivfs => op2 (default nv) + | Ofloatofsingle | Osingleoffloat => op1 (default nv) + | Ointoffloat | Ointuoffloat | Ofloatofint | Ofloatofintu => op1 (default nv) + | Olongoffloat | Olonguoffloat | Ofloatoflong | Ofloatoflongu => op1 (default nv) + | Ointofsingle | Ointuofsingle | Osingleofint | Osingleofintu => op1 (default nv) + | Olongofsingle | Olonguofsingle | Osingleoflong | Osingleoflongu => op1 (default nv) + | Ocmp c => needs_of_condition c + | Osel c ty => nv :: nv :: needs_of_condition c + end. + +Definition operation_is_redundant (op: operation) (nv: nval): bool := + match op with + | Ozext s => zle 0 s && zero_ext_redundant s nv + | Osext s => zlt 0 s && sign_ext_redundant s nv + | Oandimm n => andimm_redundant nv n + | Oorimm n => orimm_redundant nv n + | _ => false + end. + +Ltac InvAgree := + match goal with + | [H: vagree_list nil _ _ |- _ ] => inv H; InvAgree + | [H: vagree_list (_::_) _ _ |- _ ] => inv H; InvAgree + | _ => idtac + end. + +Ltac TrivialExists := + match goal with + | [ |- exists v, Some ?x = Some v /\ _ ] => exists x; split; auto + | _ => idtac + end. + +Lemma shift_sound: + forall v w s a x, + vagree v w (needs_of_shift s a x) -> + vagree (eval_shift s v a) (eval_shift s w a) x. +Proof. + intros until x; destruct s; simpl; intros. +- apply shlimm_sound; auto. +- apply shruimm_sound; auto. +- apply shrimm_sound; auto. +- apply ror_sound; auto. +Qed. + +Lemma zero_ext'_sound: + forall v w x n, + vagree v w (zero_ext' n x) -> + vagree (Val.zero_ext n v) (Val.zero_ext n w) x. +Proof. + unfold zero_ext'; intros. destruct (zle 0 n). +- apply zero_ext_sound; auto. +- assert (E: x = Nothing \/ Val.lessdef v w) by (destruct x; auto). + destruct E. subst x; simpl; auto. apply vagree_lessdef; apply Val.zero_ext_lessdef; auto. +Qed. + +Lemma sign_ext'_sound: + forall v w x n, + vagree v w (sign_ext' n x) -> + vagree (Val.sign_ext n v) (Val.sign_ext n w) x. +Proof. + unfold sign_ext'; intros. destruct (zlt 0 n). +- apply sign_ext_sound; auto. +- assert (E: x = Nothing \/ Val.lessdef v w) by (destruct x; auto). + destruct E. subst x; simpl; auto. apply vagree_lessdef; apply Val.sign_ext_lessdef; auto. +Qed. + +Section SOUNDNESS. + +Variable ge: genv. +Variable sp: block. +Variables m m': mem. +Hypothesis PERM: forall b ofs k p, Mem.perm m b ofs k p -> Mem.perm m' b ofs k p. + +Lemma needs_of_condition_sound: + forall cond args b args', + eval_condition cond args m = Some b -> + vagree_list args args' (needs_of_condition cond) -> + eval_condition cond args' m' = Some b. +Proof. + intros. unfold needs_of_condition in H0. + eapply default_needs_of_condition_sound; eauto. +Qed. + +Lemma needs_of_operation_sound: + forall op args v nv args', + eval_operation ge (Vptr sp Ptrofs.zero) op args m = Some v -> + vagree_list args args' (needs_of_operation op nv) -> + nv <> Nothing -> + exists v', + eval_operation ge (Vptr sp Ptrofs.zero) op args' m' = Some v' + /\ vagree v v' nv. +Proof. + unfold needs_of_operation; intros; destruct op; try (eapply default_needs_of_operation_sound; eauto; fail); + simpl in *; FuncInv; InvAgree; TrivialExists. +- apply shift_sound; auto. +- apply add_sound; auto. +- apply add_sound; auto using shift_sound. +- apply add_sound; auto with na. +- apply neg_sound; auto. +- apply neg_sound; auto using shift_sound. +- apply sub_sound; auto. +- apply sub_sound; auto using shift_sound. +- apply mul_sound; auto. +- apply add_sound; auto. apply mul_sound; rewrite modarith_idem; auto. +- apply sub_sound; auto. apply mul_sound; rewrite modarith_idem; auto. +- apply and_sound; auto. +- apply and_sound; auto using shift_sound. +- apply andimm_sound; auto. +- apply or_sound; auto. +- apply or_sound; auto using shift_sound. +- apply orimm_sound; auto. +- apply xor_sound; auto. +- apply xor_sound; auto using shift_sound. +- apply xor_sound; auto with na. +- apply notint_sound; auto. +- apply notint_sound; auto using shift_sound. +- apply and_sound; auto. apply notint_sound; rewrite bitwise_idem; auto. +- apply and_sound; auto. apply notint_sound; rewrite bitwise_idem; auto using shift_sound. +- apply or_sound; auto. apply notint_sound; rewrite bitwise_idem; auto. +- apply or_sound; auto. apply notint_sound; rewrite bitwise_idem; auto using shift_sound. +- apply xor_sound; auto. apply notint_sound; rewrite bitwise_idem; auto. +- apply xor_sound; auto. apply notint_sound; rewrite bitwise_idem; auto using shift_sound. +- apply zero_ext'_sound; auto. +- apply sign_ext'_sound; auto. +- apply shlimm_sound; apply zero_ext'_sound; auto. +- apply shlimm_sound; apply sign_ext'_sound; auto. +- apply zero_ext'_sound; apply shruimm_sound; auto. +- apply sign_ext'_sound; apply shrimm_sound; auto. +- destruct (eval_condition cond args m) as [b|] eqn:EC. + erewrite needs_of_condition_sound by eauto. + apply select_sound; auto. + simpl; auto with na. +Qed. + +Lemma operation_is_redundant_sound: + forall op nv arg1 args v arg1' args', + operation_is_redundant op nv = true -> + eval_operation ge (Vptr sp Ptrofs.zero) op (arg1 :: args) m = Some v -> + vagree_list (arg1 :: args) (arg1' :: args') (needs_of_operation op nv) -> + vagree v arg1' nv. +Proof. + intros. destruct op; simpl in *; try discriminate; inv H1; FuncInv; subst. +- apply andimm_redundant_sound; auto. +- apply orimm_redundant_sound; auto. +- InvBooleans. unfold zero_ext' in H5; rewrite zle_true in H5 by auto. + apply zero_ext_redundant_sound; auto. +- InvBooleans. unfold sign_ext' in H5; rewrite zlt_true in H5 by auto. + apply sign_ext_redundant_sound; auto. +Qed. + +End SOUNDNESS. diff --git a/aarch64/Op.v b/aarch64/Op.v new file mode 100644 index 00000000..a7483d56 --- /dev/null +++ b/aarch64/Op.v @@ -0,0 +1,1778 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Operators and addressing modes. The abstract syntax and dynamic + semantics for the CminorSel, RTL, LTL and Mach languages depend on the + following types, defined in this library: +- [condition]: boolean conditions for conditional branches; +- [operation]: arithmetic and logical operations; +- [addressing]: addressing modes for load and store operations. + + These types are processor-specific and correspond roughly to what the + processor can compute in one instruction. In other terms, these + types reflect the state of the program after instruction selection. + For a processor-independent set of operations, see the abstract + syntax and dynamic semantics of the Cminor language. +*) + +Require Import Axioms Coqlib BoolEqual. +Require Import AST Integers Floats Values Memory Globalenvs Events. + +Set Implicit Arguments. +Local Transparent Archi.ptr64. + +(** Shift amounts *) + +Record amount32 : Type := { + a32_amount :> int; + a32_range : Int.ltu a32_amount Int.iwordsize = true }. + +Record amount64 : Type := { + a64_amount :> int; + a64_range : Int.ltu a64_amount Int64.iwordsize' = true }. + +(** Shifted operands *) + +Inductive shift : Type := + | Slsl (**r left shift *) + | Slsr (**r right unsigned shift *) + | Sasr (**r right signed shift *) + | Sror. (**r rotate right *) + +(** Sign- or zero-extended operands *) + +Inductive extension : Type := + | Xsgn32 (**r from signed 32-bit integer to 64-bit integer *) + | Xuns32. (**r from unsigned 32-bit integer to 64-bit integer *) + +(** Conditions (boolean-valued operators). *) + +Inductive condition: Type := +(** Tests over 32-bit integers *) + | Ccomp (c: comparison) (**r signed comparison *) + | Ccompu (c: comparison) (**r unsigned comparison *) + | Ccompimm (c: comparison) (n: int) (**r signed comparison with constant *) + | Ccompuimm (c: comparison) (n: int) (**r unsigned comparison with constant *) + | Ccompshift (c: comparison) (s: shift) (a: amount32) (**r signed comparison with shift *) + | Ccompushift (c: comparison) (s: shift) (a: amount32)(**r unsigned comparison width shift *) + | Cmaskzero (n: int) (**r test [(arg & n) == 0] *) + | Cmasknotzero (n: int) (**r test [(arg & n) != 0] *) +(** Tests over 64-bit integers *) + | Ccompl (c: comparison) (**r signed comparison *) + | Ccomplu (c: comparison) (**r unsigned comparison *) + | Ccomplimm (c: comparison) (n: int64) (**r signed comparison with constant *) + | Ccompluimm (c: comparison) (n: int64) (**r unsigned comparison with constant *) + | Ccomplshift (c: comparison) (s: shift) (a: amount64)(**r signed comparison with shift *) + | Ccomplushift (c: comparison) (s: shift) (a: amount64)(**r unsigned comparison width shift *) + | Cmasklzero (n: int64) (**r test [(arg & n) == 0] *) + | Cmasklnotzero (n: int64) (**r test [(arg & n) != 0] *) +(** Tests over 64-bit floating-point numbers *) + | Ccompf (c: comparison) (**r FP comparison *) + | Cnotcompf (c: comparison) (**r negation of an FP comparison *) + | Ccompfzero (c: comparison) (**r comparison with 0.0 *) + | Cnotcompfzero (c: comparison) (**r negation of comparison with 0.0 *) +(** Tests over 32-bit floating-point numbers *) + | Ccompfs (c: comparison) (**r FP comparison *) + | Cnotcompfs (c: comparison) (**r negation of an FP comparison *) + | Ccompfszero (c: comparison) (**r equal to 0.0 *) + | Cnotcompfszero (c: comparison). (**r not equal to 0.0 *) + +(** Arithmetic and logical operations. In the descriptions, [rd] is the + result of the operation and [r1], [r2], etc, are the arguments. *) + +Inductive operation : Type := + | Omove (**r [rd = r1] *) + | Ointconst (n: int) (**r [rd] is set to the given integer constant *) + | Olongconst (n: int64) (**r [rd] is set to the given integer constant *) + | Ofloatconst (n: float) (**r [rd] is set to the given float constant *) + | Osingleconst (n: float32) (**r [rd] is set to the given float constant *) + | Oaddrsymbol (id: ident) (ofs: ptrofs) (**r [rd] is set to the address of the symbol plus the given offset *) + | Oaddrstack (ofs: ptrofs) (**r [rd] is set to the stack pointer plus the given offset *) +(** 32-bit integer arithmetic *) + | Oshift (s: shift) (a: amount32) (**r shift or rotate by immediate quantity *) + | Oadd (**r [rd = r1 + r2] *) + | Oaddshift (s: shift) (a: amount32) (**r [rd = r1 + shifted r2] *) + | Oaddimm (n: int) (**r [rd = r1 + n] *) + | Oneg (**r [rd = - r1] *) + | Onegshift (s: shift) (a: amount32) (**r [rd = - shifted r1] *) + | Osub (**r [rd = r1 - r2] *) + | Osubshift (s: shift) (a: amount32) (**r [rd = r1 - shifted r2] *) + | Omul (**r [rd = r1 * r2] *) + | Omuladd (**r [rd = r1 + r2 * r3] *) + | Omulsub (**r [rd = r1 - r2 * r3] *) + | Odiv (**r [rd = r1 / r2] (signed) *) + | Odivu (**r [rd = r1 / r2] (unsigned) *) + | Oand (**r [rd = r1 & r2] *) + | Oandshift (s: shift) (a: amount32) (**r [rd = r1 & shifted r2] *) + | Oandimm (n: int) (**r [rd = r1 & n] *) + | Oor (**r [rd = r1 | r2] *) + | Oorshift (s: shift) (a: amount32) (**r [rd = r1 | shifted r2] *) + | Oorimm (n: int) (**r [rd = r1 | n] *) + | Oxor (**r [rd = r1 ^ r2] *) + | Oxorshift (s: shift) (a: amount32) (**r [rd = r1 ^ shifted r2] *) + | Oxorimm (n: int) (**r [rd = r1 ^ n] *) + | Onot (**r [rd = ~r1] *) + | Onotshift (s: shift) (a: amount32) (**r [rd = ~ shifted r1] *) + | Obic (**r [rd = r1 & ~r2] *) + | Obicshift (s: shift) (a: amount32) (**r [rd = r1 ^ ~ shifted r2] *) + | Oorn (**r [rd = r1 | ~r2] *) + | Oornshift (s: shift) (a: amount32) (**r [rd = r1 | ~ shifted r2] *) + | Oeqv (**r [rd = r1 ^ ~r2] *) + | Oeqvshift (s: shift) (a: amount32) (**r [rd = r1 | ~ shifted r2] *) + | Oshl (**r [rd = r1 << r2] *) + | Oshr (**r [rd = r1 >> r2] (signed) *) + | Oshru (**r [rd = r1 >> r2] (unsigned) *) + | Oshrximm (n: int) (**r [rd = r1 / 2^n] (signed) *) + | Ozext (s: Z) (**r [rd = zero_ext(r1,s)] *) + | Osext (s: Z) (**r [rd = sign_ext(r1,s)] *) + | Oshlzext (s: Z) (a: amount32) (**r [rd = zero_ext(r1,s) << a] *) + | Oshlsext (s: Z) (a: amount32) (**r [rd = sign_ext(r1,s) << a] *) + | Ozextshr (a: amount32) (s: Z) (**r [rd = zero_ext(r1 >> a, s)] *) + | Osextshr (a: amount32) (s: Z) (**r [rd = sign_ext(r1 >> a, s)] *) +(** 64-bit integer arithmetic *) + | Oshiftl (s: shift) (a: amount64) (**r shift or rotate by immediate quantity *) + | Oextend (x: extension) (a: amount64) (**r convert from 32 to 64 bits and shift *) + | Omakelong (**r [rd = r1 << 32 | r2] *) + | Olowlong (**r [rd = low-word(r1)] *) + | Ohighlong (**r [rd = high-word(r1)] *) + | Oaddl (**r [rd = r1 + r2] *) + | Oaddlshift (s: shift) (a: amount64) (**r [rd = r1 + shifted r2] *) + | Oaddlext (x: extension) (a: amount64) (**r [rd = r1 + shifted, converted r2] *) + | Oaddlimm (n: int64) (**r [rd = r1 + n] *) + | Onegl (**r [rd = - r1] *) + | Oneglshift (s: shift) (a: amount64) (**r [rd = - shifted r1] *) + | Osubl (**r [rd = r1 - r2] *) + | Osublshift (s: shift) (a: amount64) (**r [rd = r1 - shifted r2] *) + | Osublext (x: extension) (a: amount64) (**r [rd = r1 - shifted, converted r2] *) + | Omull (**r [rd = r1 * r2] *) + | Omulladd (**r [rd = r1 + r2 * r3] *) + | Omullsub (**r [rd = r1 - r2 * r3] *) + | Omullhs (**r [rd = high part of r1 * r2 (signed)] *) + | Omullhu (**r [rd = high part of r1 * r2 (unsigned)] *) + | Odivl (**r [rd = r1 / r2] (signed) *) + | Odivlu (**r [rd = r1 / r2] (unsigned) *) + | Oandl (**r [rd = r1 & r2] *) + | Oandlshift (s: shift) (a: amount64) (**r [rd = r1 & shifted r2] *) + | Oandlimm (n: int64) (**r [rd = r1 & n] *) + | Oorl (**r [rd = r1 | r2] *) + | Oorlshift (s: shift) (a: amount64) (**r [rd = r1 | shifted r2] *) + | Oorlimm (n: int64) (**r [rd = r1 | n] *) + | Oxorl (**r [rd = r1 ^ r2] *) + | Oxorlshift (s: shift) (a: amount64) (**r [rd = r1 ^ shifted r2] *) + | Oxorlimm (n: int64) (**r [rd = r1 ^ n] *) + | Onotl (**r [rd = ~r1] *) + | Onotlshift (s: shift) (a: amount64) (**r [rd = ~ shifted r1] *) + | Obicl (**r [rd = r1 & ~r2] *) + | Obiclshift (s: shift) (a: amount64) (**r [rd = r1 ^ ~ shifted r2] *) + | Oornl (**r [rd = r1 | ~r2] *) + | Oornlshift (s: shift) (a: amount64) (**r [rd = r1 | ~ shifted r2] *) + | Oeqvl (**r [rd = r1 ^ ~r2] *) + | Oeqvlshift (s: shift) (a: amount64) (**r [rd = r1 | ~ shifted r2] *) + | Oshll (**r [rd = r1 << r2] *) + | Oshrl (**r [rd = r1 >> r2] (signed) *) + | Oshrlu (**r [rd = r1 >> r2] (unsigned) *) + | Oshrlximm (n: int) (**r [rd = r1 / 2^n] (signed) *) + | Ozextl (s: Z) (**r [rd = zero_ext(r1,s)] *) + | Osextl (s: Z) (**r [rd = sign_ext(r1,s)] *) + | Oshllzext (s: Z) (a: amount64) (**r [rd = zero_ext(r1,s) << a] *) + | Oshllsext (s: Z) (a: amount64) (**r [rd = sign_ext(r1,s) << a] *) + | Ozextshrl (a: amount64) (s: Z) (**r [rd = zero_ext(r1 >> a, s)] *) + | Osextshrl (a: amount64) (s: Z) (**r [rd = sign_ext(r1 >> a, s)] *) +(** 64-bit floating-point arithmetic *) + | Onegf (**r [rd = - r1] *) + | Oabsf (**r [rd = abs(r1)] *) + | Oaddf (**r [rd = r1 + r2] *) + | Osubf (**r [rd = r1 - r2] *) + | Omulf (**r [rd = r1 * r2] *) + | Odivf (**r [rd = r1 / r2] *) +(** 32-bit floating-point arithmetic *) + | Onegfs (**r [rd = - r1] *) + | Oabsfs (**r [rd = abs(r1)] *) + | Oaddfs (**r [rd = r1 + r2] *) + | Osubfs (**r [rd = r1 - r2] *) + | Omulfs (**r [rd = r1 * r2] *) + | Odivfs (**r [rd = r1 / r2] *) + | Osingleoffloat (**r [rd] is [r1] truncated to single-precision float *) + | Ofloatofsingle (**r [rd] is [r1] extended to double-precision float *) +(** Conversions between int and float *) + | Ointoffloat (**r [rd = signed_int_of_float64(r1)] *) + | Ointuoffloat (**r [rd = unsigned_int_of_float64(r1)] *) + | Ofloatofint (**r [rd = float64_of_signed_int(r1)] *) + | Ofloatofintu (**r [rd = float64_of_unsigned_int(r1)] *) + | Ointofsingle (**r [rd = signed_int_of_float32(r1)] *) + | Ointuofsingle (**r [rd = unsigned_int_of_float32(r1)] *) + | Osingleofint (**r [rd = float32_of_signed_int(r1)] *) + | Osingleofintu (**r [rd = float32_of_unsigned_int(r1)] *) + | Olongoffloat (**r [rd = signed_long_of_float64(r1)] *) + | Olonguoffloat (**r [rd = unsigned_long_of_float64(r1)] *) + | Ofloatoflong (**r [rd = float64_of_signed_long(r1)] *) + | Ofloatoflongu (**r [rd = float64_of_unsigned_long(r1)] *) + | Olongofsingle (**r [rd = signed_long_of_float32(r1)] *) + | Olonguofsingle (**r [rd = unsigned_long_of_float32(r1)] *) + | Osingleoflong (**r [rd = float32_of_signed_long(r1)] *) + | Osingleoflongu (**r [rd = float32_of_unsigned_int(r1)] *) +(** Boolean tests *) + | Ocmp (cond: condition) (**r [rd = 1] if condition holds, [rd = 0] otherwise. *) + | Osel (cond: condition) (ty: typ). (**r [rd = rs1] if condition holds, [rd = rs2] otherwise. *) + +(** Addressing modes. [r1], [r2], etc, are the arguments to the addressing. *) + +Inductive addressing: Type := + | Aindexed (ofs: int64) (**r Address is [r1 + offset] *) + | Aindexed2 (**r Address is [r1 + r2] *) + | Aindexed2shift (a: amount64) (**r Address is [r1 + r2 << a] *) + | Aindexed2ext (x: extension) (a: amount64) (**r Address is [r1 + sign-or-zero-ext(r2) << a] *) + | Aglobal (id: ident) (ofs: ptrofs) (**r Address is [global + offset] *) + | Ainstack (ofs: ptrofs). (**r Address is [stack_pointer + offset] *) + +(** Comparison functions (used in modules [CSE] and [Allocation]). *) + +Definition eq_amount32 (x y: amount32): {x=y} + {x<>y}. +Proof. + destruct x as [x Px], y as [y Py]. + destruct (Int.eq_dec x y). +- subst y. assert (Px = Py) by (apply proof_irr). subst Py. left; auto. +- right; congruence. +Defined. + +Definition eq_amount64 (x y: amount64): {x=y} + {x<>y}. +Proof. + destruct x as [x Px], y as [y Py]. + destruct (Int.eq_dec x y). +- subst y. assert (Px = Py) by (apply proof_irr). subst Py. left; auto. +- right; congruence. +Defined. + +Definition eq_shift (x y: shift): {x=y} + {x<>y}. +Proof. + decide equality. +Defined. + +Definition eq_extension (x y: extension): {x=y} + {x<>y}. +Proof. + decide equality. +Defined. + +Definition eq_condition (x y: condition) : {x=y} + {x<>y}. +Proof. + assert (forall (x y: comparison), {x=y}+{x<>y}) by decide equality. + generalize Int.eq_dec Int64.eq_dec eq_shift eq_amount32 eq_amount64; intro. + decide equality. +Defined. + +Definition eq_addressing (x y: addressing) : {x=y} + {x<>y}. +Proof. + generalize ident_eq Int64.eq_dec Ptrofs.eq_dec eq_extension eq_amount64; intros. + decide equality. +Defined. + +Definition eq_operation: forall (x y: operation), {x=y} + {x<>y}. +Proof. + intros. + generalize Int.eq_dec Int64.eq_dec Ptrofs.eq_dec Float.eq_dec Float32.eq_dec + zeq ident_eq eq_shift eq_extension eq_amount32 eq_amount64 + typ_eq eq_condition; + decide equality. +Defined. + +(** Alternative: + +Definition beq_operation: forall (x y: operation), bool. +Proof. + generalize Int.eq_dec Int64.eq_dec Ptrofs.eq_dec Float.eq_dec Float32.eq_dec + zeq ident_eq eq_shift eq_extension eq_amount32 eq_amount64 + eq_condition typ_eq; boolean_equality. +Defined. + +Definition eq_operation: forall (x y: operation), {x=y} + {x<>y}. +Proof. + decidable_equality_from beq_operation. +Defined. +*) + +(** * Evaluation functions *) + +(** Evaluation of conditions, operators and addressing modes applied + to lists of values. Return [None] when the computation can trigger an + error, e.g. integer division by zero. [eval_condition] returns a boolean, + [eval_operation] and [eval_addressing] return a value. *) + +Definition eval_shift (s: shift) (v: val) (n: amount32) : val := + match s with + | Slsl => Val.shl v (Vint n) + | Slsr => Val.shru v (Vint n) + | Sasr => Val.shr v (Vint n) + | Sror => Val.ror v (Vint n) + end. + +Definition eval_shiftl (s: shift) (v: val) (n: amount64) : val := + match s with + | Slsl => Val.shll v (Vint n) + | Slsr => Val.shrlu v (Vint n) + | Sasr => Val.shrl v (Vint n) + | Sror => Val.rorl v (Vint n) + end. + +Definition eval_extend (x: extension) (v: val) (n: amount64) : val := + Val.shll + (match x with + | Xsgn32 => Val.longofint v + | Xuns32 => Val.longofintu v + end) + (Vint n). + +Definition eval_condition (cond: condition) (vl: list val) (m: mem): option bool := + match cond, vl with + | Ccomp c, v1 :: v2 :: nil => Val.cmp_bool c v1 v2 + | Ccompu c, v1 :: v2 :: nil => Val.cmpu_bool (Mem.valid_pointer m) c v1 v2 + | Ccompimm c n, v1 :: nil => Val.cmp_bool c v1 (Vint n) + | Ccompuimm c n, v1 :: nil => Val.cmpu_bool (Mem.valid_pointer m) c v1 (Vint n) + | Ccompshift c s a, v1 :: v2 :: nil => Val.cmp_bool c v1 (eval_shift s v2 a) + | Ccompushift c s a, v1 :: v2 :: nil => Val.cmpu_bool (Mem.valid_pointer m) c v1 (eval_shift s v2 a) + | Cmaskzero n, v1 :: nil => Val.cmp_bool Ceq (Val.and v1 (Vint n)) (Vint Int.zero) + | Cmasknotzero n, v1 :: nil => Val.cmp_bool Cne (Val.and v1 (Vint n)) (Vint Int.zero) + + | Ccompl c, v1 :: v2 :: nil => Val.cmpl_bool c v1 v2 + | Ccomplu c, v1 :: v2 :: nil => Val.cmplu_bool (Mem.valid_pointer m) c v1 v2 + | Ccomplimm c n, v1 :: nil => Val.cmpl_bool c v1 (Vlong n) + | Ccompluimm c n, v1 :: nil => Val.cmplu_bool (Mem.valid_pointer m) c v1 (Vlong n) + | Ccomplshift c s a, v1 :: v2 :: nil => Val.cmpl_bool c v1 (eval_shiftl s v2 a) + | Ccomplushift c s a, v1 :: v2 :: nil => Val.cmplu_bool (Mem.valid_pointer m) c v1 (eval_shiftl s v2 a) + | Cmasklzero n, v1 :: nil => Val.cmpl_bool Ceq (Val.andl v1 (Vlong n)) (Vlong Int64.zero) + | Cmasklnotzero n, v1 :: nil => Val.cmpl_bool Cne (Val.andl v1 (Vlong n)) (Vlong Int64.zero) + + | Ccompf c, v1 :: v2 :: nil => Val.cmpf_bool c v1 v2 + | Cnotcompf c, v1 :: v2 :: nil => option_map negb (Val.cmpf_bool c v1 v2) + | Ccompfzero c, v1 :: nil => Val.cmpf_bool c v1 (Vfloat Float.zero) + | Cnotcompfzero c, v1 :: nil => option_map negb (Val.cmpf_bool c v1 (Vfloat Float.zero)) + + | Ccompfs c, v1 :: v2 :: nil => Val.cmpfs_bool c v1 v2 + | Cnotcompfs c, v1 :: v2 :: nil => option_map negb (Val.cmpfs_bool c v1 v2) + | Ccompfszero c, v1 :: nil => Val.cmpfs_bool c v1 (Vsingle Float32.zero) + | Cnotcompfszero c, v1 :: nil => option_map negb (Val.cmpfs_bool c v1 (Vsingle Float32.zero)) + + | _, _ => None + end. + +Definition eval_operation + (F V: Type) (genv: Genv.t F V) (sp: val) + (op: operation) (vl: list val) (m: mem): option val := + match op, vl with + | Omove, v1::nil => Some v1 + | Ointconst n, nil => Some (Vint n) + | Olongconst n, nil => Some (Vlong n) + | Ofloatconst n, nil => Some (Vfloat n) + | Osingleconst n, nil => Some (Vsingle n) + | Oaddrsymbol s ofs, nil => Some (Genv.symbol_address genv s ofs) + | Oaddrstack ofs, nil => Some (Val.offset_ptr sp ofs) + + | Oshift s a, v1 :: nil => Some (eval_shift s v1 a) + | Oadd, v1 :: v2 :: nil => Some (Val.add v1 v2) + | Oaddshift s a, v1 :: v2 :: nil => Some (Val.add v1 (eval_shift s v2 a)) + | Oaddimm n, v1 :: nil => Some (Val.add v1 (Vint n)) + | Oneg, v1 :: nil => Some (Val.neg v1) + | Onegshift s a, v1 :: nil => Some (Val.neg (eval_shift s v1 a)) + | Osub, v1 :: v2 :: nil => Some (Val.sub v1 v2) + | Osubshift s a, v1 :: v2 :: nil => Some (Val.sub v1 (eval_shift s v2 a)) + | Omul, v1 :: v2 :: nil => Some (Val.mul v1 v2) + | Omuladd, v1 :: v2 :: v3 :: nil => Some (Val.add v1 (Val.mul v2 v3)) + | Omulsub, v1 :: v2 :: v3 :: nil => Some (Val.sub v1 (Val.mul v2 v3)) + | Odiv, v1 :: v2 :: nil => Val.divs v1 v2 + | Odivu, v1 :: v2 :: nil => Val.divu v1 v2 + | Oand, v1 :: v2 :: nil => Some (Val.and v1 v2) + | Oandshift s a, v1 :: v2 :: nil => Some (Val.and v1 (eval_shift s v2 a)) + | Oandimm n, v1 :: nil => Some (Val.and v1 (Vint n)) + | Oor, v1 :: v2 :: nil => Some (Val.or v1 v2) + | Oorshift s a, v1 :: v2 :: nil => Some (Val.or v1 (eval_shift s v2 a)) + | Oorimm n, v1 :: nil => Some (Val.or v1 (Vint n)) + | Oxor, v1 :: v2 :: nil => Some (Val.xor v1 v2) + | Oxorshift s a, v1 :: v2 :: nil => Some (Val.xor v1 (eval_shift s v2 a)) + | Oxorimm n, v1 :: nil => Some (Val.xor v1 (Vint n)) + | Onot, v1 :: nil => Some (Val.notint v1) + | Onotshift s a, v1 :: nil => Some (Val.notint (eval_shift s v1 a)) + | Obic, v1 :: v2 :: nil => Some (Val.and v1 (Val.notint v2)) + | Obicshift s a, v1 :: v2 :: nil => Some (Val.and v1 (Val.notint (eval_shift s v2 a))) + | Oorn, v1 :: v2 :: nil => Some (Val.or v1 (Val.notint v2)) + | Oornshift s a, v1 :: v2 :: nil => Some (Val.or v1 (Val.notint (eval_shift s v2 a))) + | Oeqv, v1 :: v2 :: nil => Some (Val.xor v1 (Val.notint v2)) + | Oeqvshift s a, v1 :: v2 :: nil => Some (Val.xor v1 (Val.notint (eval_shift s v2 a))) + | Oshl, v1 :: v2 :: nil => Some (Val.shl v1 v2) + | Oshr, v1 :: v2 :: nil => Some (Val.shr v1 v2) + | Oshru, v1 :: v2 :: nil => Some (Val.shru v1 v2) + | Oshrximm n, v1::nil => Val.shrx v1 (Vint n) + | Ozext s, v1 :: nil => Some (Val.zero_ext s v1) + | Osext s, v1 :: nil => Some (Val.sign_ext s v1) + | Oshlzext s a, v1 :: nil => Some (Val.shl (Val.zero_ext s v1) (Vint a)) + | Oshlsext s a, v1 :: nil => Some (Val.shl (Val.sign_ext s v1) (Vint a)) + | Ozextshr a s, v1 :: nil => Some (Val.zero_ext s (Val.shru v1 (Vint a))) + | Osextshr a s, v1 :: nil => Some (Val.sign_ext s (Val.shr v1 (Vint a))) + + | Oshiftl s a, v1 :: nil => Some (eval_shiftl s v1 a) + | Oextend x a, v1 :: nil => Some (eval_extend x v1 a) + | Omakelong, v1::v2::nil => Some (Val.longofwords v1 v2) + | Olowlong, v1::nil => Some (Val.loword v1) + | Ohighlong, v1::nil => Some (Val.hiword v1) + | Oaddl, v1 :: v2 :: nil => Some (Val.addl v1 v2) + | Oaddlshift s a, v1 :: v2 :: nil => Some (Val.addl v1 (eval_shiftl s v2 a)) + | Oaddlext x a, v1 :: v2 :: nil => Some (Val.addl v1 (eval_extend x v2 a)) + | Oaddlimm n, v1 :: nil => Some (Val.addl v1 (Vlong n)) + | Onegl, v1 :: nil => Some (Val.negl v1) + | Oneglshift s a, v1 :: nil => Some (Val.negl (eval_shiftl s v1 a)) + | Osubl, v1 :: v2 :: nil => Some (Val.subl v1 v2) + | Osublshift s a, v1 :: v2 :: nil => Some (Val.subl v1 (eval_shiftl s v2 a)) + | Osublext x a, v1 :: v2 :: nil => Some (Val.subl v1 (eval_extend x v2 a)) + | Omull, v1 :: v2 :: nil => Some (Val.mull v1 v2) + | Omulladd, v1 :: v2 :: v3 :: nil => Some (Val.addl v1 (Val.mull v2 v3)) + | Omullsub, v1 :: v2 :: v3 :: nil => Some (Val.subl v1 (Val.mull v2 v3)) + | Omullhs, v1::v2::nil => Some (Val.mullhs v1 v2) + | Omullhu, v1::v2::nil => Some (Val.mullhu v1 v2) + | Odivl, v1 :: v2 :: nil => Val.divls v1 v2 + | Odivlu, v1 :: v2 :: nil => Val.divlu v1 v2 + | Oandl, v1 :: v2 :: nil => Some (Val.andl v1 v2) + | Oandlshift s a, v1 :: v2 :: nil => Some (Val.andl v1 (eval_shiftl s v2 a)) + | Oandlimm n, v1 :: nil => Some (Val.andl v1 (Vlong n)) + | Oorl, v1 :: v2 :: nil => Some (Val.orl v1 v2) + | Oorlshift s a, v1 :: v2 :: nil => Some (Val.orl v1 (eval_shiftl s v2 a)) + | Oorlimm n, v1 :: nil => Some (Val.orl v1 (Vlong n)) + | Oxorl, v1 :: v2 :: nil => Some (Val.xorl v1 v2) + | Oxorlshift s a, v1 :: v2 :: nil => Some (Val.xorl v1 (eval_shiftl s v2 a)) + | Oxorlimm n, v1 :: nil => Some (Val.xorl v1 (Vlong n)) + | Onotl, v1 :: nil => Some (Val.notl v1) + | Onotlshift s a, v1 :: nil => Some (Val.notl (eval_shiftl s v1 a)) + | Obicl, v1 :: v2 :: nil => Some (Val.andl v1 (Val.notl v2)) + | Obiclshift s a, v1 :: v2 :: nil => Some (Val.andl v1 (Val.notl (eval_shiftl s v2 a))) + | Oornl, v1 :: v2 :: nil => Some (Val.orl v1 (Val.notl v2)) + | Oornlshift s a, v1 :: v2 :: nil => Some (Val.orl v1 (Val.notl (eval_shiftl s v2 a))) + | Oeqvl, v1 :: v2 :: nil => Some (Val.xorl v1 (Val.notl v2)) + | Oeqvlshift s a, v1 :: v2 :: nil => Some (Val.xorl v1 (Val.notl (eval_shiftl s v2 a))) + | Oshll, v1 :: v2 :: nil => Some (Val.shll v1 v2) + | Oshrl, v1 :: v2 :: nil => Some (Val.shrl v1 v2) + | Oshrlu, v1 :: v2 :: nil => Some (Val.shrlu v1 v2) + | Oshrlximm n, v1::nil => Val.shrxl v1 (Vint n) + | Ozextl s, v1 :: nil => Some (Val.zero_ext_l s v1) + | Osextl s, v1 :: nil => Some (Val.sign_ext_l s v1) + | Oshllzext s a, v1 :: nil => Some (Val.shll (Val.zero_ext_l s v1) (Vint a)) + | Oshllsext s a, v1 :: nil => Some (Val.shll (Val.sign_ext_l s v1) (Vint a)) + | Ozextshrl a s, v1 :: nil => Some (Val.zero_ext_l s (Val.shrlu v1 (Vint a))) + | Osextshrl a s, v1 :: nil => Some (Val.sign_ext_l s (Val.shrl v1 (Vint a))) + + | Onegf, v1::nil => Some (Val.negf v1) + | Oabsf, v1::nil => Some (Val.absf v1) + | Oaddf, v1::v2::nil => Some (Val.addf v1 v2) + | Osubf, v1::v2::nil => Some (Val.subf v1 v2) + | Omulf, v1::v2::nil => Some (Val.mulf v1 v2) + | Odivf, v1::v2::nil => Some (Val.divf v1 v2) + + | Onegfs, v1::nil => Some (Val.negfs v1) + | Oabsfs, v1::nil => Some (Val.absfs v1) + | Oaddfs, v1::v2::nil => Some (Val.addfs v1 v2) + | Osubfs, v1::v2::nil => Some (Val.subfs v1 v2) + | Omulfs, v1::v2::nil => Some (Val.mulfs v1 v2) + | Odivfs, v1::v2::nil => Some (Val.divfs v1 v2) + + | Osingleoffloat, v1::nil => Some (Val.singleoffloat v1) + | Ofloatofsingle, v1::nil => Some (Val.floatofsingle v1) + | Ointoffloat, v1::nil => Val.intoffloat v1 + | Ointuoffloat, v1::nil => Val.intuoffloat v1 + | Ofloatofint, v1::nil => Val.floatofint v1 + | Ofloatofintu, v1::nil => Val.floatofintu v1 + | Ointofsingle, v1::nil => Val.intofsingle v1 + | Ointuofsingle, v1::nil => Val.intuofsingle v1 + | Osingleofint, v1::nil => Val.singleofint v1 + | Osingleofintu, v1::nil => Val.singleofintu v1 + | Olongoffloat, v1::nil => Val.longoffloat v1 + | Olonguoffloat, v1::nil => Val.longuoffloat v1 + | Ofloatoflong, v1::nil => Val.floatoflong v1 + | Ofloatoflongu, v1::nil => Val.floatoflongu v1 + | Olongofsingle, v1::nil => Val.longofsingle v1 + | Olonguofsingle, v1::nil => Val.longuofsingle v1 + | Osingleoflong, v1::nil => Val.singleoflong v1 + | Osingleoflongu, v1::nil => Val.singleoflongu v1 + + | Ocmp c, _ => Some (Val.of_optbool (eval_condition c vl m)) + | Osel c ty, v1::v2::vl => Some(Val.select (eval_condition c vl m) v1 v2 ty) + | _, _ => None + end. + +Definition eval_addressing + (F V: Type) (genv: Genv.t F V) (sp: val) + (addr: addressing) (vl: list val) : option val := + match addr, vl with + | Aindexed n, v1 :: nil => Some (Val.addl v1 (Vlong n)) + | Aindexed2, v1 :: v2 :: nil => Some (Val.addl v1 v2) + | Aindexed2shift a, v1 :: v2 :: nil => Some (Val.addl v1 (Val.shll v2 (Vint a))) + | Aindexed2ext x a, v1 :: v2 :: nil => Some (Val.addl v1 (eval_extend x v2 a)) + | Aglobal s ofs, nil => Some (Genv.symbol_address genv s ofs) + | Ainstack n, nil => Some (Val.offset_ptr sp n) + | _, _ => None + end. + +Remark eval_addressing_Ainstack: + forall (F V: Type) (genv: Genv.t F V) sp ofs, + eval_addressing genv sp (Ainstack ofs) nil = Some (Val.offset_ptr sp ofs). +Proof. + intros. reflexivity. +Qed. + +Remark eval_addressing_Ainstack_inv: + forall (F V: Type) (genv: Genv.t F V) sp ofs vl v, + eval_addressing genv sp (Ainstack ofs) vl = Some v -> vl = nil /\ v = Val.offset_ptr sp ofs. +Proof. + unfold eval_addressing; intros; destruct vl; inv H; auto. +Qed. + +Ltac FuncInv := + match goal with + | H: (match ?x with nil => _ | _ :: _ => _ end = Some _) |- _ => + destruct x; simpl in H; FuncInv + | H: (match ?v with Vundef => _ | Vint _ => _ | Vfloat _ => _ | Vptr _ _ => _ end = Some _) |- _ => + destruct v; simpl in H; FuncInv + | H: (if Archi.ptr64 then _ else _) = Some _ |- _ => + change Archi.ptr64 with true in H; simpl in H; FuncInv + | H: (Some _ = Some _) |- _ => + injection H; intros; clear H; FuncInv + | H: (None = Some _) |- _ => + discriminate H + | _ => + idtac + end. + +(** * Static typing of conditions, operators and addressing modes. *) + +Definition type_of_condition (c: condition) : list typ := + match c with + | Ccomp _ => Tint :: Tint :: nil + | Ccompu _ => Tint :: Tint :: nil + | Ccompimm _ _ => Tint :: nil + | Ccompuimm _ _ => Tint :: nil + | Ccompshift _ _ _ => Tint :: Tint :: nil + | Ccompushift _ _ _ => Tint :: Tint :: nil + | Cmaskzero _ => Tint :: nil + | Cmasknotzero _ => Tint :: nil + | Ccompl _ => Tlong :: Tlong :: nil + | Ccomplu _ => Tlong :: Tlong :: nil + | Ccomplimm _ _ => Tlong :: nil + | Ccompluimm _ _ => Tlong :: nil + | Ccomplshift _ _ _ => Tlong :: Tlong :: nil + | Ccomplushift _ _ _ => Tlong :: Tlong :: nil + | Cmasklzero _ => Tlong :: nil + | Cmasklnotzero _ => Tlong :: nil + | Ccompf _ => Tfloat :: Tfloat :: nil + | Cnotcompf _ => Tfloat :: Tfloat :: nil + | Ccompfzero _ => Tfloat :: nil + | Cnotcompfzero _ => Tfloat :: nil + | Ccompfs _ => Tsingle :: Tsingle :: nil + | Cnotcompfs _ => Tsingle :: Tsingle :: nil + | Ccompfszero _ => Tsingle :: nil + | Cnotcompfszero _ => Tsingle :: nil + end. + +Definition type_of_operation (op: operation) : list typ * typ := + match op with + | Omove => (nil, Tint) (* treated specially *) + | Ointconst _ => (nil, Tint) + | Olongconst _ => (nil, Tlong) + | Ofloatconst f => (nil, Tfloat) + | Osingleconst f => (nil, Tsingle) + | Oaddrsymbol _ _ => (nil, Tptr) + | Oaddrstack _ => (nil, Tptr) + + | Oshift _ _ => (Tint :: nil, Tint) + | Oadd => (Tint :: Tint :: nil, Tint) + | Oaddshift _ _ => (Tint :: Tint :: nil, Tint) + | Oaddimm _ => (Tint :: nil, Tint) + | Oneg => (Tint :: nil, Tint) + | Onegshift _ _ => (Tint :: nil, Tint) + | Osub => (Tint :: Tint :: nil, Tint) + | Osubshift _ _ => (Tint :: Tint :: nil, Tint) + | Omul => (Tint :: Tint :: nil, Tint) + | Omuladd => (Tint :: Tint :: Tint :: nil, Tint) + | Omulsub => (Tint :: Tint :: Tint :: nil, Tint) + | Odiv => (Tint :: Tint :: nil, Tint) + | Odivu => (Tint :: Tint :: nil, Tint) + | Oand => (Tint :: Tint :: nil, Tint) + | Oandshift _ _ => (Tint :: Tint :: nil, Tint) + | Oandimm _ => (Tint :: nil, Tint) + | Oor => (Tint :: Tint :: nil, Tint) + | Oorshift _ _ => (Tint :: Tint :: nil, Tint) + | Oorimm _ => (Tint :: nil, Tint) + | Oxor => (Tint :: Tint :: nil, Tint) + | Oxorshift _ _ => (Tint :: Tint :: nil, Tint) + | Oxorimm _ => (Tint :: nil, Tint) + | Onot => (Tint :: nil, Tint) + | Onotshift _ _ => (Tint :: nil, Tint) + | Obic => (Tint :: Tint :: nil, Tint) + | Obicshift _ _ => (Tint :: Tint :: nil, Tint) + | Oorn => (Tint :: Tint :: nil, Tint) + | Oornshift _ _ => (Tint :: Tint :: nil, Tint) + | Oeqv => (Tint :: Tint :: nil, Tint) + | Oeqvshift _ _ => (Tint :: Tint :: nil, Tint) + | Oshl => (Tint :: Tint :: nil, Tint) + | Oshr => (Tint :: Tint :: nil, Tint) + | Oshru => (Tint :: Tint :: nil, Tint) + | Oshrximm _ => (Tint :: nil, Tint) + | Ozext _ => (Tint :: nil, Tint) + | Osext _ => (Tint :: nil, Tint) + | Oshlzext _ _ => (Tint :: nil, Tint) + | Oshlsext _ _ => (Tint :: nil, Tint) + | Ozextshr _ _ => (Tint :: nil, Tint) + | Osextshr _ _ => (Tint :: nil, Tint) + + | Oshiftl _ _ => (Tlong :: nil, Tlong) + | Oextend _ _ => (Tint :: nil, Tlong) + | Omakelong => (Tint :: Tint :: nil, Tlong) + | Olowlong => (Tlong :: nil, Tint) + | Ohighlong => (Tlong :: nil, Tint) + | Oaddl => (Tlong :: Tlong :: nil, Tlong) + | Oaddlshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Oaddlext _ _ => (Tlong :: Tint :: nil, Tlong) + | Oaddlimm _ => (Tlong :: nil, Tlong) + | Onegl => (Tlong :: nil, Tlong) + | Oneglshift _ _ => (Tlong :: nil, Tlong) + | Osubl => (Tlong :: Tlong :: nil, Tlong) + | Osublshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Osublext _ _ => (Tlong :: Tint :: nil, Tlong) + | Omull => (Tlong :: Tlong :: nil, Tlong) + | Omulladd => (Tlong :: Tlong :: Tlong :: nil, Tlong) + | Omullsub => (Tlong :: Tlong :: Tlong :: nil, Tlong) + | Omullhs => (Tlong :: Tlong :: nil, Tlong) + | Omullhu => (Tlong :: Tlong :: nil, Tlong) + | Odivl => (Tlong :: Tlong :: nil, Tlong) + | Odivlu => (Tlong :: Tlong :: nil, Tlong) + | Oandl => (Tlong :: Tlong :: nil, Tlong) + | Oandlshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Oandlimm _ => (Tlong :: nil, Tlong) + | Oorl => (Tlong :: Tlong :: nil, Tlong) + | Oorlshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Oorlimm _ => (Tlong :: nil, Tlong) + | Oxorl => (Tlong :: Tlong :: nil, Tlong) + | Oxorlshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Oxorlimm _ => (Tlong :: nil, Tlong) + | Onotl => (Tlong :: nil, Tlong) + | Onotlshift _ _ => (Tlong :: nil, Tlong) + | Obicl => (Tlong :: Tlong :: nil, Tlong) + | Obiclshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Oornl => (Tlong :: Tlong :: nil, Tlong) + | Oornlshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Oeqvl => (Tlong :: Tlong :: nil, Tlong) + | Oeqvlshift _ _ => (Tlong :: Tlong :: nil, Tlong) + | Oshll => (Tlong :: Tint :: nil, Tlong) + | Oshrl => (Tlong :: Tint :: nil, Tlong) + | Oshrlu => (Tlong :: Tint :: nil, Tlong) + | Oshrlximm _ => (Tlong :: nil, Tlong) + | Ozextl _ => (Tlong :: nil, Tlong) + | Osextl _ => (Tlong :: nil, Tlong) + | Oshllzext _ _ => (Tlong :: nil, Tlong) + | Oshllsext _ _ => (Tlong :: nil, Tlong) + | Ozextshrl _ _ => (Tlong :: nil, Tlong) + | Osextshrl _ _ => (Tlong :: nil, Tlong) + + | Onegf => (Tfloat :: nil, Tfloat) + | Oabsf => (Tfloat :: nil, Tfloat) + | Oaddf => (Tfloat :: Tfloat :: nil, Tfloat) + | Osubf => (Tfloat :: Tfloat :: nil, Tfloat) + | Omulf => (Tfloat :: Tfloat :: nil, Tfloat) + | Odivf => (Tfloat :: Tfloat :: nil, Tfloat) + + | Onegfs => (Tsingle :: nil, Tsingle) + | Oabsfs => (Tsingle :: nil, Tsingle) + | Oaddfs => (Tsingle :: Tsingle :: nil, Tsingle) + | Osubfs => (Tsingle :: Tsingle :: nil, Tsingle) + | Omulfs => (Tsingle :: Tsingle :: nil, Tsingle) + | Odivfs => (Tsingle :: Tsingle :: nil, Tsingle) + | Osingleoffloat => (Tfloat :: nil, Tsingle) + | Ofloatofsingle => (Tsingle :: nil, Tfloat) + + | Ointoffloat => (Tfloat :: nil, Tint) + | Ointuoffloat => (Tfloat :: nil, Tint) + | Ofloatofint => (Tint :: nil, Tfloat) + | Ofloatofintu => (Tint :: nil, Tfloat) + | Ointofsingle => (Tsingle :: nil, Tint) + | Ointuofsingle => (Tsingle :: nil, Tint) + | Osingleofint => (Tint :: nil, Tsingle) + | Osingleofintu => (Tint :: nil, Tsingle) + | Olongoffloat => (Tfloat :: nil, Tlong) + | Olonguoffloat => (Tfloat :: nil, Tlong) + | Ofloatoflong => (Tlong :: nil, Tfloat) + | Ofloatoflongu => (Tlong :: nil, Tfloat) + | Olongofsingle => (Tsingle :: nil, Tlong) + | Olonguofsingle => (Tsingle :: nil, Tlong) + | Osingleoflong => (Tlong :: nil, Tsingle) + | Osingleoflongu => (Tlong :: nil, Tsingle) + + | Ocmp c => (type_of_condition c, Tint) + | Osel c ty => (ty :: ty :: type_of_condition c, ty) + end. + +Definition type_of_addressing (addr: addressing) : list typ := + match addr with + | Aindexed _ => Tptr :: nil + | Aindexed2 => Tptr :: Tlong :: nil + | Aindexed2shift _ => Tptr :: Tlong :: nil + | Aindexed2ext _ _ => Tptr :: Tint :: nil + | Aglobal _ _ => nil + | Ainstack _ => nil + end. + +(** Weak type soundness results for [eval_operation]: + the result values, when defined, are always of the type predicted + by [type_of_operation]. *) + +Section SOUNDNESS. + +Variable A V: Type. +Variable genv: Genv.t A V. + +Remark type_add: + forall v1 v2, Val.has_type (Val.add v1 v2) Tint. +Proof. + intros. unfold Val.has_type, Val.add. destruct v1, v2; simpl; auto. +Qed. + +Remark type_sub: + forall v1 v2, Val.has_type (Val.sub v1 v2) Tint. +Proof. + intros. unfold Val.has_type, Val.add. destruct v1, v2; simpl; auto. +Qed. + +Remark type_addl: + forall v1 v2, Val.has_type (Val.addl v1 v2) Tlong. +Proof. + intros. unfold Val.has_type, Val.addl. destruct v1, v2; simpl; auto. +Qed. + +Remark type_subl: + forall v1 v2, Val.has_type (Val.subl v1 v2) Tlong. +Proof. + intros. unfold Val.has_type, Val.addl. destruct v1, v2; simpl; auto. + destruct (eq_block b b0); auto. +Qed. + +Lemma type_of_operation_sound: + forall op vl sp v m, + op <> Omove -> + eval_operation genv sp op vl m = Some v -> + Val.has_type v (snd (type_of_operation op)). +Proof with (try exact I; try reflexivity; auto using Val.Vptr_has_type). + intros. + destruct op; simpl; simpl in H0; FuncInv; subst; simpl. + (* move *) + - congruence. + (* intconst, longconst, floatconst, singleconst *) + - exact I. + - exact I. + - exact I. + - exact I. + (* addrsymbol *) + - unfold Genv.symbol_address. destruct (Genv.find_symbol genv id)... + (* addrstack *) + - destruct sp... + (* 32-bit integer operations *) + - destruct s, v0; try exact I; simpl; rewrite a32_range... + - apply type_add. + - apply type_add. + - apply type_add. + - destruct v0... + - destruct (eval_shift s v0 a)... + - apply type_sub. + - apply type_sub. + - destruct v0... destruct v1... + - apply type_add. + - apply type_sub. + - destruct v0; destruct v1; simpl in *; inv H0. + destruct (Int.eq i0 Int.zero || Int.eq i (Int.repr Int.min_signed) && Int.eq i0 Int.mone); inv H2... + - destruct v0; destruct v1; simpl in *; inv H0. + destruct (Int.eq i0 Int.zero); inv H2... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shift s v1 a)... + - destruct v0... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shift s v1 a)... + - destruct v0... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shift s v1 a)... + - destruct v0... + - destruct v0... + - destruct (eval_shift s v0 a)... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shift s v1 a)... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shift s v1 a)... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shift s v1 a)... + - destruct v0; destruct v1; simpl... destruct (Int.ltu i0 Int.iwordsize)... + - destruct v0; destruct v1; simpl... destruct (Int.ltu i0 Int.iwordsize)... + - destruct v0; destruct v1; simpl... destruct (Int.ltu i0 Int.iwordsize)... + - destruct v0; simpl in H0; try discriminate. destruct (Int.ltu n (Int.repr 31)); inv H0... + - destruct v0... + - destruct v0... + - destruct (Val.zero_ext s v0)... simpl; rewrite a32_range... + - destruct (Val.sign_ext s v0)... simpl; rewrite a32_range... + - destruct (Val.shru v0 (Vint a))... + - destruct (Val.shr v0 (Vint a))... + (* 64-bit integer operations *) + - destruct s, v0; try exact I; simpl; rewrite a64_range... + - unfold eval_extend. destruct (match x with + | Xsgn32 => Val.longofint v0 + | Xuns32 => Val.longofintu v0 + end)... + simpl; rewrite a64_range... + - destruct v0... destruct v1... + - destruct v0... + - destruct v0... + - apply type_addl. + - apply type_addl. + - apply type_addl. + - apply type_addl. + - destruct v0... + - destruct (eval_shiftl s v0 a)... + - apply type_subl. + - apply type_subl. + - apply type_subl. + - destruct v0... destruct v1... + - apply type_addl. + - apply type_subl. + - destruct v0... destruct v1... + - destruct v0... destruct v1... + - destruct v0; destruct v1; simpl in *; inv H0. + destruct (Int64.eq i0 Int64.zero || Int64.eq i (Int64.repr Int64.min_signed) && Int64.eq i0 Int64.mone); inv H2... + - destruct v0; destruct v1; simpl in *; inv H0. + destruct (Int64.eq i0 Int64.zero); inv H2... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shiftl s v1 a)... + - destruct v0... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shiftl s v1 a)... + - destruct v0... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shiftl s v1 a)... + - destruct v0... + - destruct v0... + - destruct (eval_shiftl s v0 a)... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shiftl s v1 a)... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shiftl s v1 a)... + - destruct v0... destruct v1... + - destruct v0... destruct (eval_shiftl s v1 a)... + - destruct v0; destruct v1; simpl... destruct (Int.ltu i0 Int64.iwordsize')... + - destruct v0; destruct v1; simpl... destruct (Int.ltu i0 Int64.iwordsize')... + - destruct v0; destruct v1; simpl... destruct (Int.ltu i0 Int64.iwordsize')... + - destruct v0; simpl in H0; try discriminate. destruct (Int.ltu n (Int.repr 63)); inv H0... + - destruct v0... + - destruct v0... + - destruct (Val.zero_ext_l s v0)... simpl; rewrite a64_range... + - destruct (Val.sign_ext_l s v0)... simpl; rewrite a64_range... + - destruct (Val.shrlu v0 (Vint a))... + - destruct (Val.shrl v0 (Vint a))... + + (* 64-bit FP *) + - destruct v0... + - destruct v0... + - destruct v0; destruct v1... + - destruct v0; destruct v1... + - destruct v0; destruct v1... + - destruct v0; destruct v1... + (* 32-bit FP *) + - destruct v0... + - destruct v0... + - destruct v0; destruct v1... + - destruct v0; destruct v1... + - destruct v0; destruct v1... + - destruct v0; destruct v1... + (* singleoffloat, floatofsingle *) + - destruct v0... + - destruct v0... + (* intoffloat, intuoffloat *) + - destruct v0; simpl in H0; inv H0. destruct (Float.to_int f); inv H2... + - destruct v0; simpl in H0; inv H0. destruct (Float.to_intu f); inv H2... + (* floatofint, floatofintu *) + - destruct v0; simpl in H0; inv H0... + - destruct v0; simpl in H0; inv H0... + (* intofsingle, intuofsingle *) + - destruct v0; simpl in H0; inv H0. destruct (Float32.to_int f); inv H2... + - destruct v0; simpl in H0; inv H0. destruct (Float32.to_intu f); inv H2... + (* singleofint, singleofintu *) + - destruct v0; simpl in H0; inv H0... + - destruct v0; simpl in H0; inv H0... + (* longoffloat, longuoffloat *) + - destruct v0; simpl in H0; inv H0. destruct (Float.to_long f); inv H2... + - destruct v0; simpl in H0; inv H0. destruct (Float.to_longu f); inv H2... + (* floatoflong, floatoflongu *) + - destruct v0; simpl in H0; inv H0... + - destruct v0; simpl in H0; inv H0... + (* longofsingle, longuofsingle *) + - destruct v0; simpl in H0; inv H0. destruct (Float32.to_long f); inv H2... + - destruct v0; simpl in H0; inv H0. destruct (Float32.to_longu f); inv H2... + (* singleoflong, singleoflongu *) + - destruct v0; simpl in H0; inv H0... + - destruct v0; simpl in H0; inv H0... + (* cmp *) + - destruct (eval_condition cond vl m) as [[]|]... + - unfold Val.select. destruct (eval_condition cond vl m). apply Val.normalize_type. exact I. +Qed. + +End SOUNDNESS. + +(** * Manipulating and transforming operations *) + +(** Constructing shift amounts *) + +Section SHIFT_AMOUNT. + +Variable l: Z. +Hypothesis l_range: 0 <= l < 32. +Variable N: int. +Hypothesis N_eq: Int.unsigned N = two_p l. + +Remark mk_amount_range: + forall n, Int.ltu (Int.zero_ext l n) N = true. +Proof. + intros; unfold Int.ltu. apply zlt_true. rewrite N_eq. apply (Int.zero_ext_range l n). assumption. +Qed. + +Remark mk_amount_eq: + forall n, Int.ltu n N = true -> Int.zero_ext l n = n. +Proof. + intros. + transitivity (Int.repr (Int.unsigned (Int.zero_ext l n))). + symmetry; apply Int.repr_unsigned. + transitivity (Int.repr (Int.unsigned n)). + f_equal. rewrite Int.zero_ext_mod. apply Int.ltu_inv in H. rewrite N_eq in H. + apply Z.mod_small. assumption. assumption. + apply Int.repr_unsigned. +Qed. + +End SHIFT_AMOUNT. + +Program Definition mk_amount32 (n: int): amount32 := + {| a32_amount := Int.zero_ext 5 n |}. +Next Obligation. + apply mk_amount_range. omega. reflexivity. +Qed. + +Lemma mk_amount32_eq: forall n, + Int.ltu n Int.iwordsize = true -> a32_amount (mk_amount32 n) = n. +Proof. + intros. eapply mk_amount_eq; eauto. omega. reflexivity. +Qed. + +Program Definition mk_amount64 (n: int): amount64 := + {| a64_amount := Int.zero_ext 6 n |}. +Next Obligation. + apply mk_amount_range. omega. reflexivity. +Qed. + +Lemma mk_amount64_eq: forall n, + Int.ltu n Int64.iwordsize' = true -> a64_amount (mk_amount64 n) = n. +Proof. + intros. eapply mk_amount_eq; eauto. omega. reflexivity. +Qed. + +(** Recognition of move operations. *) + +Definition is_move_operation + (A: Type) (op: operation) (args: list A) : option A := + match op, args with + | Omove, arg :: nil => Some arg + | _, _ => None + end. + +Lemma is_move_operation_correct: + forall (A: Type) (op: operation) (args: list A) (a: A), + is_move_operation op args = Some a -> + op = Omove /\ args = a :: nil. +Proof. + intros until a. unfold is_move_operation; destruct op; + try (intros; discriminate). + destruct args. intros; discriminate. + destruct args. intros. intuition congruence. + intros; discriminate. +Qed. + +(** [negate_condition cond] returns a condition that is logically + equivalent to the negation of [cond]. *) + +Definition negate_condition (cond: condition): condition := + match cond with + | Ccomp c => Ccomp (negate_comparison c) + | Ccompu c => Ccompu (negate_comparison c) + | Ccompimm c n => Ccompimm (negate_comparison c) n + | Ccompuimm c n => Ccompuimm (negate_comparison c) n + | Ccompshift c s a => Ccompshift (negate_comparison c) s a + | Ccompushift c s a => Ccompushift (negate_comparison c) s a + | Cmaskzero n => Cmasknotzero n + | Cmasknotzero n => Cmaskzero n + | Ccompl c => Ccompl (negate_comparison c) + | Ccomplu c => Ccomplu (negate_comparison c) + | Ccomplimm c n => Ccomplimm (negate_comparison c) n + | Ccompluimm c n => Ccompluimm (negate_comparison c) n + | Ccomplshift c s a => Ccomplshift (negate_comparison c) s a + | Ccomplushift c s a => Ccomplushift (negate_comparison c) s a + | Cmasklzero n => Cmasklnotzero n + | Cmasklnotzero n => Cmasklzero n + | Ccompf c => Cnotcompf c + | Cnotcompf c => Ccompf c + | Ccompfzero c => Cnotcompfzero c + | Cnotcompfzero c => Ccompfzero c + | Ccompfs c => Cnotcompfs c + | Cnotcompfs c => Ccompfs c + | Ccompfszero c => Cnotcompfszero c + | Cnotcompfszero c => Ccompfszero c + end. + +Lemma eval_negate_condition: + forall cond vl m, + eval_condition (negate_condition cond) vl m = option_map negb (eval_condition cond vl m). +Proof. + intros. destruct cond; simpl. + repeat (destruct vl; auto). apply Val.negate_cmp_bool. + repeat (destruct vl; auto). apply Val.negate_cmpu_bool. + repeat (destruct vl; auto). apply Val.negate_cmp_bool. + repeat (destruct vl; auto). apply Val.negate_cmpu_bool. + repeat (destruct vl; auto). apply Val.negate_cmp_bool. + repeat (destruct vl; auto). apply Val.negate_cmpu_bool. + repeat (destruct vl; auto). apply (Val.negate_cmp_bool Ceq). + repeat (destruct vl; auto). apply (Val.negate_cmp_bool Cne). + repeat (destruct vl; auto). apply Val.negate_cmpl_bool. + repeat (destruct vl; auto). apply Val.negate_cmplu_bool. + repeat (destruct vl; auto). apply Val.negate_cmpl_bool. + repeat (destruct vl; auto). apply Val.negate_cmplu_bool. + repeat (destruct vl; auto). apply Val.negate_cmpl_bool. + repeat (destruct vl; auto). apply Val.negate_cmplu_bool. + repeat (destruct vl; auto). apply (Val.negate_cmpl_bool Ceq). + repeat (destruct vl; auto). apply (Val.negate_cmpl_bool Cne). + repeat (destruct vl; auto). + repeat (destruct vl; auto). destruct (Val.cmpf_bool c v v0) as [[]|]; auto. + repeat (destruct vl; auto). + repeat (destruct vl; auto). destruct (Val.cmpf_bool c v (Vfloat Float.zero)) as [[]|]; auto. + repeat (destruct vl; auto). + repeat (destruct vl; auto). destruct (Val.cmpfs_bool c v v0) as [[]|]; auto. + repeat (destruct vl; auto). + repeat (destruct vl; auto). destruct (Val.cmpfs_bool c v (Vsingle Float32.zero)) as [[]|]; auto. +Qed. + +(** Shifting stack-relative references. This is used in [Stacking]. *) + +Definition shift_stack_addressing (delta: Z) (addr: addressing) := + match addr with + | Ainstack ofs => Ainstack (Ptrofs.add ofs (Ptrofs.repr delta)) + | _ => addr + end. + +Definition shift_stack_operation (delta: Z) (op: operation) := + match op with + | Oaddrstack ofs => Oaddrstack (Ptrofs.add ofs (Ptrofs.repr delta)) + | _ => op + end. + +Lemma type_shift_stack_addressing: + forall delta addr, type_of_addressing (shift_stack_addressing delta addr) = type_of_addressing addr. +Proof. + intros. destruct addr; auto. +Qed. + +Lemma type_shift_stack_operation: + forall delta op, type_of_operation (shift_stack_operation delta op) = type_of_operation op. +Proof. + intros. destruct op; auto. +Qed. + +Lemma eval_shift_stack_addressing: + forall F V (ge: Genv.t F V) sp addr vl delta, + eval_addressing ge (Vptr sp Ptrofs.zero) (shift_stack_addressing delta addr) vl = + eval_addressing ge (Vptr sp (Ptrofs.repr delta)) addr vl. +Proof. + intros. destruct addr; simpl; auto. destruct vl; auto. + rewrite Ptrofs.add_zero_l, Ptrofs.add_commut; auto. +Qed. + +Lemma eval_shift_stack_operation: + forall F V (ge: Genv.t F V) sp op vl m delta, + eval_operation ge (Vptr sp Ptrofs.zero) (shift_stack_operation delta op) vl m = + eval_operation ge (Vptr sp (Ptrofs.repr delta)) op vl m. +Proof. + intros. destruct op; simpl; auto. destruct vl; auto. + rewrite Ptrofs.add_zero_l, Ptrofs.add_commut; auto. +Qed. + +(** Offset an addressing mode [addr] by a quantity [delta], so that + it designates the pointer [delta] bytes past the pointer designated + by [addr]. May be undefined, in which case [None] is returned. *) + +Definition offset_addressing (addr: addressing) (delta: Z) : option addressing := + match addr with + | Aindexed n => Some(Aindexed (Int64.add n (Int64.repr delta))) + | Aindexed2 => None + | Aindexed2shift _ => None + | Aindexed2ext _ _ => None + | Aglobal id n => Some(Aglobal id (Ptrofs.add n (Ptrofs.repr delta))) + | Ainstack n => Some(Ainstack (Ptrofs.add n (Ptrofs.repr delta))) + end. + +Lemma eval_offset_addressing: + forall (F V: Type) (ge: Genv.t F V) sp addr args delta addr' v, + offset_addressing addr delta = Some addr' -> + eval_addressing ge sp addr args = Some v -> + Archi.ptr64 = false -> + eval_addressing ge sp addr' args = Some(Val.add v (Vint (Int.repr delta))). +Proof. + intros. discriminate. +Qed. + +(** Operations that are so cheap to recompute that CSE should not factor them out. *) + +Definition is_trivial_op (op: operation) : bool := + match op with + | Omove => true + | Ointconst n => Int.eq (Int.sign_ext 16 n) n + | Olongconst n => Int64.eq (Int64.sign_ext 16 n) n + | Oaddrstack _ => true + | _ => false + end. + +(** Operations that depend on the memory state. *) + +Definition cond_depends_on_memory (c: condition) : bool := + match c with + | Ccomplu _ | Ccompluimm _ _ | Ccomplushift _ _ _ => true + | _ => false + end. + +Lemma cond_depends_on_memory_correct: + forall c args m1 m2, + cond_depends_on_memory c = false -> + eval_condition c args m1 = eval_condition c args m2. +Proof. + intros; destruct c; simpl; discriminate || reflexivity. +Qed. + +Definition op_depends_on_memory (op: operation) : bool := + match op with + | Ocmp c => cond_depends_on_memory c + | Osel c yu => cond_depends_on_memory c + | _ => false + end. + +Lemma op_depends_on_memory_correct: + forall (F V: Type) (ge: Genv.t F V) sp op args m1 m2, + op_depends_on_memory op = false -> + eval_operation ge sp op args m1 = eval_operation ge sp op args m2. +Proof. + intros. destruct op; auto. + simpl. rewrite (cond_depends_on_memory_correct cond args m1 m2 H). auto. + simpl. destruct args; auto. destruct args; auto. + rewrite (cond_depends_on_memory_correct cond args m1 m2 H). auto. +Qed. + +(** Global variables mentioned in an operation or addressing mode *) + +Definition globals_addressing (addr: addressing) : list ident := + match addr with + | Aglobal s ofs => s :: nil + | _ => nil + end. + +Definition globals_operation (op: operation) : list ident := + match op with + | Oaddrsymbol s ofs => s :: nil + | _ => nil + end. + +(** * Invariance and compatibility properties. *) + +(** [eval_operation] and [eval_addressing] depend on a global environment + for resolving references to global symbols. We show that they give + the same results if a global environment is replaced by another that + assigns the same addresses to the same symbols. *) + +Section GENV_TRANSF. + +Variable F1 F2 V1 V2: Type. +Variable ge1: Genv.t F1 V1. +Variable ge2: Genv.t F2 V2. +Hypothesis agree_on_symbols: + forall (s: ident), Genv.find_symbol ge2 s = Genv.find_symbol ge1 s. + +Lemma eval_addressing_preserved: + forall sp addr vl, + eval_addressing ge2 sp addr vl = eval_addressing ge1 sp addr vl. +Proof. + intros. + unfold eval_addressing; destruct addr; auto. destruct vl; auto. + unfold Genv.symbol_address. rewrite agree_on_symbols; auto. +Qed. + +Lemma eval_operation_preserved: + forall sp op vl m, + eval_operation ge2 sp op vl m = eval_operation ge1 sp op vl m. +Proof. + intros. + unfold eval_operation; destruct op; auto. destruct vl; auto. + unfold Genv.symbol_address. rewrite agree_on_symbols; auto. +Qed. + +End GENV_TRANSF. + +(** Compatibility of the evaluation functions with value injections. *) + +Section EVAL_COMPAT. + +Variable F1 F2 V1 V2: Type. +Variable ge1: Genv.t F1 V1. +Variable ge2: Genv.t F2 V2. +Variable f: meminj. + +Variable m1: mem. +Variable m2: mem. + +Hypothesis valid_pointer_inj: + forall b1 ofs b2 delta, + f b1 = Some(b2, delta) -> + Mem.valid_pointer m1 b1 (Ptrofs.unsigned ofs) = true -> + Mem.valid_pointer m2 b2 (Ptrofs.unsigned (Ptrofs.add ofs (Ptrofs.repr delta))) = true. + +Hypothesis weak_valid_pointer_inj: + forall b1 ofs b2 delta, + f b1 = Some(b2, delta) -> + Mem.weak_valid_pointer m1 b1 (Ptrofs.unsigned ofs) = true -> + Mem.weak_valid_pointer m2 b2 (Ptrofs.unsigned (Ptrofs.add ofs (Ptrofs.repr delta))) = true. + +Hypothesis weak_valid_pointer_no_overflow: + forall b1 ofs b2 delta, + f b1 = Some(b2, delta) -> + Mem.weak_valid_pointer m1 b1 (Ptrofs.unsigned ofs) = true -> + 0 <= Ptrofs.unsigned ofs + Ptrofs.unsigned (Ptrofs.repr delta) <= Ptrofs.max_unsigned. + +Hypothesis valid_different_pointers_inj: + forall b1 ofs1 b2 ofs2 b1' delta1 b2' delta2, + b1 <> b2 -> + Mem.valid_pointer m1 b1 (Ptrofs.unsigned ofs1) = true -> + Mem.valid_pointer m1 b2 (Ptrofs.unsigned ofs2) = true -> + f b1 = Some (b1', delta1) -> + f b2 = Some (b2', delta2) -> + b1' <> b2' \/ + Ptrofs.unsigned (Ptrofs.add ofs1 (Ptrofs.repr delta1)) <> Ptrofs.unsigned (Ptrofs.add ofs2 (Ptrofs.repr delta2)). + +Ltac InvInject := + match goal with + | [ H: Val.inject _ (Vint _) _ |- _ ] => + inv H; InvInject + | [ H: Val.inject _ (Vfloat _) _ |- _ ] => + inv H; InvInject + | [ H: Val.inject _ (Vptr _ _) _ |- _ ] => + inv H; InvInject + | [ H: Val.inject_list _ nil _ |- _ ] => + inv H; InvInject + | [ H: Val.inject_list _ (_ :: _) _ |- _ ] => + inv H; InvInject + | _ => idtac + end. + +Lemma eval_shift_inject: + forall v1 v2 s a, + Val.inject f v1 v2 -> Val.inject f (eval_shift s v1 a) (eval_shift s v2 a). +Proof. + intros; inv H; destruct s; simpl; auto; rewrite a32_range; auto. +Qed. + +Lemma eval_shiftl_inject: + forall v1 v2 s a, + Val.inject f v1 v2 -> Val.inject f (eval_shiftl s v1 a) (eval_shiftl s v2 a). +Proof. + intros; inv H; destruct s; simpl; auto; rewrite a64_range; auto. +Qed. + +Lemma eval_extend_inject: + forall v1 v2 x a, + Val.inject f v1 v2 -> Val.inject f (eval_extend x v1 a) (eval_extend x v2 a). +Proof. + unfold eval_extend; intros; inv H; destruct x; simpl; auto; rewrite a64_range; auto. +Qed. + +Lemma eval_condition_inj: + forall cond vl1 vl2 b, + Val.inject_list f vl1 vl2 -> + eval_condition cond vl1 m1 = Some b -> + eval_condition cond vl2 m2 = Some b. +Proof. + intros. destruct cond; simpl in H0; FuncInv; InvInject; simpl; auto. +(* 32-bit integers *) +- inv H3; inv H2; simpl in H0; inv H0; auto. +- eauto 3 using Val.cmpu_bool_inject, Mem.valid_pointer_implies. +- inv H3; simpl in H0; inv H0; auto. +- eauto 3 using Val.cmpu_bool_inject, Mem.valid_pointer_implies. +- revert H0. generalize (eval_shift_inject s a H2); intros J; inv H3; inv J; simpl; congruence. +- eauto 3 using Val.cmpu_bool_inject, Mem.valid_pointer_implies, eval_shift_inject. +- inv H3; inv H0; auto. +- inv H3; inv H0; auto. +(* 64-bit integers *) +- inv H3; inv H2; simpl in H0; inv H0; auto. +- eauto 3 using Val.cmplu_bool_inject, Mem.valid_pointer_implies. +- inv H3; simpl in H0; inv H0; auto. +- eauto 3 using Val.cmplu_bool_inject, Mem.valid_pointer_implies. +- revert H0. generalize (eval_shiftl_inject s a H2); intros J; inv H3; inv J; simpl; congruence. +- eauto 3 using Val.cmplu_bool_inject, Mem.valid_pointer_implies, eval_shiftl_inject. +- inv H3; inv H0; auto. +- inv H3; inv H0; auto. +(* 64-bit floats *) +- inv H3; inv H2; simpl in H0; inv H0; auto. +- inv H3; inv H2; simpl in H0; inv H0; auto. +- inv H3; simpl in H0; inv H0; auto. +- inv H3; simpl in H0; inv H0; auto. +(* 32-bit floats *) +- inv H3; inv H2; simpl in H0; inv H0; auto. +- inv H3; inv H2; simpl in H0; inv H0; auto. +- inv H3; simpl in H0; inv H0; auto. +- inv H3; simpl in H0; inv H0; auto. +Qed. + +Ltac TrivialExists := + match goal with + | [ |- exists v2, Some ?v1 = Some v2 /\ Val.inject _ _ v2 ] => + exists v1; split; auto + | _ => idtac + end. + +Lemma eval_operation_inj: + forall op sp1 vl1 sp2 vl2 v1, + (forall id ofs, + In id (globals_operation op) -> + Val.inject f (Genv.symbol_address ge1 id ofs) (Genv.symbol_address ge2 id ofs)) -> + Val.inject f sp1 sp2 -> + Val.inject_list f vl1 vl2 -> + eval_operation ge1 sp1 op vl1 m1 = Some v1 -> + exists v2, eval_operation ge2 sp2 op vl2 m2 = Some v2 /\ Val.inject f v1 v2. +Proof. + intros until v1; intros GL; intros. destruct op; simpl in H1; simpl; FuncInv; InvInject; TrivialExists. + (* addrsymbol *) + - apply GL; simpl; auto. + (* addrstack *) + - apply Val.offset_ptr_inject; auto. + (* shift *) + - apply eval_shift_inject; auto. + (* add *) + - apply Val.add_inject; auto. + - apply Val.add_inject; auto using eval_shift_inject. + - apply Val.add_inject; auto. + (* neg, sub *) + - inv H4; simpl; auto. + - generalize (eval_shift_inject s a H4); intros J; inv J; simpl; auto. + - apply Val.sub_inject; auto. + - apply Val.sub_inject; auto using eval_shift_inject. + (* mul, muladd, mulsub *) + - inv H4; inv H2; simpl; auto. + - apply Val.add_inject; auto. inv H2; inv H3; simpl; auto. + - apply Val.sub_inject; auto. inv H2; inv H3; simpl; auto. + (* div, divu *) + - inv H4; inv H3; simpl in H1; inv H1. simpl. + destruct (Int.eq i0 Int.zero + || Int.eq i (Int.repr Int.min_signed) && Int.eq i0 Int.mone); inv H2. + TrivialExists. + - inv H4; inv H3; simpl in H1; inv H1. simpl. + destruct (Int.eq i0 Int.zero); inv H2. TrivialExists. + (* and*) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shift_inject s a H2); intros J; inv H4; inv J; simpl; auto. + - inv H4; simpl; auto. + (* or *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shift_inject s a H2); intros J; inv H4; inv J; simpl; auto. + - inv H4; simpl; auto. + (* xor *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shift_inject s a H2); intros J; inv H4; inv J; simpl; auto. + - inv H4; simpl; auto. + (* not *) + - inv H4; simpl; auto. + - generalize (eval_shift_inject s a H4); intros J; inv J; simpl; auto. + (* bic *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shift_inject s a H2); intros J; inv H4; inv J; simpl; auto. + (* nor *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shift_inject s a H2); intros J; inv H4; inv J; simpl; auto. + (* eqv *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shift_inject s a H2); intros J; inv H4; inv J; simpl; auto. + (* shl *) + - inv H4; inv H2; simpl; auto. destruct (Int.ltu i0 Int.iwordsize); auto. + (* shr *) + - inv H4; inv H2; simpl; auto. destruct (Int.ltu i0 Int.iwordsize); auto. + (* shru *) + - inv H4; inv H2; simpl; auto. destruct (Int.ltu i0 Int.iwordsize); auto. + (* shrx *) + - inv H4; simpl in H1; try discriminate. simpl. + destruct (Int.ltu n (Int.repr 31)); inv H1. TrivialExists. + (* shift-ext *) + - inv H4; simpl; auto. + - inv H4; simpl; auto. + - inv H4; simpl; auto; rewrite a32_range; auto. + - inv H4; simpl; auto; rewrite a32_range; auto. + - inv H4; simpl; auto; rewrite a32_range; simpl; auto. + - inv H4; simpl; auto; rewrite a32_range; simpl; auto. + + (* shiftl *) + - apply eval_shiftl_inject; auto. + (* extend *) + - apply eval_extend_inject; auto. + (* makelong, low, high *) + - inv H4; inv H2; simpl; auto. + - inv H4; simpl; auto. + - inv H4; simpl; auto. + (* addl *) + - apply Val.addl_inject; auto. + - apply Val.addl_inject; auto using eval_shiftl_inject. + - apply Val.addl_inject; auto using eval_extend_inject. + - apply Val.addl_inject; auto. + (* negl, subl *) + - inv H4; simpl; auto. + - generalize (eval_shiftl_inject s a H4); intros J; inv J; simpl; auto. + - apply Val.subl_inject; auto. + - apply Val.subl_inject; auto using eval_shiftl_inject. + - apply Val.subl_inject; auto using eval_extend_inject. + (* mull, mulladd, mullsub, mullhs, mullhu *) + - inv H4; inv H2; simpl; auto. + - apply Val.addl_inject; auto. inv H2; inv H3; simpl; auto. + - apply Val.subl_inject; auto. inv H2; inv H3; simpl; auto. + - inv H4; inv H2; simpl; auto. + - inv H4; inv H2; simpl; auto. + (* divl, divlu *) + - inv H4; inv H3; simpl in H1; inv H1. simpl. + destruct (Int64.eq i0 Int64.zero + || Int64.eq i (Int64.repr Int64.min_signed) && Int64.eq i0 Int64.mone); inv H2. + TrivialExists. + - inv H4; inv H3; simpl in H1; inv H1. simpl. + destruct (Int64.eq i0 Int64.zero); inv H2. TrivialExists. + (* andl *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shiftl_inject s a H2); intros J; inv H4; inv J; simpl; auto. + - inv H4; simpl; auto. + (* orl *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shiftl_inject s a H2); intros J; inv H4; inv J; simpl; auto. + - inv H4; simpl; auto. + (* xorl *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shiftl_inject s a H2); intros J; inv H4; inv J; simpl; auto. + - inv H4; simpl; auto. + (* notl *) + - inv H4; simpl; auto. + - generalize (eval_shiftl_inject s a H4); intros J; inv J; simpl; auto. + (* bicl *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shiftl_inject s a H2); intros J; inv H4; inv J; simpl; auto. + (* norl *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shiftl_inject s a H2); intros J; inv H4; inv J; simpl; auto. + (* eqvl *) + - inv H4; inv H2; simpl; auto. + - generalize (eval_shiftl_inject s a H2); intros J; inv H4; inv J; simpl; auto. + (* shll *) + - inv H4; inv H2; simpl; auto. destruct (Int.ltu i0 Int64.iwordsize'); auto. + (* shrl *) + - inv H4; inv H2; simpl; auto. destruct (Int.ltu i0 Int64.iwordsize'); auto. + (* shrlu *) + - inv H4; inv H2; simpl; auto. destruct (Int.ltu i0 Int64.iwordsize'); auto. + (* shrlx *) + - inv H4; simpl in H1; try discriminate. simpl. + destruct (Int.ltu n (Int.repr 63)); inv H1. TrivialExists. + (* shift-ext *) + - inv H4; simpl; auto. + - inv H4; simpl; auto. + - inv H4; simpl; auto; rewrite a64_range; auto. + - inv H4; simpl; auto; rewrite a64_range; auto. + - inv H4; simpl; auto; rewrite a64_range; simpl; auto. + - inv H4; simpl; auto; rewrite a64_range; simpl; auto. + + (* negf, absf *) + - inv H4; simpl; auto. + - inv H4; simpl; auto. + (* addf, subf *) + - inv H4; inv H2; simpl; auto. + - inv H4; inv H2; simpl; auto. + (* mulf, divf *) + - inv H4; inv H2; simpl; auto. + - inv H4; inv H2; simpl; auto. + (* negfs, absfs *) + - inv H4; simpl; auto. + - inv H4; simpl; auto. + (* addfs, subfs *) + - inv H4; inv H2; simpl; auto. + - inv H4; inv H2; simpl; auto. + (* mulfs, divfs *) + - inv H4; inv H2; simpl; auto. + - inv H4; inv H2; simpl; auto. + (* singleoffloat, floatofsingle *) + - inv H4; simpl; auto. + - inv H4; simpl; auto. + (* intoffloat, intuoffloat *) + - inv H4; simpl in H1; inv H1. simpl. destruct (Float.to_int f0); simpl in H2; inv H2. + exists (Vint i); auto. + - inv H4; simpl in H1; inv H1. simpl. destruct (Float.to_intu f0); simpl in H2; inv H2. + exists (Vint i); auto. + (* floatofint, floatofintu *) + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + (* intofsingle, intuofsingle *) + - inv H4; simpl in H1; inv H1. simpl. destruct (Float32.to_int f0); simpl in H2; inv H2. + exists (Vint i); auto. + - inv H4; simpl in H1; inv H1. simpl. destruct (Float32.to_intu f0); simpl in H2; inv H2. + exists (Vint i); auto. + (* singleofint, singleofintu *) + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + (* longoffloat, longuoffloat *) + - inv H4; simpl in H1; inv H1. simpl. destruct (Float.to_long f0); simpl in H2; inv H2. + exists (Vlong i); auto. + - inv H4; simpl in H1; inv H1. simpl. destruct (Float.to_longu f0); simpl in H2; inv H2. + exists (Vlong i); auto. + (* floatoflong, floatoflongu *) + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + (* longofsingle, longuofsingle *) + - inv H4; simpl in H1; inv H1. simpl. destruct (Float32.to_long f0); simpl in H2; inv H2. + exists (Vlong i); auto. + - inv H4; simpl in H1; inv H1. simpl. destruct (Float32.to_longu f0); simpl in H2; inv H2. + exists (Vlong i); auto. + (* singleoflong, singleoflongu *) + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + - inv H4; simpl in H1; inv H1. simpl. TrivialExists. + (* cmp, sel *) + - subst v1. destruct (eval_condition cond vl1 m1) eqn:?. + exploit eval_condition_inj; eauto. intros EQ; rewrite EQ. + destruct b; simpl; constructor. + simpl; constructor. + - apply Val.select_inject; auto. + destruct (eval_condition cond vl1 m1) eqn:?; auto. + right; symmetry; eapply eval_condition_inj; eauto. +Qed. + +Lemma eval_addressing_inj: + forall addr sp1 vl1 sp2 vl2 v1, + (forall id ofs, + In id (globals_addressing addr) -> + Val.inject f (Genv.symbol_address ge1 id ofs) (Genv.symbol_address ge2 id ofs)) -> + Val.inject f sp1 sp2 -> + Val.inject_list f vl1 vl2 -> + eval_addressing ge1 sp1 addr vl1 = Some v1 -> + exists v2, eval_addressing ge2 sp2 addr vl2 = Some v2 /\ Val.inject f v1 v2. +Proof. + intros. destruct addr; simpl in H2; simpl; FuncInv; InvInject; TrivialExists. +- apply Val.addl_inject; auto. +- apply Val.addl_inject; auto. +- apply Val.addl_inject; auto. inv H3; simpl; auto; rewrite a64_range; auto. +- apply Val.addl_inject; auto using eval_extend_inject. +- apply H; simpl; auto. +- apply Val.offset_ptr_inject; auto. +Qed. + +End EVAL_COMPAT. + +(** Compatibility of the evaluation functions with the ``is less defined'' relation over values. *) + +Section EVAL_LESSDEF. + +Variable F V: Type. +Variable genv: Genv.t F V. + +Remark valid_pointer_extends: + forall m1 m2, Mem.extends m1 m2 -> + forall b1 ofs b2 delta, + Some(b1, 0) = Some(b2, delta) -> + Mem.valid_pointer m1 b1 (Ptrofs.unsigned ofs) = true -> + Mem.valid_pointer m2 b2 (Ptrofs.unsigned (Ptrofs.add ofs (Ptrofs.repr delta))) = true. +Proof. + intros. inv H0. rewrite Ptrofs.add_zero. eapply Mem.valid_pointer_extends; eauto. +Qed. + +Remark weak_valid_pointer_extends: + forall m1 m2, Mem.extends m1 m2 -> + forall b1 ofs b2 delta, + Some(b1, 0) = Some(b2, delta) -> + Mem.weak_valid_pointer m1 b1 (Ptrofs.unsigned ofs) = true -> + Mem.weak_valid_pointer m2 b2 (Ptrofs.unsigned (Ptrofs.add ofs (Ptrofs.repr delta))) = true. +Proof. + intros. inv H0. rewrite Ptrofs.add_zero. eapply Mem.weak_valid_pointer_extends; eauto. +Qed. + +Remark weak_valid_pointer_no_overflow_extends: + forall m1 b1 ofs b2 delta, + Some(b1, 0) = Some(b2, delta) -> + Mem.weak_valid_pointer m1 b1 (Ptrofs.unsigned ofs) = true -> + 0 <= Ptrofs.unsigned ofs + Ptrofs.unsigned (Ptrofs.repr delta) <= Ptrofs.max_unsigned. +Proof. + intros. inv H. rewrite Z.add_0_r. apply Ptrofs.unsigned_range_2. +Qed. + +Remark valid_different_pointers_extends: + forall m1 b1 ofs1 b2 ofs2 b1' delta1 b2' delta2, + b1 <> b2 -> + Mem.valid_pointer m1 b1 (Ptrofs.unsigned ofs1) = true -> + Mem.valid_pointer m1 b2 (Ptrofs.unsigned ofs2) = true -> + Some(b1, 0) = Some (b1', delta1) -> + Some(b2, 0) = Some (b2', delta2) -> + b1' <> b2' \/ + Ptrofs.unsigned(Ptrofs.add ofs1 (Ptrofs.repr delta1)) <> Ptrofs.unsigned(Ptrofs.add ofs2 (Ptrofs.repr delta2)). +Proof. + intros. inv H2; inv H3. auto. +Qed. + +Lemma eval_condition_lessdef: + forall cond vl1 vl2 b m1 m2, + Val.lessdef_list vl1 vl2 -> + Mem.extends m1 m2 -> + eval_condition cond vl1 m1 = Some b -> + eval_condition cond vl2 m2 = Some b. +Proof. + intros. eapply eval_condition_inj with (f := fun b => Some(b, 0)) (m1 := m1). + apply valid_pointer_extends; auto. + apply weak_valid_pointer_extends; auto. + apply weak_valid_pointer_no_overflow_extends. + apply valid_different_pointers_extends; auto. + rewrite <- val_inject_list_lessdef. eauto. auto. +Qed. + +Lemma eval_operation_lessdef: + forall sp op vl1 vl2 v1 m1 m2, + Val.lessdef_list vl1 vl2 -> + Mem.extends m1 m2 -> + eval_operation genv sp op vl1 m1 = Some v1 -> + exists v2, eval_operation genv sp op vl2 m2 = Some v2 /\ Val.lessdef v1 v2. +Proof. + intros. rewrite val_inject_list_lessdef in H. + assert (exists v2 : val, + eval_operation genv sp op vl2 m2 = Some v2 + /\ Val.inject (fun b => Some(b, 0)) v1 v2). + eapply eval_operation_inj with (m1 := m1) (sp1 := sp). + apply valid_pointer_extends; auto. + apply weak_valid_pointer_extends; auto. + apply weak_valid_pointer_no_overflow_extends. + apply valid_different_pointers_extends; auto. + intros. apply val_inject_lessdef. auto. + apply val_inject_lessdef; auto. + eauto. + auto. + destruct H2 as [v2 [A B]]. exists v2; split; auto. rewrite val_inject_lessdef; auto. +Qed. + +Lemma eval_addressing_lessdef: + forall sp addr vl1 vl2 v1, + Val.lessdef_list vl1 vl2 -> + eval_addressing genv sp addr vl1 = Some v1 -> + exists v2, eval_addressing genv sp addr vl2 = Some v2 /\ Val.lessdef v1 v2. +Proof. + intros. rewrite val_inject_list_lessdef in H. + assert (exists v2 : val, + eval_addressing genv sp addr vl2 = Some v2 + /\ Val.inject (fun b => Some(b, 0)) v1 v2). + eapply eval_addressing_inj with (sp1 := sp). + intros. rewrite <- val_inject_lessdef; auto. + rewrite <- val_inject_lessdef; auto. + eauto. auto. + destruct H1 as [v2 [A B]]. exists v2; split; auto. rewrite val_inject_lessdef; auto. +Qed. + +End EVAL_LESSDEF. + +(** Compatibility of the evaluation functions with memory injections. *) + +Section EVAL_INJECT. + +Variable F V: Type. +Variable genv: Genv.t F V. +Variable f: meminj. +Hypothesis globals: meminj_preserves_globals genv f. +Variable sp1: block. +Variable sp2: block. +Variable delta: Z. +Hypothesis sp_inj: f sp1 = Some(sp2, delta). + +Remark symbol_address_inject: + forall id ofs, Val.inject f (Genv.symbol_address genv id ofs) (Genv.symbol_address genv id ofs). +Proof. + intros. unfold Genv.symbol_address. destruct (Genv.find_symbol genv id) eqn:?; auto. + exploit (proj1 globals); eauto. intros. + econstructor; eauto. rewrite Ptrofs.add_zero; auto. +Qed. + +Lemma eval_condition_inject: + forall cond vl1 vl2 b m1 m2, + Val.inject_list f vl1 vl2 -> + Mem.inject f m1 m2 -> + eval_condition cond vl1 m1 = Some b -> + eval_condition cond vl2 m2 = Some b. +Proof. + intros. eapply eval_condition_inj with (f := f) (m1 := m1); eauto. + intros; eapply Mem.valid_pointer_inject_val; eauto. + intros; eapply Mem.weak_valid_pointer_inject_val; eauto. + intros; eapply Mem.weak_valid_pointer_inject_no_overflow; eauto. + intros; eapply Mem.different_pointers_inject; eauto. +Qed. + +Lemma eval_addressing_inject: + forall addr vl1 vl2 v1, + Val.inject_list f vl1 vl2 -> + eval_addressing genv (Vptr sp1 Ptrofs.zero) addr vl1 = Some v1 -> + exists v2, + eval_addressing genv (Vptr sp2 Ptrofs.zero) (shift_stack_addressing delta addr) vl2 = Some v2 + /\ Val.inject f v1 v2. +Proof. + intros. + rewrite eval_shift_stack_addressing. + eapply eval_addressing_inj with (sp1 := Vptr sp1 Ptrofs.zero); eauto. + intros. apply symbol_address_inject. + econstructor; eauto. rewrite Ptrofs.add_zero_l; auto. +Qed. + +Lemma eval_operation_inject: + forall op vl1 vl2 v1 m1 m2, + Val.inject_list f vl1 vl2 -> + Mem.inject f m1 m2 -> + eval_operation genv (Vptr sp1 Ptrofs.zero) op vl1 m1 = Some v1 -> + exists v2, + eval_operation genv (Vptr sp2 Ptrofs.zero) (shift_stack_operation delta op) vl2 m2 = Some v2 + /\ Val.inject f v1 v2. +Proof. + intros. + rewrite eval_shift_stack_operation. simpl. + eapply eval_operation_inj with (sp1 := Vptr sp1 Ptrofs.zero) (m1 := m1); eauto. + intros; eapply Mem.valid_pointer_inject_val; eauto. + intros; eapply Mem.weak_valid_pointer_inject_val; eauto. + intros; eapply Mem.weak_valid_pointer_inject_no_overflow; eauto. + intros; eapply Mem.different_pointers_inject; eauto. + intros. apply symbol_address_inject. + econstructor; eauto. rewrite Ptrofs.add_zero_l; auto. +Qed. + +End EVAL_INJECT. + +(** * Handling of builtin arguments *) + +Definition builtin_arg_ok_1 + (A: Type) (ba: builtin_arg A) (c: builtin_arg_constraint) := + match c, ba with + | OK_all, _ => true + | OK_const, (BA_int _ | BA_long _ | BA_float _ | BA_single _) => true + | OK_addrstack, BA_addrstack _ => true + | OK_addressing, BA_addrstack _ => true + | OK_addressing, BA_addptr (BA _) (BA_int _) => true + | OK_addressing, BA_addptr (BA _) (BA_long _) => true + | _, _ => false + end. + +Definition builtin_arg_ok + (A: Type) (ba: builtin_arg A) (c: builtin_arg_constraint) := + match ba with + | (BA _ | BA_splitlong (BA _) (BA _)) => true + | _ => builtin_arg_ok_1 ba c + end. diff --git a/aarch64/PrintOp.ml b/aarch64/PrintOp.ml new file mode 100644 index 00000000..2d45e446 --- /dev/null +++ b/aarch64/PrintOp.ml @@ -0,0 +1,247 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Pretty-printing of operators, conditions, addressing modes *) + +open Printf +open Camlcoq +open Integers +open Op + +let comparison_name = function + | Ceq -> "==" + | Cne -> "!=" + | Clt -> "<" + | Cle -> "<=" + | Cgt -> ">" + | Cge -> ">=" + +let shift pp (s, a) = + match s with + | Slsl -> fprintf pp "<< %ld" (camlint_of_coqint a) + | Slsr -> fprintf pp ">>u %ld" (camlint_of_coqint a) + | Sasr -> fprintf pp ">>s %ld" (camlint_of_coqint a) + | Sror -> fprintf pp "ror %ld" (camlint_of_coqint a) + +let shiftl pp (s, a) = + match s with + | Slsl -> fprintf pp "<<l %ld" (camlint_of_coqint a) + | Slsr -> fprintf pp ">>lu %ld" (camlint_of_coqint a) + | Sasr -> fprintf pp ">>ls %ld" (camlint_of_coqint a) + | Sror -> fprintf pp "rorl %ld" (camlint_of_coqint a) + +let extend_name = function + | Xsgn32 -> "sext" + | Xuns32 -> "zext" + +let print_condition reg pp = function + | (Ccomp c, [r1;r2]) -> + fprintf pp "%a %ss %a" reg r1 (comparison_name c) reg r2 + | (Ccompu c, [r1;r2]) -> + fprintf pp "%a %su %a" reg r1 (comparison_name c) reg r2 + | (Ccompimm(c, n), [r1]) -> + fprintf pp "%a %ss %ld" reg r1 (comparison_name c) (camlint_of_coqint n) + | (Ccompuimm(c, n), [r1]) -> + fprintf pp "%a %su %ld" reg r1 (comparison_name c) (camlint_of_coqint n) + | (Ccompshift(c, s, a), [r1;r2]) -> + fprintf pp "%a %ss %a %a" reg r1 (comparison_name c) reg r2 shift (s, a) + | (Ccompushift(c, s, a), [r1;r2]) -> + fprintf pp "%a %su %a %a" reg r1 (comparison_name c) reg r2 shift (s, a) + | (Cmaskzero n, [r1]) -> + fprintf pp "%a & 0x%lx == 0" reg r1 (camlint_of_coqint n) + | (Cmasknotzero n, [r1]) -> + fprintf pp "%a & 0x%lx != 0" reg r1 (camlint_of_coqint n) + | (Ccompl c, [r1;r2]) -> + fprintf pp "%a %sls %a" reg r1 (comparison_name c) reg r2 + | (Ccomplu c, [r1;r2]) -> + fprintf pp "%a %slu %a" reg r1 (comparison_name c) reg r2 + | (Ccomplimm(c, n), [r1]) -> + fprintf pp "%a %sls %Ld" reg r1 (comparison_name c) (camlint64_of_coqint n) + | (Ccompluimm(c, n), [r1]) -> + fprintf pp "%a %slu %Ld" reg r1 (comparison_name c) (camlint64_of_coqint n) + | (Ccomplshift(c, s, a), [r1;r2]) -> + fprintf pp "%a %sls %a %a" reg r1 (comparison_name c) reg r2 shift (s, a) + | (Ccomplushift(c, s, a), [r1;r2]) -> + fprintf pp "%a %slu %a %a" reg r1 (comparison_name c) reg r2 shift (s, a) + | (Cmasklzero n, [r1]) -> + fprintf pp "%a &l 0x%Lx == 0" reg r1 (camlint64_of_coqint n) + | (Cmasklnotzero n, [r1]) -> + fprintf pp "%a &l 0x%Lx != 0" reg r1 (camlint64_of_coqint n) + | (Ccompf c, [r1;r2]) -> + fprintf pp "%a %sf %a" reg r1 (comparison_name c) reg r2 + | (Cnotcompf c, [r1;r2]) -> + fprintf pp "%a not(%sf) %a" reg r1 (comparison_name c) reg r2 + | (Ccompfzero c, [r1]) -> + fprintf pp "%a %sf 0.0" reg r1 (comparison_name c) + | (Cnotcompfzero c, [r1]) -> + fprintf pp "%a not(%sf) 0.0" reg r1 (comparison_name c) + | (Ccompfs c, [r1;r2]) -> + fprintf pp "%a %sfs %a" reg r1 (comparison_name c) reg r2 + | (Cnotcompfs c, [r1;r2]) -> + fprintf pp "%a not(%sfs) %a" reg r1 (comparison_name c) reg r2 + | (Ccompfszero c, [r1]) -> + fprintf pp "%a %sfs 0.0" reg r1 (comparison_name c) + | (Cnotcompfszero c, [r1]) -> + fprintf pp "%a not(%sfs) 0.0" reg r1 (comparison_name c) + | _ -> + fprintf pp "<bad condition>" + +let print_operation reg pp = function + | Omove, [r1] -> reg pp r1 + | Ointconst n, [] -> fprintf pp "%ld" (camlint_of_coqint n) + | Olongconst n, [] -> fprintf pp "%LdL" (camlint64_of_coqint n) + | Ofloatconst n, [] -> fprintf pp "%F" (camlfloat_of_coqfloat n) + | Osingleconst n, [] -> fprintf pp "%Ff" (camlfloat_of_coqfloat32 n) + | Oaddrsymbol(id, ofs), [] -> + fprintf pp "\"%s\" + %Ld" (extern_atom id) (camlint64_of_ptrofs ofs) + | Oaddrstack ofs, [] -> + fprintf pp "stack(%Ld)" (camlint64_of_ptrofs ofs) +(* 32-bit integer arithmetic *) + | Oshift(s, a), [r1] -> fprintf pp "%a %a" reg r1 shift (s,a) + | Oadd, [r1;r2] -> fprintf pp "%a + %a" reg r1 reg r2 + | Oaddshift(s, a), [r1;r2] -> fprintf pp "%a + %a %a" reg r1 reg r2 shift (s,a) + | Oaddimm n, [r1] -> fprintf pp "%a + %ld" reg r1 (camlint_of_coqint n) + | Oneg, [r1] -> fprintf pp "- %a" reg r1 + | Onegshift(s, a), [r1] -> fprintf pp "- (%a %a)" reg r1 shift (s,a) + | Osub, [r1;r2] -> fprintf pp "%a - %a" reg r1 reg r2 + | Osubshift(s, a), [r1;r2] -> fprintf pp "%a - %a %a" reg r1 reg r2 shift (s,a) + | Omul, [r1;r2] -> fprintf pp "%a * %a" reg r1 reg r2 + | Omuladd, [r1;r2;r3] -> fprintf pp "%a + %a * %a" reg r3 reg r1 reg r2 + | Omulsub, [r1;r2;r3] -> fprintf pp "%a - %a * %a" reg r3 reg r1 reg r2 + | Odiv, [r1;r2] -> fprintf pp "%a /s %a" reg r1 reg r2 + | Odivu, [r1;r2] -> fprintf pp "%a /u %a" reg r1 reg r2 + | Oand, [r1;r2] -> fprintf pp "%a & %a" reg r1 reg r2 + | Oandshift(s, a), [r1;r2] -> fprintf pp "%a & %a %a" reg r1 reg r2 shift (s,a) + | Oandimm n, [r1] -> fprintf pp "%a & %ld" reg r1 (camlint_of_coqint n) + | Oor, [r1;r2] -> fprintf pp "%a | %a" reg r1 reg r2 + | Oorshift(s, a), [r1;r2] -> fprintf pp "%a | %a %a" reg r1 reg r2 shift (s,a) + | Oorimm n, [r1] -> fprintf pp "%a | %ld" reg r1 (camlint_of_coqint n) + | Oxor, [r1;r2] -> fprintf pp "%a ^ %a" reg r1 reg r2 + | Oxorshift(s, a), [r1;r2] -> fprintf pp "%a ^ %a %a" reg r1 reg r2 shift (s,a) + | Oxorimm n, [r1] -> fprintf pp "%a ^ %ld" reg r1 (camlint_of_coqint n) + | Onot, [r1] -> fprintf pp "~ %a" reg r1 + | Onotshift(s, a), [r1] -> fprintf pp "~ (%a %a)" reg r1 shift (s,a) + | Obic, [r1;r2] -> fprintf pp "%a & ~ %a" reg r1 reg r2 + | Obicshift(s, a), [r1;r2] -> fprintf pp "%a & ~ %a %a" reg r1 reg r2 shift (s,a) + | Oorn, [r1;r2] -> fprintf pp "%a | ~ %a" reg r1 reg r2 + | Oornshift(s, a), [r1;r2] -> fprintf pp "%a | ~ %a %a" reg r1 reg r2 shift (s,a) + | Oeqv, [r1;r2] -> fprintf pp "%a ^ ~ %a" reg r1 reg r2 + | Oeqvshift(s, a), [r1;r2] -> fprintf pp "%a ^ ~ %a %a" reg r1 reg r2 shift (s,a) + | Oshl, [r1;r2] -> fprintf pp "%a << %a" reg r1 reg r2 + | Oshr, [r1;r2] -> fprintf pp "%a >>s %a" reg r1 reg r2 + | Oshru, [r1;r2] -> fprintf pp "%a >>u %a" reg r1 reg r2 + | Oshrximm n, [r1] -> fprintf pp "%a >>x %ld" reg r1 (camlint_of_coqint n) + | Ozext s, [r1] -> fprintf pp "zext(%d, %a)" (Z.to_int s) reg r1 + | Osext s, [r1] -> fprintf pp "sext(%d, %a)" (Z.to_int s) reg r1 + | Oshlzext(s, a), [r1] -> fprintf pp "zext(%d, %a) << %ld" (Z.to_int s) reg r1 (camlint_of_coqint a) + | Oshlsext(s, a), [r1] -> fprintf pp "sext(%d, %a) << %ld" (Z.to_int s) reg r1 (camlint_of_coqint a) + | Ozextshr(a, s), [r1] -> fprintf pp "zext(%d, %a >>u %ld)" (Z.to_int s) reg r1 (camlint_of_coqint a) + | Osextshr(a, s), [r1] -> fprintf pp "sext(%d, %a >>s %ld)" (Z.to_int s) reg r1 (camlint_of_coqint a) +(* 64-bit integer arithmetic *) + | Oshiftl(s, a), [r1] -> fprintf pp "%a %a" reg r1 shiftl (s,a) + | Oextend(x, a), [r1] -> fprintf pp "%s(32, %a) <<l %ld" (extend_name x) reg r1 (camlint_of_coqint a) + | Omakelong, [r1;r2] -> fprintf pp "makelong(%a,%a)" reg r1 reg r2 + | Olowlong, [r1] -> fprintf pp "lowlong(%a)" reg r1 + | Ohighlong, [r1] -> fprintf pp "highlong(%a)" reg r1 + | Oaddl, [r1;r2] -> fprintf pp "%a +l %a" reg r1 reg r2 + | Oaddlshift(s, a), [r1;r2] -> fprintf pp "%a +l %a %a" reg r1 reg r2 shiftl (s,a) + | Oaddlext(x, a), [r1;r2] -> fprintf pp "%a +l %s(%a) << %ld" reg r1 (extend_name x) reg r2 (camlint_of_coqint a) + | Oaddlimm n, [r1] -> fprintf pp "%a +l %Ld" reg r1 (camlint64_of_coqint n) + | Onegl, [r1] -> fprintf pp "-l %a" reg r1 + | Oneglshift(s, a), [r1] -> fprintf pp "-l (%a %a)" reg r1 shiftl (s,a) + | Osubl, [r1;r2] -> fprintf pp "%a -l %a" reg r1 reg r2 + | Osublext(x, a), [r1;r2] -> fprintf pp "%a +l %s(%a) << %ld" reg r1 (extend_name x) reg r2 (camlint_of_coqint a) + | Osublshift(s, a), [r1;r2] -> fprintf pp "%a -l %a %a" reg r1 reg r2 shiftl (s,a) + | Omull, [r1;r2] -> fprintf pp "%a *l %a" reg r1 reg r2 + | Omulladd, [r1;r2;r3] -> fprintf pp "%a +l %a *l %a" reg r3 reg r1 reg r2 + | Omullsub, [r1;r2;r3] -> fprintf pp "%a -l %a *l %a" reg r3 reg r1 reg r2 + | Omullhs, [r1;r2] -> fprintf pp "%a *hls %a" reg r1 reg r2 + | Omullhu, [r1;r2] -> fprintf pp "%a *hlu %a" reg r1 reg r2 + | Odivl, [r1;r2] -> fprintf pp "%a /ls %a" reg r1 reg r2 + | Odivlu, [r1;r2] -> fprintf pp "%a /lu %a" reg r1 reg r2 + | Oandl, [r1;r2] -> fprintf pp "%a &l %a" reg r1 reg r2 + | Oandlshift(s, a), [r1;r2] -> fprintf pp "%a &l %a %a" reg r1 reg r2 shiftl (s,a) + | Oandlimm n, [r1] -> fprintf pp "%a &l %Ld" reg r1 (camlint64_of_coqint n) + | Oorl, [r1;r2] -> fprintf pp "%a |l %a" reg r1 reg r2 + | Oorlshift(s, a), [r1;r2] -> fprintf pp "%a |l %a %a" reg r1 reg r2 shiftl (s,a) + | Oorlimm n, [r1] -> fprintf pp "%a |l %Ld" reg r1 (camlint64_of_coqint n) + | Oxorl, [r1;r2] -> fprintf pp "%a ^l %a" reg r1 reg r2 + | Oxorlshift(s, a), [r1;r2] -> fprintf pp "%a ^l %a %a" reg r1 reg r2 shiftl (s,a) + | Oxorlimm n, [r1] -> fprintf pp "%a ^l %Ld" reg r1 (camlint64_of_coqint n) + | Onotl, [r1] -> fprintf pp "~l %a" reg r1 + | Onotlshift(s, a), [r1] -> fprintf pp "~l (%a %a)" reg r1 shiftl (s,a) + | Obicl, [r1;r2] -> fprintf pp "%a &l ~l %a" reg r1 reg r2 + | Obiclshift(s, a), [r1;r2] -> fprintf pp "%a &l ~l %a %a" reg r1 reg r2 shiftl (s,a) + | Oornl, [r1;r2] -> fprintf pp "%a |l ~l %a" reg r1 reg r2 + | Oornlshift(s, a), [r1;r2] -> fprintf pp "%a |l ~l %a %a" reg r1 reg r2 shiftl (s,a) + | Oeqvl, [r1;r2] -> fprintf pp "%a ^l ~l %a" reg r1 reg r2 + | Oeqvlshift(s, a), [r1;r2] -> fprintf pp "%a ^l ~l %a %a" reg r1 reg r2 shift (s,a) + | Oshll, [r1;r2] -> fprintf pp "%a <<l %a" reg r1 reg r2 + | Oshrl, [r1;r2] -> fprintf pp "%a >>ls %a" reg r1 reg r2 + | Oshrlu, [r1;r2] -> fprintf pp "%a >>lu %a" reg r1 reg r2 + | Oshrlximm n, [r1] -> fprintf pp "%a >>lx %ld" reg r1 (camlint_of_coqint n) + | Ozextl s, [r1] -> fprintf pp "zextl(%d, %a)" (Z.to_int s) reg r1 + | Osextl s, [r1] -> fprintf pp "sextl(%d, %a)" (Z.to_int s) reg r1 + | Oshllzext(s, a), [r1] -> fprintf pp "zextl(%d, %a) <<l %ld" (Z.to_int s) reg r1 (camlint_of_coqint a) + | Oshllsext(s, a), [r1] -> fprintf pp "sextl(%d, %a) <<l %ld" (Z.to_int s) reg r1 (camlint_of_coqint a) + | Ozextshrl(a, s), [r1] -> fprintf pp "zextl(%d, %a >>lu %ld)" (Z.to_int s) reg r1 (camlint_of_coqint a) + | Osextshrl(a, s), [r1] -> fprintf pp "sextl(%d, %a >>ls %ld)" (Z.to_int s) reg r1 (camlint_of_coqint a) +(* 64-bit floating-point arithmetic *) + | Onegf, [r1] -> fprintf pp "negf(%a)" reg r1 + | Oabsf, [r1] -> fprintf pp "absf(%a)" reg r1 + | Oaddf, [r1;r2] -> fprintf pp "%a +f %a" reg r1 reg r2 + | Osubf, [r1;r2] -> fprintf pp "%a -f %a" reg r1 reg r2 + | Omulf, [r1;r2] -> fprintf pp "%a *f %a" reg r1 reg r2 + | Odivf, [r1;r2] -> fprintf pp "%a /f %a" reg r1 reg r2 +(* 32-bit floating-point arithmetic *) + | Onegfs, [r1] -> fprintf pp "negfs(%a)" reg r1 + | Oabsfs, [r1] -> fprintf pp "absfs(%a)" reg r1 + | Oaddfs, [r1;r2] -> fprintf pp "%a +fs %a" reg r1 reg r2 + | Osubfs, [r1;r2] -> fprintf pp "%a -fs %a" reg r1 reg r2 + | Omulfs, [r1;r2] -> fprintf pp "%a *fs %a" reg r1 reg r2 + | Odivfs, [r1;r2] -> fprintf pp "%a /fs %a" reg r1 reg r2 + | Osingleoffloat, [r1] -> fprintf pp "singleoffloat(%a)" reg r1 + | Ofloatofsingle, [r1] -> fprintf pp "floatofsingle(%a)" reg r1 +(* Conversions between int and float *) + | Ointoffloat, [r1] -> fprintf pp "intoffloat(%a)" reg r1 + | Ointuoffloat, [r1] -> fprintf pp "intuoffloat(%a)" reg r1 + | Ofloatofint, [r1] -> fprintf pp "floatofint(%a)" reg r1 + | Ofloatofintu, [r1] -> fprintf pp "floatofintu(%a)" reg r1 + | Olongoffloat, [r1] -> fprintf pp "longoffloat(%a)" reg r1 + | Olonguoffloat, [r1] -> fprintf pp "longuoffloat(%a)" reg r1 + | Ofloatoflong, [r1] -> fprintf pp "floatoflong(%a)" reg r1 + | Ofloatoflongu, [r1] -> fprintf pp "floatoflongu(%a)" reg r1 + | Ointofsingle, [r1] -> fprintf pp "intofsingle(%a)" reg r1 + | Ointuofsingle, [r1] -> fprintf pp "intuofsingle(%a)" reg r1 + | Osingleofint, [r1] -> fprintf pp "singleofint(%a)" reg r1 + | Osingleofintu, [r1] -> fprintf pp "singleofintu(%a)" reg r1 + | Olongofsingle, [r1] -> fprintf pp "longofsingle(%a)" reg r1 + | Olonguofsingle, [r1] -> fprintf pp "longuofsingle(%a)" reg r1 + | Osingleoflong, [r1] -> fprintf pp "singleoflong(%a)" reg r1 + | Osingleoflongu, [r1] -> fprintf pp "singleoflongu(%a)" reg r1 +(* Boolean tests *) + | Ocmp c, args -> print_condition reg pp (c, args) + | Osel (c, ty), r1::r2::args -> + fprintf pp "%a ?%s %a : %a" + (print_condition reg) (c, args) + (PrintAST.name_of_type ty) reg r1 reg r2 + | _ -> fprintf pp "<bad operator>" + +let print_addressing reg pp = function + | Aindexed n, [r1] -> fprintf pp "%a + %Ld" reg r1 (camlint64_of_coqint n) + | Aindexed2, [r1; r2] -> fprintf pp "%a + %a" reg r1 reg r2 + | Aindexed2shift a, [r1; r2] -> fprintf pp "%a + %a << %ld" reg r1 reg r2 (camlint_of_coqint a) + | Aindexed2ext(x, a), [r1; r2] -> fprintf pp "%a + %s(%a) << %ld" reg r1 (extend_name x) reg r2 (camlint_of_coqint a) + | Aglobal(id, ofs), [] -> + fprintf pp "\"%s\" + %Ld" (extern_atom id) (camlint64_of_ptrofs ofs) + | Ainstack ofs, [] -> fprintf pp "stack(%Ld)" (camlint64_of_ptrofs ofs) + | _ -> fprintf pp "<bad addressing>" diff --git a/aarch64/SelectLong.vp b/aarch64/SelectLong.vp new file mode 100644 index 00000000..ddf6e212 --- /dev/null +++ b/aarch64/SelectLong.vp @@ -0,0 +1,478 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Instruction selection for 64-bit integer operations *) + +Require Import Coqlib Zbits. +Require Import Compopts AST Integers Floats. +Require Import Op CminorSel SelectOp. + +Local Open Scope cminorsel_scope. + +(** ** Constants **) + +Definition longconst (n: int64) : expr := + Eop (Olongconst n) Enil. + +(** ** Conversions *) + +Nondetfunction intoflong (e: expr) := + match e with + | Eop (Olongconst n) Enil => Eop (Ointconst (Int.repr (Int64.unsigned n))) Enil + | _ => Eop Olowlong (e ::: Enil) + end. + +Nondetfunction longofint (e: expr) := + match e with + | Eop (Ointconst n) Enil => longconst (Int64.repr (Int.signed n)) + | _ => Eop (Oextend Xsgn32 (mk_amount64 Int.zero)) (e ::: Enil) + end. + +Nondetfunction longofintu (e: expr) := + match e with + | Eop (Ointconst n) Enil => longconst (Int64.repr (Int.unsigned n)) + | _ => Eop (Oextend Xuns32 (mk_amount64 Int.zero)) (e ::: Enil) + end. + +(** ** Integer addition and pointer addition *) + +Nondetfunction addlimm (n: int64) (e: expr) := + if Int64.eq n Int64.zero then e else + match e with + | Eop (Olongconst m) Enil => longconst (Int64.add n m) + | Eop (Oaddrsymbol s m) Enil => Eop (Oaddrsymbol s (Ptrofs.add (Ptrofs.of_int64 n) m)) Enil + | Eop (Oaddrstack m) Enil => Eop (Oaddrstack (Ptrofs.add (Ptrofs.of_int64 n) m)) Enil + | Eop (Oaddlimm m) (t ::: Enil) => Eop (Oaddlimm(Int64.add n m)) (t ::: Enil) + | _ => Eop (Oaddlimm n) (e ::: Enil) + end. + +Nondetfunction addl (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Olongconst n1) Enil, t2 => addlimm n1 t2 + | t1, Eop (Olongconst n2) Enil => addlimm n2 t1 + | Eop (Oaddlimm n1) (t1:::Enil), Eop (Oaddlimm n2) (t2:::Enil) => + addlimm (Int64.add n1 n2) (Eop Oaddl (t1:::t2:::Enil)) + | Eop (Oaddlimm n1) (t1:::Enil), Eop (Oaddrstack n2) Enil => + Eop Oaddl (Eop (Oaddrstack (Ptrofs.add (Ptrofs.of_int64 n1) n2)) Enil ::: t1 ::: Enil) + | Eop (Oaddrstack n1) Enil, Eop (Oaddlimm n2) (t2:::Enil) => + Eop Oaddl (Eop (Oaddrstack (Ptrofs.add n1 (Ptrofs.of_int64 n2))) Enil ::: t2 ::: Enil) + | Eop (Oaddlimm n1) (t1:::Enil), t2 => + addlimm n1 (Eop Oaddl (t1:::t2:::Enil)) + | t1, Eop (Oaddlimm n2) (t2:::Enil) => + addlimm n2 (Eop Oaddl (t1:::t2:::Enil)) + | Eop (Oshiftl s a) (t1:::Enil), t2 ?? arith_shift s => + Eop (Oaddlshift s a) (t2 ::: t1 ::: Enil) + | t1, Eop (Oshiftl s a) (t2:::Enil) ?? arith_shift s => + Eop (Oaddlshift s a) (t1 ::: t2 ::: Enil) + | Eop (Oextend x a) (t1:::Enil), t2 => + Eop (Oaddlext x a) (t2 ::: t1 ::: Enil) + | t1, Eop (Oextend x a) (t2:::Enil) => + Eop (Oaddlext x a) (t1 ::: t2 ::: Enil) + | Eop Omull (t1:::t2:::Enil), t3 => + Eop Omulladd (t3:::t1:::t2:::Enil) + | t1, Eop Omull (t2:::t3:::Enil) => + Eop Omulladd (t1:::t2:::t3:::Enil) + | _, _ => Eop Oaddl (e1:::e2:::Enil) + end. + +(** ** Opposite *) + +Nondetfunction negl (e: expr) := + match e with + | Eop (Olongconst n) Enil => Eop (Olongconst (Int64.neg n)) Enil + | Eop (Oshiftl s a) (t1:::Enil) ?? arith_shift s => Eop (Oneglshift s a) (t1:::Enil) + | _ => Eop Onegl (e ::: Enil) + end. + +(** ** Integer and pointer subtraction *) + +Nondetfunction subl (e1: expr) (e2: expr) := + match e1, e2 with + | t1, Eop (Olongconst n2) Enil => + addlimm (Int64.neg n2) t1 + | Eop (Oaddlimm n1) (t1:::Enil), Eop (Oaddlimm n2) (t2:::Enil) => + addlimm (Int64.sub n1 n2) (Eop Osubl (t1:::t2:::Enil)) + | Eop (Oaddlimm n1) (t1:::Enil), t2 => + addlimm n1 (Eop Osubl (t1:::t2:::Enil)) + | t1, Eop (Oaddlimm n2) (t2:::Enil) => + addlimm (Int64.neg n2) (Eop Osubl (t1:::t2:::Enil)) + | t1, Eop (Oshiftl s a) (t2:::Enil) ?? arith_shift s => + Eop (Osublshift s a) (t1:::t2::: Enil) + | t1, Eop (Oextend x a) (t2:::Enil) => + Eop (Osublext x a) (t1 ::: t2 ::: Enil) + | t1, Eop Omull (t2:::t3:::Enil) => + Eop Omullsub (t1:::t2:::t3:::Enil) + | _, _ => Eop Osubl (e1:::e2:::Enil) + end. + +(** ** Immediate shift left *) + +Definition shllimm_base (e1: expr) (n: int) := + Eop (Oshiftl Slsl (mk_amount64 n)) (e1 ::: Enil). + +Nondetfunction shllimm (e1: expr) (n: int) := + if Int.eq n Int.zero then + e1 + else if negb (Int.ltu n Int64.iwordsize') then + Eop Oshll (e1 ::: Eop (Ointconst n) Enil ::: Enil) + else match e1 with + | Eop (Olongconst n1) Enil => + Eop (Olongconst (Int64.shl' n1 n)) Enil + | Eop (Oshiftl Slsl a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int64.iwordsize' + then shllimm_base t1 (Int.add a n) + else shllimm_base e1 n + | Eop (Ozextl s) (t1:::Enil) => + Eop (Oshllzext s (mk_amount64 n)) (t1:::Enil) + | Eop (Osextl s) (t1:::Enil) => + Eop (Oshllsext s (mk_amount64 n)) (t1:::Enil) + | Eop (Oshllzext s a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int64.iwordsize' + then Eop (Oshllzext s (mk_amount64 (Int.add a n))) (t1:::Enil) + else shllimm_base e1 n + | Eop (Oshllsext s a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int64.iwordsize' + then Eop (Oshllsext s (mk_amount64 (Int.add a n))) (t1:::Enil) + else shllimm_base e1 n + | Eop (Oextend x a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int64.iwordsize' + then Eop (Oextend x (mk_amount64 (Int.add a n))) (t1:::Enil) + else shllimm_base e1 n + | _ => + shllimm_base e1 n + end. + +(** ** Immediate shift right (logical) *) + +Definition shrluimm_base (e1: expr) (n: int) := + Eop (Oshiftl Slsr (mk_amount64 n)) (e1 ::: Enil). + +Nondetfunction shrluimm (e1: expr) (n: int) := + if Int.eq n Int.zero then + e1 + else if negb (Int.ltu n Int64.iwordsize') then + Eop Oshrlu (e1 ::: Eop (Ointconst n) Enil ::: Enil) + else match e1 with + | Eop (Olongconst n1) Enil => + Eop (Olongconst (Int64.shru' n1 n)) Enil + | Eop (Oshiftl Slsl a) (t1:::Enil) => + if Int.ltu n a + then Eop (Oshllzext (Int64.zwordsize - Int.unsigned a) (mk_amount64 (Int.sub a n))) (t1:::Enil) + else Eop (Ozextshrl (mk_amount64 (Int.sub n a)) (Int64.zwordsize - Int.unsigned n)) (t1:::Enil) + | Eop (Oshiftl Slsr a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int64.iwordsize' + then shrluimm_base t1 (Int.add a n) + else shrluimm_base e1 n + | Eop (Ozextl s) (t1:::Enil) => + if zlt (Int.unsigned n) s + then Eop (Ozextshrl (mk_amount64 n) (s - Int.unsigned n)) (t1:::Enil) + else Eop (Olongconst Int64.zero) Enil + | _ => + shrluimm_base e1 n + end. + +(** ** Immediate shift right (arithmetic) *) + +Definition shrlimm_base (e1: expr) (n: int) := + Eop (Oshiftl Sasr (mk_amount64 n)) (e1 ::: Enil). + +Nondetfunction shrlimm (e1: expr) (n: int) := + if Int.eq n Int.zero then + e1 + else if negb (Int.ltu n Int64.iwordsize') then + Eop Oshrl (e1 ::: Eop (Ointconst n) Enil ::: Enil) + else match e1 with + | Eop (Olongconst n1) Enil => + Eop (Olongconst (Int64.shr' n1 n)) Enil + | Eop (Oshiftl Slsl a) (t1:::Enil) => + if Int.ltu n a + then Eop (Oshllsext (Int64.zwordsize - Int.unsigned a) (mk_amount64 (Int.sub a n))) (t1:::Enil) + else Eop (Osextshrl (mk_amount64 (Int.sub n a)) (Int64.zwordsize - Int.unsigned n)) (t1:::Enil) + | Eop (Oshiftl Sasr a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int64.iwordsize' + then shrlimm_base t1 (Int.add a n) + else shrlimm_base e1 n + | Eop (Osextl s) (t1:::Enil) => + if zlt (Int.unsigned n) s && zlt s Int64.zwordsize + then Eop (Osextshrl (mk_amount64 n) (s - Int.unsigned n)) (t1:::Enil) + else shrlimm_base e1 n + | _ => + shrlimm_base e1 n + end. + +(** ** Integer multiply *) + +Definition mullimm_base (n1: int64) (e2: expr) := + match Int64.one_bits' n1 with + | i :: nil => + shllimm e2 i + | i :: j :: nil => + Elet e2 (addl (shllimm (Eletvar 0) i) (shllimm (Eletvar 0) j)) + | _ => + Eop Omull (Eop (Olongconst n1) Enil ::: e2 ::: Enil) + end. + +Nondetfunction mullimm (n1: int64) (e2: expr) := + if Int64.eq n1 Int64.zero then Eop (Olongconst Int64.zero) Enil + else if Int64.eq n1 Int64.one then e2 + else match e2 with + | Eop (Olongconst n2) Enil => Eop (Olongconst (Int64.mul n1 n2)) Enil + | Eop (Oaddlimm n2) (t2:::Enil) => addlimm (Int64.mul n1 n2) (mullimm_base n1 t2) + | _ => mullimm_base n1 e2 + end. + +Nondetfunction mull (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Olongconst n1) Enil, t2 => mullimm n1 t2 + | t1, Eop (Olongconst n2) Enil => mullimm n2 t1 + | _, _ => Eop Omull (e1:::e2:::Enil) + end. + +Definition mullhs (e1: expr) (n2: int64) := + Eop Omullhs (e1 ::: longconst n2 ::: Enil). + +Definition mullhu (e1: expr) (n2: int64) := + Eop Omullhu (e1 ::: longconst n2 ::: Enil). + +(** ** Integer conversions *) + +Nondetfunction zero_ext_l (sz: Z) (e: expr) := + match e with + | Eop (Olongconst n) Enil => Eop (Olongconst (Int64.zero_ext sz n)) Enil + | Eop (Oshiftl Slsr a) (t1:::Enil) => Eop (Ozextshrl a sz) (t1:::Enil) + | Eop (Oshiftl Slsl a) (t1:::Enil) => + if zlt (Int.unsigned a) sz + then Eop (Oshllzext (sz - Int.unsigned a) a) (t1:::Enil) + else Eop (Ozextl sz) (e:::Enil) + | _ => Eop (Ozextl sz) (e:::Enil) + end. + +(** ** Bitwise not *) + +Nondetfunction notl (e: expr) := + match e with + | Eop (Olongconst n) Enil => Eop (Olongconst (Int64.not n)) Enil + | Eop (Oshiftl s a) (t1:::Enil) => Eop (Onotlshift s a) (t1:::Enil) + | Eop Onotl (t1:::Enil) => t1 + | Eop (Onotlshift s a) (t1:::Enil) => Eop (Oshiftl s a) (t1:::Enil) + | Eop Obicl (t1:::t2:::Enil) => Eop Oornl (t2:::t1:::Enil) + | Eop Oornl (t1:::t2:::Enil) => Eop Obicl (t2:::t1:::Enil) + | Eop Oxorl (t1:::t2:::Enil) => Eop Oeqvl (t1:::t2:::Enil) + | Eop Oeqvl (t1:::t2:::Enil) => Eop Oxorl (t1:::t2:::Enil) + | _ => Eop Onotl (e:::Enil) + end. + +(** ** Bitwise and *) + +Definition andlimm_base (n1: int64) (e2: expr) := + if Int64.eq n1 Int64.zero then Eop (Olongconst Int64.zero) Enil else + if Int64.eq n1 Int64.mone then e2 else + match Z_is_power2m1 (Int64.unsigned n1) with + | Some s => zero_ext_l s e2 + | None => Eop (Oandlimm n1) (e2 ::: Enil) + end. + +Nondetfunction andlimm (n1: int64) (e2: expr) := + match e2 with + | Eop (Olongconst n2) Enil => Eop (Olongconst (Int64.and n1 n2)) Enil + | Eop (Oandlimm n2) (t2:::Enil) => andlimm_base (Int64.and n1 n2) t2 + | Eop (Ozextl s) (t2:::Enil) => + if zle 0 s + then andlimm_base (Int64.and n1 (Int64.repr (two_p s - 1))) t2 + else andlimm_base n1 e2 + | _ => andlimm_base n1 e2 + end. + +Nondetfunction andl (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Olongconst n1) Enil, t2 => andlimm n1 t2 + | t1, Eop (Olongconst n2) Enil => andlimm n2 t1 + | Eop Onotl (t1:::Enil), t2 => Eop Obicl (t2:::t1:::Enil) + | t1, Eop Onotl (t2:::Enil) => Eop Obicl (t1:::t2:::Enil) + | Eop (Onotlshift s a) (t1:::Enil), t2 => Eop (Obiclshift s a) (t2:::t1:::Enil) + | t1, Eop (Onotlshift s a) (t2:::Enil) => Eop (Obiclshift s a) (t1:::t2:::Enil) + | Eop (Oshiftl s a) (t1:::Enil), t2 => Eop (Oandlshift s a) (t2:::t1:::Enil) + | t1, Eop (Oshiftl s a) (t2:::Enil) => Eop (Oandlshift s a) (t1:::t2:::Enil) + | _, _ => Eop Oandl (e1:::e2:::Enil) + end. + +(** ** Bitwise or *) + +Nondetfunction orlimm (n1: int64) (e2: expr) := + if Int64.eq n1 Int64.zero then e2 + else if Int64.eq n1 Int64.mone then Eop (Olongconst Int64.mone) Enil + else match e2 with + | Eop (Olongconst n2) Enil => Eop (Olongconst (Int64.or n1 n2)) Enil + | Eop (Oorlimm n2) (t2:::Enil) => Eop (Oorlimm (Int64.or n1 n2)) (t2:::Enil) + | _ => Eop (Oorlimm n1) (e2:::Enil) + end. + +Nondetfunction orl (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Olongconst n1) Enil, t2 => orlimm n1 t2 + | t1, Eop (Olongconst n2) Enil => orlimm n2 t1 + | Eop Onotl (t1:::Enil), t2 => Eop Oornl (t2:::t1:::Enil) + | t1, Eop Onotl (t2:::Enil) => Eop Oornl (t1:::t2:::Enil) + | Eop (Onotlshift s a) (t1:::Enil), t2 => Eop (Oornlshift s a) (t2:::t1:::Enil) + | t1, Eop (Onotlshift s a) (t2:::Enil) => Eop (Oornlshift s a) (t1:::t2:::Enil) + | Eop (Oshiftl Slsl a1) (t1:::Enil), Eop (Oshiftl Slsr a2) (t2:::Enil) => + if Int.eq (Int.add a1 a2) Int64.iwordsize' && same_expr_pure t1 t2 + then Eop (Oshiftl Sror a2) (t2:::Enil) + else Eop (Oorlshift Slsr a2) (Eop (Oshiftl Slsl a1) (t1:::Enil):::t2:::Enil) + | Eop (Oshiftl Slsr a1) (t1:::Enil), Eop (Oshiftl Slsl a2) (t2:::Enil) => + if Int.eq (Int.add a2 a1) Int64.iwordsize' && same_expr_pure t1 t2 + then Eop (Oshiftl Sror a1) (t1:::Enil) + else Eop (Oorlshift Slsl a2) (Eop (Oshiftl Slsr a1) (t1:::Enil):::t2:::Enil) + | Eop (Oshiftl s a) (t1:::Enil), t2 => Eop (Oorlshift s a) (t2:::t1:::Enil) + | t1, Eop (Oshiftl s a) (t2:::Enil) => Eop (Oorlshift s a) (t1:::t2:::Enil) + | _, _ => Eop Oorl (e1:::e2:::Enil) + end. + +(** ** Bitwise xor *) + +Definition xorlimm_base (n1: int64) (e2: expr) := + if Int64.eq n1 Int64.zero then e2 else + if Int64.eq n1 Int64.mone then notl e2 else + Eop (Oxorlimm n1) (e2:::Enil). + +Nondetfunction xorlimm (n1: int64) (e2: expr) := + match e2 with + | Eop (Olongconst n2) Enil => Eop (Olongconst (Int64.xor n1 n2)) Enil + | Eop (Oxorlimm n2) (t2:::Enil) => xorlimm_base (Int64.xor n1 n2) t2 + | _ => xorlimm_base n1 e2 + end. + +Nondetfunction xorl (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Olongconst n1) Enil, t2 => xorlimm n1 t2 + | t1, Eop (Olongconst n2) Enil => xorlimm n2 t1 + | Eop Onotl (t1:::Enil), t2 => Eop Oeqvl (t2:::t1:::Enil) + | t1, Eop Onotl (t2:::Enil) => Eop Oeqvl (t1:::t2:::Enil) + | Eop (Onotlshift s a) (t1:::Enil), t2 => Eop (Oeqvlshift s a) (t2:::t1:::Enil) + | t1, Eop (Onotlshift s a) (t2:::Enil) => Eop (Oeqvlshift s a) (t1:::t2:::Enil) + | Eop (Oshiftl s a) (t1:::Enil), t2 => Eop (Oxorlshift s a) (t2:::t1:::Enil) + | t1, Eop (Oshiftl s a) (t2:::Enil) => Eop (Oxorlshift s a) (t1:::t2:::Enil) + | _, _ => Eop Oxorl (e1:::e2:::Enil) + end. + +(** ** Integer division and modulus *) + +Definition modl_aux (divop: operation) (e1 e2: expr) := + Elet e1 + (Elet (lift e2) + (Eop Omullsub (Eletvar 1 ::: + Eop divop (Eletvar 1 ::: Eletvar 0 ::: Enil) ::: + Eletvar 0 ::: + Enil))). + +Definition divls_base (e1: expr) (e2: expr) := Eop Odivl (e1:::e2:::Enil). +Definition modls_base := modl_aux Odivl. +Definition divlu_base (e1: expr) (e2: expr) := Eop Odivlu (e1:::e2:::Enil). +Definition modlu_base := modl_aux Odivlu. + +Definition shrxlimm (e1: expr) (n2: int) := + if Int.eq n2 Int.zero then e1 else Eop (Oshrlximm n2) (e1:::Enil). + +(** ** General shifts *) + +Nondetfunction shll (e1: expr) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => shllimm e1 n2 + | _ => Eop Oshll (e1:::e2:::Enil) + end. + +Nondetfunction shrl (e1: expr) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => shrlimm e1 n2 + | _ => Eop Oshrl (e1:::e2:::Enil) + end. + +Nondetfunction shrlu (e1: expr) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => shrluimm e1 n2 + | _ => Eop Oshrlu (e1:::e2:::Enil) + end. + +(** ** Comparisons *) + +Nondetfunction complimm (default: comparison -> int64 -> condition) + (sem: comparison -> int64 -> int64 -> bool) + (c: comparison) (e1: expr) (n2: int64) := + match c, e1 with + | c, Eop (Olongconst n1) Enil => + Eop (Ointconst (if sem c n1 n2 then Int.one else Int.zero)) Enil + | Ceq, Eop (Oandlimm m) (t1:::Enil) => + if Int64.eq n2 Int64.zero + then Eop (Ocmp (Cmasklzero m)) (t1:::Enil) + else Eop (Ocmp (default c n2)) (e1:::Enil) + | Cne, Eop (Oandlimm m) (t1:::Enil) => + if Int64.eq n2 Int64.zero + then Eop (Ocmp (Cmasklnotzero m)) (t1:::Enil) + else Eop (Ocmp (default c n2)) (e1:::Enil) + | _, _ => + Eop (Ocmp (default c n2)) (e1:::Enil) + end. + +Nondetfunction cmpl (c: comparison) (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Olongconst n1) Enil, t2 => + complimm Ccomplimm Int64.cmp (swap_comparison c) t2 n1 + | t1, Eop (Olongconst n2) Enil => + complimm Ccomplimm Int64.cmp c t1 n2 + | Eop (Oshiftl s a) (t1:::Enil), t2 ?? arith_shift s => + Eop (Ocmp (Ccomplshift (swap_comparison c) s a)) (t2:::t1:::Enil) + | t1, Eop (Oshiftl s a) (t2:::Enil) ?? arith_shift s => + Eop (Ocmp (Ccomplshift c s a)) (t1:::t2:::Enil) + | _, _ => + Eop (Ocmp (Ccompl c)) (e1:::e2:::Enil) + end. + +Nondetfunction cmplu (c: comparison) (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Olongconst n1) Enil, t2 => + complimm Ccompluimm Int64.cmpu (swap_comparison c) t2 n1 + | t1, Eop (Olongconst n2) Enil => + complimm Ccompluimm Int64.cmpu c t1 n2 + | Eop (Oshiftl s a) (t1:::Enil), t2 ?? arith_shift s => + Eop (Ocmp (Ccomplushift (swap_comparison c) s a)) (t2:::t1:::Enil) + | t1, Eop (Oshiftl s a) (t2:::Enil) ?? arith_shift s => + Eop (Ocmp (Ccomplushift c s a)) (t1:::t2:::Enil) + | _, _ => + Eop (Ocmp (Ccomplu c)) (e1:::e2:::Enil) + end. + +(** ** Floating-point conversions *) + +Definition longoffloat (e: expr) := + Eop Olongoffloat (e:::Enil). + +Definition longuoffloat (e: expr) := + Eop Olonguoffloat (e:::Enil). + +Definition floatoflong (e: expr) := + Eop Ofloatoflong (e:::Enil). + +Definition floatoflongu (e: expr) := + Eop Ofloatoflongu (e:::Enil). + +Definition longofsingle (e: expr) := + Eop Olongofsingle (e:::Enil). + +Definition longuofsingle (e: expr) := + Eop Olonguofsingle (e:::Enil). + +Definition singleoflong (e: expr) := + Eop Osingleoflong (e:::Enil). + +Definition singleoflongu (e: expr) := + Eop Osingleoflongu (e:::Enil). + diff --git a/aarch64/SelectLongproof.v b/aarch64/SelectLongproof.v new file mode 100644 index 00000000..b051369c --- /dev/null +++ b/aarch64/SelectLongproof.v @@ -0,0 +1,764 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Correctness of instruction selection for 64-bit integer operators *) + +Require Import Coqlib Zbits. +Require Import AST Integers Floats Values Memory Globalenvs. +Require Import Cminor Op CminorSel. +Require Import SelectOp SelectLong SelectOpproof. + +Local Open Scope cminorsel_scope. +Local Transparent Archi.ptr64. + +(** * Correctness of the smart constructors *) + +Section CMCONSTR. + +Variable ge: genv. +Variable sp: val. +Variable e: env. +Variable m: mem. + +Definition unary_constructor_sound (cstr: expr -> expr) (sem: val -> val) : Prop := + forall le a x, + eval_expr ge sp e m le a x -> + exists v, eval_expr ge sp e m le (cstr a) v /\ Val.lessdef (sem x) v. + +Definition binary_constructor_sound (cstr: expr -> expr -> expr) (sem: val -> val -> val) : Prop := + forall le a x b y, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + exists v, eval_expr ge sp e m le (cstr a b) v /\ Val.lessdef (sem x y) v. + +Definition partial_unary_constructor_sound (cstr: expr -> expr) (sem: val -> option val) : Prop := + forall le a x y, + eval_expr ge sp e m le a x -> + sem x = Some y -> + exists v, eval_expr ge sp e m le (cstr a) v /\ Val.lessdef y v. + +Definition partial_binary_constructor_sound (cstr: expr -> expr -> expr) (sem: val -> val -> option val) : Prop := + forall le a x b y z, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + sem x y = Some z -> + exists v, eval_expr ge sp e m le (cstr a b) v /\ Val.lessdef z v. + +(** ** Constants *) + +Theorem eval_longconst: + forall le n, eval_expr ge sp e m le (longconst n) (Vlong n). +Proof. + intros; EvalOp. +Qed. + +(** ** Conversions *) + +Theorem eval_intoflong: unary_constructor_sound intoflong Val.loword. +Proof. + unfold intoflong; red; intros until x; destruct (intoflong_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_longofintu: unary_constructor_sound longofintu Val.longofintu. +Proof. + unfold longofintu; red; intros until x; destruct (longofintu_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. simpl. unfold eval_extend. rewrite mk_amount64_eq by reflexivity. + destruct x; simpl; auto. rewrite Int64.shl'_zero. auto. +Qed. + +Theorem eval_longofint: unary_constructor_sound longofint Val.longofint. +Proof. + unfold longofint; red; intros until x; destruct (longofint_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. simpl. unfold eval_extend. rewrite mk_amount64_eq by reflexivity. + destruct x; simpl; auto. rewrite Int64.shl'_zero. auto. +Qed. + +(** ** Addition, opposite, subtraction *) + +Theorem eval_addlimm: + forall n, unary_constructor_sound (addlimm n) (fun x => Val.addl x (Vlong n)). +Proof. + red; unfold addlimm; intros until x. + predSpec Int64.eq Int64.eq_spec n Int64.zero. +- subst n. intros. exists x; split; auto. + destruct x; simpl; auto. + rewrite Int64.add_zero; auto. + rewrite Ptrofs.add_zero; auto. +- case (addlimm_match a); intros; InvEval; subst. ++ rewrite Int64.add_commut; TrivialExists. ++ TrivialExists. simpl. rewrite Ptrofs.add_commut, Genv.shift_symbol_address_64; auto. ++ econstructor; split. EvalOp. destruct sp; simpl; auto. + rewrite Ptrofs.add_assoc, (Ptrofs.add_commut m0); auto. ++ rewrite Val.addl_assoc, Int64.add_commut; TrivialExists. ++ TrivialExists. +Qed. + +Theorem eval_addl: binary_constructor_sound addl Val.addl. +Proof. + red; intros until y. + unfold addl; case (addl_match a b); intros; InvEval; subst. +- rewrite Val.addl_commut. apply eval_addlimm; auto. +- apply eval_addlimm; auto. +- replace (Val.addl (Val.addl v1 (Vlong n1)) (Val.addl v0 (Vlong n2))) + with (Val.addl (Val.addl v1 v0) (Val.addl (Vlong n1) (Vlong n2))). + apply eval_addlimm. EvalOp. + repeat rewrite Val.addl_assoc. decEq. apply Val.addl_permut. +- TrivialExists. simpl. + rewrite Val.addl_commut, Val.addl_assoc. f_equal; f_equal. + destruct sp; simpl; auto. rewrite Ptrofs.add_assoc, (Ptrofs.add_commut n2). auto. +- TrivialExists. simpl. + rewrite <- (Val.addl_commut v1), <- (Val.addl_commut (Val.addl v1 (Vlong n2))). + rewrite Val.addl_assoc. f_equal; f_equal. + destruct sp; simpl; auto. rewrite Ptrofs.add_assoc. auto. +- replace (Val.addl (Val.addl v1 (Vlong n1)) y) + with (Val.addl (Val.addl v1 y) (Vlong n1)). + apply eval_addlimm. EvalOp. + repeat rewrite Val.addl_assoc. decEq. apply Val.addl_commut. +- rewrite <- Val.addl_assoc. apply eval_addlimm. EvalOp. +- rewrite Val.addl_commut. TrivialExists. +- TrivialExists. +- rewrite Val.addl_commut. TrivialExists. +- TrivialExists. +- rewrite Val.addl_commut. TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_negl: unary_constructor_sound negl (fun v => Val.subl (Vlong Int64.zero) v). +Proof. + red; intros until x; unfold negl. case (negl_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_subl: binary_constructor_sound subl Val.subl. +Proof. + red; intros until y; unfold subl; case (subl_match a b); intros; InvEval; subst. +- rewrite Val.subl_addl_opp. apply eval_addlimm; auto. +- rewrite Val.subl_addl_l. rewrite Val.subl_addl_r. + rewrite Val.addl_assoc. simpl. rewrite Int64.add_commut. rewrite <- Int64.sub_add_opp. + apply eval_addlimm; EvalOp. +- rewrite Val.subl_addl_l. apply eval_addlimm; EvalOp. +- rewrite Val.subl_addl_r. apply eval_addlimm; EvalOp. +- TrivialExists. +- TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +(** ** Immediate shifts *) + +Remark eval_shllimm_base: forall le a n x, + eval_expr ge sp e m le a x -> + Int.ltu n Int64.iwordsize' = true -> + eval_expr ge sp e m le (shllimm_base a n) (Val.shll x (Vint n)). +Proof. +Local Opaque mk_amount64. + unfold shlimm_base; intros; EvalOp. simpl. rewrite mk_amount64_eq by auto. auto. +Qed. + +Theorem eval_shllimm: + forall n, unary_constructor_sound (fun a => shllimm a n) + (fun x => Val.shll x (Vint n)). +Proof. + red; intros until x; unfold shllimm. + predSpec Int.eq Int.eq_spec n Int.zero; [| destruct (Int.ltu n Int64.iwordsize') eqn:L]; simpl. +- intros; subst. exists x; split; auto. destruct x; simpl; auto. rewrite Int64.shl'_zero; auto. +- destruct (shllimm_match a); intros; InvEval; subst. ++ TrivialExists. simpl; rewrite L; auto. ++ destruct (Int.ltu (Int.add a n) Int64.iwordsize') eqn:L2. +* econstructor; split. eapply eval_shllimm_base; eauto. + destruct v1; simpl; auto. rewrite a64_range; simpl. rewrite L, L2. + rewrite Int64.shl'_shl'; auto using a64_range. +* econstructor; split; [|eauto]. apply eval_shllimm_base; auto. EvalOp. ++ TrivialExists. simpl. rewrite mk_amount64_eq; auto. ++ TrivialExists. simpl. rewrite mk_amount64_eq; auto. ++ destruct (Int.ltu (Int.add a n) Int64.iwordsize') eqn:L2. +* TrivialExists. simpl. rewrite mk_amount64_eq by auto. + destruct (Val.zero_ext_l s v1); simpl; auto. + rewrite a64_range; simpl; rewrite L, L2. + rewrite Int64.shl'_shl'; auto using a64_range. +* econstructor; split. eapply eval_shllimm_base; eauto. EvalOp; simpl; eauto. auto. ++ destruct (Int.ltu (Int.add a n) Int64.iwordsize') eqn:L2. +* TrivialExists. simpl. rewrite mk_amount64_eq by auto. + destruct (Val.sign_ext_l s v1); simpl; auto. + rewrite a64_range; simpl; rewrite L, L2. + rewrite Int64.shl'_shl'; auto using a64_range. +* econstructor; split. eapply eval_shllimm_base; eauto. EvalOp; simpl; eauto. auto. ++ destruct (Int.ltu (Int.add a n) Int64.iwordsize') eqn:L2. +* TrivialExists. simpl. unfold eval_extend. rewrite mk_amount64_eq by auto. + destruct (match x0 with Xsgn32 => Val.longofint v1 | Xuns32 => Val.longofintu v1 end); simpl; auto. + rewrite a64_range; simpl; rewrite L, L2. + rewrite Int64.shl'_shl'; auto using a64_range. +* econstructor; split. eapply eval_shllimm_base; eauto. EvalOp; simpl; eauto. auto. ++ econstructor; eauto using eval_shllimm_base. +- intros; TrivialExists. +Qed. + +Remark eval_shrluimm_base: forall le a n x, + eval_expr ge sp e m le a x -> + Int.ltu n Int64.iwordsize' = true -> + eval_expr ge sp e m le (shrluimm_base a n) (Val.shrlu x (Vint n)). +Proof. + unfold shrluimm_base; intros; EvalOp. simpl. rewrite mk_amount64_eq by auto. auto. +Qed. + +Remark sub_shift_amount: + forall y z, + Int.ltu y Int64.iwordsize' = true -> Int.ltu z Int64.iwordsize' = true -> Int.unsigned y <= Int.unsigned z -> + Int.ltu (Int.sub z y) Int64.iwordsize' = true. +Proof. + intros. unfold Int.ltu; apply zlt_true. + apply Int.ltu_inv in H. apply Int.ltu_inv in H0. + change (Int.unsigned Int64.iwordsize') with Int64.zwordsize in *. + unfold Int.sub; rewrite Int.unsigned_repr. omega. + assert (Int64.zwordsize < Int.max_unsigned) by reflexivity. omega. +Qed. + +Theorem eval_shrluimm: + forall n, unary_constructor_sound (fun a => shrluimm a n) + (fun x => Val.shrlu x (Vint n)). +Proof. +Local Opaque Int64.zwordsize. + red; intros until x; unfold shrluimm. + predSpec Int.eq Int.eq_spec n Int.zero; [| destruct (Int.ltu n Int64.iwordsize') eqn:L]; simpl. +- intros; subst. exists x; split; auto. destruct x; simpl; auto. rewrite Int64.shru'_zero; auto. +- destruct (shrluimm_match a); intros; InvEval; subst. ++ TrivialExists. simpl; rewrite L; auto. ++ destruct (Int.ltu n a) eqn:L2. +* assert (L3: Int.ltu (Int.sub a n) Int64.iwordsize' = true). + { apply sub_shift_amount; auto using a64_range. + apply Int.ltu_inv in L2. omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount64_eq, L3, a64_range by auto. + simpl. rewrite L. rewrite Int64.shru'_shl', L2 by auto using a64_range. auto. +* assert (L3: Int.ltu (Int.sub n a) Int64.iwordsize' = true). + { apply sub_shift_amount; auto using a64_range. + unfold Int.ltu in L2. destruct zlt in L2; discriminate || omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount64_eq, L3, a64_range by auto. + simpl. rewrite L. rewrite Int64.shru'_shl', L2 by auto using a64_range. auto. ++ destruct (Int.ltu (Int.add a n) Int64.iwordsize') eqn:L2. +* econstructor; split. eapply eval_shrluimm_base; eauto. + destruct v1; simpl; auto. rewrite a64_range; simpl. rewrite L, L2. + rewrite Int64.shru'_shru'; auto using a64_range. +* econstructor; split; [|eauto]. apply eval_shrluimm_base; auto. EvalOp. ++ destruct (zlt (Int.unsigned n) s). +* econstructor; split. EvalOp. rewrite mk_amount64_eq by auto. + destruct v1; simpl; auto. rewrite ! L; simpl. + set (s' := s - Int.unsigned n). + replace s with (s' + Int.unsigned n) by (unfold s'; omega). + rewrite Int64.shru'_zero_ext. auto. unfold s'; omega. +* econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite ! L; simpl. + rewrite Int64.shru'_zero_ext_0 by omega. auto. ++ econstructor; eauto using eval_shrluimm_base. +- intros; TrivialExists. +Qed. + +Remark eval_shrlimm_base: forall le a n x, + eval_expr ge sp e m le a x -> + Int.ltu n Int64.iwordsize' = true -> + eval_expr ge sp e m le (shrlimm_base a n) (Val.shrl x (Vint n)). +Proof. + unfold shrlimm_base; intros; EvalOp. simpl. rewrite mk_amount64_eq by auto. auto. +Qed. + +Theorem eval_shrlimm: + forall n, unary_constructor_sound (fun a => shrlimm a n) + (fun x => Val.shrl x (Vint n)). +Proof. + red; intros until x; unfold shrlimm. + predSpec Int.eq Int.eq_spec n Int.zero; [| destruct (Int.ltu n Int64.iwordsize') eqn:L]; simpl. +- intros; subst. exists x; split; auto. destruct x; simpl; auto. rewrite Int64.shr'_zero; auto. +- destruct (shrlimm_match a); intros; InvEval; subst. ++ TrivialExists. simpl; rewrite L; auto. ++ destruct (Int.ltu n a) eqn:L2. +* assert (L3: Int.ltu (Int.sub a n) Int64.iwordsize' = true). + { apply sub_shift_amount; auto using a64_range. + apply Int.ltu_inv in L2. omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount64_eq, L3, a64_range by auto. + simpl. rewrite L. rewrite Int64.shr'_shl', L2 by auto using a64_range. auto. +* assert (L3: Int.ltu (Int.sub n a) Int64.iwordsize' = true). + { apply sub_shift_amount; auto using a64_range. + unfold Int.ltu in L2. destruct zlt in L2; discriminate || omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount64_eq, L3, a64_range by auto. + simpl. rewrite L. rewrite Int64.shr'_shl', L2 by auto using a64_range. auto. ++ destruct (Int.ltu (Int.add a n) Int64.iwordsize') eqn:L2. +* econstructor; split. eapply eval_shrlimm_base; eauto. + destruct v1; simpl; auto. rewrite a64_range; simpl. rewrite L, L2. + rewrite Int64.shr'_shr'; auto using a64_range. +* econstructor; split; [|eauto]. apply eval_shrlimm_base; auto. EvalOp. ++ destruct (zlt (Int.unsigned n) s && zlt s Int64.zwordsize) eqn:E. +* InvBooleans. econstructor; split. EvalOp. rewrite mk_amount64_eq by auto. + destruct v1; simpl; auto. rewrite ! L; simpl. + set (s' := s - Int.unsigned n). + replace s with (s' + Int.unsigned n) by (unfold s'; omega). + rewrite Int64.shr'_sign_ext. auto. unfold s'; omega. unfold s'; omega. +* econstructor; split; [|eauto]. apply eval_shrlimm_base; auto. EvalOp. ++ econstructor; eauto using eval_shrlimm_base. +- intros; TrivialExists. +Qed. + +(** ** Multiplication *) + +Lemma eval_mullimm_base: + forall n, unary_constructor_sound (mullimm_base n) (fun x => Val.mull x (Vlong n)). +Proof. + intros; red; intros; unfold mullimm_base. + assert (DFL: exists v, eval_expr ge sp e m le (Eop Omull (Eop (Olongconst n) Enil ::: a ::: Enil)) v /\ Val.lessdef (Val.mull x (Vlong n)) v). + { rewrite Val.mull_commut; TrivialExists. } + generalize (Int64.one_bits'_decomp n); generalize (Int64.one_bits'_range n); + destruct (Int64.one_bits' n) as [ | i [ | j []]]; intros P Q. +- apply DFL. +- replace (Val.mull x (Vlong n)) with (Val.shll x (Vint i)). + apply eval_shllimm; auto. + simpl in Q. destruct x; auto; simpl. rewrite P by auto with coqlib. + rewrite Q, Int64.add_zero, Int64.shl'_mul. auto. +- exploit (eval_shllimm i (x :: le) (Eletvar 0) x). constructor; auto. intros [v1 [A1 B1]]. + exploit (eval_shllimm j (x :: le) (Eletvar 0) x). constructor; auto. intros [v2 [A2 B2]]. + exploit (eval_addl (x :: le)). eexact A1. eexact A2. intros [v [A B]]. + exists v; split. econstructor; eauto. + simpl in Q. rewrite Q, Int64.add_zero. eapply Val.lessdef_trans; [|eexact B]. + eapply Val.lessdef_trans; [|eapply Val.addl_lessdef; eauto]. + destruct x; simpl; auto; rewrite ! P by auto with coqlib. + rewrite Int64.mul_add_distr_r, <- ! Int64.shl'_mul. auto. +- apply DFL. +Qed. + +Theorem eval_mullimm: + forall n, unary_constructor_sound (mullimm n) (fun x => Val.mull x (Vlong n)). +Proof. + intros; red; intros until x; unfold mullimm. + predSpec Int64.eq Int64.eq_spec n Int64.zero. + intros. exists (Vlong Int64.zero); split. EvalOp. + destruct x; simpl; auto. subst n. rewrite Int64.mul_zero. auto. + predSpec Int64.eq Int64.eq_spec n Int64.one. + intros. exists x; split; auto. + destruct x; simpl; auto. subst n. rewrite Int64.mul_one. auto. + case (mullimm_match a); intros; InvEval; subst. +- TrivialExists. simpl. rewrite Int64.mul_commut; auto. +- rewrite Val.mull_addl_distr_l. + exploit eval_mullimm_base; eauto. instantiate (1 := n). intros [v' [A1 B1]]. + exploit (eval_addlimm (Int64.mul n n2) le (mullimm_base n t2) v'). auto. intros [v'' [A2 B2]]. + exists v''; split; auto. eapply Val.lessdef_trans. eapply Val.addl_lessdef; eauto. + rewrite Val.mull_commut; auto. +- apply eval_mullimm_base; auto. +Qed. + +Theorem eval_mull: binary_constructor_sound mull Val.mull. +Proof. + red; intros until y; unfold mull; case (mull_match a b); intros; InvEval; subst. +- rewrite Val.mull_commut. apply eval_mullimm; auto. +- apply eval_mullimm; auto. +- TrivialExists. +Qed. + +Theorem eval_mullhu: + forall n, unary_constructor_sound (fun a => mullhu a n) (fun v => Val.mullhu v (Vlong n)). +Proof. + unfold mullhu; red; intros; TrivialExists. +Qed. + +Theorem eval_mullhs: + forall n, unary_constructor_sound (fun a => mullhs a n) (fun v => Val.mullhs v (Vlong n)). +Proof. + unfold mullhs; red; intros; TrivialExists. +Qed. + +(** Integer conversions *) + +Theorem eval_zero_ext_l: + forall sz, 0 <= sz -> unary_constructor_sound (zero_ext_l sz) (Val.zero_ext_l sz). +Proof. + intros; red; intros until x; unfold zero_ext_l; case (zero_ext_l_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +- destruct (zlt (Int.unsigned a0) sz). ++ econstructor; split. EvalOp. destruct v1; simpl; auto. rewrite a64_range; simpl. + apply Val.lessdef_same. f_equal. rewrite Int64.shl'_zero_ext by omega. f_equal. omega. ++ TrivialExists. +- TrivialExists. +Qed. + +(** Bitwise not, and, or, xor *) + +Theorem eval_notl: unary_constructor_sound notl Val.notl. +Proof. + assert (INV: forall v, Val.lessdef (Val.notl (Val.notl v)) v). + { destruct v; auto. simpl; rewrite Int64.not_involutive; auto. } + unfold notl; red; intros until x; case (notl_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +- exists v1; auto. +- exists (eval_shiftl s v1 a0); split; auto. EvalOp. +- econstructor; split. EvalOp. + destruct v1; simpl; auto; destruct v0; simpl; auto. + rewrite Int64.not_and_or_not, Int64.not_involutive, Int64.or_commut. auto. +- econstructor; split. EvalOp. + destruct v1; simpl; auto; destruct v0; simpl; auto. + rewrite Int64.not_or_and_not, Int64.not_involutive, Int64.and_commut. auto. +- econstructor; split. EvalOp. + destruct v1; simpl; auto; destruct v0; simpl; auto. + unfold Int64.not; rewrite ! Int64.xor_assoc. auto. +- econstructor; split. EvalOp. + destruct v1; simpl; auto; destruct v0; simpl; auto. + unfold Int64.not; rewrite ! Int64.xor_assoc, Int64.xor_idem, Int64.xor_zero. auto. +- TrivialExists. +Qed. + +Lemma eval_andlimm_base: + forall n, unary_constructor_sound (andlimm_base n) (fun x => Val.andl x (Vlong n)). +Proof. + intros; red; intros. unfold andlimm_base. + predSpec Int64.eq Int64.eq_spec n Int64.zero. + exists (Vlong Int64.zero); split. EvalOp. + destruct x; simpl; auto. subst n. rewrite Int64.and_zero. auto. + predSpec Int64.eq Int64.eq_spec n Int64.mone. + exists x; split; auto. + subst. destruct x; simpl; auto. rewrite Int64.and_mone; auto. + destruct (Z_is_power2m1 (Int64.unsigned n)) as [s|] eqn:P. + assert (0 <= s) by (eapply Z_is_power2m1_nonneg; eauto). + rewrite <- (Int64.repr_unsigned n), (Z_is_power2m1_sound _ _ P), <- Val.zero_ext_andl by auto. + apply eval_zero_ext_l; auto. + TrivialExists. +Qed. + +Theorem eval_andlimm: + forall n, unary_constructor_sound (andlimm n) (fun x => Val.andl x (Vlong n)). +Proof. + intros; red; intros until x. unfold andlimm. + case (andlimm_match a); intros; InvEval; subst. +- rewrite Int64.and_commut; TrivialExists. +- rewrite Val.andl_assoc, Int64.and_commut. apply eval_andlimm_base; auto. +- destruct (zle 0 s). ++ replace (Val.zero_ext_l s v1) with (Val.andl v1 (Vlong (Int64.repr (two_p s - 1)))). + rewrite Val.andl_assoc, Int64.and_commut. + apply eval_andlimm_base; auto. + destruct v1; simpl; auto. rewrite Int64.zero_ext_and by auto. auto. ++ apply eval_andlimm_base. EvalOp. +- apply eval_andlimm_base; auto. +Qed. + +Theorem eval_andl: binary_constructor_sound andl Val.andl. +Proof. + red; intros until y; unfold andl; case (andl_match a b); intros; InvEval; subst. +- rewrite Val.andl_commut; apply eval_andlimm; auto. +- apply eval_andlimm; auto. +- rewrite Val.andl_commut; TrivialExists. +- TrivialExists. +- rewrite Val.andl_commut; TrivialExists. +- TrivialExists. +- rewrite Val.andl_commut; TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_orlimm: + forall n, unary_constructor_sound (orlimm n) (fun x => Val.orl x (Vlong n)). +Proof. + intros; red; intros until x. unfold orlimm. + predSpec Int64.eq Int64.eq_spec n Int64.zero. + intros. subst. exists x; split; auto. + destruct x; simpl; auto. rewrite Int64.or_zero; auto. + predSpec Int64.eq Int64.eq_spec n Int64.mone. + intros. exists (Vlong Int64.mone); split. EvalOp. + destruct x; simpl; auto. subst n. rewrite Int64.or_mone. auto. + destruct (orlimm_match a); intros; InvEval; subst. +- rewrite Int64.or_commut; TrivialExists. +- rewrite Val.orl_assoc, Int64.or_commut; TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_orl: binary_constructor_sound orl Val.orl. +Proof. + red; intros until y; unfold orl; case (orl_match a b); intros; InvEval; subst. +- rewrite Val.orl_commut. apply eval_orlimm; auto. +- apply eval_orlimm; auto. +- rewrite Val.orl_commut; TrivialExists. +- TrivialExists. +- rewrite Val.orl_commut; TrivialExists. +- TrivialExists. +- (* shl - shru *) + destruct (Int.eq (Int.add a1 a2) Int64.iwordsize' && same_expr_pure t1 t2) eqn:?. ++ InvBooleans. apply Int.same_if_eq in H. + exploit eval_same_expr; eauto. intros [EQ1 EQ2]. subst. + econstructor; split. EvalOp. + destruct v0; simpl; auto. rewrite ! a64_range. simpl. rewrite <- Int64.or_ror'; auto using a64_range. ++ TrivialExists. +- (* shru - shl *) + destruct (Int.eq (Int.add a2 a1) Int64.iwordsize' && same_expr_pure t1 t2) eqn:?. ++ InvBooleans. apply Int.same_if_eq in H. + exploit eval_same_expr; eauto. intros [EQ1 EQ2]. subst. + econstructor; split. EvalOp. + destruct v0; simpl; auto. rewrite ! a64_range. simpl. + rewrite Int64.or_commut, <- Int64.or_ror'; auto using a64_range. ++ TrivialExists. +- rewrite Val.orl_commut; TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Lemma eval_xorlimm_base: + forall n, unary_constructor_sound (xorlimm_base n) (fun x => Val.xorl x (Vlong n)). +Proof. + intros; red; intros. unfold xorlimm_base. + predSpec Int64.eq Int64.eq_spec n Int64.zero. + intros. exists x; split. auto. + destruct x; simpl; auto. subst n. rewrite Int64.xor_zero. auto. + predSpec Int64.eq Int64.eq_spec n Int64.mone. + subst n. change (Val.xorl x (Vlong Int64.mone)) with (Val.notl x). apply eval_notl; auto. + TrivialExists. +Qed. + +Theorem eval_xorlimm: + forall n, unary_constructor_sound (xorlimm n) (fun x => Val.xorl x (Vlong n)). +Proof. + intros; red; intros until x. unfold xorlimm. + destruct (xorlimm_match a); intros; InvEval; subst. +- rewrite Int64.xor_commut; TrivialExists. +- rewrite Val.xorl_assoc; simpl. rewrite (Int64.xor_commut n2). apply eval_xorlimm_base; auto. +- apply eval_xorlimm_base; auto. +Qed. + +Theorem eval_xorl: binary_constructor_sound xorl Val.xorl. +Proof. + red; intros until y; unfold xorl; case (xorl_match a b); intros; InvEval; subst. +- rewrite Val.xorl_commut; apply eval_xorlimm; auto. +- apply eval_xorlimm; auto. +- rewrite Val.xorl_commut; TrivialExists. +- TrivialExists. +- rewrite Val.xorl_commut; TrivialExists. +- TrivialExists. +- rewrite Val.xorl_commut; TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +(** ** Integer division and modulus *) + +Theorem eval_divls_base: partial_binary_constructor_sound divls_base Val.divls. +Proof. + red; intros; unfold divls_base; TrivialExists. +Qed. + +Theorem eval_modls_base: partial_binary_constructor_sound modls_base Val.modls. +Proof. + red; intros; unfold modls_base, modl_aux. + exploit Val.modls_divls; eauto. intros (q & A & B). subst z. + TrivialExists. repeat (econstructor; eauto with evalexpr). exact A. +Qed. + +Theorem eval_divlu_base: partial_binary_constructor_sound divlu_base Val.divlu. +Proof. + red; intros; unfold divlu_base; TrivialExists. +Qed. + +Theorem eval_modlu_base: partial_binary_constructor_sound modlu_base Val.modlu. +Proof. + red; intros; unfold modlu_base, modl_aux. + exploit Val.modlu_divlu; eauto. intros (q & A & B). subst z. + TrivialExists. repeat (econstructor; eauto with evalexpr). exact A. +Qed. + +Theorem eval_shrxlimm: + forall le a n x z, + eval_expr ge sp e m le a x -> + Val.shrxl x (Vint n) = Some z -> + exists v, eval_expr ge sp e m le (shrxlimm a n) v /\ Val.lessdef z v. +Proof. + intros; unfold shrxlimm. + predSpec Int.eq Int.eq_spec n Int.zero. +- subst n. exists x; split; auto. + destruct x; simpl in H0; try discriminate. + change (Int.ltu Int.zero (Int.repr 63)) with true in H0; inv H0. + rewrite Int64.shrx'_zero. auto. +- TrivialExists. +Qed. + +(** General shifts *) + +Theorem eval_shll: binary_constructor_sound shll Val.shll. +Proof. + red; intros until y; unfold shll; case (shll_match b); intros. + InvEval. apply eval_shllimm; auto. + TrivialExists. +Qed. + +Theorem eval_shrl: binary_constructor_sound shrl Val.shrl. +Proof. + red; intros until y; unfold shrl; case (shrl_match b); intros. + InvEval. apply eval_shrlimm; auto. + TrivialExists. +Qed. + +Theorem eval_shrlu: binary_constructor_sound shrlu Val.shrlu. +Proof. + red; intros until y; unfold shrlu; case (shrlu_match b); intros. + InvEval. apply eval_shrluimm; auto. + TrivialExists. +Qed. + +(** Comparisons *) + +Remark option_map_of_bool_inv: forall ov w, + option_map Val.of_bool ov = Some w -> Val.of_optbool ov = w. +Proof. + intros. destruct ov; inv H; auto. +Qed. + +Section COMP_IMM. + +Variable default: comparison -> int64 -> condition. +Variable intsem: comparison -> int64 -> int64 -> bool. +Variable sem: comparison -> val -> val -> option val. + +Hypothesis sem_int: forall c x y, + sem c (Vlong x) (Vlong y) = Some (Val.of_bool (intsem c x y)). +Hypothesis sem_undef: forall c v, + sem c Vundef v = None. +Hypothesis sem_eq: forall x y, + sem Ceq (Vlong x) (Vlong y) = Some (Val.of_bool (Int64.eq x y)). +Hypothesis sem_ne: forall x y, + sem Cne (Vlong x) (Vlong y) = Some (Val.of_bool (negb (Int64.eq x y))). +Hypothesis sem_default: forall c v n, + sem c v (Vlong n) = option_map Val.of_bool (eval_condition (default c n) (v :: nil) m). + +Lemma eval_complimm_default: forall le a x c n2 v, + sem c x (Vlong n2) = Some v -> + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le (Eop (Ocmp (default c n2)) (a:::Enil)) v. +Proof. + intros. EvalOp. simpl. rewrite sem_default in H. apply option_map_of_bool_inv in H. + congruence. +Qed. + +Lemma eval_complimm: + forall le c a n2 x v, + eval_expr ge sp e m le a x -> + sem c x (Vlong n2) = Some v -> + eval_expr ge sp e m le (complimm default intsem c a n2) v. +Proof. + intros until x; unfold complimm; case (complimm_match c a); intros; InvEval; subst. +- (* constant *) + rewrite sem_int in H0; inv H0. EvalOp. destruct (intsem c0 n1 n2); auto. +- (* mask zero *) + predSpec Int64.eq Int64.eq_spec n2 Int64.zero. ++ subst n2. destruct v1; simpl in H0; rewrite ? sem_undef, ? sem_eq in H0; inv H0. + EvalOp. ++ eapply eval_complimm_default; eauto. EvalOp. +- (* mask not zero *) + predSpec Int64.eq Int64.eq_spec n2 Int64.zero. ++ subst n2. destruct v1; simpl in H0; rewrite ? sem_undef, ? sem_ne in H0; inv H0. + EvalOp. ++ eapply eval_complimm_default; eauto. EvalOp. +- (* default *) + eapply eval_complimm_default; eauto. +Qed. + +Hypothesis sem_swap: + forall c x y, sem (swap_comparison c) x y = sem c y x. + +Lemma eval_complimm_swap: + forall le c a n2 x v, + eval_expr ge sp e m le a x -> + sem c (Vlong n2) x = Some v -> + eval_expr ge sp e m le (complimm default intsem (swap_comparison c) a n2) v. +Proof. + intros. eapply eval_complimm; eauto. rewrite sem_swap; auto. +Qed. + +End COMP_IMM. + +Theorem eval_cmpl: + forall c le a x b y v, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + Val.cmpl c x y = Some v -> + eval_expr ge sp e m le (cmpl c a b) v. +Proof. + intros until y; unfold cmpl; case (cmpl_match a b); intros; InvEval; subst. +- apply eval_complimm_swap with (sem := Val.cmpl) (x := y); auto. + intros; unfold Val.cmpl; rewrite Val.swap_cmpl_bool; auto. +- apply eval_complimm with (sem := Val.cmpl) (x := x); auto. +- EvalOp. simpl. rewrite Val.swap_cmpl_bool. apply option_map_of_bool_inv in H1. congruence. +- EvalOp. simpl. apply option_map_of_bool_inv in H1. congruence. +- EvalOp. simpl. apply option_map_of_bool_inv in H1. congruence. +Qed. + +Theorem eval_cmplu: + forall c le a x b y v, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + Val.cmplu (Mem.valid_pointer m) c x y = Some v -> + eval_expr ge sp e m le (cmplu c a b) v. +Proof. + intros until y; unfold cmplu; case (cmplu_match a b); intros; InvEval; subst. +- apply eval_complimm_swap with (sem := Val.cmplu (Mem.valid_pointer m)) (x := y); auto. + intros; unfold Val.cmplu; rewrite Val.swap_cmplu_bool; auto. +- apply eval_complimm with (sem := Val.cmplu (Mem.valid_pointer m)) (x := x); auto. +- EvalOp. simpl. rewrite Val.swap_cmplu_bool. apply option_map_of_bool_inv in H1. congruence. +- EvalOp. simpl. apply option_map_of_bool_inv in H1. congruence. +- EvalOp. simpl. apply option_map_of_bool_inv in H1. congruence. +Qed. + + +(** Floating-point conversions *) + +Theorem eval_longoffloat: partial_unary_constructor_sound longoffloat Val.longoffloat. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_longuoffloat: partial_unary_constructor_sound longuoffloat Val.longuoffloat. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_floatoflong: partial_unary_constructor_sound floatoflong Val.floatoflong. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_floatoflongu: partial_unary_constructor_sound floatoflongu Val.floatoflongu. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_longofsingle: partial_unary_constructor_sound longofsingle Val.longofsingle. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_longuofsingle: partial_unary_constructor_sound longuofsingle Val.longuofsingle. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_singleoflong: partial_unary_constructor_sound singleoflong Val.singleoflong. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_singleoflongu: partial_unary_constructor_sound singleoflongu Val.singleoflongu. +Proof. + red; intros; TrivialExists. +Qed. + +End CMCONSTR. diff --git a/aarch64/SelectOp.vp b/aarch64/SelectOp.vp new file mode 100644 index 00000000..5bd96987 --- /dev/null +++ b/aarch64/SelectOp.vp @@ -0,0 +1,566 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Instruction selection for operators *) + +Require Import Coqlib Zbits. +Require Import Compopts AST Integers Floats Builtins. +Require Import Op CminorSel. + +Local Open Scope cminorsel_scope. + +(** "ror" shifted operands are not supported by arithmetic operations *) + +Definition arith_shift (s: shift) := + match s with Sror => false | _ => true end. + +(** ** Constants **) + +Definition addrsymbol (id: ident) (ofs: ptrofs) := + Eop (Oaddrsymbol id ofs) Enil. + +Definition addrstack (ofs: ptrofs) := + Eop (Oaddrstack ofs) Enil. + +(** ** Integer addition *) + +Nondetfunction addimm (n: int) (e: expr) := + if Int.eq n Int.zero then e else + match e with + | Eop (Ointconst m) Enil => Eop (Ointconst (Int.add n m)) Enil + | Eop (Oaddimm m) (t ::: Enil) => Eop (Oaddimm(Int.add n m)) (t ::: Enil) + | _ => Eop (Oaddimm n) (e ::: Enil) + end. + +Nondetfunction add (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Ointconst n1) Enil, t2 => addimm n1 t2 + | t1, Eop (Ointconst n2) Enil => addimm n2 t1 + | Eop (Oaddimm n1) (t1:::Enil), Eop (Oaddimm n2) (t2:::Enil) => + addimm (Int.add n1 n2) (Eop Oadd (t1:::t2:::Enil)) + | Eop (Oaddimm n1) (t1:::Enil), t2 => + addimm n1 (Eop Oadd (t1:::t2:::Enil)) + | t1, Eop (Oaddimm n2) (t2:::Enil) => + addimm n2 (Eop Oadd (t1:::t2:::Enil)) + | Eop (Oshift s a) (t1:::Enil), t2 ?? arith_shift s => + Eop (Oaddshift s a) (t2 ::: t1 ::: Enil) + | t1, Eop (Oshift s a) (t2:::Enil) ?? arith_shift s => + Eop (Oaddshift s a) (t1 ::: t2 ::: Enil) + | Eop Omul (t1:::t2:::Enil), t3 => + Eop Omuladd (t3:::t1:::t2:::Enil) + | t1, Eop Omul (t2:::t3:::Enil) => + Eop Omuladd (t1:::t2:::t3:::Enil) + | _, _ => Eop Oadd (e1:::e2:::Enil) + end. + +(** ** Opposite *) + +Nondetfunction negint (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Ointconst (Int.neg n)) Enil + | Eop (Oshift s a) (t1:::Enil) ?? arith_shift s => Eop (Onegshift s a) (t1:::Enil) + | _ => Eop Oneg (e ::: Enil) + end. + +(** ** Integer and pointer subtraction *) + +Nondetfunction sub (e1: expr) (e2: expr) := + match e1, e2 with + | t1, Eop (Ointconst n2) Enil => + addimm (Int.neg n2) t1 + | Eop (Oaddimm n1) (t1:::Enil), Eop (Oaddimm n2) (t2:::Enil) => + addimm (Int.sub n1 n2) (Eop Osub (t1:::t2:::Enil)) + | Eop (Oaddimm n1) (t1:::Enil), t2 => + addimm n1 (Eop Osub (t1:::t2:::Enil)) + | t1, Eop (Oaddimm n2) (t2:::Enil) => + addimm (Int.neg n2) (Eop Osub (t1:::t2:::Enil)) + | t1, Eop (Oshift s a) (t2:::Enil) ?? arith_shift s => + Eop (Osubshift s a) (t1:::t2::: Enil) + | t1, Eop Omul (t2:::t3:::Enil) => + Eop Omulsub (t1:::t2:::t3:::Enil) + | _, _ => Eop Osub (e1:::e2:::Enil) + end. + +(** ** Immediate shift left *) + +Definition shlimm_base (e1: expr) (n: int) := + Eop (Oshift Slsl (mk_amount32 n)) (e1 ::: Enil). + +Nondetfunction shlimm (e1: expr) (n: int) := + if Int.eq n Int.zero then + e1 + else if negb (Int.ltu n Int.iwordsize) then + Eop Oshl (e1 ::: Eop (Ointconst n) Enil ::: Enil) + else match e1 with + | Eop (Ointconst n1) Enil => + Eop (Ointconst (Int.shl n1 n)) Enil + | Eop (Oshift Slsl a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int.iwordsize + then shlimm_base t1 (Int.add a n) + else shlimm_base e1 n + | Eop (Ozext s) (t1:::Enil) => + Eop (Oshlzext s (mk_amount32 n)) (t1:::Enil) + | Eop (Osext s) (t1:::Enil) => + Eop (Oshlsext s (mk_amount32 n)) (t1:::Enil) + | Eop (Oshlzext s a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int.iwordsize + then Eop (Oshlzext s (mk_amount32 (Int.add a n))) (t1:::Enil) + else shlimm_base e1 n + | Eop (Oshlsext s a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int.iwordsize + then Eop (Oshlsext s (mk_amount32 (Int.add a n))) (t1:::Enil) + else shlimm_base e1 n + | _ => + shlimm_base e1 n + end. + +(** ** Immediate shift right (logical) *) + +Definition shruimm_base (e1: expr) (n: int) := + Eop (Oshift Slsr (mk_amount32 n)) (e1 ::: Enil). + +Nondetfunction shruimm (e1: expr) (n: int) := + if Int.eq n Int.zero then + e1 + else if negb (Int.ltu n Int.iwordsize) then + Eop Oshru (e1 ::: Eop (Ointconst n) Enil ::: Enil) + else match e1 with + | Eop (Ointconst n1) Enil => + Eop (Ointconst (Int.shru n1 n)) Enil + | Eop (Oshift Slsl a) (t1:::Enil) => + if Int.ltu n a + then Eop (Oshlzext (Int.zwordsize - Int.unsigned a) (mk_amount32 (Int.sub a n))) (t1:::Enil) + else Eop (Ozextshr (mk_amount32 (Int.sub n a)) (Int.zwordsize - Int.unsigned n)) (t1:::Enil) + | Eop (Oshift Slsr a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int.iwordsize + then shruimm_base t1 (Int.add a n) + else shruimm_base e1 n + | Eop (Ozext s) (t1:::Enil) => + if zlt (Int.unsigned n) s + then Eop (Ozextshr (mk_amount32 n) (s - Int.unsigned n)) (t1:::Enil) + else Eop (Ointconst Int.zero) Enil + | _ => + shruimm_base e1 n + end. + +(** ** Immediate shift right (arithmetic) *) + +Definition shrimm_base (e1: expr) (n: int) := + Eop (Oshift Sasr (mk_amount32 n)) (e1 ::: Enil). + +Nondetfunction shrimm (e1: expr) (n: int) := + if Int.eq n Int.zero then + e1 + else if negb (Int.ltu n Int.iwordsize) then + Eop Oshr (e1 ::: Eop (Ointconst n) Enil ::: Enil) + else match e1 with + | Eop (Ointconst n1) Enil => + Eop (Ointconst (Int.shr n1 n)) Enil + | Eop (Oshift Slsl a) (t1:::Enil) => + if Int.ltu n a + then Eop (Oshlsext (Int.zwordsize - Int.unsigned a) (mk_amount32 (Int.sub a n))) (t1:::Enil) + else Eop (Osextshr (mk_amount32 (Int.sub n a)) (Int.zwordsize - Int.unsigned n)) (t1:::Enil) + | Eop (Oshift Sasr a) (t1:::Enil) => + if Int.ltu (Int.add a n) Int.iwordsize + then shrimm_base t1 (Int.add a n) + else shrimm_base e1 n + | Eop (Osext s) (t1:::Enil) => + if zlt (Int.unsigned n) s && zlt s Int.zwordsize + then Eop (Osextshr (mk_amount32 n) (s - Int.unsigned n)) (t1:::Enil) + else shrimm_base e1 n + | _ => + shrimm_base e1 n + end. + +(** ** Integer multiply *) + +Definition mulimm_base (n1: int) (e2: expr) := + match Int.one_bits n1 with + | i :: nil => + shlimm e2 i + | i :: j :: nil => + Elet e2 (add (shlimm (Eletvar 0) i) (shlimm (Eletvar 0) j)) + | _ => + Eop Omul (Eop (Ointconst n1) Enil ::: e2 ::: Enil) + end. + +Nondetfunction mulimm (n1: int) (e2: expr) := + if Int.eq n1 Int.zero then Eop (Ointconst Int.zero) Enil + else if Int.eq n1 Int.one then e2 + else match e2 with + | Eop (Ointconst n2) Enil => Eop (Ointconst (Int.mul n1 n2)) Enil + | Eop (Oaddimm n2) (t2:::Enil) => addimm (Int.mul n1 n2) (mulimm_base n1 t2) + | _ => mulimm_base n1 e2 + end. + +Nondetfunction mul (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Ointconst n1) Enil, t2 => mulimm n1 t2 + | t1, Eop (Ointconst n2) Enil => mulimm n2 t1 + | _, _ => Eop Omul (e1:::e2:::Enil) + end. + +Definition mulhs (e1: expr) (e2: expr) := + Eop Olowlong + (Eop (Oshiftl Sasr (mk_amount64 (Int.repr 32))) + (Eop Omull (Eop (Oextend Xsgn32 (mk_amount64 Int.zero)) (e1 ::: Enil) ::: + Eop (Oextend Xsgn32 (mk_amount64 Int.zero)) (e2 ::: Enil) ::: Enil) ::: Enil) + ::: Enil). + +Definition mulhu (e1: expr) (e2: expr) := + Eop Olowlong + (Eop (Oshiftl Slsr (mk_amount64 (Int.repr 32))) + (Eop Omull (Eop (Oextend Xuns32 (mk_amount64 Int.zero)) (e1 ::: Enil) ::: + Eop (Oextend Xuns32 (mk_amount64 Int.zero)) (e2 ::: Enil) ::: Enil) ::: Enil) + ::: Enil). + +(** ** Integer conversions *) + +Nondetfunction zero_ext (sz: Z) (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Ointconst (Int.zero_ext sz n)) Enil + | Eop (Oshift Slsr a) (t1:::Enil) => Eop (Ozextshr a sz) (t1:::Enil) + | Eop (Oshift Slsl a) (t1:::Enil) => + if zlt (Int.unsigned a) sz + then Eop (Oshlzext (sz - Int.unsigned a) a) (t1:::Enil) + else Eop (Ozext sz) (e:::Enil) + | _ => Eop (Ozext sz) (e:::Enil) + end. + +Nondetfunction sign_ext (sz: Z) (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Ointconst (Int.sign_ext sz n)) Enil + | Eop (Oshift Sasr a) (t1:::Enil) => Eop (Osextshr a sz) (t1:::Enil) + | Eop (Oshift Slsl a) (t1:::Enil) => + if zlt (Int.unsigned a) sz + then Eop (Oshlsext (sz - Int.unsigned a) a) (t1:::Enil) + else Eop (Osext sz) (e:::Enil) + | _ => Eop (Osext sz) (e:::Enil) + end. + +Definition cast8unsigned (e: expr) := zero_ext 8 e. +Definition cast8signed (e: expr) := sign_ext 8 e. +Definition cast16unsigned (e: expr) := zero_ext 16 e. +Definition cast16signed (e: expr) := sign_ext 16 e. + +(** ** Bitwise not *) + +Nondetfunction notint (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Ointconst (Int.not n)) Enil + | Eop (Oshift s a) (t1:::Enil) => Eop (Onotshift s a) (t1:::Enil) + | Eop Onot (t1:::Enil) => t1 + | Eop (Onotshift s a) (t1:::Enil) => Eop (Oshift s a) (t1:::Enil) + | Eop Obic (t1:::t2:::Enil) => Eop Oorn (t2:::t1:::Enil) + | Eop Oorn (t1:::t2:::Enil) => Eop Obic (t2:::t1:::Enil) + | Eop Oxor (t1:::t2:::Enil) => Eop Oeqv (t1:::t2:::Enil) + | Eop Oeqv (t1:::t2:::Enil) => Eop Oxor (t1:::t2:::Enil) + | _ => Eop Onot (e:::Enil) + end. + +(** ** Bitwise and *) + +Definition andimm_base (n1: int) (e2: expr) := + if Int.eq n1 Int.zero then Eop (Ointconst Int.zero) Enil else + if Int.eq n1 Int.mone then e2 else + match Z_is_power2m1 (Int.unsigned n1) with + | Some s => zero_ext s e2 + | None => Eop (Oandimm n1) (e2 ::: Enil) + end. + +Nondetfunction andimm (n1: int) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => Eop (Ointconst (Int.and n1 n2)) Enil + | Eop (Oandimm n2) (t2:::Enil) => andimm_base (Int.and n1 n2) t2 + | Eop (Ozext s) (t2:::Enil) => + if zle 0 s + then andimm_base (Int.and n1 (Int.repr (two_p s - 1))) t2 + else andimm_base n1 e2 + | _ => andimm_base n1 e2 + end. + +Nondetfunction and (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Ointconst n1) Enil, t2 => andimm n1 t2 + | t1, Eop (Ointconst n2) Enil => andimm n2 t1 + | Eop Onot (t1:::Enil), t2 => Eop Obic (t2:::t1:::Enil) + | t1, Eop Onot (t2:::Enil) => Eop Obic (t1:::t2:::Enil) + | Eop (Onotshift s a) (t1:::Enil), t2 => Eop (Obicshift s a) (t2:::t1:::Enil) + | t1, Eop (Onotshift s a) (t2:::Enil) => Eop (Obicshift s a) (t1:::t2:::Enil) + | Eop (Oshift s a) (t1:::Enil), t2 => Eop (Oandshift s a) (t2:::t1:::Enil) + | t1, Eop (Oshift s a) (t2:::Enil) => Eop (Oandshift s a) (t1:::t2:::Enil) + | _, _ => Eop Oand (e1:::e2:::Enil) + end. + +(** ** Bitwise or *) + +Nondetfunction orimm (n1: int) (e2: expr) := + if Int.eq n1 Int.zero then e2 + else if Int.eq n1 Int.mone then Eop (Ointconst Int.mone) Enil + else match e2 with + | Eop (Ointconst n2) Enil => Eop (Ointconst (Int.or n1 n2)) Enil + | Eop (Oorimm n2) (t2:::Enil) => Eop (Oorimm (Int.or n1 n2)) (t2:::Enil) + | _ => Eop (Oorimm n1) (e2:::Enil) + end. + +Definition same_expr_pure (e1 e2: expr) := + match e1, e2 with + | Evar v1, Evar v2 => if ident_eq v1 v2 then true else false + | _, _ => false + end. + +Nondetfunction or (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Ointconst n1) Enil, t2 => orimm n1 t2 + | t1, Eop (Ointconst n2) Enil => orimm n2 t1 + | Eop Onot (t1:::Enil), t2 => Eop Oorn (t2:::t1:::Enil) + | t1, Eop Onot (t2:::Enil) => Eop Oorn (t1:::t2:::Enil) + | Eop (Onotshift s a) (t1:::Enil), t2 => Eop (Oornshift s a) (t2:::t1:::Enil) + | t1, Eop (Onotshift s a) (t2:::Enil) => Eop (Oornshift s a) (t1:::t2:::Enil) + | Eop (Oshift Slsl a1) (t1:::Enil), Eop (Oshift Slsr a2) (t2:::Enil) => + if Int.eq (Int.add a1 a2) Int.iwordsize && same_expr_pure t1 t2 + then Eop (Oshift Sror a2) (t2:::Enil) + else Eop (Oorshift Slsr a2) (Eop (Oshift Slsl a1) (t1:::Enil):::t2:::Enil) + | Eop (Oshift Slsr a1) (t1:::Enil), Eop (Oshift Slsl a2) (t2:::Enil) => + if Int.eq (Int.add a2 a1) Int.iwordsize && same_expr_pure t1 t2 + then Eop (Oshift Sror a1) (t1:::Enil) + else Eop (Oorshift Slsl a2) (Eop (Oshift Slsr a1) (t1:::Enil):::t2:::Enil) + | Eop (Oshift s a) (t1:::Enil), t2 => Eop (Oorshift s a) (t2:::t1:::Enil) + | t1, Eop (Oshift s a) (t2:::Enil) => Eop (Oorshift s a) (t1:::t2:::Enil) + | _, _ => Eop Oor (e1:::e2:::Enil) + end. + +(** ** Bitwise xor *) + +Definition xorimm_base (n1: int) (e2: expr) := + if Int.eq n1 Int.zero then e2 else + if Int.eq n1 Int.mone then notint e2 else + Eop (Oxorimm n1) (e2:::Enil). + +Nondetfunction xorimm (n1: int) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => Eop (Ointconst (Int.xor n1 n2)) Enil + | Eop (Oxorimm n2) (t2:::Enil) => xorimm_base (Int.xor n1 n2) t2 + | _ => xorimm_base n1 e2 + end. + +Nondetfunction xor (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Ointconst n1) Enil, t2 => xorimm n1 t2 + | t1, Eop (Ointconst n2) Enil => xorimm n2 t1 + | Eop Onot (t1:::Enil), t2 => Eop Oeqv (t2:::t1:::Enil) + | t1, Eop Onot (t2:::Enil) => Eop Oeqv (t1:::t2:::Enil) + | Eop (Onotshift s a) (t1:::Enil), t2 => Eop (Oeqvshift s a) (t2:::t1:::Enil) + | t1, Eop (Onotshift s a) (t2:::Enil) => Eop (Oeqvshift s a) (t1:::t2:::Enil) + | Eop (Oshift s a) (t1:::Enil), t2 => Eop (Oxorshift s a) (t2:::t1:::Enil) + | t1, Eop (Oshift s a) (t2:::Enil) => Eop (Oxorshift s a) (t1:::t2:::Enil) + | _, _ => Eop Oxor (e1:::e2:::Enil) + end. + +(** ** Integer division and modulus *) + +Definition mod_aux (divop: operation) (e1 e2: expr) := + Elet e1 + (Elet (lift e2) + (Eop Omulsub (Eletvar 1 ::: + Eop divop (Eletvar 1 ::: Eletvar 0 ::: Enil) ::: + Eletvar 0 ::: + Enil))). + +Definition divs_base (e1: expr) (e2: expr) := Eop Odiv (e1:::e2:::Enil). +Definition mods_base := mod_aux Odiv. +Definition divu_base (e1: expr) (e2: expr) := Eop Odivu (e1:::e2:::Enil). +Definition modu_base := mod_aux Odivu. + +Definition shrximm (e1: expr) (n2: int) := + if Int.eq n2 Int.zero then e1 else Eop (Oshrximm n2) (e1:::Enil). + +(** ** General shifts *) + +Nondetfunction shl (e1: expr) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => shlimm e1 n2 + | _ => Eop Oshl (e1:::e2:::Enil) + end. + +Nondetfunction shr (e1: expr) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => shrimm e1 n2 + | _ => Eop Oshr (e1:::e2:::Enil) + end. + +Nondetfunction shru (e1: expr) (e2: expr) := + match e2 with + | Eop (Ointconst n2) Enil => shruimm e1 n2 + | _ => Eop Oshru (e1:::e2:::Enil) + end. + +(** ** Floating-point arithmetic *) + +Definition negf (e: expr) := Eop Onegf (e ::: Enil). +Definition absf (e: expr) := Eop Oabsf (e ::: Enil). +Definition addf (e1 e2: expr) := Eop Oaddf (e1 ::: e2 ::: Enil). +Definition subf (e1 e2: expr) := Eop Osubf (e1 ::: e2 ::: Enil). +Definition mulf (e1 e2: expr) := Eop Omulf (e1 ::: e2 ::: Enil). + +Definition negfs (e: expr) := Eop Onegfs (e ::: Enil). +Definition absfs (e: expr) := Eop Oabsfs (e ::: Enil). +Definition addfs (e1 e2: expr) := Eop Oaddfs (e1 ::: e2 ::: Enil). +Definition subfs (e1 e2: expr) := Eop Osubfs (e1 ::: e2 ::: Enil). +Definition mulfs (e1 e2: expr) := Eop Omulfs (e1 ::: e2 ::: Enil). + +(** ** Comparisons *) + +Nondetfunction compimm (default: comparison -> int -> condition) + (sem: comparison -> int -> int -> bool) + (c: comparison) (e1: expr) (n2: int) := + match c, e1 with + | c, Eop (Ointconst n1) Enil => + Eop (Ointconst (if sem c n1 n2 then Int.one else Int.zero)) Enil + | Ceq, Eop (Ocmp c) el => + if Int.eq_dec n2 Int.zero then + Eop (Ocmp (negate_condition c)) el + else if Int.eq_dec n2 Int.one then + Eop (Ocmp c) el + else + Eop (Ointconst Int.zero) Enil + | Cne, Eop (Ocmp c) el => + if Int.eq_dec n2 Int.zero then + Eop (Ocmp c) el + else if Int.eq_dec n2 Int.one then + Eop (Ocmp (negate_condition c)) el + else + Eop (Ointconst Int.one) Enil + | Ceq, Eop (Oandimm m) (t1:::Enil) => + if Int.eq n2 Int.zero + then Eop (Ocmp (Cmaskzero m)) (t1:::Enil) + else Eop (Ocmp (default c n2)) (e1:::Enil) + | Cne, Eop (Oandimm m) (t1:::Enil) => + if Int.eq n2 Int.zero + then Eop (Ocmp (Cmasknotzero m)) (t1:::Enil) + else Eop (Ocmp (default c n2)) (e1:::Enil) + | _, _ => + Eop (Ocmp (default c n2)) (e1:::Enil) + end. + +Nondetfunction comp (c: comparison) (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Ointconst n1) Enil, t2 => + compimm Ccompimm Int.cmp (swap_comparison c) t2 n1 + | t1, Eop (Ointconst n2) Enil => + compimm Ccompimm Int.cmp c t1 n2 + | Eop (Oshift s a) (t1:::Enil), t2 ?? arith_shift s => + Eop (Ocmp (Ccompshift (swap_comparison c) s a)) (t2:::t1:::Enil) + | t1, Eop (Oshift s a) (t2:::Enil) ?? arith_shift s => + Eop (Ocmp (Ccompshift c s a)) (t1:::t2:::Enil) + | _, _ => + Eop (Ocmp (Ccomp c)) (e1:::e2:::Enil) + end. + +Nondetfunction compu (c: comparison) (e1: expr) (e2: expr) := + match e1, e2 with + | Eop (Ointconst n1) Enil, t2 => + compimm Ccompuimm Int.cmpu (swap_comparison c) t2 n1 + | t1, Eop (Ointconst n2) Enil => + compimm Ccompuimm Int.cmpu c t1 n2 + | Eop (Oshift s a) (t1:::Enil), t2 ?? arith_shift s => + Eop (Ocmp (Ccompushift (swap_comparison c) s a)) (t2:::t1:::Enil) + | t1, Eop (Oshift s a) (t2:::Enil) ?? arith_shift s => + Eop (Ocmp (Ccompushift c s a)) (t1:::t2:::Enil) + | _, _ => + Eop (Ocmp (Ccompu c)) (e1:::e2:::Enil) + end. + +Definition compf (c: comparison) (e1: expr) (e2: expr) := + Eop (Ocmp (Ccompf c)) (e1:::e2:::Enil). + +Definition compfs (c: comparison) (e1: expr) (e2: expr) := + Eop (Ocmp (Ccompfs c)) (e1:::e2:::Enil). + +(** ** Floating-point conversions *) + +Definition intoffloat (e: expr) := Eop Ointoffloat (e ::: Enil). +Definition intuoffloat (e: expr) := Eop Ointuoffloat (e ::: Enil). + +Nondetfunction floatofintu (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Ofloatconst (Float.of_intu n)) Enil + | _ => Eop Ofloatofintu (e ::: Enil) + end. + +Nondetfunction floatofint (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Ofloatconst (Float.of_int n)) Enil + | _ => Eop Ofloatofint (e ::: Enil) + end. + +Definition intofsingle (e: expr) := Eop Ointofsingle (e ::: Enil). +Definition intuofsingle (e: expr) := Eop Ointuofsingle (e ::: Enil). + +Nondetfunction singleofint (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Osingleconst (Float32.of_int n)) Enil + | _ => Eop Osingleofint (e ::: Enil) + end. + +Nondetfunction singleofintu (e: expr) := + match e with + | Eop (Ointconst n) Enil => Eop (Osingleconst (Float32.of_intu n)) Enil + | _ => Eop Osingleofintu (e ::: Enil) + end. + +Definition singleoffloat (e: expr) := Eop Osingleoffloat (e ::: Enil). +Definition floatofsingle (e: expr) := Eop Ofloatofsingle (e ::: Enil). + +(** ** Selection *) + +Definition select (ty: typ) (cond: condition) (args: exprlist) (e1 e2: expr) := + if match ty with + | Tint => true + | Tlong => true + | Tfloat => true + | Tsingle => true + | _ => false + end + then Some (Eop (Osel cond ty) (e1 ::: e2 ::: args)) + else None. + +(** ** Recognition of addressing modes for load and store operations *) + +Nondetfunction addressing (chunk: memory_chunk) (e: expr) := + match e with + | Eop (Oaddrstack n) Enil => (Ainstack n, Enil) + | Eop (Oaddrsymbol id ofs) Enil => (Aglobal id ofs, Enil) + | Eop (Oaddlimm n) (e1:::Enil) => (Aindexed n, e1:::Enil) + | Eop (Oaddlshift Slsl a) (e1:::e2:::Enil) => (Aindexed2shift a, e1:::e2:::Enil) + | Eop (Oaddlext x a) (e1:::e2:::Enil) => (Aindexed2ext x a, e1:::e2:::Enil) + | Eop Oaddl (e1:::e2:::Enil) => (Aindexed2, e1:::e2:::Enil) + | _ => (Aindexed Int64.zero, e:::Enil) + end. + +(** ** Arguments of builtins *) + +Nondetfunction builtin_arg (e: expr) := + match e with + | Eop (Ointconst n) Enil => BA_int n + | Eop (Olongconst n) Enil => BA_long n + | Eop (Oaddrsymbol id ofs) Enil => BA_addrglobal id ofs + | Eop (Oaddrstack ofs) Enil => BA_addrstack ofs + | Eload chunk (Ainstack ofs) Enil => BA_loadstack chunk ofs + | Eop (Oaddlimm n) (e1:::Enil) => BA_addptr (BA e1) (BA_long n) + | _ => BA e + end. + +(** Platform-specific known builtins *) + +Definition platform_builtin (b: platform_builtin) (args: exprlist) : option expr := + None. diff --git a/aarch64/SelectOpproof.v b/aarch64/SelectOpproof.v new file mode 100644 index 00000000..b78a5ed8 --- /dev/null +++ b/aarch64/SelectOpproof.v @@ -0,0 +1,1070 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Correctness of instruction selection for operators *) + +Require Import Coqlib Zbits. +Require Import AST Integers Floats Values Memory Builtins Globalenvs. +Require Import Cminor Op CminorSel. +Require Import SelectOp. + +Local Open Scope cminorsel_scope. +Local Transparent Archi.ptr64. + +(** * Useful lemmas and tactics *) + +(** The following are trivial lemmas and custom tactics that help + perform backward (inversion) and forward reasoning over the evaluation + of operator applications. *) + +Ltac EvalOp := + eauto with evalexpr; + match goal with + | [ |- eval_expr _ _ _ _ _ _ _ ] => eapply eval_Eop; [EvalOp|try reflexivity; auto] + | [ |- eval_exprlist _ _ _ _ _ _ _ ] => econstructor; EvalOp + | _ => idtac + end. + +Ltac InvEval1 := + match goal with + | [ H: (eval_expr _ _ _ _ _ (Eop _ Enil) _) |- _ ] => + inv H; InvEval1 + | [ H: (eval_expr _ _ _ _ _ (Eop _ (_ ::: Enil)) _) |- _ ] => + inv H; InvEval1 + | [ H: (eval_expr _ _ _ _ _ (Eop _ (_ ::: _ ::: Enil)) _) |- _ ] => + inv H; InvEval1 + | [ H: (eval_exprlist _ _ _ _ _ Enil _) |- _ ] => + inv H; InvEval1 + | [ H: (eval_exprlist _ _ _ _ _ (_ ::: _) _) |- _ ] => + inv H; InvEval1 + | _ => + idtac + end. + +Ltac InvEval2 := + match goal with + | [ H: (eval_operation _ _ _ nil _ = Some _) |- _ ] => + simpl in H; inv H + | [ H: (eval_operation _ _ _ (_ :: nil) _ = Some _) |- _ ] => + simpl in H; FuncInv + | [ H: (eval_operation _ _ _ (_ :: _ :: nil) _ = Some _) |- _ ] => + simpl in H; FuncInv + | [ H: (eval_operation _ _ _ (_ :: _ :: _ :: nil) _ = Some _) |- _ ] => + simpl in H; FuncInv + | _ => + idtac + end. + +Ltac InvEval := InvEval1; InvEval2; InvEval2. + +Ltac TrivialExists := + match goal with + | [ |- exists v, _ /\ Val.lessdef ?a v ] => exists a; split; [EvalOp | auto] + end. + +(** * Correctness of the smart constructors *) + +Section CMCONSTR. + +Variable ge: genv. +Variable sp: val. +Variable e: env. +Variable m: mem. + +(** We now show that the code generated by "smart constructor" functions + such as [Selection.notint] behaves as expected. Continuing the + [notint] example, we show that if the expression [e] + evaluates to some integer value [Vint n], then [Selection.notint e] + evaluates to a value [Vint (Int.not n)] which is indeed the integer + negation of the value of [e]. + + All proofs follow a common pattern: +- Reasoning by case over the result of the classification functions + (such as [add_match] for integer addition), gathering additional + information on the shape of the argument expressions in the non-default + cases. +- Inversion of the evaluations of the arguments, exploiting the additional + information thus gathered. +- Equational reasoning over the arithmetic operations performed, + using the lemmas from the [Int] and [Float] modules. +- Construction of an evaluation derivation for the expression returned + by the smart constructor. +*) + +Definition unary_constructor_sound (cstr: expr -> expr) (sem: val -> val) : Prop := + forall le a x, + eval_expr ge sp e m le a x -> + exists v, eval_expr ge sp e m le (cstr a) v /\ Val.lessdef (sem x) v. + +Definition binary_constructor_sound (cstr: expr -> expr -> expr) (sem: val -> val -> val) : Prop := + forall le a x b y, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + exists v, eval_expr ge sp e m le (cstr a b) v /\ Val.lessdef (sem x y) v. + +(** ** Constants *) + +Theorem eval_addrsymbol: + forall le id ofs, + exists v, eval_expr ge sp e m le (addrsymbol id ofs) v /\ Val.lessdef (Genv.symbol_address ge id ofs) v. +Proof. + intros. unfold addrsymbol. TrivialExists. +Qed. + +Theorem eval_addrstack: + forall le ofs, + exists v, eval_expr ge sp e m le (addrstack ofs) v /\ Val.lessdef (Val.offset_ptr sp ofs) v. +Proof. + intros. unfold addrstack. TrivialExists. +Qed. + +(** ** Addition, opposite, subtraction *) + +Theorem eval_addimm: + forall n, unary_constructor_sound (addimm n) (fun x => Val.add x (Vint n)). +Proof. + red; unfold addimm; intros until x. + predSpec Int.eq Int.eq_spec n Int.zero. +- subst n. intros. exists x; split; auto. + destruct x; simpl; auto. rewrite Int.add_zero; auto. +- case (addimm_match a); intros; InvEval; simpl; TrivialExists; simpl. ++ rewrite Int.add_commut. auto. ++ subst x. rewrite Val.add_assoc. rewrite Int.add_commut. auto. +Qed. + +Theorem eval_add: binary_constructor_sound add Val.add. +Proof. + red; intros until y. + unfold add; case (add_match a b); intros; InvEval; subst. +- rewrite Val.add_commut. apply eval_addimm; auto. +- apply eval_addimm; auto. +- replace (Val.add (Val.add v1 (Vint n1)) (Val.add v0 (Vint n2))) + with (Val.add (Val.add v1 v0) (Val.add (Vint n1) (Vint n2))). + apply eval_addimm. EvalOp. + repeat rewrite Val.add_assoc. decEq. apply Val.add_permut. +- replace (Val.add (Val.add v1 (Vint n1)) y) + with (Val.add (Val.add v1 y) (Vint n1)). + apply eval_addimm. EvalOp. + repeat rewrite Val.add_assoc. decEq. apply Val.add_commut. +- rewrite <- Val.add_assoc. apply eval_addimm. EvalOp. +- rewrite Val.add_commut. TrivialExists. +- TrivialExists. +- rewrite Val.add_commut. TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_negint: unary_constructor_sound negint (fun v => Val.sub Vzero v). +Proof. + red; intros until x; unfold negint. case (negint_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_sub: binary_constructor_sound sub Val.sub. +Proof. + red; intros until y; unfold sub; case (sub_match a b); intros; InvEval; subst. +- rewrite Val.sub_add_opp. apply eval_addimm; auto. +- rewrite Val.sub_add_l. rewrite Val.sub_add_r. + rewrite Val.add_assoc. simpl. rewrite Int.add_commut. rewrite <- Int.sub_add_opp. + apply eval_addimm; EvalOp. +- rewrite Val.sub_add_l. apply eval_addimm; EvalOp. +- rewrite Val.sub_add_r. apply eval_addimm; EvalOp. +- TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +(** ** Immediate shifts *) + +Remark eval_shlimm_base: forall le a n x, + eval_expr ge sp e m le a x -> + Int.ltu n Int.iwordsize = true -> + eval_expr ge sp e m le (shlimm_base a n) (Val.shl x (Vint n)). +Proof. +Local Opaque mk_amount32. + unfold shlimm_base; intros; EvalOp. simpl. rewrite mk_amount32_eq by auto. auto. +Qed. + +Theorem eval_shlimm: + forall n, unary_constructor_sound (fun a => shlimm a n) + (fun x => Val.shl x (Vint n)). +Proof. + red; intros until x; unfold shlimm. + predSpec Int.eq Int.eq_spec n Int.zero; [| destruct (Int.ltu n Int.iwordsize) eqn:L]; simpl. +- intros; subst. exists x; split; auto. destruct x; simpl; auto. rewrite Int.shl_zero; auto. +- destruct (shlimm_match a); intros; InvEval; subst. ++ TrivialExists. simpl; rewrite L; auto. ++ destruct (Int.ltu (Int.add a n) Int.iwordsize) eqn:L2. +* econstructor; split. eapply eval_shlimm_base; eauto. + destruct v1; simpl; auto. rewrite a32_range; simpl. rewrite L, L2. + rewrite Int.shl_shl; auto using a32_range. +* econstructor; split; [|eauto]. apply eval_shlimm_base; auto. EvalOp. ++ TrivialExists. simpl. rewrite mk_amount32_eq; auto. ++ TrivialExists. simpl. rewrite mk_amount32_eq; auto. ++ destruct (Int.ltu (Int.add a n) Int.iwordsize) eqn:L2. +* TrivialExists. simpl. rewrite mk_amount32_eq by auto. + destruct (Val.zero_ext s v1); simpl; auto. + rewrite a32_range; simpl; rewrite L, L2. + rewrite Int.shl_shl; auto using a32_range. +* econstructor; split. eapply eval_shlimm_base; eauto. EvalOp; simpl; eauto. auto. ++ destruct (Int.ltu (Int.add a n) Int.iwordsize) eqn:L2. +* TrivialExists. simpl. rewrite mk_amount32_eq by auto. + destruct (Val.sign_ext s v1); simpl; auto. + rewrite a32_range; simpl; rewrite L, L2. + rewrite Int.shl_shl; auto using a32_range. +* econstructor; split. eapply eval_shlimm_base; eauto. EvalOp; simpl; eauto. auto. ++ econstructor; eauto using eval_shlimm_base. +- intros; TrivialExists. +Qed. + +Remark eval_shruimm_base: forall le a n x, + eval_expr ge sp e m le a x -> + Int.ltu n Int.iwordsize = true -> + eval_expr ge sp e m le (shruimm_base a n) (Val.shru x (Vint n)). +Proof. + unfold shruimm_base; intros; EvalOp. simpl. rewrite mk_amount32_eq by auto. auto. +Qed. + +Remark sub_shift_amount: + forall y z, + Int.ltu y Int.iwordsize = true -> Int.ltu z Int.iwordsize = true -> Int.unsigned y <= Int.unsigned z -> + Int.ltu (Int.sub z y) Int.iwordsize = true. +Proof. + intros. unfold Int.ltu; apply zlt_true. rewrite Int.unsigned_repr_wordsize. + apply Int.ltu_iwordsize_inv in H. apply Int.ltu_iwordsize_inv in H0. + unfold Int.sub; rewrite Int.unsigned_repr. omega. + generalize Int.wordsize_max_unsigned; omega. +Qed. + +Theorem eval_shruimm: + forall n, unary_constructor_sound (fun a => shruimm a n) + (fun x => Val.shru x (Vint n)). +Proof. +Local Opaque Int.zwordsize. + red; intros until x; unfold shruimm. + predSpec Int.eq Int.eq_spec n Int.zero; [| destruct (Int.ltu n Int.iwordsize) eqn:L]; simpl. +- intros; subst. exists x; split; auto. destruct x; simpl; auto. rewrite Int.shru_zero; auto. +- destruct (shruimm_match a); intros; InvEval; subst. ++ TrivialExists. simpl; rewrite L; auto. ++ destruct (Int.ltu n a) eqn:L2. +* assert (L3: Int.ltu (Int.sub a n) Int.iwordsize = true). + { apply sub_shift_amount; auto using a32_range. + apply Int.ltu_inv in L2. omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount32_eq, L3, a32_range by auto. + simpl. rewrite L. rewrite Int.shru_shl, L2 by auto using a32_range. auto. +* assert (L3: Int.ltu (Int.sub n a) Int.iwordsize = true). + { apply sub_shift_amount; auto using a32_range. + unfold Int.ltu in L2. destruct zlt in L2; discriminate || omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount32_eq, L3, a32_range by auto. + simpl. rewrite L. rewrite Int.shru_shl, L2 by auto using a32_range. auto. ++ destruct (Int.ltu (Int.add a n) Int.iwordsize) eqn:L2. +* econstructor; split. eapply eval_shruimm_base; eauto. + destruct v1; simpl; auto. rewrite a32_range; simpl. rewrite L, L2. + rewrite Int.shru_shru; auto using a32_range. +* econstructor; split; [|eauto]. apply eval_shruimm_base; auto. EvalOp. ++ destruct (zlt (Int.unsigned n) s). +* econstructor; split. EvalOp. rewrite mk_amount32_eq by auto. + destruct v1; simpl; auto. rewrite ! L; simpl. + set (s' := s - Int.unsigned n). + replace s with (s' + Int.unsigned n) by (unfold s'; omega). + rewrite Int.shru_zero_ext. auto. unfold s'; omega. +* econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite ! L; simpl. + rewrite Int.shru_zero_ext_0 by omega. auto. ++ econstructor; eauto using eval_shruimm_base. +- intros; TrivialExists. +Qed. + +Remark eval_shrimm_base: forall le a n x, + eval_expr ge sp e m le a x -> + Int.ltu n Int.iwordsize = true -> + eval_expr ge sp e m le (shrimm_base a n) (Val.shr x (Vint n)). +Proof. + unfold shrimm_base; intros; EvalOp. simpl. rewrite mk_amount32_eq by auto. auto. +Qed. + +Theorem eval_shrimm: + forall n, unary_constructor_sound (fun a => shrimm a n) + (fun x => Val.shr x (Vint n)). +Proof. + red; intros until x; unfold shrimm. + predSpec Int.eq Int.eq_spec n Int.zero; [| destruct (Int.ltu n Int.iwordsize) eqn:L]; simpl. +- intros; subst. exists x; split; auto. destruct x; simpl; auto. rewrite Int.shr_zero; auto. +- destruct (shrimm_match a); intros; InvEval; subst. ++ TrivialExists. simpl; rewrite L; auto. ++ destruct (Int.ltu n a) eqn:L2. +* assert (L3: Int.ltu (Int.sub a n) Int.iwordsize = true). + { apply sub_shift_amount; auto using a32_range. + apply Int.ltu_inv in L2. omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount32_eq, L3, a32_range by auto. + simpl. rewrite L. rewrite Int.shr_shl, L2 by auto using a32_range. auto. +* assert (L3: Int.ltu (Int.sub n a) Int.iwordsize = true). + { apply sub_shift_amount; auto using a32_range. + unfold Int.ltu in L2. destruct zlt in L2; discriminate || omega. } + econstructor; split. EvalOp. + destruct v1; simpl; auto. rewrite mk_amount32_eq, L3, a32_range by auto. + simpl. rewrite L. rewrite Int.shr_shl, L2 by auto using a32_range. auto. ++ destruct (Int.ltu (Int.add a n) Int.iwordsize) eqn:L2. +* econstructor; split. eapply eval_shrimm_base; eauto. + destruct v1; simpl; auto. rewrite a32_range; simpl. rewrite L, L2. + rewrite Int.shr_shr; auto using a32_range. +* econstructor; split; [|eauto]. apply eval_shrimm_base; auto. EvalOp. ++ destruct (zlt (Int.unsigned n) s && zlt s Int.zwordsize) eqn:E. +* InvBooleans. econstructor; split. EvalOp. rewrite mk_amount32_eq by auto. + destruct v1; simpl; auto. rewrite ! L; simpl. + set (s' := s - Int.unsigned n). + replace s with (s' + Int.unsigned n) by (unfold s'; omega). + rewrite Int.shr_sign_ext. auto. unfold s'; omega. unfold s'; omega. +* econstructor; split; [|eauto]. apply eval_shrimm_base; auto. EvalOp. ++ econstructor; eauto using eval_shrimm_base. +- intros; TrivialExists. +Qed. + +(** ** Multiplication *) + +Lemma eval_mulimm_base: + forall n, unary_constructor_sound (mulimm_base n) (fun x => Val.mul x (Vint n)). +Proof. + intros; red; intros; unfold mulimm_base. + assert (DFL: exists v, eval_expr ge sp e m le (Eop Omul (Eop (Ointconst n) Enil ::: a ::: Enil)) v /\ Val.lessdef (Val.mul x (Vint n)) v). + { rewrite Val.mul_commut; TrivialExists. } + generalize (Int.one_bits_decomp n); generalize (Int.one_bits_range n); + destruct (Int.one_bits n) as [ | i [ | j []]]; intros P Q. +- apply DFL. +- replace (Val.mul x (Vint n)) with (Val.shl x (Vint i)). + apply eval_shlimm; auto. + simpl in Q. rewrite <- Val.shl_mul, Q, Int.add_zero. simpl. rewrite P by auto with coqlib. auto. +- exploit (eval_shlimm i (x :: le) (Eletvar 0) x). constructor; auto. intros [v1 [A1 B1]]. + exploit (eval_shlimm j (x :: le) (Eletvar 0) x). constructor; auto. intros [v2 [A2 B2]]. + exploit (eval_add (x :: le)). eexact A1. eexact A2. intros [v [A B]]. + exists v; split. econstructor; eauto. + simpl in Q. rewrite Q, Int.add_zero. + replace (Vint (Int.add (Int.shl Int.one i) (Int.shl Int.one j))) + with (Val.add (Val.shl Vone (Vint i)) (Val.shl Vone (Vint j))). + rewrite Val.mul_add_distr_r. + repeat rewrite Val.shl_mul. eapply Val.lessdef_trans; [|eauto]. apply Val.add_lessdef; auto. + simpl. repeat rewrite P by auto with coqlib. auto. +- apply DFL. +Qed. + +Theorem eval_mulimm: + forall n, unary_constructor_sound (mulimm n) (fun x => Val.mul x (Vint n)). +Proof. + intros; red; intros until x; unfold mulimm. + predSpec Int.eq Int.eq_spec n Int.zero. + intros. exists (Vint Int.zero); split. EvalOp. + destruct x; simpl; auto. subst n. rewrite Int.mul_zero. auto. + predSpec Int.eq Int.eq_spec n Int.one. + intros. exists x; split; auto. + destruct x; simpl; auto. subst n. rewrite Int.mul_one. auto. + case (mulimm_match a); intros; InvEval; subst. +- TrivialExists. simpl. rewrite Int.mul_commut; auto. +- rewrite Val.mul_add_distr_l. + exploit eval_mulimm_base; eauto. instantiate (1 := n). intros [v' [A1 B1]]. + exploit (eval_addimm (Int.mul n n2) le (mulimm_base n t2) v'). auto. intros [v'' [A2 B2]]. + exists v''; split; auto. eapply Val.lessdef_trans. eapply Val.add_lessdef; eauto. + rewrite Val.mul_commut; auto. +- apply eval_mulimm_base; auto. +Qed. + +Theorem eval_mul: binary_constructor_sound mul Val.mul. +Proof. + red; intros until y; unfold mul; case (mul_match a b); intros; InvEval; subst. +- rewrite Val.mul_commut. apply eval_mulimm; auto. +- apply eval_mulimm; auto. +- TrivialExists. +Qed. + +Theorem eval_mulhs: binary_constructor_sound mulhs Val.mulhs. +Proof. + unfold mulhs; red; intros. econstructor; split. EvalOp. + unfold eval_shiftl, eval_extend. rewrite ! mk_amount64_eq by auto. + destruct x; simpl; auto. destruct y; simpl; auto. + change (Int.ltu Int.zero Int64.iwordsize') with true; simpl. + rewrite ! Int64.shl'_zero. + change (Int.ltu (Int.repr 32) Int64.iwordsize') with true; simpl. + apply Val.lessdef_same. f_equal. + transitivity (Int.repr (Z.shiftr (Int.signed i * Int.signed i0) 32)). + unfold Int.mulhs; f_equal. rewrite Zshiftr_div_two_p by omega. reflexivity. + apply Int.same_bits_eq; intros n N. + change Int.zwordsize with 32 in *. + assert (N1: 0 <= n < 64) by omega. + rewrite Int64.bits_loword by auto. + rewrite Int64.bits_shr' by auto. + change (Int.unsigned (Int.repr 32)) with 32. change Int64.zwordsize with 64. + rewrite zlt_true by omega. + rewrite Int.testbit_repr by auto. + unfold Int64.mul. rewrite Int64.testbit_repr by (change Int64.zwordsize with 64; omega). + transitivity (Z.testbit (Int.signed i * Int.signed i0) (n + 32)). + rewrite Z.shiftr_spec by omega. auto. + apply Int64.same_bits_eqm. apply Int64.eqm_mult; apply Int64.eqm_unsigned_repr. + change Int64.zwordsize with 64; omega. +Qed. + +Theorem eval_mulhu: binary_constructor_sound mulhu Val.mulhu. +Proof. + unfold mulhu; red; intros. econstructor; split. EvalOp. + unfold eval_shiftl, eval_extend. rewrite ! mk_amount64_eq by auto. + destruct x; simpl; auto. destruct y; simpl; auto. + change (Int.ltu Int.zero Int64.iwordsize') with true; simpl. + rewrite ! Int64.shl'_zero. + change (Int.ltu (Int.repr 32) Int64.iwordsize') with true; simpl. + apply Val.lessdef_same. f_equal. + transitivity (Int.repr (Z.shiftr (Int.unsigned i * Int.unsigned i0) 32)). + unfold Int.mulhu; f_equal. rewrite Zshiftr_div_two_p by omega. reflexivity. + apply Int.same_bits_eq; intros n N. + change Int.zwordsize with 32 in *. + assert (N1: 0 <= n < 64) by omega. + rewrite Int64.bits_loword by auto. + rewrite Int64.bits_shru' by auto. + change (Int.unsigned (Int.repr 32)) with 32. change Int64.zwordsize with 64. + rewrite zlt_true by omega. + rewrite Int.testbit_repr by auto. + unfold Int64.mul. rewrite Int64.testbit_repr by (change Int64.zwordsize with 64; omega). + transitivity (Z.testbit (Int.unsigned i * Int.unsigned i0) (n + 32)). + rewrite Z.shiftr_spec by omega. auto. + apply Int64.same_bits_eqm. apply Int64.eqm_mult; apply Int64.eqm_unsigned_repr. + change Int64.zwordsize with 64; omega. +Qed. + +(** Integer conversions *) + +Theorem eval_zero_ext: + forall sz, 0 <= sz -> unary_constructor_sound (zero_ext sz) (Val.zero_ext sz). +Proof. + intros; red; intros until x; unfold zero_ext; case (zero_ext_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +- destruct (zlt (Int.unsigned a0) sz). ++ econstructor; split. EvalOp. destruct v1; simpl; auto. rewrite a32_range; simpl. + apply Val.lessdef_same. f_equal. rewrite Int.shl_zero_ext by omega. f_equal. omega. ++ TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_sign_ext: + forall sz, 0 < sz -> unary_constructor_sound (sign_ext sz) (Val.sign_ext sz). +Proof. + intros; red; intros until x; unfold sign_ext; case (sign_ext_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +- destruct (zlt (Int.unsigned a0) sz). ++ econstructor; split. EvalOp. destruct v1; simpl; auto. rewrite a32_range; simpl. + apply Val.lessdef_same. f_equal. rewrite Int.shl_sign_ext by omega. f_equal. omega. ++ TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_cast8signed: unary_constructor_sound cast8signed (Val.sign_ext 8). +Proof. + apply eval_sign_ext; omega. +Qed. + +Theorem eval_cast8unsigned: unary_constructor_sound cast8unsigned (Val.zero_ext 8). +Proof. + apply eval_zero_ext; omega. +Qed. + +Theorem eval_cast16signed: unary_constructor_sound cast16signed (Val.sign_ext 16). +Proof. + apply eval_sign_ext; omega. +Qed. + +Theorem eval_cast16unsigned: unary_constructor_sound cast16unsigned (Val.zero_ext 16). +Proof. + apply eval_zero_ext; omega. +Qed. + +(** Bitwise not, and, or, xor *) + +Theorem eval_notint: unary_constructor_sound notint Val.notint. +Proof. + assert (INV: forall v, Val.lessdef (Val.notint (Val.notint v)) v). + { destruct v; auto. simpl; rewrite Int.not_involutive; auto. } + unfold notint; red; intros until x; case (notint_match a); intros; InvEval; subst. +- TrivialExists. +- TrivialExists. +- exists v1; auto. +- exists (eval_shift s v1 a0); split; auto. EvalOp. +- econstructor; split. EvalOp. + destruct v1; simpl; auto; destruct v0; simpl; auto. + rewrite Int.not_and_or_not, Int.not_involutive, Int.or_commut. auto. +- econstructor; split. EvalOp. + destruct v1; simpl; auto; destruct v0; simpl; auto. + rewrite Int.not_or_and_not, Int.not_involutive, Int.and_commut. auto. +- econstructor; split. EvalOp. + rewrite ! Val.not_xor, Val.xor_assoc; auto. +- econstructor; split. EvalOp. + destruct v1; simpl; auto; destruct v0; simpl; auto. + unfold Int.not; rewrite ! Int.xor_assoc, Int.xor_idem, Int.xor_zero. auto. +- TrivialExists. +Qed. + +Lemma eval_andimm_base: + forall n, unary_constructor_sound (andimm_base n) (fun x => Val.and x (Vint n)). +Proof. + intros; red; intros. unfold andimm_base. + predSpec Int.eq Int.eq_spec n Int.zero. + exists (Vint Int.zero); split. EvalOp. + destruct x; simpl; auto. subst n. rewrite Int.and_zero. auto. + predSpec Int.eq Int.eq_spec n Int.mone. + exists x; split; auto. + subst. destruct x; simpl; auto. rewrite Int.and_mone; auto. + destruct (Z_is_power2m1 (Int.unsigned n)) as [s|] eqn:P. + assert (0 <= s) by (eapply Z_is_power2m1_nonneg; eauto). + rewrite <- (Int.repr_unsigned n), (Z_is_power2m1_sound _ _ P), <- Val.zero_ext_and by auto. + apply eval_zero_ext; auto. + TrivialExists. +Qed. + +Theorem eval_andimm: + forall n, unary_constructor_sound (andimm n) (fun x => Val.and x (Vint n)). +Proof. + intros; red; intros until x. unfold andimm. + case (andimm_match a); intros; InvEval; subst. +- rewrite Int.and_commut; TrivialExists. +- rewrite Val.and_assoc, Int.and_commut. apply eval_andimm_base; auto. +- destruct (zle 0 s). ++ rewrite Val.zero_ext_and, Val.and_assoc, Int.and_commut by auto. + apply eval_andimm_base; auto. ++ apply eval_andimm_base. EvalOp. +- apply eval_andimm_base; auto. +Qed. + +Theorem eval_and: binary_constructor_sound and Val.and. +Proof. + red; intros until y; unfold and; case (and_match a b); intros; InvEval; subst. +- rewrite Val.and_commut; apply eval_andimm; auto. +- apply eval_andimm; auto. +- rewrite Val.and_commut; TrivialExists. +- TrivialExists. +- rewrite Val.and_commut; TrivialExists. +- TrivialExists. +- rewrite Val.and_commut; TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_orimm: + forall n, unary_constructor_sound (orimm n) (fun x => Val.or x (Vint n)). +Proof. + intros; red; intros until x. unfold orimm. + predSpec Int.eq Int.eq_spec n Int.zero. + intros. subst. exists x; split; auto. + destruct x; simpl; auto. rewrite Int.or_zero; auto. + predSpec Int.eq Int.eq_spec n Int.mone. + intros. exists (Vint Int.mone); split. EvalOp. + destruct x; simpl; auto. subst n. rewrite Int.or_mone. auto. + destruct (orimm_match a); intros; InvEval; subst. +- rewrite Int.or_commut; TrivialExists. +- rewrite Val.or_assoc, Int.or_commut; TrivialExists. +- TrivialExists. +Qed. + +Remark eval_same_expr: + forall a1 a2 le v1 v2, + same_expr_pure a1 a2 = true -> + eval_expr ge sp e m le a1 v1 -> + eval_expr ge sp e m le a2 v2 -> + a1 = a2 /\ v1 = v2. +Proof. + intros. destruct a1; try discriminate. destruct a2; try discriminate. + simpl in H; destruct (ident_eq i i0); inv H. + split. auto. inv H0; inv H1; congruence. +Qed. + +Theorem eval_or: binary_constructor_sound or Val.or. +Proof. + red; intros until y; unfold or; case (or_match a b); intros; InvEval; subst. +- rewrite Val.or_commut. apply eval_orimm; auto. +- apply eval_orimm; auto. +- rewrite Val.or_commut; TrivialExists. +- TrivialExists. +- rewrite Val.or_commut; TrivialExists. +- TrivialExists. +- (* shl - shru *) + destruct (Int.eq (Int.add a1 a2) Int.iwordsize && same_expr_pure t1 t2) eqn:?. ++ InvBooleans. apply Int.same_if_eq in H. + exploit eval_same_expr; eauto. intros [EQ1 EQ2]. subst. + econstructor; split. EvalOp. + destruct v0; simpl; auto. rewrite ! a32_range. simpl. rewrite <- Int.or_ror; auto using a32_range. ++ TrivialExists. +- (* shru - shl *) + destruct (Int.eq (Int.add a2 a1) Int.iwordsize && same_expr_pure t1 t2) eqn:?. ++ InvBooleans. apply Int.same_if_eq in H. + exploit eval_same_expr; eauto. intros [EQ1 EQ2]. subst. + econstructor; split. EvalOp. + destruct v0; simpl; auto. rewrite ! a32_range. simpl. + rewrite Int.or_commut, <- Int.or_ror; auto using a32_range. ++ TrivialExists. +- rewrite Val.or_commut; TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +Lemma eval_xorimm_base: + forall n, unary_constructor_sound (xorimm_base n) (fun x => Val.xor x (Vint n)). +Proof. + intros; red; intros. unfold xorimm_base. + predSpec Int.eq Int.eq_spec n Int.zero. + intros. exists x; split. auto. + destruct x; simpl; auto. subst n. rewrite Int.xor_zero. auto. + predSpec Int.eq Int.eq_spec n Int.mone. + subst n. rewrite <- Val.not_xor. apply eval_notint; auto. + TrivialExists. +Qed. + +Theorem eval_xorimm: + forall n, unary_constructor_sound (xorimm n) (fun x => Val.xor x (Vint n)). +Proof. + intros; red; intros until x. unfold xorimm. + destruct (xorimm_match a); intros; InvEval; subst. +- rewrite Int.xor_commut; TrivialExists. +- rewrite Val.xor_assoc; simpl. rewrite (Int.xor_commut n2). apply eval_xorimm_base; auto. +- apply eval_xorimm_base; auto. +Qed. + +Theorem eval_xor: binary_constructor_sound xor Val.xor. +Proof. + red; intros until y; unfold xor; case (xor_match a b); intros; InvEval; subst. +- rewrite Val.xor_commut; apply eval_xorimm; auto. +- apply eval_xorimm; auto. +- rewrite Val.xor_commut; TrivialExists. +- TrivialExists. +- rewrite Val.xor_commut; TrivialExists. +- TrivialExists. +- rewrite Val.xor_commut; TrivialExists. +- TrivialExists. +- TrivialExists. +Qed. + +(** ** Integer division and modulus *) + +Theorem eval_divs_base: + forall le a b x y z, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + Val.divs x y = Some z -> + exists v, eval_expr ge sp e m le (divs_base a b) v /\ Val.lessdef z v. +Proof. + intros; unfold divs_base; TrivialExists. +Qed. + +Theorem eval_mods_base: + forall le a b x y z, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + Val.mods x y = Some z -> + exists v, eval_expr ge sp e m le (mods_base a b) v /\ Val.lessdef z v. +Proof. + intros; unfold mods_base, mod_aux. + exploit Val.mods_divs; eauto. intros (q & A & B). subst z. + TrivialExists. repeat (econstructor; eauto with evalexpr). exact A. +Qed. + +Theorem eval_divu_base: + forall le a x b y z, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + Val.divu x y = Some z -> + exists v, eval_expr ge sp e m le (divu_base a b) v /\ Val.lessdef z v. +Proof. + intros; unfold divu_base; TrivialExists. +Qed. + +Theorem eval_modu_base: + forall le a x b y z, + eval_expr ge sp e m le a x -> + eval_expr ge sp e m le b y -> + Val.modu x y = Some z -> + exists v, eval_expr ge sp e m le (modu_base a b) v /\ Val.lessdef z v. +Proof. + intros; unfold modu_base, mod_aux. + exploit Val.modu_divu; eauto. intros (q & A & B). subst z. + TrivialExists. repeat (econstructor; eauto with evalexpr). exact A. +Qed. + +Theorem eval_shrximm: + forall le a n x z, + eval_expr ge sp e m le a x -> + Val.shrx x (Vint n) = Some z -> + exists v, eval_expr ge sp e m le (shrximm a n) v /\ Val.lessdef z v. +Proof. + intros; unfold shrximm. + predSpec Int.eq Int.eq_spec n Int.zero. +- subst n. exists x; split; auto. + destruct x; simpl in H0; try discriminate. + change (Int.ltu Int.zero (Int.repr 31)) with true in H0; inv H0. + rewrite Int.shrx_zero by (compute; auto). auto. +- TrivialExists. +Qed. + +(** General shifts *) + +Theorem eval_shl: binary_constructor_sound shl Val.shl. +Proof. + red; intros until y; unfold shl; case (shl_match b); intros. + InvEval. apply eval_shlimm; auto. + TrivialExists. +Qed. + +Theorem eval_shr: binary_constructor_sound shr Val.shr. +Proof. + red; intros until y; unfold shr; case (shr_match b); intros. + InvEval. apply eval_shrimm; auto. + TrivialExists. +Qed. + +Theorem eval_shru: binary_constructor_sound shru Val.shru. +Proof. + red; intros until y; unfold shru; case (shru_match b); intros. + InvEval. apply eval_shruimm; auto. + TrivialExists. +Qed. + +(** Floating-point operations *) + +Theorem eval_negf: unary_constructor_sound negf Val.negf. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_absf: unary_constructor_sound absf Val.absf. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_addf: binary_constructor_sound addf Val.addf. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_subf: binary_constructor_sound subf Val.subf. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_mulf: binary_constructor_sound mulf Val.mulf. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_negfs: unary_constructor_sound negfs Val.negfs. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_absfs: unary_constructor_sound absfs Val.absfs. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_addfs: binary_constructor_sound addfs Val.addfs. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_subfs: binary_constructor_sound subfs Val.subfs. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_mulfs: binary_constructor_sound mulfs Val.mulfs. +Proof. + red; intros; TrivialExists. +Qed. + +Section COMP_IMM. + +Variable default: comparison -> int -> condition. +Variable intsem: comparison -> int -> int -> bool. +Variable sem: comparison -> val -> val -> val. + +Hypothesis sem_int: forall c x y, sem c (Vint x) (Vint y) = Val.of_bool (intsem c x y). +Hypothesis sem_undef: forall c v, sem c Vundef v = Vundef. +Hypothesis sem_eq: forall x y, sem Ceq (Vint x) (Vint y) = Val.of_bool (Int.eq x y). +Hypothesis sem_ne: forall x y, sem Cne (Vint x) (Vint y) = Val.of_bool (negb (Int.eq x y)). +Hypothesis sem_default: forall c v n, sem c v (Vint n) = Val.of_optbool (eval_condition (default c n) (v :: nil) m). + +Lemma eval_compimm: + forall le c a n2 x, + eval_expr ge sp e m le a x -> + exists v, eval_expr ge sp e m le (compimm default intsem c a n2) v + /\ Val.lessdef (sem c x (Vint n2)) v. +Proof. + intros until x. + unfold compimm; case (compimm_match c a); intros; InvEval; subst. +- (* constant *) + rewrite sem_int. TrivialExists. simpl. destruct (intsem c0 n1 n2); auto. +- (* eq cmp *) + inv H. simpl in H5. inv H5. + destruct (Int.eq_dec n2 Int.zero). subst n2. TrivialExists. + simpl. rewrite eval_negate_condition. + destruct (eval_condition c0 vl m); simpl. + unfold Vtrue, Vfalse. destruct b; simpl; rewrite sem_eq; auto. + rewrite sem_undef; auto. + destruct (Int.eq_dec n2 Int.one). subst n2. TrivialExists. + simpl. destruct (eval_condition c0 vl m); simpl. + unfold Vtrue, Vfalse. destruct b; simpl; rewrite sem_eq; auto. + rewrite sem_undef; auto. + exists (Vint Int.zero); split. EvalOp. + destruct (eval_condition c0 vl m); simpl. + unfold Vtrue, Vfalse. destruct b; rewrite sem_eq; rewrite Int.eq_false; auto. + rewrite sem_undef; auto. +- (* ne cmp *) + inv H. simpl in H5. inv H5. + destruct (Int.eq_dec n2 Int.zero). subst n2. TrivialExists. + simpl. destruct (eval_condition c0 vl m); simpl. + unfold Vtrue, Vfalse. destruct b; simpl; rewrite sem_ne; auto. + rewrite sem_undef; auto. + destruct (Int.eq_dec n2 Int.one). subst n2. TrivialExists. + simpl. rewrite eval_negate_condition. destruct (eval_condition c0 vl m); simpl. + unfold Vtrue, Vfalse. destruct b; simpl; rewrite sem_ne; auto. + rewrite sem_undef; auto. + exists (Vint Int.one); split. EvalOp. + destruct (eval_condition c0 vl m); simpl. + unfold Vtrue, Vfalse. destruct b; rewrite sem_ne; rewrite Int.eq_false; auto. + rewrite sem_undef; auto. +- (* mask zero *) + predSpec Int.eq Int.eq_spec n2 Int.zero. ++ subst n2. econstructor; split. EvalOp. simpl. + destruct v1; simpl; try (rewrite sem_undef; auto). + rewrite sem_eq. destruct (Int.eq (Int.and i m0) Int.zero); auto. ++ TrivialExists. simpl. rewrite sem_default. auto. +- (* mask not zero *) + predSpec Int.eq Int.eq_spec n2 Int.zero. ++ subst n2. econstructor; split. EvalOp. simpl. + destruct v1; simpl; try (rewrite sem_undef; auto). + rewrite sem_ne. destruct (Int.eq (Int.and i m0) Int.zero); auto. ++ TrivialExists. simpl. rewrite sem_default. auto. +- (* default *) + TrivialExists. simpl. rewrite sem_default. auto. +Qed. + +Hypothesis sem_swap: + forall c x y, sem (swap_comparison c) x y = sem c y x. + +Lemma eval_compimm_swap: + forall le c a n2 x, + eval_expr ge sp e m le a x -> + exists v, eval_expr ge sp e m le (compimm default intsem (swap_comparison c) a n2) v + /\ Val.lessdef (sem c (Vint n2) x) v. +Proof. + intros. rewrite <- sem_swap. eapply eval_compimm; eauto. +Qed. + +End COMP_IMM. + +Theorem eval_comp: + forall c, binary_constructor_sound (comp c) (Val.cmp c). +Proof. + intros; red; intros until y. unfold comp; case (comp_match a b); intros; InvEval; subst. +- eapply eval_compimm_swap; eauto. + intros. unfold Val.cmp. rewrite Val.swap_cmp_bool; auto. +- eapply eval_compimm; eauto. +- TrivialExists. simpl. rewrite Val.swap_cmp_bool. auto. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_compu: + forall c, binary_constructor_sound (compu c) (Val.cmpu (Mem.valid_pointer m) c). +Proof. + intros; red; intros until y. unfold compu; case (compu_match a b); intros; InvEval; subst. +- eapply eval_compimm_swap; eauto. + intros. unfold Val.cmpu. rewrite Val.swap_cmpu_bool; auto. +- eapply eval_compimm; eauto. +- TrivialExists. simpl. rewrite Val.swap_cmpu_bool. auto. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_compf: + forall c, binary_constructor_sound (compf c) (Val.cmpf c). +Proof. + intros; red; intros. unfold compf. TrivialExists. +Qed. + +Theorem eval_compfs: + forall c, binary_constructor_sound (compfs c) (Val.cmpfs c). +Proof. + intros; red; intros. unfold compfs. TrivialExists. +Qed. + +(** Floating-point conversions *) + +Theorem eval_singleoffloat: unary_constructor_sound singleoffloat Val.singleoffloat. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_floatofsingle: unary_constructor_sound floatofsingle Val.floatofsingle. +Proof. + red; intros; TrivialExists. +Qed. + +Theorem eval_intoffloat: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.intoffloat x = Some y -> + exists v, eval_expr ge sp e m le (intoffloat a) v /\ Val.lessdef y v. +Proof. + intros; TrivialExists. +Qed. + +Theorem eval_floatofint: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.floatofint x = Some y -> + exists v, eval_expr ge sp e m le (floatofint a) v /\ Val.lessdef y v. +Proof. + intros until y; unfold floatofint. case (floatofint_match a); intros; InvEval. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_intuoffloat: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.intuoffloat x = Some y -> + exists v, eval_expr ge sp e m le (intuoffloat a) v /\ Val.lessdef y v. +Proof. + intros; TrivialExists. +Qed. + +Theorem eval_floatofintu: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.floatofintu x = Some y -> + exists v, eval_expr ge sp e m le (floatofintu a) v /\ Val.lessdef y v. +Proof. + intros until y; unfold floatofintu. case (floatofintu_match a); intros; InvEval. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_intofsingle: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.intofsingle x = Some y -> + exists v, eval_expr ge sp e m le (intofsingle a) v /\ Val.lessdef y v. +Proof. + intros; TrivialExists. +Qed. + +Theorem eval_singleofint: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.singleofint x = Some y -> + exists v, eval_expr ge sp e m le (singleofint a) v /\ Val.lessdef y v. +Proof. + intros until y; unfold singleofint. case (singleofint_match a); intros; InvEval. +- TrivialExists. +- TrivialExists. +Qed. + +Theorem eval_intuofsingle: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.intuofsingle x = Some y -> + exists v, eval_expr ge sp e m le (intuofsingle a) v /\ Val.lessdef y v. +Proof. + intros; TrivialExists. +Qed. + +Theorem eval_singleofintu: + forall le a x y, + eval_expr ge sp e m le a x -> + Val.singleofintu x = Some y -> + exists v, eval_expr ge sp e m le (singleofintu a) v /\ Val.lessdef y v. +Proof. + intros until y; unfold singleofintu. case (singleofintu_match a); intros; InvEval. +- TrivialExists. +- TrivialExists. +Qed. + +(** Selection *) + +Theorem eval_select: + forall le ty cond al vl a1 v1 a2 v2 a b, + select ty cond al a1 a2 = Some a -> + eval_exprlist ge sp e m le al vl -> + eval_expr ge sp e m le a1 v1 -> + eval_expr ge sp e m le a2 v2 -> + eval_condition cond vl m = Some b -> + exists v, + eval_expr ge sp e m le a v + /\ Val.lessdef (Val.select (Some b) v1 v2 ty) v. +Proof. + unfold select; intros. + destruct (match ty with Tint | Tlong | Tfloat | Tsingle => true | _ => false end); inv H. + rewrite <- H3; TrivialExists. +Qed. + +(** Addressing modes *) + +Theorem eval_addressing: + forall le chunk a v b ofs, + eval_expr ge sp e m le a v -> + v = Vptr b ofs -> + match addressing chunk a with (mode, args) => + exists vl, + eval_exprlist ge sp e m le args vl /\ + eval_addressing ge sp mode vl = Some v + end. +Proof. + intros until v. unfold addressing; case (addressing_match a); intros; InvEval. +- econstructor; split. EvalOp. simpl; auto. +- econstructor; split. EvalOp. simpl; auto. +- econstructor; split. EvalOp. simpl. + destruct v1; try discriminate. rewrite <- H; auto. +- econstructor; split. EvalOp. simpl. congruence. +- econstructor; split. EvalOp. simpl. congruence. +- econstructor; split. EvalOp. simpl. congruence. +- econstructor; split. EvalOp. simpl. rewrite H0. simpl. rewrite Ptrofs.add_zero; auto. +Qed. + +(** Builtins *) + +Theorem eval_builtin_arg: + forall a v, + eval_expr ge sp e m nil a v -> + CminorSel.eval_builtin_arg ge sp e m (builtin_arg a) v. +Proof. + intros until v. unfold builtin_arg; case (builtin_arg_match a); intros; InvEval. +- constructor. +- constructor. +- constructor. +- constructor. +- inv H. InvEval. simpl in H6. inv H6. constructor; auto. +- subst v. repeat constructor; auto. +- constructor; auto. +Qed. + +(** Platform-specific known builtins *) + +Theorem eval_platform_builtin: + forall bf al a vl v le, + platform_builtin bf al = Some a -> + eval_exprlist ge sp e m le al vl -> + platform_builtin_sem bf vl = Some v -> + exists v', eval_expr ge sp e m le a v' /\ Val.lessdef v v'. +Proof. + intros. discriminate. +Qed. + +End CMCONSTR. diff --git a/aarch64/Stacklayout.v b/aarch64/Stacklayout.v new file mode 100644 index 00000000..86ba9f45 --- /dev/null +++ b/aarch64/Stacklayout.v @@ -0,0 +1,140 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Machine- and ABI-dependent layout information for activation records. *) + +Require Import Coqlib. +Require Import AST Memory Separation. +Require Import Bounds. + +Local Open Scope sep_scope. + +(** The general shape of activation records is as follows, + from bottom (lowest offsets) to top: +- Space for outgoing arguments to function calls. +- Back link to parent frame +- Return address +- Saved values of callee-save registers used by the function. +- Local stack slots. +- Space for the stack-allocated data declared in Cminor. + +The stack pointer is kept 16-aligned. +*) + +Definition fe_ofs_arg := 0. + +Definition make_env (b: bounds) : frame_env := + let olink := align (4 * b.(bound_outgoing)) 8 in (* back link *) + let oretaddr := olink + 8 in (* return address *) + let ocs := oretaddr + 8 in (* callee-saves *) + let ol := align (size_callee_save_area b ocs) 8 in (* locals *) + let ostkdata := align (ol + 4 * b.(bound_local)) 8 in (* stack data *) + let sz := align (ostkdata + b.(bound_stack_data)) 16 in + {| fe_size := sz; + fe_ofs_link := olink; + fe_ofs_retaddr := oretaddr; + fe_ofs_local := ol; + fe_ofs_callee_save := ocs; + fe_stack_data := ostkdata; + fe_used_callee_save := b.(used_callee_save) |}. + +Lemma frame_env_separated: + forall b sp m P, + let fe := make_env b in + m |= range sp 0 (fe_stack_data fe) ** range sp (fe_stack_data fe + bound_stack_data b) (fe_size fe) ** P -> + m |= range sp (fe_ofs_local fe) (fe_ofs_local fe + 4 * bound_local b) + ** range sp fe_ofs_arg (fe_ofs_arg + 4 * bound_outgoing b) + ** range sp (fe_ofs_link fe) (fe_ofs_link fe + size_chunk Mptr) + ** range sp (fe_ofs_retaddr fe) (fe_ofs_retaddr fe + size_chunk Mptr) + ** range sp (fe_ofs_callee_save fe) (size_callee_save_area b (fe_ofs_callee_save fe)) + ** P. +Proof. +Local Opaque Z.add Z.mul sepconj range. + intros; simpl. + set (olink := align (4 * b.(bound_outgoing)) 8). + set (oretaddr := olink + 8). + set (ocs := oretaddr + 8). + set (ol := align (size_callee_save_area b ocs) 8). + set (ostkdata := align (ol + 4 * b.(bound_local)) 8). + change (size_chunk Mptr) with 8. + generalize b.(bound_local_pos) b.(bound_outgoing_pos) b.(bound_stack_data_pos); intros. + assert (0 <= 4 * b.(bound_outgoing)) by omega. + assert (4 * b.(bound_outgoing) <= olink) by (apply align_le; omega). + assert (olink + 8 <= oretaddr) by (unfold oretaddr; omega). + assert (oretaddr + 8 <= ocs) by (unfold ocs; omega). + assert (ocs <= size_callee_save_area b ocs) by (apply size_callee_save_area_incr). + assert (size_callee_save_area b ocs <= ol) by (apply align_le; omega). + assert (ol + 4 * b.(bound_local) <= ostkdata) by (apply align_le; omega). +(* Reorder as: + outgoing + back link + retaddr + callee-save + local *) + rewrite sep_swap12. + rewrite sep_swap23. + rewrite sep_swap34. + rewrite sep_swap45. +(* Apply range_split and range_split2 repeatedly *) + unfold fe_ofs_arg. + apply range_split_2. fold olink; omega. omega. + apply range_split. omega. + apply range_split. omega. + apply range_split_2. fold ol. omega. omega. + apply range_drop_right with ostkdata. omega. + eapply sep_drop2. eexact H. +Qed. + +Lemma frame_env_range: + forall b, + let fe := make_env b in + 0 <= fe_stack_data fe /\ fe_stack_data fe + bound_stack_data b <= fe_size fe. +Proof. + intros; simpl. + set (olink := align (4 * b.(bound_outgoing)) 8). + set (oretaddr := olink + 8). + set (ocs := oretaddr + 8). + set (ol := align (size_callee_save_area b ocs) 8). + set (ostkdata := align (ol + 4 * b.(bound_local)) 8). + generalize b.(bound_local_pos) b.(bound_outgoing_pos) b.(bound_stack_data_pos); intros. + assert (0 <= 4 * b.(bound_outgoing)) by omega. + assert (4 * b.(bound_outgoing) <= olink) by (apply align_le; omega). + assert (olink + 8 <= oretaddr) by (unfold oretaddr; omega). + assert (oretaddr + 8 <= ocs) by (unfold ocs; omega). + assert (ocs <= size_callee_save_area b ocs) by (apply size_callee_save_area_incr). + assert (size_callee_save_area b ocs <= ol) by (apply align_le; omega). + assert (ol + 4 * b.(bound_local) <= ostkdata) by (apply align_le; omega). + split. omega. apply align_le. omega. +Qed. + +Lemma frame_env_aligned: + forall b, + let fe := make_env b in + (8 | fe_ofs_arg) + /\ (8 | fe_ofs_local fe) + /\ (8 | fe_stack_data fe) + /\ (align_chunk Mptr | fe_ofs_link fe) + /\ (align_chunk Mptr | fe_ofs_retaddr fe). +Proof. + intros; simpl. + set (olink := align (4 * b.(bound_outgoing)) 8). + set (oretaddr := olink + 8). + set (ocs := oretaddr + 8). + set (ol := align (size_callee_save_area b ocs) 8). + set (ostkdata := align (ol + 4 * b.(bound_local)) 8). + change (align_chunk Mptr) with 8. + split. apply Z.divide_0_r. + split. apply align_divides; omega. + split. apply align_divides; omega. + split. apply align_divides; omega. + apply Z.divide_add_r. apply align_divides; omega. apply Z.divide_refl. +Qed. diff --git a/aarch64/TargetPrinter.ml b/aarch64/TargetPrinter.ml new file mode 100644 index 00000000..e54673dd --- /dev/null +++ b/aarch64/TargetPrinter.ml @@ -0,0 +1,592 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(* Printing AArch64 assembly code in asm syntax *) + +open Printf +open Camlcoq +open Sections +open AST +open Asm +open AisAnnot +open PrintAsmaux +open Fileinfo + +(* Recognition of FP numbers that are supported by the fmov #imm instructions: + "a normalized binary floating point encoding with 1 sign bit, + 4 bits of fraction and a 3-bit exponent" +*) + +let is_immediate_float64 bits = + let exp = (Int64.(to_int (shift_right_logical bits 52)) land 0x7FF) - 1023 in + let mant = Int64.logand bits 0xF_FFFF_FFFF_FFFFL in + exp >= -3 && exp <= 4 && Int64.logand mant 0xF_0000_0000_0000L = mant + +let is_immediate_float32 bits = + let exp = (Int32.(to_int (shift_right_logical bits 23)) land 0xFF) - 127 in + let mant = Int32.logand bits 0x7F_FFFFl in + exp >= -3 && exp <= 4 && Int32.logand mant 0x78_0000l = mant + +(* Module containing the printing functions *) + +module Target : TARGET = + struct + +(* Basic printing functions *) + + let comment = "//" + + let symbol = elf_symbol + let symbol_offset = elf_symbol_offset + let label = elf_label + + let print_label oc lbl = label oc (transl_label lbl) + + let intsz oc (sz, n) = + match sz with X -> coqint64 oc n | W -> coqint oc n + + let xreg_name = function + | X0 -> "x0" | X1 -> "x1" | X2 -> "x2" | X3 -> "x3" + | X4 -> "x4" | X5 -> "x5" | X6 -> "x6" | X7 -> "x7" + | X8 -> "x8" | X9 -> "x9" | X10 -> "x10" | X11 -> "x11" + | X12 -> "x12" | X13 -> "x13" | X14 -> "x14" | X15 -> "x15" + | X16 -> "x16" | X17 -> "x17" | X18 -> "x18" | X19 -> "x19" + | X20 -> "x20" | X21 -> "x21" | X22 -> "x22" | X23 -> "x23" + | X24 -> "x24" | X25 -> "x25" | X26 -> "x26" | X27 -> "x27" + | X28 -> "x28" | X29 -> "x29" | X30 -> "x30" + + let wreg_name = function + | X0 -> "w0" | X1 -> "w1" | X2 -> "w2" | X3 -> "w3" + | X4 -> "w4" | X5 -> "w5" | X6 -> "w6" | X7 -> "w7" + | X8 -> "w8" | X9 -> "w9" | X10 -> "w10" | X11 -> "w11" + | X12 -> "w12" | X13 -> "w13" | X14 -> "w14" | X15 -> "w15" + | X16 -> "w16" | X17 -> "w17" | X18 -> "w18" | X19 -> "w19" + | X20 -> "w20" | X21 -> "w21" | X22 -> "w22" | X23 -> "w23" + | X24 -> "w24" | X25 -> "w25" | X26 -> "w26" | X27 -> "w27" + | X28 -> "w28" | X29 -> "w29" | X30 -> "w30" + + let xreg0_name = function RR0 r -> xreg_name r | XZR -> "xzr" + let wreg0_name = function RR0 r -> wreg_name r | XZR -> "wzr" + + let xregsp_name = function RR1 r -> xreg_name r | XSP -> "sp" + let wregsp_name = function RR1 r -> wreg_name r | XSP -> "wsp" + + let dreg_name = function + | D0 -> "d0" | D1 -> "d1" | D2 -> "d2" | D3 -> "d3" + | D4 -> "d4" | D5 -> "d5" | D6 -> "d6" | D7 -> "d7" + | D8 -> "d8" | D9 -> "d9" | D10 -> "d10" | D11 -> "d11" + | D12 -> "d12" | D13 -> "d13" | D14 -> "d14" | D15 -> "d15" + | D16 -> "d16" | D17 -> "d17" | D18 -> "d18" | D19 -> "d19" + | D20 -> "d20" | D21 -> "d21" | D22 -> "d22" | D23 -> "d23" + | D24 -> "d24" | D25 -> "d25" | D26 -> "d26" | D27 -> "d27" + | D28 -> "d28" | D29 -> "d29" | D30 -> "d30" | D31 -> "d31" + + let sreg_name = function + | D0 -> "s0" | D1 -> "s1" | D2 -> "s2" | D3 -> "s3" + | D4 -> "s4" | D5 -> "s5" | D6 -> "s6" | D7 -> "s7" + | D8 -> "s8" | D9 -> "s9" | D10 -> "s10" | D11 -> "s11" + | D12 -> "s12" | D13 -> "s13" | D14 -> "s14" | D15 -> "s15" + | D16 -> "s16" | D17 -> "s17" | D18 -> "s18" | D19 -> "s19" + | D20 -> "s20" | D21 -> "s21" | D22 -> "s22" | D23 -> "s23" + | D24 -> "s24" | D25 -> "s25" | D26 -> "s26" | D27 -> "s27" + | D28 -> "s28" | D29 -> "s29" | D30 -> "s30" | D31 -> "s31" + + let xreg oc r = output_string oc (xreg_name r) + let wreg oc r = output_string oc (wreg_name r) + let ireg oc (sz, r) = + output_string oc (match sz with X -> xreg_name r | W -> wreg_name r) + + let xreg0 oc r = output_string oc (xreg0_name r) + let wreg0 oc r = output_string oc (wreg0_name r) + let ireg0 oc (sz, r) = + output_string oc (match sz with X -> xreg0_name r | W -> wreg0_name r) + + let xregsp oc r = output_string oc (xregsp_name r) + let iregsp oc (sz, r) = + output_string oc (match sz with X -> xregsp_name r | W -> wregsp_name r) + + let dreg oc r = output_string oc (dreg_name r) + let sreg oc r = output_string oc (sreg_name r) + let freg oc (sz, r) = + output_string oc (match sz with D -> dreg_name r | S -> sreg_name r) + + let preg_asm oc ty = function + | IR r -> if ty = Tint then wreg oc r else xreg oc r + | FR r -> if ty = Tsingle then sreg oc r else dreg oc r + | _ -> assert false + + let preg_annot = function + | IR r -> xreg_name r + | FR r -> dreg_name r + | _ -> assert false + +(* Names of sections *) + + let name_of_section = function + | Section_text -> ".text" + | Section_data i | Section_small_data i -> + if i then ".data" else common_section () + | Section_const i | Section_small_const i -> + if i || (not !Clflags.option_fcommon) then ".section .rodata" else "COMM" + | Section_string -> ".section .rodata" + | Section_literal -> ".section .rodata" + | Section_jumptable -> ".section .rodata" + | Section_debug_info _ -> ".section .debug_info,\"\",%progbits" + | Section_debug_loc -> ".section .debug_loc,\"\",%progbits" + | Section_debug_abbrev -> ".section .debug_abbrev,\"\",%progbits" + | Section_debug_line _ -> ".section .debug_line,\"\",%progbits" + | Section_debug_ranges -> ".section .debug_ranges,\"\",%progbits" + | Section_debug_str -> ".section .debug_str,\"MS\",%progbits,1" + | Section_user(s, wr, ex) -> + sprintf ".section \"%s\",\"a%s%s\",%%progbits" + s (if wr then "w" else "") (if ex then "x" else "") + | Section_ais_annotation -> sprintf ".section \"__compcert_ais_annotations\",\"\",@note" + + let section oc sec = + fprintf oc " %s\n" (name_of_section sec) + +(* Associate labels to floating-point constants and to symbols. *) + + let emit_constants oc lit = + if exists_constants () then begin + section oc lit; + if Hashtbl.length literal64_labels > 0 then + begin + fprintf oc " .balign 8\n"; + Hashtbl.iter + (fun bf lbl -> fprintf oc "%a: .quad 0x%Lx\n" label lbl bf) + literal64_labels + end; + if Hashtbl.length literal32_labels > 0 then + begin + fprintf oc " .balign 4\n"; + Hashtbl.iter + (fun bf lbl -> + fprintf oc "%a: .long 0x%lx\n" label lbl bf) + literal32_labels + end; + reset_literals () + end + +(* Emit .file / .loc debugging directives *) + + let print_file_line oc file line = + print_file_line oc comment file line + +(* Name of testable condition *) + + let condition_name = function + | TCeq -> "eq" + | TCne -> "ne" + | TChs -> "hs" + | TClo -> "lo" + | TCmi -> "mi" + | TCpl -> "pl" + | TChi -> "hi" + | TCls -> "ls" + | TCge -> "ge" + | TClt -> "lt" + | TCgt -> "gt" + | TCle -> "le" + +(* Print an addressing mode *) + + let addressing oc = function + | ADimm(base, n) -> fprintf oc "[%a, #%a]" xregsp base coqint64 n + | ADreg(base, r) -> fprintf oc "[%a, %a]" xregsp base xreg r + | ADlsl(base, r, n) -> fprintf oc "[%a, %a, lsl #%a]" xregsp base xreg r coqint n + | ADsxt(base, r, n) -> fprintf oc "[%a, %a, sxtw #%a]" xregsp base wreg r coqint n + | ADuxt(base, r, n) -> fprintf oc "[%a, %a, uxtw #%a]" xregsp base wreg r coqint n + | ADadr(base, id, ofs) -> fprintf oc "[%a, #:lo12:%a]" xregsp base symbol_offset (id, ofs) + | ADpostincr(base, n) -> fprintf oc "[%a], #%a" xregsp base coqint64 n + +(* Print a shifted operand *) + let shiftop oc = function + | SOnone -> () + | SOlsl n -> fprintf oc ", lsl #%a" coqint n + | SOlsr n -> fprintf oc ", lsr #%a" coqint n + | SOasr n -> fprintf oc ", asr #%a" coqint n + | SOror n -> fprintf oc ", ror #%a" coqint n + +(* Print a sign- or zero-extended operand *) + let extendop oc = function + | EOsxtb n -> fprintf oc ", sxtb #%a" coqint n + | EOsxth n -> fprintf oc ", sxth #%a" coqint n + | EOsxtw n -> fprintf oc ", sxtw #%a" coqint n + | EOuxtb n -> fprintf oc ", uxtb #%a" coqint n + | EOuxth n -> fprintf oc ", uxth #%a" coqint n + | EOuxtw n -> fprintf oc ", uxtw #%a" coqint n + | EOuxtx n -> fprintf oc ", uxtx #%a" coqint n + +(* Printing of instructions *) + let print_instruction oc = function + (* Branches *) + | Pb lbl -> + fprintf oc " b %a\n" print_label lbl + | Pbc(c, lbl) -> + fprintf oc " b.%s %a\n" (condition_name c) print_label lbl + | Pbl(id, sg) -> + fprintf oc " bl %a\n" symbol id + | Pbs(id, sg) -> + fprintf oc " b %a\n" symbol id + | Pblr(r, sg) -> + fprintf oc " blr %a\n" xreg r + | Pbr(r, sg) -> + fprintf oc " br %a\n" xreg r + | Pret r -> + fprintf oc " ret %a\n" xreg r + | Pcbnz(sz, r, lbl) -> + fprintf oc " cbnz %a, %a\n" ireg (sz, r) print_label lbl + | Pcbz(sz, r, lbl) -> + fprintf oc " cbz %a, %a\n" ireg (sz, r) print_label lbl + | Ptbnz(sz, r, n, lbl) -> + fprintf oc " tbnz %a, #%a, %a\n" ireg (sz, r) coqint n print_label lbl + | Ptbz(sz, r, n, lbl) -> + fprintf oc " tbz %a, #%a, %a\n" ireg (sz, r) coqint n print_label lbl + (* Memory loads and stores *) + | Pldrw(rd, a) | Pldrw_a(rd, a) -> + fprintf oc " ldr %a, %a\n" wreg rd addressing a + | Pldrx(rd, a) | Pldrx_a(rd, a) -> + fprintf oc " ldr %a, %a\n" xreg rd addressing a + | Pldrb(sz, rd, a) -> + fprintf oc " ldrb %a, %a\n" wreg rd addressing a + | Pldrsb(sz, rd, a) -> + fprintf oc " ldrsb %a, %a\n" ireg (sz, rd) addressing a + | Pldrh(sz, rd, a) -> + fprintf oc " ldrh %a, %a\n" wreg rd addressing a + | Pldrsh(sz, rd, a) -> + fprintf oc " ldrsh %a, %a\n" ireg (sz, rd) addressing a + | Pldrzw(rd, a) -> + fprintf oc " ldr %a, %a\n" wreg rd addressing a + (* the upper 32 bits of Xrd are set to 0, performing zero-extension *) + | Pldrsw(rd, a) -> + fprintf oc " ldrsw %a, %a\n" xreg rd addressing a + | Pldp(rd1, rd2, a) -> + fprintf oc " ldp %a, %a, %a\n" xreg rd1 xreg rd2 addressing a + | Pstrw(rs, a) | Pstrw_a(rs, a) -> + fprintf oc " str %a, %a\n" wreg rs addressing a + | Pstrx(rs, a) | Pstrx_a(rs, a) -> + fprintf oc " str %a, %a\n" xreg rs addressing a + | Pstrb(rs, a) -> + fprintf oc " strb %a, %a\n" wreg rs addressing a + | Pstrh(rs, a) -> + fprintf oc " strh %a, %a\n" wreg rs addressing a + | Pstp(rs1, rs2, a) -> + fprintf oc " stp %a, %a, %a\n" xreg rs1 xreg rs2 addressing a + (* Integer arithmetic, immediate *) + | Paddimm(sz, rd, r1, n) -> + fprintf oc " add %a, %a, #%a\n" iregsp (sz, rd) iregsp (sz, r1) intsz (sz, n) + | Psubimm(sz, rd, r1, n) -> + fprintf oc " sub %a, %a, #%a\n" iregsp (sz, rd) iregsp (sz, r1) intsz (sz, n) + | Pcmpimm(sz, r1, n) -> + fprintf oc " cmp %a, #%a\n" ireg (sz, r1) intsz (sz, n) + | Pcmnimm(sz, r1, n) -> + fprintf oc " cmn %a, #%a\n" ireg (sz, r1) intsz (sz, n) + (* Move integer register *) + | Pmov(rd, r1) -> + fprintf oc " mov %a, %a\n" xregsp rd xregsp r1 + (* Logical, immediate *) + | Pandimm(sz, rd, r1, n) -> + fprintf oc " and %a, %a, #%a\n" ireg (sz, rd) ireg0 (sz, r1) intsz (sz, n) + | Peorimm(sz, rd, r1, n) -> + fprintf oc " eor %a, %a, #%a\n" ireg (sz, rd) ireg0 (sz, r1) intsz (sz, n) + | Porrimm(sz, rd, r1, n) -> + fprintf oc " orr %a, %a, #%a\n" ireg (sz, rd) ireg0 (sz, r1) intsz (sz, n) + | Ptstimm(sz, r1, n) -> + fprintf oc " tst %a, #%a\n" ireg (sz, r1) intsz (sz, n) + (* Move wide immediate *) + | Pmovz(sz, rd, n, pos) -> + fprintf oc " movz %a, #%d, lsl #%d\n" ireg (sz, rd) (Z.to_int n) (Z.to_int pos) + | Pmovn(sz, rd, n, pos) -> + fprintf oc " movn %a, #%d, lsl #%d\n" ireg (sz, rd) (Z.to_int n) (Z.to_int pos) + | Pmovk(sz, rd, n, pos) -> + fprintf oc " movk %a, #%d, lsl #%d\n" ireg (sz, rd) (Z.to_int n) (Z.to_int pos) + (* PC-relative addressing *) + | Padrp(rd, id, ofs) -> + fprintf oc " adrp %a, %a\n" xreg rd symbol_offset (id, ofs) + | Paddadr(rd, r1, id, ofs) -> + fprintf oc " add %a, %a, #:lo12:%a\n" xreg rd xreg r1 symbol_offset (id, ofs) + (* Bit-field operations *) + | Psbfiz(sz, rd, r1, r, s) -> + fprintf oc " sbfiz %a, %a, %a, %d\n" ireg (sz, rd) ireg (sz, r1) coqint r (Z.to_int s) + | Psbfx(sz, rd, r1, r, s) -> + fprintf oc " sbfx %a, %a, %a, %d\n" ireg (sz, rd) ireg (sz, r1) coqint r (Z.to_int s) + | Pubfiz(sz, rd, r1, r, s) -> + fprintf oc " ubfiz %a, %a, %a, %d\n" ireg (sz, rd) ireg (sz, r1) coqint r (Z.to_int s) + | Pubfx(sz, rd, r1, r, s) -> + fprintf oc " ubfx %a, %a, %a, %d\n" ireg (sz, rd) ireg (sz, r1) coqint r (Z.to_int s) + (* Integer arithmetic, shifted register *) + | Padd(sz, rd, r1, r2, s) -> + fprintf oc " add %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Psub(sz, rd, r1, r2, s) -> + fprintf oc " sub %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Pcmp(sz, r1, r2, s) -> + fprintf oc " cmp %a, %a%a\n" ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Pcmn(sz, r1, r2, s) -> + fprintf oc " cmn %a, %a%a\n" ireg0 (sz, r1) ireg (sz, r2) shiftop s + (* Integer arithmetic, extending register *) + | Paddext(rd, r1, r2, x) -> + fprintf oc " add %a, %a, %a%a\n" xregsp rd xregsp r1 wreg r2 extendop x + | Psubext(rd, r1, r2, x) -> + fprintf oc " sub %a, %a, %a%a\n" xregsp rd xregsp r1 wreg r2 extendop x + | Pcmpext(r1, r2, x) -> + fprintf oc " cmp %a, %a%a\n" xreg r1 wreg r2 extendop x + | Pcmnext(r1, r2, x) -> + fprintf oc " cmn %a, %a%a\n" xreg r1 wreg r2 extendop x + (* Logical, shifted register *) + | Pand(sz, rd, r1, r2, s) -> + fprintf oc " and %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Pbic(sz, rd, r1, r2, s) -> + fprintf oc " bic %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Peon(sz, rd, r1, r2, s) -> + fprintf oc " eon %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Peor(sz, rd, r1, r2, s) -> + fprintf oc " eor %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Porr(sz, rd, r1, r2, s) -> + fprintf oc " orr %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Porn(sz, rd, r1, r2, s) -> + fprintf oc " orn %a, %a, %a%a\n" ireg (sz, rd) ireg0 (sz, r1) ireg (sz, r2) shiftop s + | Ptst(sz, r1, r2, s) -> + fprintf oc " tst %a, %a%a\n" ireg0 (sz, r1) ireg (sz, r2) shiftop s + (* Variable shifts *) + | Pasrv(sz, rd, r1, r2) -> + fprintf oc " asr %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) + | Plslv(sz, rd, r1, r2) -> + fprintf oc " lsl %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) + | Plsrv(sz, rd, r1, r2) -> + fprintf oc " lsr %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) + | Prorv(sz, rd, r1, r2) -> + fprintf oc " ror %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) + (* Bit operations *) + | Pcls(sz, rd, r1) -> + fprintf oc " cls %a, %a\n" ireg (sz, rd) ireg (sz, r1) + | Pclz(sz, rd, r1) -> + fprintf oc " clz %a, %a\n" ireg (sz, rd) ireg (sz, r1) + | Prev(sz, rd, r1) -> + fprintf oc " rev %a, %a\n" ireg (sz, rd) ireg (sz, r1) + | Prev16(sz, rd, r1) -> + fprintf oc " rev16 %a, %a\n" ireg (sz, rd) ireg (sz, r1) + (* Conditional data processing *) + | Pcsel(rd, r1, r2, c) -> + fprintf oc " csel %a, %a, %a, %s\n" xreg rd xreg r1 xreg r2 (condition_name c) + | Pcset(rd, c) -> + fprintf oc " cset %a, %s\n" xreg rd (condition_name c) + (* Integer multiply/divide *) + | Pmadd(sz, rd, r1, r2, r3) -> + fprintf oc " madd %a, %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) ireg0 (sz, r3) + | Pmsub(sz, rd, r1, r2, r3) -> + fprintf oc " msub %a, %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) ireg0 (sz, r3) + | Psmulh(rd, r1, r2) -> + fprintf oc " smulh %a, %a, %a\n" xreg rd xreg r1 xreg r2 + | Pumulh(rd, r1, r2) -> + fprintf oc " umulh %a, %a, %a\n" xreg rd xreg r1 xreg r2 + | Psdiv(sz, rd, r1, r2) -> + fprintf oc " sdiv %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) + | Pudiv(sz, rd, r1, r2) -> + fprintf oc " udiv %a, %a, %a\n" ireg (sz, rd) ireg (sz, r1) ireg (sz, r2) + (* Floating-point loads and stores *) + | Pldrs(rd, a) -> + fprintf oc " ldr %a, %a\n" sreg rd addressing a + | Pldrd(rd, a) | Pldrd_a(rd, a) -> + fprintf oc " ldr %a, %a\n" dreg rd addressing a + | Pstrs(rd, a) -> + fprintf oc " str %a, %a\n" sreg rd addressing a + | Pstrd(rd, a) | Pstrd_a(rd, a) -> + fprintf oc " str %a, %a\n" dreg rd addressing a + (* Floating-point move *) + | Pfmov(rd, r1) -> + fprintf oc " fmov %a, %a\n" dreg rd dreg r1 + | Pfmovimmd(rd, f) -> + let d = camlint64_of_coqint (Floats.Float.to_bits f) in + if is_immediate_float64 d then + fprintf oc " fmov %a, #%.7f\n" dreg rd (Int64.float_of_bits d) + else begin + let lbl = label_literal64 d in + fprintf oc " adrp x16, %a\n" label lbl; + fprintf oc " ldr %a, [x16, #:lo12:%a] %s %.18g\n" dreg rd label lbl comment (Int64.float_of_bits d) + end + | Pfmovimms(rd, f) -> + let d = camlint_of_coqint (Floats.Float32.to_bits f) in + if is_immediate_float32 d then + fprintf oc " fmov %a, #%.7f\n" sreg rd (Int32.float_of_bits d) + else begin + let lbl = label_literal32 d in + fprintf oc " adrp x16, %a\n" label lbl; + fprintf oc " ldr %a, [x16, #:lo12:%a] %s %.18g\n" sreg rd label lbl comment (Int32.float_of_bits d) + end + | Pfmovi(D, rd, r1) -> + fprintf oc " fmov %a, %a\n" dreg rd xreg0 r1 + | Pfmovi(S, rd, r1) -> + fprintf oc " fmov %a, %a\n" sreg rd wreg0 r1 + (* Floating-point conversions *) + | Pfcvtds(rd, r1) -> + fprintf oc " fcvt %a, %a\n" dreg rd sreg r1 + | Pfcvtsd(rd, r1) -> + fprintf oc " fcvt %a, %a\n" sreg rd dreg r1 + | Pfcvtzs(isz, fsz, rd, r1) -> + fprintf oc " fcvtzs %a, %a\n" ireg (isz, rd) freg (fsz, r1) + | Pfcvtzu(isz, fsz, rd, r1) -> + fprintf oc " fcvtzu %a, %a\n" ireg (isz, rd) freg (fsz, r1) + | Pscvtf(fsz, isz, rd, r1) -> + fprintf oc " scvtf %a, %a\n" freg (fsz, rd) ireg (isz, r1) + | Pucvtf(fsz, isz, rd, r1) -> + fprintf oc " ucvtf %a, %a\n" freg (fsz, rd) ireg (isz, r1) + (* Floating-point arithmetic *) + | Pfabs(sz, rd, r1) -> + fprintf oc " fabs %a, %a\n" freg (sz, rd) freg (sz, r1) + | Pfneg(sz, rd, r1) -> + fprintf oc " fneg %a, %a\n" freg (sz, rd) freg (sz, r1) + | Pfsqrt(sz, rd, r1) -> + fprintf oc " fsqrt %a, %a\n" freg (sz, rd) freg (sz, r1) + | Pfadd(sz, rd, r1, r2) -> + fprintf oc " fadd %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) + | Pfdiv(sz, rd, r1, r2) -> + fprintf oc " fdiv %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) + | Pfmul(sz, rd, r1, r2) -> + fprintf oc " fmul %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) + | Pfnmul(sz, rd, r1, r2) -> + fprintf oc " fnmul %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) + | Pfsub(sz, rd, r1, r2) -> + fprintf oc " fsub %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) + | Pfmadd(sz, rd, r1, r2, r3) -> + fprintf oc " fmadd %a, %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) freg (sz, r3) + | Pfmsub(sz, rd, r1, r2, r3) -> + fprintf oc " fmsub %a, %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) freg (sz, r3) + | Pfnmadd(sz, rd, r1, r2, r3) -> + fprintf oc " fnmadd %a, %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) freg (sz, r3) + | Pfnmsub(sz, rd, r1, r2, r3) -> + fprintf oc " fnmsub %a, %a, %a, %a\n" freg (sz, rd) freg (sz, r1) freg (sz, r2) freg (sz, r3) + (* Floating-point comparison *) + | Pfcmp(sz, r1, r2) -> + fprintf oc " fcmp %a, %a\n" freg (sz, r1) freg (sz, r2) + | Pfcmp0(sz, r1) -> + fprintf oc " fcmp %a, #0.0\n" freg (sz, r1) + (* Floating-point conditional select *) + | Pfsel(rd, r1, r2, c) -> + fprintf oc " fcsel %a, %a, %a, %s\n" dreg rd dreg r1 dreg r2 (condition_name c) + (* No-op *) + | Pnop -> + fprintf oc " nop\n" + (* Pseudo-instructions expanded in Asmexpand *) + | Pallocframe(sz, linkofs) -> assert false + | Pfreeframe(sz, linkofs) -> assert false + | Pcvtx2w rd -> assert false + (* Pseudo-instructions not yet expanded *) + | Plabel lbl -> + fprintf oc "%a:\n" print_label lbl + | Ploadsymbol(rd, id) -> + fprintf oc " adrp %a, :got:%a\n" xreg rd symbol id; + fprintf oc " ldr %a, [%a, #:got_lo12:%a]\n" xreg rd xreg rd symbol id + | Pcvtsw2x(rd, r1) -> + fprintf oc " sxtw %a, %a\n" xreg rd wreg r1 + | Pcvtuw2x(rd, r1) -> + fprintf oc " uxtw %a, %a\n" xreg rd wreg r1 + | Pbtbl(r1, tbl) -> + let lbl = new_label() in + fprintf oc " adr x16, %a\n" label lbl; + fprintf oc " add x16, x16, %a, uxtw #2\n" wreg r1; + fprintf oc " br x16\n"; + fprintf oc "%a:" label lbl; + List.iter (fun l -> fprintf oc " b %a\n" print_label l) tbl + | Pcfi_adjust sz -> + cfi_adjust oc (camlint_of_coqint sz) + | Pcfi_rel_offset ofs -> + cfi_rel_offset oc "lr" (camlint_of_coqint ofs) + | Pbuiltin(ef, args, res) -> + begin match ef with + | EF_annot(kind,txt, targs) -> + begin match (P.to_int kind) with + | 1 -> let annot = annot_text preg_annot "sp" (camlstring_of_coqstring txt) args in + fprintf oc "%s annotation: %S\n" comment annot + | 2 -> let lbl = new_label () in + fprintf oc "%a:\n" label lbl; + add_ais_annot lbl preg_annot "sp" (camlstring_of_coqstring txt) args + | _ -> assert false + end + | EF_debug(kind, txt, targs) -> + print_debug_info comment print_file_line preg_annot "sp" oc + (P.to_int kind) (extern_atom txt) args + | EF_inline_asm(txt, sg, clob) -> + fprintf oc "%s begin inline assembly\n\t" comment; + print_inline_asm preg_asm oc (camlstring_of_coqstring txt) sg args res; + fprintf oc "%s end inline assembly\n" comment + | _ -> + assert false + end + + let get_section_names name = + let (text, lit) = + match C2C.atom_sections name with + | t :: l :: _ -> (t, l) + | _ -> (Section_text, Section_literal) in + text,lit,Section_jumptable + + let print_align oc alignment = + fprintf oc " .balign %d\n" alignment + + let print_jumptable oc jmptbl = + let print_tbl oc (lbl, tbl) = + fprintf oc "%a:\n" label lbl; + List.iter + (fun l -> fprintf oc " .long %a - %a\n" + print_label l label lbl) + tbl in + if !jumptables <> [] then + begin + section oc jmptbl; + fprintf oc " .balign 4\n"; + List.iter (print_tbl oc) !jumptables; + jumptables := [] + end + + let print_fun_info = elf_print_fun_info + + let print_optional_fun_info _ = () + + let print_var_info = elf_print_var_info + + let print_comm_symb oc sz name align = + if C2C.atom_is_static name then + fprintf oc " .local %a\n" symbol name; + fprintf oc " .comm %a, %s, %d\n" + symbol name + (Z.to_string sz) + align + + let print_instructions oc fn = + current_function_sig := fn.fn_sig; + List.iter (print_instruction oc) fn.fn_code + +(* Data *) + + let address = ".quad" + + let print_prologue oc = + if !Clflags.option_g then begin + section oc Section_text; + end + + let print_epilogue oc = + if !Clflags.option_g then begin + Debug.compute_gnu_file_enum (fun f -> ignore (print_file oc f)); + section oc Section_text; + end + + let default_falignment = 2 + + let cfi_startproc oc = () + let cfi_endproc oc = () + + end + +let sel_target () = + (module Target:TARGET) diff --git a/aarch64/ValueAOp.v b/aarch64/ValueAOp.v new file mode 100644 index 00000000..e0d98c85 --- /dev/null +++ b/aarch64/ValueAOp.v @@ -0,0 +1,319 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +Require Import Coqlib Compopts. +Require Import AST Integers Floats Values Memory Globalenvs. +Require Import Op RTL ValueDomain. + +(** Value analysis for AArch64 operators *) + +Definition eval_static_shift (s: shift) (v: aval) (n: amount32) : aval := + match s with + | Slsl => shl v (I n) + | Slsr => shru v (I n) + | Sasr => shr v (I n) + | Sror => ror v (I n) + end. + +Definition eval_static_shiftl (s: shift) (v: aval) (n: amount64) : aval := + match s with + | Slsl => shll v (I n) + | Slsr => shrlu v (I n) + | Sasr => shrl v (I n) + | Sror => rorl v (I n) + end. + +Definition eval_static_extend (x: extension) (v: aval) (n: amount64) : aval := + shll (match x with Xsgn32 => longofint v | Xuns32 => longofintu v end) (I n). + +Definition eval_static_condition (cond: condition) (vl: list aval): abool := + match cond, vl with + | Ccomp c, v1 :: v2 :: nil => cmp_bool c v1 v2 + | Ccompu c, v1 :: v2 :: nil => cmpu_bool c v1 v2 + | Ccompimm c n, v1 :: nil => cmp_bool c v1 (I n) + | Ccompuimm c n, v1 :: nil => cmpu_bool c v1 (I n) + | Ccompshift c s a, v1 :: v2 :: nil => cmp_bool c v1 (eval_static_shift s v2 a) + | Ccompushift c s a, v1 :: v2 :: nil => cmpu_bool c v1 (eval_static_shift s v2 a) + | Cmaskzero m, v1 :: nil => maskzero v1 m + | Cmasknotzero m, v1 :: nil => cnot (maskzero v1 m) + | Ccompl c, v1 :: v2 :: nil => cmpl_bool c v1 v2 + | Ccomplu c, v1 :: v2 :: nil => cmplu_bool c v1 v2 + | Ccomplimm c n, v1 :: nil => cmpl_bool c v1 (L n) + | Ccompluimm c n, v1 :: nil => cmplu_bool c v1 (L n) + | Ccomplshift c s a, v1 :: v2 :: nil => cmpl_bool c v1 (eval_static_shiftl s v2 a) + | Ccomplushift c s a, v1 :: v2 :: nil => cmplu_bool c v1 (eval_static_shiftl s v2 a) + | Cmasklzero m, v1 :: nil => cmpl_bool Ceq (andl v1 (L m)) (L Int64.zero) + | Cmasklnotzero m, v1 :: nil => cmpl_bool Cne (andl v1 (L m)) (L Int64.zero) + | Ccompf c, v1 :: v2 :: nil => cmpf_bool c v1 v2 + | Cnotcompf c, v1 :: v2 :: nil => cnot (cmpf_bool c v1 v2) + | Ccompfzero c, v1 :: nil => cmpf_bool c v1 (F Float.zero) + | Cnotcompfzero c, v1 :: nil => cnot (cmpf_bool c v1 (F Float.zero)) + | Ccompfs c, v1 :: v2 :: nil => cmpfs_bool c v1 v2 + | Cnotcompfs c, v1 :: v2 :: nil => cnot (cmpfs_bool c v1 v2) + | Ccompfszero c, v1 :: nil => cmpfs_bool c v1 (FS Float32.zero) + | Cnotcompfszero c, v1 :: nil => cnot (cmpfs_bool c v1 (FS Float32.zero)) + | _, _ => Bnone + end. + +Definition eval_static_addressing (addr: addressing) (vl: list aval): aval := + match addr, vl with + | Aindexed n, v1 :: nil => addl v1 (L n) + | Aindexed2, v1 :: v2 :: nil => addl v1 v2 + | Aindexed2shift a, v1 :: v2 :: nil => addl v1 (shll v2 (I a)) + | Aindexed2ext x a, v1 :: v2 :: nil => addl v1 (eval_static_extend x v2 a) + | Aglobal s ofs, nil => Ptr (Gl s ofs) + | Ainstack ofs, nil => Ptr (Stk ofs) + | _, _ => Vbot + end. + +Definition eval_static_operation (op: operation) (vl: list aval): aval := + match op, vl with + | Omove, v1::nil => v1 + | Ointconst n, nil => I n + | Olongconst n, nil => L n + | Ofloatconst n, nil => if propagate_float_constants tt then F n else ntop + | Osingleconst n, nil => if propagate_float_constants tt then FS n else ntop + | Oaddrsymbol id ofs, nil => Ptr (Gl id ofs) + | Oaddrstack ofs, nil => Ptr (Stk ofs) + + | Oshift s a, v1::nil => eval_static_shift s v1 a + | Oadd, v1::v2::nil => add v1 v2 + | Oaddshift s a, v1::v2::nil => add v1 (eval_static_shift s v2 a) + | Oaddimm n, v1::nil => add v1 (I n) + | Oneg, v1::nil => neg v1 + | Onegshift s a, v1::nil => neg (eval_static_shift s v1 a) + | Osub, v1::v2::nil => sub v1 v2 + | Osubshift s a, v1::v2::nil => sub v1 (eval_static_shift s v2 a) + | Omul, v1::v2::nil => mul v1 v2 + | Omuladd, v1::v2::v3::nil => add v1 (mul v2 v3) + | Omulsub, v1::v2::v3::nil => sub v1 (mul v2 v3) + | Odiv, v1::v2::nil => divs v1 v2 + | Odivu, v1::v2::nil => divu v1 v2 + | Oand, v1::v2::nil => and v1 v2 + | Oandshift s a, v1::v2::nil => and v1 (eval_static_shift s v2 a) + | Oandimm n, v1::nil => and v1 (I n) + | Oor, v1::v2::nil => or v1 v2 + | Oorshift s a, v1::v2::nil => or v1 (eval_static_shift s v2 a) + | Oorimm n, v1::nil => or v1 (I n) + | Oxor, v1::v2::nil => xor v1 v2 + | Oxorshift s a, v1::v2::nil => xor v1 (eval_static_shift s v2 a) + | Oxorimm n, v1::nil => xor v1 (I n) + | Onot, v1::nil => notint v1 + | Onotshift s a, v1::nil => notint (eval_static_shift s v1 a) + | Obic, v1::v2::nil => and v1 (notint v2) + | Obicshift s a, v1::v2::nil => and v1 (notint (eval_static_shift s v2 a)) + | Oorn, v1::v2::nil => or v1 (notint v2) + | Oornshift s a, v1::v2::nil => or v1 (notint (eval_static_shift s v2 a)) + | Oeqv, v1::v2::nil => xor v1 (notint v2) + | Oeqvshift s a, v1::v2::nil => xor v1 (notint (eval_static_shift s v2 a)) + | Oshl, v1::v2::nil => shl v1 v2 + | Oshr, v1::v2::nil => shr v1 v2 + | Oshru, v1::v2::nil => shru v1 v2 + | Oshrximm n, v1::nil => shrx v1 (I n) + | Ozext s, v1::nil => zero_ext s v1 + | Osext s, v1::nil => sign_ext s v1 + | Oshlzext s a, v1::nil => shl (zero_ext s v1) (I a) + | Oshlsext s a, v1::nil => shl (sign_ext s v1) (I a) + | Ozextshr a s, v1::nil => zero_ext s (shru v1 (I a)) + | Osextshr a s, v1::nil => sign_ext s (shr v1 (I a)) + + | Oshiftl s a, v1::nil => eval_static_shiftl s v1 a + | Oextend x a, v1::nil => eval_static_extend x v1 a + | Omakelong, v1::v2::nil => longofwords v1 v2 + | Olowlong, v1::nil => loword v1 + | Ohighlong, v1::nil => hiword v1 + | Oaddl, v1::v2::nil => addl v1 v2 + | Oaddlshift s a, v1::v2::nil => addl v1 (eval_static_shiftl s v2 a) + | Oaddlext x a, v1::v2::nil => addl v1 (eval_static_extend x v2 a) + | Oaddlimm n, v1::nil => addl v1 (L n) + | Onegl, v1::nil => negl v1 + | Oneglshift s a, v1::nil => negl (eval_static_shiftl s v1 a) + | Osubl, v1::v2::nil => subl v1 v2 + | Osublshift s a, v1::v2::nil => subl v1 (eval_static_shiftl s v2 a) + | Osublext x a, v1::v2::nil => subl v1 (eval_static_extend x v2 a) + | Omull, v1::v2::nil => mull v1 v2 + | Omulladd, v1::v2::v3::nil => addl v1 (mull v2 v3) + | Omullsub, v1::v2::v3::nil => subl v1 (mull v2 v3) + | Omullhs, v1::v2::nil => mullhs v1 v2 + | Omullhu, v1::v2::nil => mullhu v1 v2 + | Odivl, v1::v2::nil => divls v1 v2 + | Odivlu, v1::v2::nil => divlu v1 v2 + | Oandl, v1::v2::nil => andl v1 v2 + | Oandlshift s a, v1::v2::nil => andl v1 (eval_static_shiftl s v2 a) + | Oandlimm n, v1::nil => andl v1 (L n) + | Oorl, v1::v2::nil => orl v1 v2 + | Oorlshift s a, v1::v2::nil => orl v1 (eval_static_shiftl s v2 a) + | Oorlimm n, v1::nil => orl v1 (L n) + | Oxorl, v1::v2::nil => xorl v1 v2 + | Oxorlshift s a, v1::v2::nil => xorl v1 (eval_static_shiftl s v2 a) + | Oxorlimm n, v1::nil => xorl v1 (L n) + | Onotl, v1::nil => notl v1 + | Onotlshift s a, v1::nil => notl (eval_static_shiftl s v1 a) + | Obicl, v1::v2::nil => andl v1 (notl v2) + | Obiclshift s a, v1::v2::nil => andl v1 (notl (eval_static_shiftl s v2 a)) + | Oornl, v1::v2::nil => orl v1 (notl v2) + | Oornlshift s a, v1::v2::nil => orl v1 (notl (eval_static_shiftl s v2 a)) + | Oeqvl, v1::v2::nil => xorl v1 (notl v2) + | Oeqvlshift s a, v1::v2::nil => xorl v1 (notl (eval_static_shiftl s v2 a)) + | Oshll, v1::v2::nil => shll v1 v2 + | Oshrl, v1::v2::nil => shrl v1 v2 + | Oshrlu, v1::v2::nil => shrlu v1 v2 + | Oshrlximm n, v1::nil => shrxl v1 (I n) + | Ozextl s, v1::nil => zero_ext_l s v1 + | Osextl s, v1::nil => sign_ext_l s v1 + | Oshllzext s a, v1::nil => shll (zero_ext_l s v1) (I a) + | Oshllsext s a, v1::nil => shll (sign_ext_l s v1) (I a) + | Ozextshrl a s, v1::nil => zero_ext_l s (shrlu v1 (I a)) + | Osextshrl a s, v1::nil => sign_ext_l s (shrl v1 (I a)) + + | Onegf, v1::nil => negf v1 + | Oabsf, v1::nil => absf v1 + | Oaddf, v1::v2::nil => addf v1 v2 + | Osubf, v1::v2::nil => subf v1 v2 + | Omulf, v1::v2::nil => mulf v1 v2 + | Odivf, v1::v2::nil => divf v1 v2 + + | Onegfs, v1::nil => negfs v1 + | Oabsfs, v1::nil => absfs v1 + | Oaddfs, v1::v2::nil => addfs v1 v2 + | Osubfs, v1::v2::nil => subfs v1 v2 + | Omulfs, v1::v2::nil => mulfs v1 v2 + | Odivfs, v1::v2::nil => divfs v1 v2 + + | Osingleoffloat, v1::nil => singleoffloat v1 + | Ofloatofsingle, v1::nil => floatofsingle v1 + | Ointoffloat, v1::nil => intoffloat v1 + | Ointuoffloat, v1::nil => intuoffloat v1 + | Ofloatofint, v1::nil => floatofint v1 + | Ofloatofintu, v1::nil => floatofintu v1 + | Ointofsingle, v1::nil => intofsingle v1 + | Ointuofsingle, v1::nil => intuofsingle v1 + | Osingleofint, v1::nil => singleofint v1 + | Osingleofintu, v1::nil => singleofintu v1 + | Olongoffloat, v1::nil => longoffloat v1 + | Olonguoffloat, v1::nil => longuoffloat v1 + | Ofloatoflong, v1::nil => floatoflong v1 + | Ofloatoflongu, v1::nil => floatoflongu v1 + | Olongofsingle, v1::nil => longofsingle v1 + | Olonguofsingle, v1::nil => longuofsingle v1 + | Osingleoflong, v1::nil => singleoflong v1 + | Osingleoflongu, v1::nil => singleoflongu v1 + + | Ocmp c, _ => of_optbool (eval_static_condition c vl) + | Osel c ty, v1::v2::vl => select (eval_static_condition c vl) v1 v2 + + | _, _ => Vbot + end. + +Section SOUNDNESS. + +Variable bc: block_classification. +Variable ge: genv. +Hypothesis GENV: genv_match bc ge. +Variable sp: block. +Hypothesis STACK: bc sp = BCstack. + +Ltac InvHyps := + match goal with + | [H: None = Some _ |- _ ] => discriminate + | [H: Some _ = Some _ |- _] => inv H + | [H1: match ?vl with nil => _ | _ :: _ => _ end = Some _ , + H2: list_forall2 _ ?vl _ |- _ ] => inv H2; InvHyps + | _ => idtac + end. + +Lemma eval_static_shift_sound: forall v av s n, + vmatch bc v av -> vmatch bc (eval_shift s v n) (eval_static_shift s av n). +Proof. + intros. unfold eval_shift, eval_static_shift; destruct s; auto with va. +Qed. + +Lemma eval_static_shiftl_sound: forall v av s n, + vmatch bc v av -> vmatch bc (eval_shiftl s v n) (eval_static_shiftl s av n). +Proof. + intros. unfold eval_shiftl, eval_static_shiftl; destruct s; auto with va. +Qed. + +Lemma eval_static_extend_sound: forall v av x n, + vmatch bc v av -> vmatch bc (eval_extend x v n) (eval_static_extend x av n). +Proof. + intros. unfold eval_extend, eval_static_extend; destruct x; auto with va. +Qed. + +Hint Resolve eval_static_shift_sound eval_static_shiftl_sound eval_static_extend_sound: va. + +Theorem eval_static_condition_sound: + forall cond vargs m aargs, + list_forall2 (vmatch bc) vargs aargs -> + cmatch (eval_condition cond vargs m) (eval_static_condition cond aargs). +Proof. + intros until aargs; intros VM. inv VM. + destruct cond; auto with va. + inv H0. + destruct cond; simpl; eauto with va. + replace (Val.cmp_bool Ceq (Val.and a1 (Vint n)) (Vint Int.zero)) + with (Val.maskzero_bool a1 n) by (destruct a1; auto). + eauto with va. + replace (Val.cmp_bool Cne (Val.and a1 (Vint n)) (Vint Int.zero)) + with (option_map negb (Val.maskzero_bool a1 n)) by (destruct a1; auto). + eauto with va. + inv H2. + destruct cond; simpl; eauto with va. + destruct cond; auto with va. +Qed. + +Lemma symbol_address_sound: + forall id ofs, + vmatch bc (Genv.symbol_address ge id ofs) (Ptr (Gl id ofs)). +Proof. + intros; apply symbol_address_sound; apply GENV. +Qed. + +Lemma symbol_address_sound_2: + forall id ofs, + vmatch bc (Genv.symbol_address ge id ofs) (Ifptr (Gl id ofs)). +Proof. + intros. unfold Genv.symbol_address. destruct (Genv.find_symbol ge id) as [b|] eqn:F. + constructor. constructor. apply GENV; auto. + constructor. +Qed. + +Hint Resolve symbol_address_sound symbol_address_sound_2: va. + +Theorem eval_static_addressing_sound: + forall addr vargs vres aargs, + eval_addressing ge (Vptr sp Ptrofs.zero) addr vargs = Some vres -> + list_forall2 (vmatch bc) vargs aargs -> + vmatch bc vres (eval_static_addressing addr aargs). +Proof. + unfold eval_addressing, eval_static_addressing; intros; + destruct addr; InvHyps; eauto with va. + rewrite Ptrofs.add_zero_l; eauto with va. +Qed. + +Theorem eval_static_operation_sound: + forall op vargs m vres aargs, + eval_operation ge (Vptr sp Ptrofs.zero) op vargs m = Some vres -> + list_forall2 (vmatch bc) vargs aargs -> + vmatch bc vres (eval_static_operation op aargs). +Proof. + unfold eval_operation, eval_static_operation; intros; + destruct op; InvHyps; eauto with va. + destruct (propagate_float_constants tt); constructor. + destruct (propagate_float_constants tt); constructor. + rewrite Ptrofs.add_zero_l; eauto with va. + apply of_optbool_sound. eapply eval_static_condition_sound; eauto. + apply select_sound; eauto using eval_static_condition_sound. +Qed. + +End SOUNDNESS. + diff --git a/aarch64/extractionMachdep.v b/aarch64/extractionMachdep.v new file mode 100644 index 00000000..e82056e2 --- /dev/null +++ b/aarch64/extractionMachdep.v @@ -0,0 +1,24 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and INRIA Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(* Additional extraction directives specific to the AArch64 port *) + +Require Archi Asm. + +(* Archi *) + +Extract Constant Archi.pic_code => "fun () -> false". (* for the time being *) + +(* Asm *) +Extract Constant Asm.symbol_low => "fun _ _ _ -> assert false". +Extract Constant Asm.symbol_high => "fun _ _ _ -> assert false". +Extract Constant Asmgen.symbol_is_aligned => "C2C.atom_is_aligned". diff --git a/arm/Archi.v b/arm/Archi.v index 39a424ec..16d6c71d 100644 --- a/arm/Archi.v +++ b/arm/Archi.v @@ -16,7 +16,7 @@ (** Architecture-dependent parameters for ARM *) -Require Import ZArith. +Require Import ZArith List. (*From Flocq*) Require Import Binary Bits. @@ -34,30 +34,57 @@ Proof. unfold splitlong, ptr64; congruence. Qed. -Definition default_nan_64 : { x : binary64 | is_nan _ _ x = true } := - exist _ (B754_nan 53 1024 false (iter_nat 51 _ xO xH) (eq_refl true)) (eq_refl true). +Definition default_nan_64 := (false, iter_nat 51 _ xO xH). +Definition default_nan_32 := (false, iter_nat 22 _ xO xH). + +(** Choose the first signaling NaN, if any; + otherwise choose the first NaN; + otherwise use default. *) + +Definition choose_nan (is_signaling: positive -> bool) + (default: bool * positive) + (l0: list (bool * positive)) : bool * positive := + let fix choose_snan (l1: list (bool * positive)) := + match l1 with + | nil => + match l0 with nil => default | n :: _ => n end + | ((s, p) as n) :: l1 => + if is_signaling p then n else choose_snan l1 + end + in choose_snan l0. + +Lemma choose_nan_idem: forall is_signaling default n, + choose_nan is_signaling default (n :: n :: nil) = + choose_nan is_signaling default (n :: nil). +Proof. + intros. destruct n as [s p]; unfold choose_nan; simpl. + destruct (is_signaling p); auto. +Qed. + +Definition choose_nan_64 := + choose_nan (fun p => negb (Pos.testbit p 51)) default_nan_64. + +Definition choose_nan_32 := + choose_nan (fun p => negb (Pos.testbit p 22)) default_nan_32. -Definition choose_binop_pl_64 (pl1 pl2 : positive) := - (** Choose second NaN if pl2 is sNaN but pl1 is qNan. - In all other cases, choose first NaN *) - (Pos.testbit pl1 51 && negb (Pos.testbit pl2 51))%bool. +Lemma choose_nan_64_idem: forall n, + choose_nan_64 (n :: n :: nil) = choose_nan_64 (n :: nil). +Proof. intros; apply choose_nan_idem. Qed. -Definition default_nan_32 : { x : binary32 | is_nan _ _ x = true } := - exist _ (B754_nan 24 128 false (iter_nat 22 _ xO xH) (eq_refl true)) (eq_refl true). +Lemma choose_nan_32_idem: forall n, + choose_nan_32 (n :: n :: nil) = choose_nan_32 (n :: nil). +Proof. intros; apply choose_nan_idem. Qed. -Definition choose_binop_pl_32 (pl1 pl2 : positive) := - (** Choose second NaN if pl2 is sNaN but pl1 is qNan. - In all other cases, choose first NaN *) - (Pos.testbit pl1 22 && negb (Pos.testbit pl2 22))%bool. +Definition fma_order {A: Type} (x y z: A) := (z, x, y). -Definition fpu_returns_default_qNaN := false. +Definition fma_invalid_mul_is_nan := true. Definition float_of_single_preserves_sNaN := false. Global Opaque ptr64 big_endian splitlong - default_nan_64 choose_binop_pl_64 - default_nan_32 choose_binop_pl_32 - fpu_returns_default_qNaN + default_nan_64 choose_nan_64 + default_nan_32 choose_nan_32 + fma_order fma_invalid_mul_is_nan float_of_single_preserves_sNaN. (** Which ABI to use: either the standard ARM EABI with floats passed @@ -220,6 +220,7 @@ Inductive instruction : Type := | Plabel: label -> instruction (**r define a code label *) | Ploadsymbol: ireg -> ident -> ptrofs -> instruction (**r load the address of a symbol *) | Pmovite: testcond -> ireg -> shift_op -> shift_op -> instruction (**r integer conditional move *) + | Pfmovite: testcond -> freg -> freg -> freg -> instruction (**r FP conditional move *) | Pbtbl: ireg -> list label -> instruction (**r N-way branch through a jump table *) | Pbuiltin: external_function -> list (builtin_arg preg) -> builtin_res preg -> instruction (**r built-in function (pseudo) *) | Padc: ireg -> ireg -> shift_op -> instruction (**r add with carry *) @@ -783,6 +784,14 @@ Definition exec_instr (f: function) (i: instruction) (rs: regset) (m: mem) : out | None => Vundef end in Next (nextinstr (rs#r1 <- v)) m + | Pfmovite cond r1 ifso ifnot => + let v := + match eval_testcond cond rs with + | Some true => rs#ifso + | Some false => rs#ifnot + | None => Vundef + end in + Next (nextinstr (rs#r1 <- v)) m | Pbtbl r tbl => match rs#r with | Vint n => diff --git a/arm/AsmToJSON.ml b/arm/AsmToJSON.ml index 3874e141..e850fed6 100644 --- a/arm/AsmToJSON.ml +++ b/arm/AsmToJSON.ml @@ -19,21 +19,25 @@ open BinNums open Camlcoq open Json -let mnemonic_names = [ "Padc"; "Padd"; "Padds"; "Pand";"Pannot"; "Pasr"; "Pb"; "Pbc"; "Pbic"; "Pblreg"; - "Pblsymb"; "Pbne"; "Pbreg"; "Pbsymb"; "Pbtbl"; "Pclz"; "Pcmp"; "Pcmn"; "Pconstants"; "Pfcpy_iif"; - "Pfcpy_fii"; "Pfcpy_fi"; "Pfcpy_sf"; "Pflid_lbl"; "Pflis_lbl"; "Pdmb"; "Pdsb"; "Peor"; "Pfabsd"; - "Pfabss"; "Pfaddd"; "Pfadds"; "Pfcmpd"; "Pfcmps"; "Pfcmpzd"; "Pfcmpzs"; - "Pfcpyd"; "Pfcpy_fs"; "Pfcpy_if";"Pfcvtds"; "Pfcvtsd"; "Pfdivd"; "Pfdivs"; "Pfldd"; - "Pflid"; "Pflds"; "Pflid_imm"; "Pflis_imm"; "Pfmuld"; "Pfmuls"; "Pfnegd"; - "Pfnegs"; "Pfsitod"; "Pfsitos"; "Pfsqrt"; "Pfstd"; - "Pfsts"; "Pfsubd"; "Pfsubs"; "Pftosizd"; "Pftosizs"; "Pftouizd"; - "Pftouizs"; "Pfuitod"; "Pfuitos"; "Pinlineasm"; "Pisb"; "Plabel"; "Pldr"; - "Ploadsymbol_lbl"; "Pldr_p"; "Pldrb"; "Pldrb_p"; "Pldrh"; "Pldrh_p"; "Pldrsb"; - "Pldrsh"; "Plsl"; "Plsr"; "Pmla"; "Pmov"; "Pmovite"; - "Pmovt"; "Pmovw"; "Pmul"; "Pmvn"; "Ploadsymbol_imm"; "Pnop"; "Porr"; - "Ppush"; "Prev"; "Prev16"; "Prsb"; "Prsbs"; "Prsc"; "Psbc"; "Psbfx"; "Psdiv"; "Psmull"; - "Pstr"; "Pstr_p"; "Pstrb"; "Pstrb_p"; "Pstrh"; "Pstrh_p"; "Psub"; "Psubs"; "Pudiv"; - "Pumull" ] +module StringSet = Set.Make(String) + +let mnemonic_names = StringSet.of_list + [ "Padc"; "Padd"; "Padds"; "Pand";"Pannot"; "Pasr"; "Pb"; "Pbc"; "Pbic"; + "Pblreg"; "Pblsymb"; "Pbne"; "Pbreg"; "Pbsymb"; "Pbtbl"; "Pclz"; "Pcmp"; + "Pcmn"; "Pconstants"; "Pfcpy_iif"; "Pfcpy_fii"; "Pfcpy_fi"; "Pfcpy_sf"; + "Pflid_lbl"; "Pflis_lbl"; "Pdmb"; "Pdsb"; "Peor"; "Pfabsd"; "Pfabss"; + "Pfaddd"; "Pfadds"; "Pfcmpd"; "Pfcmps"; "Pfcmpzd"; "Pfcmpzs"; "Pfcpyd"; + "Pfcpy_fs"; "Pfcpy_if";"Pfcvtds"; "Pfcvtsd"; "Pfdivd"; "Pfdivs"; "Pfldd"; + "Pflid"; "Pflds"; "Pflid_imm"; "Pflis_imm"; "Pfmuld"; "Pfmuls"; "Pfnegd"; + "Pfnegs"; "Pfsitod"; "Pfsitos"; "Pfsqrt"; "Pfstd"; "Pfsts"; "Pfsubd"; + "Pfsubs"; "Pftosizd"; "Pftosizs"; "Pftouizd"; "Pftouizs"; "Pfuitod"; + "Pfuitos"; "Pinlineasm"; "Pisb"; "Plabel"; "Pldr"; "Ploadsymbol_lbl"; + "Pldr_p"; "Pldrb"; "Pldrb_p"; "Pldrh"; "Pldrh_p"; "Pldrsb"; "Pldrsh"; + "Plsl"; "Plsr"; "Pmla"; "Pmov"; "Pmovite"; "Pfmovite"; "Pmovt"; "Pmovw"; + "Pmul"; "Pmvn"; "Ploadsymbol_imm"; "Pnop"; "Porr"; "Ppush"; "Prev"; + "Prev16"; "Prsb"; "Prsbs"; "Prsc"; "Psbc"; "Psbfx"; "Psdiv"; "Psmull"; + "Pstr"; "Pstr_p"; "Pstrb"; "Pstrb_p"; "Pstrh"; "Pstrh_p"; "Psub"; "Psubs"; + "Pudiv";"Pumull" ] type instruction_arg = | ALabel of positive @@ -143,7 +147,7 @@ let pp_instructions pp ic = | _ -> true) ic in let instruction pp n args = - assert (List.mem n mnemonic_names); + assert (StringSet.mem n mnemonic_names); pp_jobject_start pp; pp_jmember ~first:true pp "Instruction Name" pp_jstring n; pp_jmember pp "Args" (pp_jarray pp_arg) args; @@ -259,7 +263,8 @@ let pp_instructions pp ic = | Pmla(r1, r2, r3, r4) -> instruction pp "Pmla" [Ireg r1; Ireg r2; Ireg r3; Ireg r4] | Pmov(r1, so) -> instruction pp "Pmov" [Ireg r1; Shift so] | Pmovite(cond, r1, so1, so2) -> instruction pp "Pmovite" [Ireg r1; Condition (TargetPrinter.condition_name cond); Shift so1; Condition (TargetPrinter.neg_condition_name cond); Shift so2] - | Pmovt(r1, n) -> instruction pp "Pmovt" [Ireg r1; Long n] + | Pfmovite(cond, r1, r2, r3) -> instruction pp "Pfmovite" [DFreg r1; Condition (TargetPrinter.condition_name cond); DFreg r2; Condition (TargetPrinter.neg_condition_name cond); DFreg r3] + | Pmovt(r1, n) -> instruction pp "Pmovt" [Ireg r1; Long n] | Pmovw(r1, n) -> instruction pp "Pmovw" [Ireg r1; Long n] | Pmul(r1, r2, r3) -> instruction pp "Pmul" [Ireg r1; Ireg r2; Ireg r3] | Pmvn(r1, so) -> instruction pp "Pmvn" [Ireg r1; Shift so] @@ -312,8 +317,8 @@ let print_if prog sourcename = | Some f -> let f = Filename.concat !sdump_folder f in let oc = open_out_bin f in - JsonAST.pp_ast (Format.formatter_of_out_channel oc) pp_instructions prog sourcename; + JsonAST.pp_ast oc pp_instructions prog sourcename; close_out oc let pp_mnemonics pp = - JsonAST.pp_mnemonics pp mnemonic_names + JsonAST.pp_mnemonics pp (StringSet.elements mnemonic_names) diff --git a/arm/Asmexpand.ml b/arm/Asmexpand.ml index d9424d11..89aab5c7 100644 --- a/arm/Asmexpand.ml +++ b/arm/Asmexpand.ml @@ -18,7 +18,7 @@ open Asm open Asmexpandaux open AST open Camlcoq -open Integers +open! Integers exception Error of string @@ -304,6 +304,11 @@ let expand_builtin_va_start r = let expand_builtin_inline name args res = match name, args, res with (* Integer arithmetic *) + | "__builtin_bswap64" , [BA_splitlong(BA(IR ah), BA(IR al))], + BR_splitlong(BR(IR rh), BR(IR rl)) -> + expand_int64_arith (rl = al) rl (fun rl -> + emit (Prev (rl, ah)); + emit (Prev (rh, al))) | ("__builtin_bswap" | "__builtin_bswap32"), [BA(IR a1)], BR(IR res) -> emit (Prev (res, a1)) | "__builtin_bswap16", [BA(IR a1)], BR(IR res) -> diff --git a/arm/Asmgen.v b/arm/Asmgen.v index f12ea870..1a1e7f2f 100644 --- a/arm/Asmgen.v +++ b/arm/Asmgen.v @@ -555,6 +555,19 @@ Definition transl_op do r <- ireg_of res; transl_cond cmp args (Pmovite (cond_for_cond cmp) r (SOimm Int.one) (SOimm Int.zero) :: k) + | Osel cmp ty, a1 :: a2 :: args => + match preg_of res with + | IR r => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; + transl_cond cmp args + (Pmovite (cond_for_cond cmp) r (SOreg r1) (SOreg r2) :: k) + | FR r => + do r1 <- freg_of a1; do r2 <- freg_of a2; + transl_cond cmp args + (Pfmovite (cond_for_cond cmp) r r1 r2 :: k) + | _ => + Error(msg "Asmgen.Osel") + end | _, _ => Error(msg "Asmgen.transl_op") end. diff --git a/arm/Asmgenproof.v b/arm/Asmgenproof.v index 2c001f45..25f91d23 100644 --- a/arm/Asmgenproof.v +++ b/arm/Asmgenproof.v @@ -270,6 +270,7 @@ Opaque Int.eq. destruct Archi.thumb2_support; TailNoLabel. eapply tail_nolabel_trans; TailNoLabel. eapply tail_nolabel_trans. eapply transl_cond_label; eauto. TailNoLabel. + destruct (preg_of r); monadInv H; (eapply tail_nolabel_trans; [eapply transl_cond_label; eauto|TailNoLabel]). Qed. Remark transl_memory_access_label: diff --git a/arm/Asmgenproof1.v b/arm/Asmgenproof1.v index 98cd5eea..807e069d 100644 --- a/arm/Asmgenproof1.v +++ b/arm/Asmgenproof1.v @@ -16,6 +16,7 @@ Require Import Coqlib. Require Import Errors. Require Import Maps. Require Import AST. +Require Import Zbits. Require Import Integers. Require Import Floats. Require Import Values. @@ -355,7 +356,7 @@ Proof. rewrite Int.and_assoc. change 65535 with (two_p 16 - 1). rewrite Int.and_idem. apply Int.same_bits_eq; intros. rewrite Int.bits_or, Int.bits_and, Int.bits_shl, Int.testbit_repr by auto. - rewrite Int.Ztestbit_two_p_m1 by omega. change (Int.unsigned (Int.repr 16)) with 16. + rewrite Ztestbit_two_p_m1 by omega. change (Int.unsigned (Int.repr 16)) with 16. destruct (zlt i 16). rewrite andb_true_r, orb_false_r; auto. rewrite andb_false_r; simpl. rewrite Int.bits_shru by omega. @@ -1188,7 +1189,7 @@ Lemma transl_op_correct_same: forall op args res k c (rs: regset) m v, transl_op op args res k = OK c -> eval_operation ge rs#IR13 op (map rs (map preg_of args)) m = Some v -> - match op with Ocmp _ => False | Oaddrstack _ => False | _ => True end -> + match op with Ocmp _ => False | Osel _ _ => False | Oaddrstack _ => False | _ => True end -> exists rs', exec_straight ge fn c rs m k rs' m /\ rs'#(preg_of res) = v @@ -1332,6 +1333,8 @@ Transparent destroyed_by_op. intuition Simpl. (* Ocmp *) contradiction. + (* Osel *) + contradiction. Qed. Lemma transl_op_correct: @@ -1368,6 +1371,27 @@ Proof. split; intros; Simpl. destruct (eval_condition c0 rs ## (preg_of ## args) m) as [b|]; simpl; auto. destruct B as [B1 B2]; rewrite B1. destruct b; auto. +- (* Osel *) + clear SAME. simpl in H. ArgsInv. simpl in H0; inv H0. + assert (D1: data_preg (preg_of m0) = true) by auto with asmgen. + assert (D2: data_preg (preg_of m1) = true) by auto with asmgen. + destruct (preg_of res) eqn:RES; monadInv H. ++ inv EQ2. rewrite (ireg_of_eq _ _ EQ), (ireg_of_eq _ _ EQ1) in *. + exploit transl_cond_correct; eauto. instantiate (1 := rs). instantiate (1 := m). intros [rs1 [A [B C]]]. + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto. + split; intros; Simpl. + rewrite ! C by auto. + destruct (eval_condition c0 rs ## (preg_of ## args) m) as [b|]; simpl; auto. + destruct B as [B1 B2]; rewrite B1. destruct b; apply Val.lessdef_normalize. ++ inv EQ2. rewrite (freg_of_eq _ _ EQ), (freg_of_eq _ _ EQ1) in *. + exploit transl_cond_correct; eauto. instantiate (1 := rs). instantiate (1 := m). intros [rs1 [A [B C]]]. + econstructor; split. + eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto. + split; intros; Simpl. + rewrite ! C by auto. + destruct (eval_condition c0 rs ## (preg_of ## args) m) as [b|]; simpl; auto. + destruct B as [B1 B2]; rewrite B1. destruct b; apply Val.lessdef_normalize. Qed. (** Translation of loads and stores. *) diff --git a/arm/Builtins1.v b/arm/Builtins1.v new file mode 100644 index 00000000..53c83d7e --- /dev/null +++ b/arm/Builtins1.v @@ -0,0 +1,33 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Platform-specific built-in functions *) + +Require Import String Coqlib. +Require Import AST Integers Floats Values. +Require Import Builtins0. + +Inductive platform_builtin : Type := . + +Local Open Scope string_scope. + +Definition platform_builtin_table : list (string * platform_builtin) := + nil. + +Definition platform_builtin_sig (b: platform_builtin) : signature := + match b with end. + +Definition platform_builtin_sem (b: platform_builtin) : builtin_sem (sig_res (platform_builtin_sig b)) := + match b with end. diff --git a/arm/CBuiltins.ml b/arm/CBuiltins.ml index ec4f4aaa..d6a1ea35 100644 --- a/arm/CBuiltins.ml +++ b/arm/CBuiltins.ml @@ -18,10 +18,10 @@ open C let builtins = { - Builtins.typedefs = [ + builtin_typedefs = [ "__builtin_va_list", TPtr(TVoid [], []) ]; - Builtins.functions = [ + builtin_functions = [ (* Integer arithmetic *) "__builtin_clz", (TInt(IInt, []), [TInt(IUInt, [])], false); diff --git a/arm/ConstpropOp.vp b/arm/ConstpropOp.vp index d62240ef..8555d3aa 100644 --- a/arm/ConstpropOp.vp +++ b/arm/ConstpropOp.vp @@ -20,7 +20,7 @@ Require Import Integers. Require Import Floats. Require Import Op. Require Import Registers. -Require Import ValueDomain. +Require Import ValueDomain ValueAOp. (** * Converting known values to constants *) @@ -131,6 +131,15 @@ Nondetfunction make_cmp (c: condition) (args: list reg) (vl: list aval) := make_cmp_base c args vl end. +Definition make_select (c: condition) (ty: typ) + (r1 r2: reg) (args: list reg) (vl: list aval) := + match resolve_branch (eval_static_condition c vl) with + | Some b => (Omove, (if b then r1 else r2) :: nil) + | None => + let (c', args') := cond_strength_reduction c args vl in + (Osel c' ty, r1 :: r2 :: args') + end. + Definition make_addimm (n: int) (r: reg) := if Int.eq n Int.zero then (Omove, r :: nil) @@ -284,6 +293,7 @@ Nondetfunction op_strength_reduction | Oshr, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shrimm n2 r1 r2 | Oshru, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shruimm n2 r1 r2 | Ocmp c, args, vl => make_cmp c args vl + | Osel c ty, r1 :: r2 :: args, v1 :: v2 :: vl => make_select c ty r1 r2 args vl | Omulf, r1 :: r2 :: nil, v1 :: F n2 :: nil => make_mulfimm n2 r1 r1 r2 | Omulf, r1 :: r2 :: nil, F n1 :: v2 :: nil => make_mulfimm n1 r2 r1 r2 | Omulfs, r1 :: r2 :: nil, v1 :: FS n2 :: nil => make_mulfsimm n2 r1 r1 r2 diff --git a/arm/ConstpropOpproof.v b/arm/ConstpropOpproof.v index 079ba2be..a4f5c29c 100644 --- a/arm/ConstpropOpproof.v +++ b/arm/ConstpropOpproof.v @@ -24,7 +24,7 @@ Require Import Events. Require Import Op. Require Import Registers. Require Import RTL. -Require Import ValueDomain. +Require Import ValueDomain ValueAOp ValueAnalysis. Require Import ConstpropOp. Local Transparent Archi.ptr64. @@ -234,6 +234,28 @@ Proof. - apply make_cmp_base_correct; auto. Qed. +Lemma make_select_correct: + forall c ty r1 r2 args vl, + vl = map (fun r => AE.get r ae) args -> + let (op', args') := make_select c ty r1 r2 args vl in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op' rs##args' m = Some v + /\ Val.lessdef (Val.select (eval_condition c rs##args m) rs#r1 rs#r2 ty) v. +Proof. + unfold make_select; intros. + destruct (resolve_branch (eval_static_condition c vl)) as [b|] eqn:RB. +- exists (if b then rs#r1 else rs#r2); split. ++ simpl. destruct b; auto. ++ destruct (eval_condition c rs##args m) as [b'|] eqn:EC; simpl; auto. + assert (b = b'). + { eapply resolve_branch_sound; eauto. + rewrite <- EC. apply eval_static_condition_sound with bc. + subst vl. exact (aregs_sound _ _ _ args MATCH). } + subst b'. apply Val.lessdef_normalize. +- generalize (cond_strength_reduction_correct c args vl H). + destruct (cond_strength_reduction c args vl) as [cond' args']; intros EQ. + econstructor; split. simpl; eauto. rewrite EQ; auto. +Qed. + Lemma make_addimm_correct: forall n r, let (op, args) := make_addimm n r in @@ -616,6 +638,8 @@ Proof. InvApproxRegs; SimplVM. inv H0. apply make_shruimm_correct; auto. (* cmp *) inv H0. apply make_cmp_correct; auto. +(* select *) + inv H0. apply make_select_correct; congruence. (* mulf *) InvApproxRegs; SimplVM; inv H0. rewrite <- H2. apply make_mulfimm_correct; auto. InvApproxRegs; SimplVM; inv H0. fold (Val.mulf (Vfloat n1) rs#r2). diff --git a/arm/Conventions1.v b/arm/Conventions1.v index c5277e8d..fe49a781 100644 --- a/arm/Conventions1.v +++ b/arm/Conventions1.v @@ -104,13 +104,12 @@ Definition is_float_reg (r: mreg): bool := representation with a single LDM instruction. *) Definition loc_result (s: signature) : rpair mreg := - match s.(sig_res) with - | None => One R0 - | Some (Tint | Tany32) => One R0 - | Some (Tfloat | Tsingle | Tany64) => One F0 - | Some Tlong => if Archi.big_endian - then Twolong R0 R1 - else Twolong R1 R0 + match proj_sig_res s with + | Tint | Tany32 => One R0 + | Tfloat | Tsingle | Tany64 => One F0 + | Tlong => if Archi.big_endian + then Twolong R0 R1 + else Twolong R1 R0 end. (** The result registers have types compatible with that given in the signature. *) @@ -119,7 +118,7 @@ Lemma loc_result_type: forall sig, subtype (proj_sig_res sig) (typ_rpair mreg_type (loc_result sig)) = true. Proof. - intros. unfold proj_sig_res, loc_result. destruct (sig_res sig) as [[]|]; destruct Archi.big_endian; auto. + intros. unfold loc_result. destruct (proj_sig_res sig); destruct Archi.big_endian; auto. Qed. (** The result locations are caller-save registers *) @@ -129,7 +128,7 @@ Lemma loc_result_caller_save: forall_rpair (fun r => is_callee_save r = false) (loc_result s). Proof. intros. - unfold loc_result. destruct (sig_res s) as [[]|]; destruct Archi.big_endian; simpl; auto. + unfold loc_result. destruct (proj_sig_res s); destruct Archi.big_endian; simpl; auto. Qed. (** If the result is in a pair of registers, those registers are distinct and have type [Tint] at least. *) @@ -139,14 +138,13 @@ Lemma loc_result_pair: match loc_result sg with | One _ => True | Twolong r1 r2 => - r1 <> r2 /\ sg.(sig_res) = Some Tlong + r1 <> r2 /\ proj_sig_res sg = Tlong /\ subtype Tint (mreg_type r1) = true /\ subtype Tint (mreg_type r2) = true /\ Archi.ptr64 = false end. Proof. - intros; unfold loc_result; destruct (sig_res sg) as [[]|]; destruct Archi.big_endian; auto. - intuition congruence. - intuition congruence. + intros; unfold loc_result; destruct (proj_sig_res sg); auto. + destruct Archi.big_endian; intuition congruence. Qed. (** The location of the result depends only on the result part of the signature *) @@ -154,7 +152,7 @@ Qed. Lemma loc_result_exten: forall s1 s2, s1.(sig_res) = s2.(sig_res) -> loc_result s1 = loc_result s2. Proof. - intros. unfold loc_result. rewrite H; auto. + intros. unfold loc_result, proj_sig_res. rewrite H; auto. Qed. (** ** Location of function arguments *) @@ -271,48 +269,6 @@ Definition loc_arguments (s: signature) : list (rpair loc) := else loc_arguments_hf s.(sig_args) 0 0 0 end. -(** [size_arguments s] returns the number of [Outgoing] slots used - to call a function with signature [s]. *) - -Fixpoint size_arguments_hf (tyl: list typ) (ir fr ofs: Z) {struct tyl} : Z := - match tyl with - | nil => ofs - | (Tint|Tany32) :: tys => - if zlt ir 4 - then size_arguments_hf tys (ir + 1) fr ofs - else size_arguments_hf tys ir fr (ofs + 1) - | (Tfloat|Tany64) :: tys => - if zlt fr 8 - then size_arguments_hf tys ir (fr + 1) ofs - else size_arguments_hf tys ir fr (align ofs 2 + 2) - | Tsingle :: tys => - if zlt fr 8 - then size_arguments_hf tys ir (fr + 1) ofs - else size_arguments_hf tys ir fr (ofs + 1) - | Tlong :: tys => - let ir := align ir 2 in - if zlt ir 4 - then size_arguments_hf tys (ir + 2) fr ofs - else size_arguments_hf tys ir fr (align ofs 2 + 2) - end. - -Fixpoint size_arguments_sf (tyl: list typ) (ofs: Z) {struct tyl} : Z := - match tyl with - | nil => Z.max 0 ofs - | (Tint | Tsingle | Tany32) :: tys => size_arguments_sf tys (ofs + 1) - | (Tfloat | Tlong | Tany64) :: tys => size_arguments_sf tys (align ofs 2 + 2) - end. - -Definition size_arguments (s: signature) : Z := - match Archi.abi with - | Archi.Softfloat => - size_arguments_sf s.(sig_args) (-4) - | Archi.Hardfloat => - if s.(sig_cc).(cc_vararg) - then size_arguments_sf s.(sig_args) (-4) - else size_arguments_hf s.(sig_args) 0 0 0 - end. - (** Argument locations are either non-temporary registers or [Outgoing] stack slots at nonnegative offsets. *) @@ -473,173 +429,15 @@ Qed. Hint Resolve loc_arguments_acceptable: locs. -(** The offsets of [Outgoing] arguments are below [size_arguments s]. *) - -Remark size_arguments_hf_above: - forall tyl ir fr ofs0, - ofs0 <= size_arguments_hf tyl ir fr ofs0. -Proof. - induction tyl; simpl; intros. - omega. - destruct a. - destruct (zlt ir 4); eauto. apply Z.le_trans with (ofs0 + 1); auto; omega. - destruct (zlt fr 8); eauto. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. - set (ir' := align ir 2). - destruct (zlt ir' 4); eauto. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. - destruct (zlt fr 8); eauto. - apply Z.le_trans with (ofs0 + 1); eauto. omega. - destruct (zlt ir 4); eauto. apply Z.le_trans with (ofs0 + 1); auto; omega. - destruct (zlt fr 8); eauto. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. -Qed. - -Remark size_arguments_sf_above: - forall tyl ofs0, - Z.max 0 ofs0 <= size_arguments_sf tyl ofs0. -Proof. - induction tyl; simpl; intros. - omega. - destruct a; (eapply Z.le_trans; [idtac|eauto]). - xomega. - assert (ofs0 <= align ofs0 2) by (apply align_le; omega). xomega. - assert (ofs0 <= align ofs0 2) by (apply align_le; omega). xomega. - xomega. - xomega. - assert (ofs0 <= align ofs0 2) by (apply align_le; omega). xomega. -Qed. - -Lemma size_arguments_above: - forall s, size_arguments s >= 0. -Proof. - intros; unfold size_arguments. apply Z.le_ge. - assert (0 <= size_arguments_sf (sig_args s) (-4)). - { change 0 with (Z.max 0 (-4)). apply size_arguments_sf_above. } - assert (0 <= size_arguments_hf (sig_args s) 0 0 0). - { apply size_arguments_hf_above. } - destruct Archi.abi; [ | destruct (cc_vararg (sig_cc s)) ]; auto. -Qed. - -Lemma loc_arguments_hf_bounded: - forall ofs ty tyl ir fr ofs0, - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments_hf tyl ir fr ofs0)) -> - ofs + typesize ty <= size_arguments_hf tyl ir fr ofs0. -Proof. - induction tyl; simpl; intros. - elim H. - destruct a. -- (* int *) - destruct (zlt ir 4); destruct H. - discriminate. - eauto. - inv H. apply size_arguments_hf_above. - eauto. -- (* float *) - destruct (zlt fr 8); destruct H. - discriminate. - eauto. - inv H. apply size_arguments_hf_above. - eauto. -- (* long *) - destruct (zlt (align ir 2) 4). - destruct H. discriminate. destruct H. discriminate. eauto. - destruct Archi.big_endian. - destruct H. inv H. - eapply Z.le_trans. 2: apply size_arguments_hf_above. simpl; omega. - destruct H. inv H. - rewrite <- Z.add_assoc. simpl. apply size_arguments_hf_above. - eauto. - destruct H. inv H. - rewrite <- Z.add_assoc. simpl. apply size_arguments_hf_above. - destruct H. inv H. - eapply Z.le_trans. 2: apply size_arguments_hf_above. simpl; omega. - eauto. -- (* float *) - destruct (zlt fr 8); destruct H. - discriminate. - eauto. - inv H. apply size_arguments_hf_above. - eauto. -- (* any32 *) - destruct (zlt ir 4); destruct H. - discriminate. - eauto. - inv H. apply size_arguments_hf_above. - eauto. -- (* any64 *) - destruct (zlt fr 8); destruct H. - discriminate. - eauto. - inv H. apply size_arguments_hf_above. - eauto. -Qed. - -Lemma loc_arguments_sf_bounded: - forall ofs ty tyl ofs0, - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments_sf tyl ofs0)) -> - Z.max 0 (ofs + typesize ty) <= size_arguments_sf tyl ofs0. -Proof. - induction tyl; simpl; intros. - elim H. - destruct a. -- (* int *) - destruct H. - destruct (zlt ofs0 0); inv H. apply size_arguments_sf_above. - eauto. -- (* float *) - destruct H. - destruct (zlt (align ofs0 2) 0); inv H. apply size_arguments_sf_above. - eauto. -- (* long *) - destruct H. - destruct Archi.big_endian. - destruct (zlt (align ofs0 2) 0); inv H. - eapply Z.le_trans. 2: apply size_arguments_sf_above. simpl; xomega. - destruct (zlt (align ofs0 2) 0); inv H. - rewrite <- Z.add_assoc. simpl. apply size_arguments_sf_above. - destruct H. - destruct Archi.big_endian. - destruct (zlt (align ofs0 2) 0); inv H. - rewrite <- Z.add_assoc. simpl. apply size_arguments_sf_above. - destruct (zlt (align ofs0 2) 0); inv H. - eapply Z.le_trans. 2: apply size_arguments_sf_above. simpl; xomega. - eauto. -- (* float *) - destruct H. - destruct (zlt ofs0 0); inv H. apply size_arguments_sf_above. - eauto. -- (* any32 *) - destruct H. - destruct (zlt ofs0 0); inv H. apply size_arguments_sf_above. - eauto. -- (* any64 *) - destruct H. - destruct (zlt (align ofs0 2) 0); inv H. apply size_arguments_sf_above. - eauto. -Qed. - -Lemma loc_arguments_bounded: - forall (s: signature) (ofs: Z) (ty: typ), - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments s)) -> - ofs + typesize ty <= size_arguments s. -Proof. - unfold loc_arguments, size_arguments; intros. - assert (In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments_sf (sig_args s) (-4))) -> - ofs + typesize ty <= size_arguments_sf (sig_args s) (-4)). - { intros. eapply Z.le_trans. 2: eapply loc_arguments_sf_bounded; eauto. xomega. } - assert (In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments_hf (sig_args s) 0 0 0)) -> - ofs + typesize ty <= size_arguments_hf (sig_args s) 0 0 0). - { intros. eapply loc_arguments_hf_bounded; eauto. } - destruct Archi.abi; [ | destruct (cc_vararg (sig_cc s)) ]; eauto. -Qed. - Lemma loc_arguments_main: loc_arguments signature_main = nil. Proof. unfold loc_arguments. destruct Archi.abi; reflexivity. Qed. + +(** ** Normalization of function results *) + +(** No normalization needed. *) + +Definition return_value_needs_normalization (t: rettype) := false. diff --git a/arm/NeedOp.v b/arm/NeedOp.v index dee7cae1..c70c7e40 100644 --- a/arm/NeedOp.v +++ b/arm/NeedOp.v @@ -83,6 +83,7 @@ Definition needs_of_operation (op: operation) (nv: nval): list nval := | Omakelong => op2 (default nv) | Olowlong | Ohighlong => op1 (default nv) | Ocmp c => needs_of_condition c + | Osel c ty => nv :: nv :: needs_of_condition c end. Definition operation_is_redundant (op: operation) (nv: nval): bool := @@ -183,6 +184,10 @@ Proof. - apply notint_sound; auto. - apply notint_sound. apply needs_of_shift_sound; auto. - apply needs_of_shift_sound; auto. +- destruct (eval_condition c args m) as [b|] eqn:EC. + erewrite needs_of_condition_sound by eauto. + apply select_sound; auto. + simpl; auto with na. Qed. Lemma operation_is_redundant_sound: @@ -140,7 +140,9 @@ Inductive operation : Type := | Olowlong: operation (**r [rd = low-word(r1)] *) | Ohighlong: operation (**r [rd = high-word(r1)] *) (*c Boolean tests: *) - | Ocmp: condition -> operation. (**r [rd = 1] if condition holds, [rd = 0] otherwise. *) + | Ocmp: condition -> operation (**r [rd = 1] if condition holds, [rd = 0] otherwise. *) + | Osel: condition -> typ -> operation. + (**r [rd = rs1] if condition holds, [rd = rs2] otherwise. *) (** Addressing modes. [r1], [r2], etc, are the arguments to the addressing. *) @@ -174,7 +176,7 @@ Defined. Definition eq_operation (x y: operation): {x=y} + {x<>y}. Proof. - generalize Int.eq_dec Ptrofs.eq_dec ident_eq; intros. + generalize Int.eq_dec Ptrofs.eq_dec ident_eq typ_eq; intros. generalize Float.eq_dec Float32.eq_dec; intros. generalize eq_shift; intro. generalize eq_condition; intro. @@ -294,6 +296,7 @@ Definition eval_operation | Olowlong, v1::nil => Some(Val.loword v1) | Ohighlong, v1::nil => Some(Val.hiword v1) | Ocmp c, _ => Some(Val.of_optbool (eval_condition c vl m)) + | Osel c ty, v1::v2::vl => Some(Val.select (eval_condition c vl m) v1 v2 ty) | _, _ => None end. @@ -419,6 +422,7 @@ Definition type_of_operation (op: operation) : list typ * typ := | Olowlong => (Tlong :: nil, Tint) | Ohighlong => (Tlong :: nil, Tint) | Ocmp c => (type_of_condition c, Tint) + | Osel c ty => (ty :: ty :: type_of_condition c, ty) end. Definition type_of_addressing (addr: addressing) : list typ := @@ -511,6 +515,7 @@ Proof with (try exact I; try reflexivity). destruct v0... destruct v0... destruct (eval_condition c vl m)... destruct b... + unfold Val.select. destruct (eval_condition c vl m). apply Val.normalize_type. exact I. Qed. End SOUNDNESS. @@ -532,7 +537,7 @@ Lemma mk_shift_amount_eq: forall n, Int.ltu n Int.iwordsize = true -> s_amount (mk_shift_amount n) = n. Proof. intros; simpl. unfold Int.modu. transitivity (Int.repr (Int.unsigned n)). - decEq. apply Zmod_small. apply Int.ltu_inv; auto. + decEq. apply Z.mod_small. apply Int.ltu_inv; auto. apply Int.repr_unsigned. Qed. @@ -682,19 +687,37 @@ Definition is_trivial_op (op: operation) : bool := (** Operations that depend on the memory state. *) +Definition condition_depends_on_memory (c: condition) : bool := + match c with + | Ccompu _ | Ccompushift _ _| Ccompuimm _ _ => true + | _ => false + end. + Definition op_depends_on_memory (op: operation) : bool := match op with - | Ocmp (Ccompu _ | Ccompushift _ _| Ccompuimm _ _) => true + | Ocmp c => condition_depends_on_memory c + | Osel c ty => condition_depends_on_memory c | _ => false end. +Lemma condition_depends_on_memory_correct: + forall c args m1 m2, + condition_depends_on_memory c = false -> + eval_condition c args m1 = eval_condition c args m2. +Proof. + intros. destruct c; simpl; auto; discriminate. +Qed. + Lemma op_depends_on_memory_correct: forall (F V: Type) (ge: Genv.t F V) sp op args m1 m2, op_depends_on_memory op = false -> eval_operation ge sp op args m1 = eval_operation ge sp op args m2. Proof. - intros until m2. destruct op; simpl; try congruence. - intros. destruct c; simpl; auto; congruence. + intros until m2. destruct op; simpl; try congruence; intros C. +- f_equal; f_equal; apply condition_depends_on_memory_correct; auto. +- destruct args; auto. destruct args; auto. + rewrite (condition_depends_on_memory_correct c args m1 m2 C). + auto. Qed. (** Global variables mentioned in an operation or addressing mode *) @@ -929,6 +952,10 @@ Proof. exploit eval_condition_inj; eauto. intros EQ; rewrite EQ. destruct b; simpl; constructor. simpl; constructor. + + apply Val.select_inject; auto. + destruct (eval_condition c vl1 m1) eqn:?; auto. + right; symmetry; eapply eval_condition_inj; eauto. Qed. Lemma eval_addressing_inj: diff --git a/arm/PrintOp.ml b/arm/PrintOp.ml index 642fff80..d74acf3f 100644 --- a/arm/PrintOp.ml +++ b/arm/PrintOp.ml @@ -129,6 +129,10 @@ let print_operation reg pp = function | Olowlong, [r1] -> fprintf pp "lowlong(%a)" reg r1 | Ohighlong, [r1] -> fprintf pp "highlong(%a)" reg r1 | Ocmp c, args -> print_condition reg pp (c, args) + | Osel (c, ty), r1::r2::args -> + fprintf pp "%a ?%s %a : %a" + (print_condition reg) (c, args) + (PrintAST.name_of_type ty) reg r1 reg r2 | _ -> fprintf pp "<bad operator>" let print_addressing reg pp = function diff --git a/arm/SelectOp.vp b/arm/SelectOp.vp index c361df65..1220abc4 100644 --- a/arm/SelectOp.vp +++ b/arm/SelectOp.vp @@ -38,11 +38,8 @@ Require Import Coqlib. Require Import Compopts. -Require Import AST. -Require Import Integers. -Require Import Floats. -Require Import Op. -Require Import CminorSel. +Require Import AST Integers Floats Builtins. +Require Import Op CminorSel. Local Open Scope cminorsel_scope. @@ -382,6 +379,16 @@ Definition compf (c: comparison) (e1: expr) (e2: expr) := Definition compfs (c: comparison) (e1: expr) (e2: expr) := Eop (Ocmp (Ccompfs c)) (e1 ::: e2 ::: Enil). +Definition select (ty: typ) (cond: condition) (args: exprlist) (e1 e2: expr) := + if match ty with + | Tint => true + | Tfloat => true + | Tsingle => true + | _ => false + end + then Some (Eop (Osel cond ty) (e1 ::: e2 ::: args)) + else None. + (** ** Integer conversions *) Definition cast8unsigned (e: expr) := andimm (Int.repr 255) e. @@ -508,3 +515,8 @@ Nondetfunction builtin_arg (e: expr) := | Eop (Oaddimm n) (e1:::Enil) => BA_addptr (BA e1) (BA_int n) | _ => BA e end. + +(** Platform-specific known builtins *) + +Definition platform_builtin (b: platform_builtin) (args: exprlist) : option expr := + None. diff --git a/arm/SelectOpproof.v b/arm/SelectOpproof.v index d4aac9b6..70f8f191 100644 --- a/arm/SelectOpproof.v +++ b/arm/SelectOpproof.v @@ -13,16 +13,9 @@ (** Correctness of instruction selection for operators *) Require Import Coqlib. -Require Import Maps. -Require Import AST. -Require Import Integers. -Require Import Floats. -Require Import Values. -Require Import Memory. -Require Import Globalenvs. -Require Import Cminor. -Require Import Op. -Require Import CminorSel. +Require Import AST Integers Floats. +Require Import Values Memory Builtins Globalenvs. +Require Import Cminor Op CminorSel. Require Import SelectOp. Local Open Scope cminorsel_scope. @@ -735,6 +728,22 @@ Proof. intros; red; intros. unfold compfs. TrivialExists. Qed. +Theorem eval_select: + forall le ty cond al vl a1 v1 a2 v2 a b, + select ty cond al a1 a2 = Some a -> + eval_exprlist ge sp e m le al vl -> + eval_expr ge sp e m le a1 v1 -> + eval_expr ge sp e m le a2 v2 -> + eval_condition cond vl m = Some b -> + exists v, + eval_expr ge sp e m le a v + /\ Val.lessdef (Val.select (Some b) v1 v2 ty) v. +Proof. + unfold select; intros. + destruct (match ty with Tint | Tfloat | Tsingle => true | _ => false end); inv H. + rewrite <- H3; TrivialExists. +Qed. + Theorem eval_cast8signed: unary_constructor_sound cast8signed (Val.sign_ext 8). Proof. red; intros until x. unfold cast8signed; case (cast8signed_match a); intros. @@ -745,7 +754,7 @@ Qed. Theorem eval_cast8unsigned: unary_constructor_sound cast8unsigned (Val.zero_ext 8). Proof. red; intros until x. unfold cast8unsigned. - rewrite Val.zero_ext_and. apply eval_andimm. compute; auto. + rewrite Val.zero_ext_and. apply eval_andimm. omega. Qed. Theorem eval_cast16signed: unary_constructor_sound cast16signed (Val.sign_ext 16). @@ -758,7 +767,7 @@ Qed. Theorem eval_cast16unsigned: unary_constructor_sound cast16unsigned (Val.zero_ext 16). Proof. red; intros until x. unfold cast8unsigned. - rewrite Val.zero_ext_and. apply eval_andimm. compute; auto. + rewrite Val.zero_ext_and. apply eval_andimm. omega. Qed. Theorem eval_singleoffloat: unary_constructor_sound singleoffloat Val.singleoffloat. @@ -893,4 +902,16 @@ Proof. - constructor; auto. Qed. +(** Platform-specific known builtins *) + +Theorem eval_platform_builtin: + forall bf al a vl v le, + platform_builtin bf al = Some a -> + eval_exprlist ge sp e m le al vl -> + platform_builtin_sem bf vl = Some v -> + exists v', eval_expr ge sp e m le a v' /\ Val.lessdef v v'. +Proof. + intros. discriminate. +Qed. + End CMCONSTR. diff --git a/arm/Stacklayout.v b/arm/Stacklayout.v index 462d83ad..8e91f14b 100644 --- a/arm/Stacklayout.v +++ b/arm/Stacklayout.v @@ -64,7 +64,7 @@ Lemma frame_env_separated: ** range sp (fe_ofs_callee_save fe) (size_callee_save_area b (fe_ofs_callee_save fe)) ** P. Proof. -Local Opaque Z.add Z.mul sepconj range. +Local Opaque Z.add Z.mul sepconj range'. intros; simpl. set (olink := 4 * b.(bound_outgoing)); set (ora := olink + 4); diff --git a/arm/TargetPrinter.ml b/arm/TargetPrinter.ml index bf37b0e4..03e06a65 100644 --- a/arm/TargetPrinter.ml +++ b/arm/TargetPrinter.ml @@ -113,9 +113,9 @@ struct let freg_single oc r = output_string oc (single_float_reg_name r) let freg_param_single oc r = output_string oc (single_param_reg_name r) - let preg oc = function + let preg_asm oc ty = function | IR r -> ireg oc r - | FR r -> freg oc r + | FR r -> if ty = Tsingle then freg_single oc r else freg oc r | _ -> assert false (* In Thumb2 mode, some arithmetic instructions have shorter encodings @@ -148,9 +148,9 @@ struct let name_of_section = function | Section_text -> ".text" | Section_data i | Section_small_data i -> - if i then ".data" else "COMM" + if i then ".data" else common_section () | Section_const i | Section_small_const i -> - if i then ".section .rodata" else "COMM" + if i || (not !Clflags.option_fcommon) then ".section .rodata" else "COMM" | Section_string -> ".section .rodata" | Section_literal -> ".text" | Section_jumptable -> ".text" @@ -443,6 +443,12 @@ struct (condition_name cond) ireg r1 shift_op ifso; fprintf oc " mov%s %a, %a\n" (neg_condition_name cond) ireg r1 shift_op ifnot + | Pfmovite(cond, r1, ifso, ifnot) -> + fprintf oc " ite %s\n" (condition_name cond); + fprintf oc " vmov%s.f64 %a, %a\n" + (condition_name cond) freg r1 freg ifso; + fprintf oc " vmov%s.f64 %a, %a\n" + (neg_condition_name cond) freg r1 freg ifnot | Pbtbl(r, tbl) -> if !Clflags.option_mthumb then begin fprintf oc " lsl r14, %a, #2\n" ireg r; @@ -474,7 +480,7 @@ struct (P.to_int kind) (extern_atom txt) args | EF_inline_asm(txt, sg, clob) -> fprintf oc "%s begin inline assembly\n\t" comment; - print_inline_asm preg oc (camlstring_of_coqstring txt) sg args res; + print_inline_asm preg_asm oc (camlstring_of_coqstring txt) sg args res; fprintf oc "%s end inline assembly\n" comment | _ -> assert false diff --git a/arm/ValueAOp.v b/arm/ValueAOp.v index e19ddd6d..a3fd9d7d 100644 --- a/arm/ValueAOp.v +++ b/arm/ValueAOp.v @@ -127,6 +127,7 @@ Definition eval_static_operation (op: operation) (vl: list aval): aval := | Olowlong, v1::nil => loword v1 | Ohighlong, v1::nil => hiword v1 | Ocmp c, _ => of_optbool (eval_static_condition c vl) + | Osel c ty, v1::v2::vl => select (eval_static_condition c vl) v1 v2 | _, _ => Vbot end. @@ -205,6 +206,7 @@ Proof. rewrite Ptrofs.add_zero_l; eauto with va. fold (Val.sub (Vint i) a1). auto with va. apply of_optbool_sound. eapply eval_static_condition_sound; eauto. + apply select_sound; auto. eapply eval_static_condition_sound; eauto. Qed. End SOUNDNESS. diff --git a/backend/Allocation.v b/backend/Allocation.v index cf62295d..08e0a4f4 100644 --- a/backend/Allocation.v +++ b/backend/Allocation.v @@ -36,7 +36,7 @@ Require Import Op Registers RTL Locations Conventions RTLtyping LTL. - a [Lbranch s] instruction. The [block_shape] type below describes all possible cases of structural - maching between an RTL instruction and an LTL basic block. + matching between an RTL instruction and an LTL basic block. *) Inductive move: Type := @@ -734,11 +734,11 @@ Function add_equations_args (rl: list reg) (tyl: list typ) (ll: list (rpair loc) (** [add_equations_res] is similar but is specialized to the case where there is only one pseudo-register. *) -Function add_equations_res (r: reg) (oty: option typ) (p: rpair mreg) (e: eqs) : option eqs := - match p, oty with +Function add_equations_res (r: reg) (ty: typ) (p: rpair mreg) (e: eqs) : option eqs := + match p, ty with | One mr, _ => Some (add_equation (Eq Full r (R mr)) e) - | Twolong mr1 mr2, Some Tlong => + | Twolong mr1 mr2, Tlong => if Archi.ptr64 then None else Some (add_equation (Eq Low r (R mr2)) (add_equation (Eq High r (R mr1)) e)) | _, _ => @@ -1084,7 +1084,7 @@ Definition transfer_aux (f: RTL.function) (env: regenv) | BStailcall sg ros args mv1 ros' => let args' := loc_arguments sg in assertion (tailcall_is_possible sg); - assertion (opt_typ_eq sg.(sig_res) f.(RTL.fn_sig).(sig_res)); + assertion (rettype_eq sg.(sig_res) f.(RTL.fn_sig).(sig_res)); assertion (ros_compatible_tailcall ros'); do e1 <- add_equation_ros ros ros' empty_eqs; do e2 <- add_equations_args args (sig_args sg) args' e1; @@ -1114,7 +1114,7 @@ Definition transfer_aux (f: RTL.function) (env: regenv) track_moves env mv empty_eqs | BSreturn (Some arg) mv => let arg' := loc_result (RTL.fn_sig f) in - do e1 <- add_equations_res arg (sig_res (RTL.fn_sig f)) arg' empty_eqs; + do e1 <- add_equations_res arg (proj_sig_res (RTL.fn_sig f)) arg' empty_eqs; track_moves env mv e1 end. diff --git a/backend/Allocproof.v b/backend/Allocproof.v index 1804f46b..51755912 100644 --- a/backend/Allocproof.v +++ b/backend/Allocproof.v @@ -1301,10 +1301,10 @@ Proof. Qed. Lemma add_equations_res_lessdef: - forall r oty l e e' rs ls, - add_equations_res r oty l e = Some e' -> + forall r ty l e e' rs ls, + add_equations_res r ty l e = Some e' -> satisf rs ls e' -> - Val.has_type rs#r (match oty with Some ty => ty | None => Tint end) -> + Val.has_type rs#r ty -> Val.lessdef rs#r (Locmap.getpair (map_rpair R l) ls). Proof. intros. functional inversion H; simpl. @@ -1892,7 +1892,7 @@ Qed. Inductive match_stackframes: list RTL.stackframe -> list LTL.stackframe -> signature -> Prop := | match_stackframes_nil: forall sg, - sg.(sig_res) = Some Tint -> + sg.(sig_res) = Tint -> match_stackframes nil nil sg | match_stackframes_cons: forall res f sp pc rs s tf bb ls ts sg an e env @@ -2425,13 +2425,13 @@ Proof. (return_regs (parent_locset ts) ls1)) with (Locmap.getpair (map_rpair R (loc_result (RTL.fn_sig f))) ls1). eapply add_equations_res_lessdef; eauto. - rewrite H13. apply WTRS. + rewrite <- H14. apply WTRS. generalize (loc_result_caller_save (RTL.fn_sig f)). destruct (loc_result (RTL.fn_sig f)); simpl. intros A; rewrite A; auto. intros [A B]; rewrite A, B; auto. apply return_regs_agree_callee_save. - unfold proj_sig_res. rewrite <- H11; rewrite H13. apply WTRS. + rewrite <- H11, <- H14. apply WTRS. (* internal function *) - monadInv FUN. simpl in *. @@ -2463,7 +2463,8 @@ Proof. simpl. destruct (loc_result (ef_sig ef)) eqn:RES; simpl. rewrite Locmap.gss; auto. generalize (loc_result_pair (ef_sig ef)); rewrite RES; intros (A & B & C & D & E). - exploit external_call_well_typed; eauto. unfold proj_sig_res; rewrite B. intros WTRES'. + assert (WTRES': Val.has_type v' Tlong). + { rewrite <- B. eapply external_call_well_typed; eauto. } rewrite Locmap.gss. rewrite Locmap.gso by (red; auto). rewrite Locmap.gss. rewrite val_longofwords_eq_1 by auto. auto. red; intros. rewrite (AG l H0). diff --git a/backend/Asmexpandaux.ml b/backend/Asmexpandaux.ml index f5c76925..0530abe4 100644 --- a/backend/Asmexpandaux.ml +++ b/backend/Asmexpandaux.ml @@ -96,7 +96,7 @@ let translate_annot sp preg_to_dwarf annot = | a::_ -> aux a) let builtin_nop = - let signature ={sig_args = []; sig_res = None; sig_cc = cc_default} in + let signature ={sig_args = []; sig_res = Tvoid; sig_cc = cc_default} in let name = coqstring_of_camlstring "__builtin_nop" in Pbuiltin(EF_builtin(name,signature),[],BR_none) diff --git a/backend/Asmexpandaux.mli b/backend/Asmexpandaux.mli index d80b4aec..e2320418 100644 --- a/backend/Asmexpandaux.mli +++ b/backend/Asmexpandaux.mli @@ -22,7 +22,7 @@ val emit: instruction -> unit val new_label: unit -> label (* Compute a fresh label *) val is_current_function_variadic: unit -> bool - (* Test wether the current function is a variadic function *) + (* Test whether the current function is a variadic function *) val get_current_function_args: unit -> typ list (* Get the types of the current function arguments *) val get_current_function_sig: unit -> signature @@ -33,4 +33,4 @@ val get_current_function: unit -> coq_function (* Get the current function *) val expand: positive -> int -> (preg -> int) -> (instruction -> unit) -> instruction list -> unit (* Expand the instruction sequence of a function. Takes the function id, the register number of the stackpointer, a - function to get the dwarf mapping of varibale names and for the expansion of simple instructions *) + function to get the dwarf mapping of variable names and for the expansion of simple instructions *) diff --git a/backend/Asmgenproof0.v b/backend/Asmgenproof0.v index 70c4323c..3638c465 100644 --- a/backend/Asmgenproof0.v +++ b/backend/Asmgenproof0.v @@ -897,6 +897,55 @@ Proof. apply code_tail_next_int with i; auto. Qed. +(** A variant that supports zero steps of execution *) + +Inductive exec_straight_opt: code -> regset -> mem -> code -> regset -> mem -> Prop := + | exec_straight_opt_refl: forall c rs m, + exec_straight_opt c rs m c rs m + | exec_straight_opt_intro: forall c1 rs1 m1 c2 rs2 m2, + exec_straight c1 rs1 m1 c2 rs2 m2 -> + exec_straight_opt c1 rs1 m1 c2 rs2 m2. + +Lemma exec_straight_opt_left: + forall c3 rs3 m3 c1 rs1 m1 c2 rs2 m2, + exec_straight c1 rs1 m1 c2 rs2 m2 -> + exec_straight_opt c2 rs2 m2 c3 rs3 m3 -> + exec_straight c1 rs1 m1 c3 rs3 m3. +Proof. + destruct 2; intros. auto. eapply exec_straight_trans; eauto. +Qed. + +Lemma exec_straight_opt_right: + forall c3 rs3 m3 c1 rs1 m1 c2 rs2 m2, + exec_straight_opt c1 rs1 m1 c2 rs2 m2 -> + exec_straight c2 rs2 m2 c3 rs3 m3 -> + exec_straight c1 rs1 m1 c3 rs3 m3. +Proof. + destruct 1; intros. auto. eapply exec_straight_trans; eauto. +Qed. + +Lemma exec_straight_opt_step: + forall i c rs1 m1 rs2 m2 c' rs3 m3, + exec_instr ge fn i rs1 m1 = Next rs2 m2 -> + rs2#PC = Val.offset_ptr rs1#PC Ptrofs.one -> + exec_straight_opt c rs2 m2 c' rs3 m3 -> + exec_straight (i :: c) rs1 m1 c' rs3 m3. +Proof. + intros. inv H1. +- apply exec_straight_one; auto. +- eapply exec_straight_step; eauto. +Qed. + +Lemma exec_straight_opt_step_opt: + forall i c rs1 m1 rs2 m2 c' rs3 m3, + exec_instr ge fn i rs1 m1 = Next rs2 m2 -> + rs2#PC = Val.offset_ptr rs1#PC Ptrofs.one -> + exec_straight_opt c rs2 m2 c' rs3 m3 -> + exec_straight_opt (i :: c) rs1 m1 c' rs3 m3. +Proof. + intros. apply exec_straight_opt_intro. eapply exec_straight_opt_step; eauto. +Qed. + End STRAIGHTLINE. (** * Properties of the Mach call stack *) diff --git a/backend/CSE.v b/backend/CSE.v index 6d3f6f33..ecfa1f9e 100644 --- a/backend/CSE.v +++ b/backend/CSE.v @@ -14,7 +14,7 @@ proceeds by value numbering over extended basic blocks. *) Require Import Coqlib Maps Errors Integers Floats Lattice Kildall. -Require Import AST Linking. +Require Import AST Linking Builtins. Require Import Values Memory. Require Import Op Registers RTL. Require Import ValueDomain ValueAnalysis CSEdomain CombineOp. @@ -444,10 +444,10 @@ Module Solver := BBlock_solver(Numbering). ([EF_external], [EF_runtime], [EF_malloc], [EF_free]). - Forget equations involving loads but keep equations over registers. This is appropriate for builtins that can modify memory, - e.g. volatile stores, or [EF_builtin] + e.g. volatile stores, or [EF_builtin] for unknown builtin functions. - Keep all equations, taking advantage of the fact that neither memory - nor registers are modified. This is appropriate for annotations - and for volatile loads. + nor registers are modified. This is appropriate for annotations, + volatile loads, and known builtin functions. *) Definition transfer (f: function) (approx: PMap.t VA.t) (pc: node) (before: numbering) := @@ -473,8 +473,13 @@ Definition transfer (f: function) (approx: PMap.t VA.t) (pc: node) (before: numb match ef with | EF_external _ _ | EF_runtime _ _ | EF_malloc | EF_free | EF_inline_asm _ _ _ => empty_numbering - | EF_builtin _ _ | EF_vstore _ => + | EF_vstore _ => set_res_unknown (kill_all_loads before) res + | EF_builtin name sg => + match lookup_builtin_function name sg with + | Some bf => set_res_unknown before res + | None => set_res_unknown (kill_all_loads before) res + end | EF_memcpy sz al => match args with | dst :: src :: nil => diff --git a/backend/CSEproof.v b/backend/CSEproof.v index d6bde348..03c7ecfc 100644 --- a/backend/CSEproof.v +++ b/backend/CSEproof.v @@ -14,7 +14,7 @@ Require Import Coqlib Maps Errors Integers Floats Lattice Kildall. Require Import AST Linking. -Require Import Values Memory Events Globalenvs Smallstep. +Require Import Values Memory Builtins Events Globalenvs Smallstep. Require Import Op Registers RTL. Require Import ValueDomain ValueAOp ValueAnalysis. Require Import CSEdomain CombineOp CombineOpproof CSE. @@ -544,7 +544,7 @@ Lemma kill_loads_after_storebytes_holds: bc sp = BCstack -> ematch bc rs ae -> approx = VA.State ae am -> - length bytes = nat_of_Z sz -> sz >= 0 -> + length bytes = Z.to_nat sz -> sz >= 0 -> numbering_holds valu ge (Vptr sp Ptrofs.zero) rs m' (kill_loads_after_storebytes approx n dst sz). Proof. @@ -557,7 +557,7 @@ Proof. simpl. rewrite negb_false_iff in H8. eapply Mem.load_storebytes_other. eauto. - rewrite H6. rewrite nat_of_Z_eq by auto. + rewrite H6. rewrite Z2Nat.id by omega. eapply pdisjoint_sound. eauto. unfold aaddressing. apply match_aptr_of_aval. eapply eval_static_addressing_sound; eauto. erewrite <- regs_valnums_sound by eauto. eauto with va. @@ -598,9 +598,9 @@ Proof. exploit Mem.storebytes_split; eauto. intros (m2 & SB2 & SB3). clear SB23. assert (L1: Z.of_nat (length bytes1) = n1). - { erewrite Mem.loadbytes_length by eauto. apply nat_of_Z_eq. unfold n1; omega. } + { erewrite Mem.loadbytes_length by eauto. apply Z2Nat.id. unfold n1; omega. } assert (L2: Z.of_nat (length bytes2) = n2). - { erewrite Mem.loadbytes_length by eauto. apply nat_of_Z_eq. unfold n2; omega. } + { erewrite Mem.loadbytes_length by eauto. apply Z2Nat.id. unfold n2; omega. } rewrite L1 in *. rewrite L2 in *. assert (LB': Mem.loadbytes m2 b2 (ofs2 + n1) n2 = Some bytes2). { rewrite <- L2. eapply Mem.loadbytes_storebytes_same; eauto. } @@ -1129,7 +1129,9 @@ Proof. { exists valu. apply set_res_unknown_holds. eapply kill_all_loads_hold; eauto. } destruct ef. + apply CASE1. - + apply CASE3. + + destruct (lookup_builtin_function name sg) as [bf|] eqn:LK. + ++ apply CASE2. simpl in H1; red in H1; rewrite LK in H1; inv H1. auto. + ++ apply CASE3. + apply CASE1. + apply CASE2; inv H1; auto. + apply CASE3. diff --git a/backend/Cminor.v b/backend/Cminor.v index 11941da3..91a4c104 100644 --- a/backend/Cminor.v +++ b/backend/Cminor.v @@ -591,6 +591,70 @@ Proof. red; intros; inv H; simpl; try omega; eapply external_call_trace_length; eauto. Qed. +(** This semantics is determinate. *) + +Lemma eval_expr_determ: + forall ge sp e m a v, eval_expr ge sp e m a v -> + forall v', eval_expr ge sp e m a v' -> v' = v. +Proof. + induction 1; intros v' E'; inv E'. +- congruence. +- congruence. +- assert (v0 = v1) by eauto. congruence. +- assert (v0 = v1) by eauto. assert (v3 = v2) by eauto. congruence. +- assert (vaddr0 = vaddr) by eauto. congruence. +Qed. + +Lemma eval_exprlist_determ: + forall ge sp e m al vl, eval_exprlist ge sp e m al vl -> + forall vl', eval_exprlist ge sp e m al vl' -> vl' = vl. +Proof. + induction 1; intros vl' E'; inv E'. + - auto. + - f_equal; eauto using eval_expr_determ. +Qed. + +Ltac Determ := + try congruence; + match goal with + | [ |- match_traces _ E0 E0 /\ (_ -> _) ] => + split; [constructor|intros _; Determ] + | [ H: is_call_cont ?k |- _ ] => + contradiction || (clear H; Determ) + | [ H1: eval_expr _ _ _ _ ?a ?v1, H2: eval_expr _ _ _ _ ?a ?v2 |- _ ] => + assert (v1 = v2) by (eapply eval_expr_determ; eauto); + clear H1 H2; Determ + | [ H1: eval_exprlist _ _ _ _ ?a ?v1, H2: eval_exprlist _ _ _ _ ?a ?v2 |- _ ] => + assert (v1 = v2) by (eapply eval_exprlist_determ; eauto); + clear H1 H2; Determ + | _ => idtac + end. + +Lemma semantics_determinate: + forall (p: program), determinate (semantics p). +Proof. + intros. constructor; set (ge := Genv.globalenv p); simpl; intros. +- (* determ *) + inv H; inv H0; Determ. + + subst vargs0. exploit external_call_determ. eexact H2. eexact H13. + intros (A & B). split; intros; auto. + apply B in H; destruct H; congruence. + + subst v0. assert (b0 = b) by (inv H2; inv H13; auto). subst b0; auto. + + assert (n0 = n) by (inv H2; inv H14; auto). subst n0; auto. + + exploit external_call_determ. eexact H1. eexact H7. + intros (A & B). split; intros; auto. + apply B in H; destruct H; congruence. +- (* single event *) + red; simpl. destruct 1; simpl; try omega; + eapply external_call_trace_length; eauto. +- (* initial states *) + inv H; inv H0. unfold ge0, ge1 in *. congruence. +- (* nostep final state *) + red; intros; red; intros. inv H; inv H0. +- (* final states *) + inv H; inv H0; auto. +Qed. + (** * Alternate operational semantics (big-step) *) (** We now define another semantics for Cminor without [goto] that follows @@ -612,12 +676,24 @@ Definition outcome_block (out: outcome) : outcome := | out => out end. +(* +Definition outcome_result_value + (out: outcome) (retsig: rettype) (vres: val) : Prop := + match out with + | Out_normal => vres = Vundef + | Out_return None => vres = Vundef + | Out_return (Some v) => retsig <> Tvoid /\ vres = v + | Out_tailcall_return v => vres = v + | _ => False + end. +*) + Definition outcome_result_value - (out: outcome) (retsig: option typ) (vres: val) : Prop := + (out: outcome) (vres: val) : Prop := match out with | Out_normal => vres = Vundef | Out_return None => vres = Vundef - | Out_return (Some v) => retsig <> None /\ vres = v + | Out_return (Some v) => vres = v | Out_tailcall_return v => vres = v | _ => False end. @@ -647,7 +723,7 @@ Inductive eval_funcall: Mem.alloc m 0 f.(fn_stackspace) = (m1, sp) -> set_locals f.(fn_vars) (set_params vargs f.(fn_params)) = e -> exec_stmt f (Vptr sp Ptrofs.zero) e m1 f.(fn_body) t e2 m2 out -> - outcome_result_value out f.(fn_sig).(sig_res) vres -> + outcome_result_value out vres -> outcome_free_mem out m2 sp f.(fn_stackspace) m3 -> eval_funcall m (Internal f) vargs t m3 vres | eval_funcall_external: @@ -931,7 +1007,7 @@ Proof. subst vres. replace k with (call_cont k') by congruence. apply star_one. apply step_return_0; auto. (* Out_return Some *) - destruct H3. subst vres. + subst vres. replace k with (call_cont k') by congruence. apply star_one. eapply step_return_1; eauto. (* Out_tailcall_return *) diff --git a/backend/Cminortyping.v b/backend/Cminortyping.v new file mode 100644 index 00000000..92ec45f2 --- /dev/null +++ b/backend/Cminortyping.v @@ -0,0 +1,803 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +Require Import Coqlib Maps Errors. +Require Import AST Integers Floats Values Memory Globalenvs Events Smallstep. +Require Import Cminor. +Require Import Unityping. + +Local Open Scope string_scope. +Local Open Scope error_monad_scope. + +(** * Type inference algorithm *) + +Definition type_constant (c: constant) : typ := + match c with + | Ointconst _ => Tint + | Ofloatconst _ => Tfloat + | Osingleconst _ => Tsingle + | Olongconst _ => Tlong + | Oaddrsymbol _ _ => Tptr + | Oaddrstack _ => Tptr + end. + +Definition type_unop (op: unary_operation) : typ * typ := + match op with + | Ocast8unsigned | Ocast8signed | Ocast16unsigned | Ocast16signed + | Onegint | Onotint => (Tint, Tint) + | Onegf | Oabsf => (Tfloat, Tfloat) + | Onegfs | Oabsfs => (Tsingle, Tsingle) + | Osingleoffloat => (Tfloat, Tsingle) + | Ofloatofsingle => (Tsingle, Tfloat) + | Ointoffloat | Ointuoffloat => (Tfloat, Tint) + | Ofloatofint | Ofloatofintu => (Tint, Tfloat) + | Ointofsingle | Ointuofsingle => (Tsingle, Tint) + | Osingleofint | Osingleofintu => (Tint, Tsingle) + | Onegl | Onotl => (Tlong, Tlong) + | Ointoflong => (Tlong, Tint) + | Olongofint | Olongofintu => (Tint, Tlong) + | Olongoffloat | Olonguoffloat => (Tfloat, Tlong) + | Ofloatoflong | Ofloatoflongu => (Tlong, Tfloat) + | Olongofsingle | Olonguofsingle => (Tsingle, Tlong) + | Osingleoflong | Osingleoflongu => (Tlong, Tsingle) + end. + +Definition type_binop (op: binary_operation) : typ * typ * typ := + match op with + | Oadd | Osub | Omul | Odiv | Odivu | Omod | Omodu + | Oand | Oor | Oxor | Oshl | Oshr | Oshru => (Tint, Tint, Tint) + | Oaddf | Osubf | Omulf | Odivf => (Tfloat, Tfloat, Tfloat) + | Oaddfs| Osubfs| Omulfs| Odivfs => (Tsingle, Tsingle, Tsingle) + | Oaddl | Osubl | Omull | Odivl | Odivlu | Omodl | Omodlu + | Oandl | Oorl | Oxorl => (Tlong, Tlong, Tlong) + | Oshll | Oshrl | Oshrlu => (Tlong, Tint, Tlong) + | Ocmp _ | Ocmpu _ => (Tint, Tint, Tint) + | Ocmpf _ => (Tfloat, Tfloat, Tint) + | Ocmpfs _ => (Tsingle, Tsingle, Tint) + | Ocmpl _ | Ocmplu _ => (Tlong, Tlong, Tint) + end. + +Module RTLtypes <: TYPE_ALGEBRA. + +Definition t := typ. +Definition eq := typ_eq. +Definition default := Tint. + +End RTLtypes. + +Module S := UniSolver(RTLtypes). + +Definition expect (e: S.typenv) (t1 t2: typ) : res S.typenv := + if typ_eq t1 t2 then OK e else Error (msg "type mismatch"). + +Fixpoint type_expr (e: S.typenv) (a: expr) (t: typ) : res S.typenv := + match a with + | Evar id => S.set e id t + | Econst c => expect e (type_constant c) t + | Eunop op a1 => + let '(targ1, tres) := type_unop op in + do e1 <- type_expr e a1 targ1; + expect e1 tres t + | Ebinop op a1 a2 => + let '(targ1, targ2, tres) := type_binop op in + do e1 <- type_expr e a1 targ1; + do e2 <- type_expr e1 a2 targ2; + expect e2 tres t + | Eload chunk a1 => + do e1 <- type_expr e a1 Tptr; + expect e1 (type_of_chunk chunk) t + end. + +Fixpoint type_exprlist (e: S.typenv) (al: list expr) (tl: list typ) : res S.typenv := + match al, tl with + | nil, nil => OK e + | a :: al, t :: tl => do e1 <- type_expr e a t; type_exprlist e1 al tl + | _, _ => Error (msg "arity mismatch") + end. + +Definition type_assign (e: S.typenv) (id: ident) (a: expr) : res S.typenv := + match a with + | Evar id' => + do (changed, e1) <- S.move e id id'; OK e1 + | Econst c => + S.set e id (type_constant c) + | Eunop op a1 => + let '(targ1, tres) := type_unop op in + do e1 <- type_expr e a1 targ1; + S.set e1 id tres + | Ebinop op a1 a2 => + let '(targ1, targ2, tres) := type_binop op in + do e1 <- type_expr e a1 targ1; + do e2 <- type_expr e1 a2 targ2; + S.set e2 id tres + | Eload chunk a1 => + do e1 <- type_expr e a1 Tptr; + S.set e1 id (type_of_chunk chunk) + end. + +Definition opt_set (e: S.typenv) (optid: option ident) (ty: typ) : res S.typenv := + match optid with + | None => OK e + | Some id => S.set e id ty + end. + +Fixpoint type_stmt (tret: rettype) (e: S.typenv) (s: stmt) : res S.typenv := + match s with + | Sskip => OK e + | Sassign id a => type_assign e id a + | Sstore chunk a1 a2 => + do e1 <- type_expr e a1 Tptr; type_expr e1 a2 (type_of_chunk chunk) + | Scall optid sg fn args => + do e1 <- type_expr e fn Tptr; + do e2 <- type_exprlist e1 args sg.(sig_args); + opt_set e2 optid (proj_sig_res sg) + | Stailcall sg fn args => + assertion (rettype_eq sg.(sig_res) tret); + do e1 <- type_expr e fn Tptr; + type_exprlist e1 args sg.(sig_args) + | Sbuiltin optid ef args => + let sg := ef_sig ef in + do e1 <- type_exprlist e args sg.(sig_args); + opt_set e1 optid (proj_sig_res sg) + | Sseq s1 s2 => + do e1 <- type_stmt tret e s1; type_stmt tret e1 s2 + | Sifthenelse a s1 s2 => + do e1 <- type_expr e a Tint; + do e2 <- type_stmt tret e1 s1; + type_stmt tret e2 s2 + | Sloop s1 => + type_stmt tret e s1 + | Sblock s1 => + type_stmt tret e s1 + | Sexit n => + OK e + | Sswitch sz a tbl dfl => + type_expr e a (if sz then Tlong else Tint) + | Sreturn opta => + match opta with + | None => OK e + | Some a => type_expr e a (proj_rettype tret) +(* + if rettype_eq tret Tvoid + then Error (msg "inconsistent return") + else type_expr e a (proj_rettype tret) +*) + end + | Slabel lbl s1 => + type_stmt tret e s1 + | Sgoto lbl => + OK e + end. + +Definition typenv := ident -> typ. + +Definition type_function (f: function) : res typenv := + do e1 <- S.set_list S.initial f.(fn_params) f.(fn_sig).(sig_args); + do e2 <- type_stmt f.(fn_sig).(sig_res) e1 f.(fn_body); + S.solve e2. + +(** * Relational specification of the type system *) + +Section SPEC. + +Variable env: ident -> typ. +Variable tret: rettype. + +Inductive wt_expr: expr -> typ -> Prop := + | wt_Evar: forall id, + wt_expr (Evar id) (env id) + | wt_Econst: forall c, + wt_expr (Econst c) (type_constant c) + | wt_Eunop: forall op a1 targ1 tres, + type_unop op = (targ1, tres) -> + wt_expr a1 targ1 -> + wt_expr (Eunop op a1) tres + | wt_Ebinop: forall op a1 a2 targ1 targ2 tres, + type_binop op = (targ1, targ2, tres) -> + wt_expr a1 targ1 -> wt_expr a2 targ2 -> + wt_expr (Ebinop op a1 a2) tres + | wt_Eload: forall chunk a1, + wt_expr a1 Tptr -> + wt_expr (Eload chunk a1) (type_of_chunk chunk). + +Definition wt_opt_assign (optid: option ident) (ty: rettype) : Prop := + match optid with + | Some id => proj_rettype ty = env id + | _ => True + end. + +Inductive wt_stmt: stmt -> Prop := + | wt_Sskip: + wt_stmt Sskip + | wt_Sassign: forall id a, + wt_expr a (env id) -> + wt_stmt (Sassign id a) + | wt_Sstore: forall chunk a1 a2, + wt_expr a1 Tptr -> wt_expr a2 (type_of_chunk chunk) -> + wt_stmt (Sstore chunk a1 a2) + | wt_Scall: forall optid sg a1 al, + wt_expr a1 Tptr -> list_forall2 wt_expr al sg.(sig_args) -> + wt_opt_assign optid sg.(sig_res) -> + wt_stmt (Scall optid sg a1 al) + | wt_Stailcall: forall sg a1 al, + wt_expr a1 Tptr -> list_forall2 wt_expr al sg.(sig_args) -> + sg.(sig_res) = tret -> + wt_stmt (Stailcall sg a1 al) + | wt_Sbuiltin: forall optid ef al, + list_forall2 wt_expr al (ef_sig ef).(sig_args) -> + wt_opt_assign optid (ef_sig ef).(sig_res) -> + wt_stmt (Sbuiltin optid ef al) + | wt_Sseq: forall s1 s2, + wt_stmt s1 -> wt_stmt s2 -> + wt_stmt (Sseq s1 s2) + | wt_Sifthenelse: forall a s1 s2, + wt_expr a Tint -> wt_stmt s1 -> wt_stmt s2 -> + wt_stmt (Sifthenelse a s1 s2) + | wt_Sloop: forall s1, + wt_stmt s1 -> + wt_stmt (Sloop s1) + | wt_Sblock: forall s1, + wt_stmt s1 -> + wt_stmt (Sblock s1) + | wt_Sexit: forall n, + wt_stmt (Sexit n) + | wt_Sswitch: forall (sz: bool) a tbl dfl, + wt_expr a (if sz then Tlong else Tint) -> + wt_stmt (Sswitch sz a tbl dfl) + | wt_Sreturn_none: + wt_stmt (Sreturn None) + | wt_Sreturn_some: forall a, + wt_expr a (proj_rettype tret) -> + wt_stmt (Sreturn (Some a)) + | wt_Slabel: forall lbl s1, + wt_stmt s1 -> + wt_stmt (Slabel lbl s1) + | wt_Sgoto: forall lbl, + wt_stmt (Sgoto lbl). + +End SPEC. + +Inductive wt_function (env: typenv) (f: function) : Prop := + wt_function_intro: + type_function f = OK env -> (**r to ensure uniqueness of [env] *) + List.map env f.(fn_params) = f.(fn_sig).(sig_args) -> + wt_stmt env f.(fn_sig).(sig_res) f.(fn_body) -> + wt_function env f. + +Inductive wt_fundef: fundef -> Prop := + | wt_fundef_internal: forall env f, + wt_function env f -> + wt_fundef (Internal f) + | wt_fundef_external: forall ef, + wt_fundef (External ef). + +Definition wt_program (p: program): Prop := + forall i f, In (i, Gfun f) (prog_defs p) -> wt_fundef f. + +(** * Soundness of type inference *) + +Lemma expect_incr: forall te e t1 t2 e', + expect e t1 t2 = OK e' -> S.satisf te e' -> S.satisf te e. +Proof. + unfold expect; intros. destruct (typ_eq t1 t2); inv H; auto. +Qed. +Hint Resolve expect_incr: ty. + +Lemma expect_sound: forall e t1 t2 e', + expect e t1 t2 = OK e' -> t1 = t2. +Proof. + unfold expect; intros. destruct (typ_eq t1 t2); inv H; auto. +Qed. + +Lemma type_expr_incr: forall te a t e e', + type_expr e a t = OK e' -> S.satisf te e' -> S.satisf te e. +Proof. + induction a; simpl; intros until e'; intros T SAT; try (monadInv T); eauto with ty. +- destruct (type_unop u) as [targ1 tres]; monadInv T; eauto with ty. +- destruct (type_binop b) as [[targ1 targ2] tres]; monadInv T; eauto with ty. +Qed. +Hint Resolve type_expr_incr: ty. + +Lemma type_expr_sound: forall te a t e e', + type_expr e a t = OK e' -> S.satisf te e' -> wt_expr te a t. +Proof. + induction a; simpl; intros until e'; intros T SAT; try (monadInv T). +- erewrite <- S.set_sound by eauto. constructor. +- erewrite <- expect_sound by eauto. constructor. +- destruct (type_unop u) as [targ1 tres] eqn:TU; monadInv T. + erewrite <- expect_sound by eauto. econstructor; eauto with ty. +- destruct (type_binop b) as [[targ1 targ2] tres] eqn:TB; monadInv T. + erewrite <- expect_sound by eauto. econstructor; eauto with ty. +- erewrite <- expect_sound by eauto. econstructor; eauto with ty. +Qed. + +Lemma type_exprlist_incr: forall te al tl e e', + type_exprlist e al tl = OK e' -> S.satisf te e' -> S.satisf te e. +Proof. + induction al; destruct tl; simpl; intros until e'; intros T SAT; monadInv T; eauto with ty. +Qed. +Hint Resolve type_exprlist_incr: ty. + +Lemma type_exprlist_sound: forall te al tl e e', + type_exprlist e al tl = OK e' -> S.satisf te e' -> list_forall2 (wt_expr te) al tl. +Proof. + induction al; destruct tl; simpl; intros until e'; intros T SAT; monadInv T. +- constructor. +- constructor; eauto using type_expr_sound with ty. +Qed. + +Lemma type_assign_incr: forall te id a e e', + type_assign e id a = OK e' -> S.satisf te e' -> S.satisf te e. +Proof. + induction a; simpl; intros until e'; intros T SAT; try (monadInv T); eauto with ty. +- destruct (type_unop u) as [targ1 tres]; monadInv T; eauto with ty. +- destruct (type_binop b) as [[targ1 targ2] tres]; monadInv T; eauto with ty. +Qed. +Hint Resolve type_assign_incr: ty. + +Lemma type_assign_sound: forall te id a e e', + type_assign e id a = OK e' -> S.satisf te e' -> wt_expr te a (te id). +Proof. + induction a; simpl; intros until e'; intros T SAT; try (monadInv T). +- erewrite S.move_sound by eauto. constructor. +- erewrite S.set_sound by eauto. constructor. +- destruct (type_unop u) as [targ1 tres] eqn:TU; monadInv T. + erewrite S.set_sound by eauto. econstructor; eauto using type_expr_sound with ty. +- destruct (type_binop b) as [[targ1 targ2] tres] eqn:TB; monadInv T. + erewrite S.set_sound by eauto. econstructor; eauto using type_expr_sound with ty. +- erewrite S.set_sound by eauto. econstructor; eauto using type_expr_sound with ty. +Qed. + +Lemma opt_set_incr: forall te optid optty e e', + opt_set e optid optty = OK e' -> S.satisf te e' -> S.satisf te e. +Proof. + unfold opt_set; intros. destruct optid, optty; try (monadInv H); eauto with ty. +Qed. +Hint Resolve opt_set_incr: ty. + +Lemma opt_set_sound: forall te optid sg e e', + opt_set e optid (proj_sig_res sg) = OK e' -> S.satisf te e' -> + wt_opt_assign te optid sg.(sig_res). +Proof. + unfold opt_set; intros; red. destruct optid. +- erewrite S.set_sound by eauto. auto. +- inv H. auto. +Qed. + +Lemma type_stmt_incr: forall te tret s e e', + type_stmt tret e s = OK e' -> S.satisf te e' -> S.satisf te e. +Proof. + induction s; simpl; intros e1 e2 T SAT; try (monadInv T); eauto with ty. +- destruct tret, o; try (monadInv T); eauto with ty. +Qed. +Hint Resolve type_stmt_incr: ty. + +Lemma type_stmt_sound: forall te tret s e e', + type_stmt tret e s = OK e' -> S.satisf te e' -> wt_stmt te tret s. +Proof. + induction s; simpl; intros e1 e2 T SAT; try (monadInv T). +- constructor. +- constructor; eauto using type_assign_sound. +- constructor; eauto using type_expr_sound with ty. +- constructor; eauto using type_expr_sound, type_exprlist_sound, opt_set_sound with ty. +- constructor; eauto using type_expr_sound, type_exprlist_sound with ty. +- constructor; eauto using type_exprlist_sound, opt_set_sound with ty. +- constructor; eauto with ty. +- constructor; eauto using type_expr_sound with ty. +- constructor; eauto. +- constructor; eauto. +- constructor. +- constructor; eauto using type_expr_sound with ty. +- destruct o; try (monadInv T); econstructor; eauto using type_expr_sound with ty. +- constructor; eauto. +- constructor. +Qed. + +Theorem type_function_sound: forall f env, + type_function f = OK env -> wt_function env f. +Proof. + intros. generalize H; unfold type_function; intros T; monadInv T. + assert (S.satisf env x0) by (apply S.solve_sound; auto). + constructor; eauto using S.set_list_sound, type_stmt_sound with ty. +Qed. + +(** * Semantic soundness of the type system *) + +Definition wt_env (env: typenv) (e: Cminor.env) : Prop := + forall id v, e!id = Some v -> Val.has_type v (env id). + +Definition def_env (f: function) (e: Cminor.env) : Prop := + forall id, In id f.(fn_params) \/ In id f.(fn_vars) -> exists v, e!id = Some v. + +Inductive wt_cont_call: cont -> rettype -> Prop := + | wt_cont_Kstop: + wt_cont_call Kstop Tint + | wt_cont_Kcall: forall optid f sp e k tret env + (WT_FN: wt_function env f) + (WT_CONT: wt_cont env f.(fn_sig).(sig_res) k) + (WT_ENV: wt_env env e) + (DEF_ENV: def_env f e) + (WT_DEST: wt_opt_assign env optid tret), + wt_cont_call (Kcall optid f sp e k) tret + +with wt_cont: typenv -> rettype -> cont -> Prop := + | wt_cont_Kseq: forall env tret s k, + wt_stmt env tret s -> + wt_cont env tret k -> + wt_cont env tret (Kseq s k) + | wt_cont_Kblock: forall env tret k, + wt_cont env tret k -> + wt_cont env tret (Kblock k) + | wt_cont_other: forall env tret k, + wt_cont_call k tret -> + wt_cont env tret k. + +Inductive wt_state: state -> Prop := + | wt_normal_state: forall f s k sp e m env + (WT_FN: wt_function env f) + (WT_STMT: wt_stmt env f.(fn_sig).(sig_res) s) + (WT_CONT: wt_cont env f.(fn_sig).(sig_res) k) + (WT_ENV: wt_env env e) + (DEF_ENV: def_env f e), + wt_state (State f s k sp e m) + | wt_call_state: forall f args k m + (WT_FD: wt_fundef f) + (WT_ARGS: Val.has_type_list args (funsig f).(sig_args)) + (WT_CONT: wt_cont_call k (funsig f).(sig_res)), + wt_state (Callstate f args k m) + | wt_return_state: forall v k m tret + (WT_RES: Val.has_type v (proj_rettype tret)) + (WT_CONT: wt_cont_call k tret), + wt_state (Returnstate v k m). + +Lemma wt_is_call_cont: + forall env tret k, wt_cont env tret k -> is_call_cont k -> wt_cont_call k tret. +Proof. + destruct 1; intros ICC; contradiction || auto. +Qed. + +Lemma call_cont_wt: + forall env tret k, wt_cont env tret k -> wt_cont_call (call_cont k) tret. +Proof. + induction 1; simpl; auto. inversion H; subst; auto. +Qed. + +Lemma wt_env_assign: forall env id e v, + wt_env env e -> Val.has_type v (env id) -> wt_env env (PTree.set id v e). +Proof. + intros; red; intros. rewrite PTree.gsspec in H1; destruct (peq id0 id). +- congruence. +- auto. +Qed. + +Lemma def_env_assign: forall f e id v, + def_env f e -> def_env f (PTree.set id v e). +Proof. + intros; red; intros i IN. rewrite PTree.gsspec. destruct (peq i id). + exists v; auto. + auto. +Qed. + +Lemma wt_env_set_params: forall env il vl, + Val.has_type_list vl (map env il) -> wt_env env (set_params vl il). +Proof. + induction il as [ | i il]; destruct vl as [ | vl]; simpl; intros; try contradiction. +- red; intros. rewrite PTree.gempty in H0; discriminate. +- destruct H. apply wt_env_assign; auto. +Qed. + +Lemma def_set_params: forall id il vl, + In id il -> exists v, PTree.get id (set_params vl il) = Some v. +Proof. + induction il as [ | i il]; simpl; intros. +- contradiction. +- destruct vl as [ | v vl]; rewrite PTree.gsspec; destruct (peq id i). + econstructor; eauto. + apply IHil; intuition congruence. + econstructor; eauto. + apply IHil; intuition congruence. +Qed. + +Lemma wt_env_set_locals: forall env il e, + wt_env env e -> wt_env env (set_locals il e). +Proof. + induction il as [ | i il]; simpl; intros. +- auto. +- apply wt_env_assign; auto. exact I. +Qed. + +Lemma def_set_locals: forall id il e, + (exists v, PTree.get id e = Some v) \/ In id il -> + exists v, PTree.get id (set_locals il e) = Some v. +Proof. + induction il as [ | i il]; simpl; intros. +- tauto. +- rewrite PTree.gsspec; destruct (peq id i). + econstructor; eauto. + apply IHil; intuition congruence. +Qed. + +Lemma wt_find_label: forall env tret lbl s k, + wt_stmt env tret s -> wt_cont env tret k -> + match find_label lbl s k with + | Some (s', k') => wt_stmt env tret s' /\ wt_cont env tret k' + | None => True + end. +Proof. + induction s; intros k WS WK; simpl; auto. +- inv WS. assert (wt_cont env tret (Kseq s2 k)) by (constructor; auto). + specialize (IHs1 _ H1 H). destruct (find_label lbl s1 (Kseq s2 k)). + auto. apply IHs2; auto. +- inv WS. specialize (IHs1 _ H3 WK). destruct (find_label lbl s1 k). + auto. apply IHs2; auto. +- inversion WS; subst. apply IHs; auto. constructor; auto. +- inv WS. apply IHs; auto. constructor; auto. +- inv WS. destruct (ident_eq lbl l). auto. apply IHs; auto. +Qed. + +Section SUBJECT_REDUCTION. + +Variable p: program. + +Hypothesis wt_p: wt_program p. + +Let ge := Genv.globalenv p. + +Ltac VHT := + match goal with + | [ |- Val.has_type (if Archi.ptr64 then _ else _) _] => unfold Val.has_type; destruct Archi.ptr64 eqn:?; VHT + | [ |- Val.has_type (match ?v with _ => _ end) _] => destruct v; VHT + | [ |- Val.has_type (Vptr _ _) Tptr ] => apply Val.Vptr_has_type + | [ |- Val.has_type _ _ ] => exact I + | [ |- Val.has_type (?f _ _ _ _ _) _ ] => unfold f; VHT + | [ |- Val.has_type (?f _ _ _ _) _ ] => unfold f; VHT + | [ |- Val.has_type (?f _ _) _ ] => unfold f; VHT + | [ |- Val.has_type (?f _ _ _) _ ] => unfold f; VHT + | [ |- Val.has_type (?f _) _ ] => unfold f; VHT + | [ |- True ] => exact I + | [ |- ?x = ?x ] => reflexivity + | _ => idtac + end. + +Ltac VHT' := + match goal with + | [ H: None = Some _ |- _ ] => discriminate + | [ H: Some _ = Some _ |- _ ] => inv H; VHT + | [ H: match ?x with _ => _ end = Some _ |- _ ] => destruct x; VHT' + | [ H: ?f _ _ _ _ = Some _ |- _ ] => unfold f in H; VHT' + | [ H: ?f _ _ _ = Some _ |- _ ] => unfold f in H; VHT' + | [ H: ?f _ _ = Some _ |- _ ] => unfold f in H; VHT' + | [ H: ?f _ = Some _ |- _ ] => unfold f in H; VHT' + | _ => idtac + end. + +Lemma type_constant_sound: forall sp cst v, + eval_constant ge sp cst = Some v -> + Val.has_type v (type_constant cst). +Proof. + intros until v; intros EV. destruct cst; simpl in *; inv EV; VHT. +Qed. + +Lemma type_unop_sound: forall op v1 v, + eval_unop op v1 = Some v -> Val.has_type v (snd (type_unop op)). +Proof. + unfold eval_unop; intros op v1 v EV; destruct op; simpl; VHT'. +Qed. + +Lemma type_binop_sound: forall op v1 v2 m v, + eval_binop op v1 v2 m = Some v -> Val.has_type v (snd (type_binop op)). +Proof. + unfold eval_binop; intros op v1 v2 m v EV; destruct op; simpl; VHT'; + destruct (eq_block b b0); VHT. +Qed. + +Lemma wt_eval_expr: forall env sp e m a v, + eval_expr ge sp e m a v -> + forall t, + wt_expr env a t -> + wt_env env e -> + Val.has_type v t. +Proof. + induction 1; intros t WT ENV. +- inv WT. apply ENV; auto. +- inv WT. eapply type_constant_sound; eauto. +- inv WT. replace t with (snd (type_unop op)) by (rewrite H3; auto). eapply type_unop_sound; eauto. +- inv WT. replace t with (snd (type_binop op)) by (rewrite H5; auto). eapply type_binop_sound; eauto. +- inv WT. destruct vaddr; try discriminate. eapply Mem.load_type; eauto. +Qed. + +Lemma wt_eval_exprlist: forall env sp e m al vl, + eval_exprlist ge sp e m al vl -> + forall tl, + list_forall2 (wt_expr env) al tl -> + wt_env env e -> + Val.has_type_list vl tl. +Proof. + induction 1; intros tl WT ENV; inv WT; simpl. +- auto. +- split. eapply wt_eval_expr; eauto. eauto. +Qed. + +Lemma wt_find_funct: forall v fd, + Genv.find_funct ge v = Some fd -> wt_fundef fd. +Proof. + intros. eapply Genv.find_funct_prop; eauto. +Qed. + +Lemma subject_reduction: + forall st1 t st2, step ge st1 t st2 -> + forall (WT: wt_state st1), wt_state st2. +Proof. + destruct 1; intros; inv WT. +- inv WT_CONT. econstructor; eauto. inv H. +- inv WT_CONT. econstructor; eauto. inv H. +- econstructor; eauto using wt_is_call_cont. exact I. +- inv WT_STMT. econstructor; eauto using wt_Sskip. + apply wt_env_assign; auto. eapply wt_eval_expr; eauto. + apply def_env_assign; auto. +- econstructor; eauto using wt_Sskip. +- inv WT_STMT. econstructor; eauto. + eapply wt_find_funct; eauto. + eapply wt_eval_exprlist; eauto. + econstructor; eauto. +- inv WT_STMT. econstructor; eauto. + eapply wt_find_funct; eauto. + eapply wt_eval_exprlist; eauto. + rewrite H8; eapply call_cont_wt; eauto. +- inv WT_STMT. exploit external_call_well_typed; eauto. intros TRES. + econstructor; eauto using wt_Sskip. + destruct optid; auto. apply wt_env_assign; auto. rewrite <- H5; auto. + destruct optid; auto. apply def_env_assign; auto. +- inv WT_STMT. econstructor; eauto. econstructor; eauto. +- inv WT_STMT. destruct b; econstructor; eauto. +- inv WT_STMT. econstructor; eauto. econstructor; eauto. constructor; auto. +- inv WT_STMT. econstructor; eauto. econstructor; eauto. +- inv WT_CONT. econstructor; eauto. inv H. +- inv WT_CONT. econstructor; eauto using wt_Sskip. inv H. +- inv WT_CONT. econstructor; eauto using wt_Sexit. inv H. +- econstructor; eauto using wt_Sexit. +- inv WT_STMT. econstructor; eauto using call_cont_wt. exact I. +- inv WT_STMT. econstructor; eauto using call_cont_wt. + eapply wt_eval_expr; eauto. +- inv WT_STMT. econstructor; eauto. +- inversion WT_FN; subst. + assert (WT_CK: wt_cont env (sig_res (fn_sig f)) (call_cont k)). + { constructor. eapply call_cont_wt; eauto. } + generalize (wt_find_label _ _ lbl _ _ H2 WT_CK). + rewrite H. intros [WT_STMT' WT_CONT']. econstructor; eauto. +- inv WT_FD. inversion H1; subst. econstructor; eauto. + constructor; auto. + apply wt_env_set_locals. apply wt_env_set_params. rewrite H2; auto. + red; intros. apply def_set_locals. destruct H4; auto. left; apply def_set_params; auto. +- exploit external_call_well_typed; eauto. intros. + econstructor; eauto. +- inv WT_CONT. econstructor; eauto using wt_Sskip. + red in WT_DEST. + destruct optid. rewrite WT_DEST in WT_RES. apply wt_env_assign; auto. assumption. + destruct optid. apply def_env_assign; auto. assumption. +Qed. + +Lemma subject_reduction_star: + forall st1 t st2, star step ge st1 t st2 -> + forall (WT: wt_state st1), wt_state st2. +Proof. + induction 1; eauto using subject_reduction. +Qed. + +Lemma wt_initial_state: + forall S, initial_state p S -> wt_state S. +Proof. + intros. inv H. constructor. eapply Genv.find_funct_ptr_prop; eauto. + rewrite H3; constructor. + rewrite H3; constructor. +Qed. + +End SUBJECT_REDUCTION. + +(** * Safe expressions *) + +(** Function parameters and declared local variables are always defined + throughout the execution of a function. The following [known_idents] + data structure represents the set of those variables, with efficient membership. *) + +Definition known_idents := PTree.t unit. + +Definition is_known (ki: known_idents) (id: ident) := + match ki!id with Some _ => true | None => false end. + +Definition known_id (f: function) : known_idents := + let add (ki: known_idents) (id: ident) := PTree.set id tt ki in + List.fold_left add f.(fn_vars) + (List.fold_left add f.(fn_params) (PTree.empty unit)). + +(** A Cminor expression is safe if it always evaluates to a value, + never causing a run-time error. *) + +Definition safe_unop (op: unary_operation) : bool := + match op with + | Ointoffloat | Ointuoffloat | Ofloatofint | Ofloatofintu => false + | Ointofsingle | Ointuofsingle | Osingleofint | Osingleofintu => false + | Olongoffloat | Olonguoffloat | Ofloatoflong | Ofloatoflongu => false + | Olongofsingle | Olonguofsingle | Osingleoflong | Osingleoflongu => false + | _ => true + end. + +Definition safe_binop (op: binary_operation) : bool := + match op with + | Odiv | Odivu | Omod | Omodu => false + | Odivl | Odivlu | Omodl | Omodlu => false + | Ocmpl _ | Ocmplu _ => false + | _ => true + end. + +Fixpoint safe_expr (ki: known_idents) (a: expr) : bool := + match a with + | Evar v => is_known ki v + | Econst c => true + | Eunop op e1 => safe_unop op && safe_expr ki e1 + | Ebinop op e1 e2 => safe_binop op && safe_expr ki e1 && safe_expr ki e2 + | Eload chunk e => false + end. + +(** Soundness of [known_id]. *) + +Lemma known_id_sound_1: + forall f id x, (known_id f)!id = Some x -> In id f.(fn_params) \/ In id f.(fn_vars). +Proof. + unfold known_id. + set (add := fun (ki: known_idents) (id: ident) => PTree.set id tt ki). + intros. + assert (REC: forall l ki, (fold_left add l ki)!id = Some x -> In id l \/ ki!id = Some x). + { induction l as [ | i l ]; simpl; intros. + - auto. + - apply IHl in H0. destruct H0; auto. unfold add in H0; rewrite PTree.gsspec in H0. + destruct (peq id i); auto. } + apply REC in H. destruct H; auto. apply REC in H. destruct H; auto. + rewrite PTree.gempty in H; discriminate. +Qed. + +Lemma known_id_sound_2: + forall f id, is_known (known_id f) id = true -> In id f.(fn_params) \/ In id f.(fn_vars). +Proof. + unfold is_known; intros. destruct (known_id f)!id eqn:E; try discriminate. + eapply known_id_sound_1; eauto. +Qed. + +(** Expressions that satisfy [safe_expr] always evaluate to a value. *) + +Lemma eval_safe_expr: + forall ge f sp e m a, + def_env f e -> + safe_expr (known_id f) a = true -> + exists v, eval_expr ge sp e m a v. +Proof. + induction a; simpl; intros. + - apply known_id_sound_2 in H0. + destruct (H i H0) as [v E]. + exists v; constructor; auto. + - destruct (eval_constant ge sp c) as [v|] eqn:E. + exists v; constructor; auto. + destruct c; discriminate. + - InvBooleans. destruct IHa as [v1 E1]; auto. + destruct (eval_unop u v1) as [v|] eqn:E. + exists v; econstructor; eauto. + destruct u; discriminate. + - InvBooleans. + destruct IHa1 as [v1 E1]; auto. + destruct IHa2 as [v2 E2]; auto. + destruct (eval_binop b v1 v2 m) as [v|] eqn:E. + exists v; econstructor; eauto. + destruct b; discriminate. + - discriminate. +Qed. + + diff --git a/backend/Constprop.v b/backend/Constprop.v index d8211ffe..4aab7677 100644 --- a/backend/Constprop.v +++ b/backend/Constprop.v @@ -15,7 +15,7 @@ and the corresponding code rewriting. *) Require Import Coqlib Maps Integers Floats Lattice Kildall. -Require Import AST Linking. +Require Import AST Linking Builtins. Require Compopts Machregs. Require Import Op Registers RTL. Require Import Liveness ValueDomain ValueAOp ValueAnalysis. @@ -139,6 +139,30 @@ Definition builtin_strength_reduction | _ => builtin_args_strength_reduction ae al (Machregs.builtin_constraints ef) end. +(* +Definition transf_builtin + (ae: AE.t) (am: amem) (rm: romem) + (ef: external_function) + (args: list (builtin_arg reg)) (res: builtin_res reg) (s: node) := + let dfl := Ibuiltin ef (builtin_strength_reduction ae ef args) res s in + match ef, res with + | EF_builtin name sg, BR rd => + match lookup_builtin_function name sg with + | Some bf => + match eval_static_builtin_function ae am rm bf args with + | Some a => + match const_for_result a with + | Some cop => Iop cop nil rd s + | None => dfl + end + | None => dfl + end + | None => dfl + end + | _, _ => dfl + end. +*) + Definition transf_instr (f: function) (an: PMap.t VA.t) (rm: romem) (pc: node) (instr: instruction) := match an!!pc with @@ -176,7 +200,23 @@ Definition transf_instr (f: function) (an: PMap.t VA.t) (rm: romem) | Itailcall sig ros args => Itailcall sig (transf_ros ae ros) args | Ibuiltin ef args res s => - Ibuiltin ef (builtin_strength_reduction ae ef args) res s + let dfl := Ibuiltin ef (builtin_strength_reduction ae ef args) res s in + match ef, res with + | EF_builtin name sg, BR rd => + match lookup_builtin_function name sg with + | Some bf => + match eval_static_builtin_function ae am rm bf args with + | Some a => + match const_for_result a with + | Some cop => Iop cop nil rd s + | None => dfl + end + | None => dfl + end + | None => dfl + end + | _, _ => dfl + end | Icond cond args s1 s2 => let aargs := aregs ae args in match resolve_branch (eval_static_condition cond aargs) with diff --git a/backend/Constpropproof.v b/backend/Constpropproof.v index e28519ca..a5d08a0f 100644 --- a/backend/Constpropproof.v +++ b/backend/Constpropproof.v @@ -14,7 +14,7 @@ Require Import Coqlib Maps Integers Floats Lattice Kildall. Require Import AST Linking. -Require Import Values Events Memory Globalenvs Smallstep. +Require Import Values Builtins Events Memory Globalenvs Smallstep. Require Compopts Machregs. Require Import Op Registers RTL. Require Import Liveness ValueDomain ValueAOp ValueAnalysis. @@ -474,19 +474,41 @@ Proof. - (* Ibuiltin *) rename pc'0 into pc. TransfInstr; intros. Opaque builtin_strength_reduction. - exploit builtin_strength_reduction_correct; eauto. intros (vargs' & P & Q). - exploit (@eval_builtin_args_lessdef _ ge (fun r => rs#r) (fun r => rs'#r)). + set (dfl := Ibuiltin ef (builtin_strength_reduction ae ef args) res pc') in *. + set (rm := romem_for cu) in *. + assert (DFL: (fn_code (transf_function rm f))!pc = Some dfl -> + exists (n2 : nat) (s2' : state), + step tge + (State s' (transf_function rm f) (Vptr sp0 Ptrofs.zero) pc rs' m'0) t s2' /\ + match_states n2 + (State s f (Vptr sp0 Ptrofs.zero) pc' (regmap_setres res vres rs) m') s2'). + { + exploit builtin_strength_reduction_correct; eauto. intros (vargs' & P & Q). + exploit (@eval_builtin_args_lessdef _ ge (fun r => rs#r) (fun r => rs'#r)). apply REGS. eauto. eexact P. - intros (vargs'' & U & V). - exploit external_call_mem_extends; eauto. - intros [v' [m2' [A [B [C D]]]]]. + intros (vargs'' & U & V). + exploit external_call_mem_extends; eauto. + intros (v' & m2' & A & B & C & D). + econstructor; econstructor; split. + eapply exec_Ibuiltin; eauto. + eapply eval_builtin_args_preserved. eexact symbols_preserved. eauto. + eapply external_call_symbols_preserved; eauto. apply senv_preserved. + eapply match_states_succ; eauto. + apply set_res_lessdef; auto. + } + destruct ef; auto. + destruct res; auto. + destruct (lookup_builtin_function name sg) as [bf|] eqn:LK; auto. + destruct (eval_static_builtin_function ae am rm bf args) as [a|] eqn:ES; auto. + destruct (const_for_result a) as [cop|] eqn:CR; auto. + clear DFL. simpl in H1; red in H1; rewrite LK in H1; inv H1. + exploit const_for_result_correct; eauto. + eapply eval_static_builtin_function_sound; eauto. + intros (v' & A & B). left; econstructor; econstructor; split. - eapply exec_Ibuiltin; eauto. - eapply eval_builtin_args_preserved. eexact symbols_preserved. eauto. - eapply external_call_symbols_preserved; eauto. apply senv_preserved. + eapply exec_Iop; eauto. eapply match_states_succ; eauto. - apply set_res_lessdef; auto. - + apply set_reg_lessdef; auto. - (* Icond, preserved *) rename pc'0 into pc. TransfInstr. set (ac := eval_static_condition cond (aregs ae args)). diff --git a/backend/Conventions.v b/backend/Conventions.v index 989bfa05..14ffb587 100644 --- a/backend/Conventions.v +++ b/backend/Conventions.v @@ -34,6 +34,73 @@ Proof. apply IHpl; auto. Qed. +(** ** Stack size of function arguments *) + +(** [size_arguments s] returns the number of [Outgoing] slots used + to call a function with signature [s]. *) + +Definition max_outgoing_1 (accu: Z) (l: loc) : Z := + match l with + | S Outgoing ofs ty => Z.max accu (ofs + typesize ty) + | _ => accu + end. + +Definition max_outgoing_2 (accu: Z) (rl: rpair loc) : Z := + match rl with + | One l => max_outgoing_1 accu l + | Twolong l1 l2 => max_outgoing_1 (max_outgoing_1 accu l1) l2 + end. + +Definition size_arguments (s: signature) : Z := + List.fold_left max_outgoing_2 (loc_arguments s) 0. + +(** The offsets of [Outgoing] arguments are below [size_arguments s]. *) + +Remark fold_max_outgoing_above: + forall l n, fold_left max_outgoing_2 l n >= n. +Proof. + assert (A: forall n l, max_outgoing_1 n l >= n). + { intros; unfold max_outgoing_1. destruct l as [_ | []]; xomega. } + induction l; simpl; intros. + - omega. + - eapply Zge_trans. eauto. + destruct a; simpl. apply A. eapply Zge_trans; eauto. +Qed. + +Lemma size_arguments_above: + forall s, size_arguments s >= 0. +Proof. + intros. apply fold_max_outgoing_above. +Qed. + +Lemma loc_arguments_bounded: + forall (s: signature) (ofs: Z) (ty: typ), + In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments s)) -> + ofs + typesize ty <= size_arguments s. +Proof. + intros until ty. + assert (A: forall n l, n <= max_outgoing_1 n l). + { intros; unfold max_outgoing_1. destruct l as [_ | []]; xomega. } + assert (B: forall p n, + In (S Outgoing ofs ty) (regs_of_rpair p) -> + ofs + typesize ty <= max_outgoing_2 n p). + { intros. destruct p; simpl in H; intuition; subst; simpl. + - xomega. + - eapply Z.le_trans. 2: apply A. xomega. + - xomega. } + assert (C: forall l n, + In (S Outgoing ofs ty) (regs_of_rpairs l) -> + ofs + typesize ty <= fold_left max_outgoing_2 l n). + { induction l; simpl; intros. + - contradiction. + - rewrite in_app_iff in H. destruct H. + + eapply Z.le_trans. eapply B; eauto. + apply Z.ge_le. apply fold_max_outgoing_above. + + apply IHl; auto. + } + apply C. +Qed. + (** ** Location of function parameters *) (** A function finds the values of its parameter in the same locations @@ -128,8 +195,6 @@ Definition callee_save_loc (l: loc) := | S sl ofs ty => sl <> Outgoing end. -Hint Unfold callee_save_loc. - Definition agree_callee_save (ls1 ls2: Locmap.t) : Prop := forall l, callee_save_loc l -> ls1 l = ls2 l. diff --git a/backend/Deadcodeproof.v b/backend/Deadcodeproof.v index 199ac922..2edc0395 100644 --- a/backend/Deadcodeproof.v +++ b/backend/Deadcodeproof.v @@ -106,7 +106,7 @@ Local Transparent Mem.loadbytes. unfold Mem.loadbytes; intros. destruct H. destruct (Mem.range_perm_dec m1 b ofs (ofs + n) Cur Readable); inv H0. rewrite pred_dec_true. econstructor; split; eauto. - apply GETN. intros. rewrite nat_of_Z_max in H. + apply GETN. intros. rewrite Z_to_nat_max in H. assert (ofs <= i < ofs + n) by xomega. apply ma_memval0; auto. red; intros; eauto. @@ -966,7 +966,7 @@ Ltac UseTransfer := intros. eapply nlive_remove; eauto. unfold adst, vanalyze; rewrite AN; eapply aaddr_arg_sound_1; eauto. erewrite Mem.loadbytes_length in H1 by eauto. - rewrite nat_of_Z_eq in H1 by omega. auto. + rewrite Z2Nat.id in H1 by omega. auto. eauto. intros (tm' & A & B). econstructor; split. @@ -993,7 +993,7 @@ Ltac UseTransfer := intros (bc & A & B & C). intros. eapply nlive_contains; eauto. erewrite Mem.loadbytes_length in H0 by eauto. - rewrite nat_of_Z_eq in H0 by omega. auto. + rewrite Z2Nat.id in H0 by omega. auto. + (* annot *) destruct (transfer_builtin_args (kill_builtin_res res ne, nm) _x2) as (ne1, nm1) eqn:TR. InvSoundState. diff --git a/backend/IRC.ml b/backend/IRC.ml index 43955897..b359da35 100644 --- a/backend/IRC.ml +++ b/backend/IRC.ml @@ -240,7 +240,8 @@ type graph = { let class_of_type = function | Tint | Tlong -> 0 | Tfloat | Tsingle -> 1 - | Tany32 | Tany64 -> assert false + | Tany32 -> 0 + | Tany64 -> if Archi.ptr64 then 0 else 1 let class_of_reg r = if Conventions1.is_float_reg r then 1 else 0 diff --git a/backend/Inliningaux.ml b/backend/Inliningaux.ml index 842e0c93..2e83eb0c 100644 --- a/backend/Inliningaux.ml +++ b/backend/Inliningaux.ml @@ -16,7 +16,7 @@ open FSetAVL open Maps open Op open Ordered -open !RTL +open! RTL module PSet = Make(OrderedPositive) diff --git a/backend/Inliningproof.v b/backend/Inliningproof.v index 2dcb8956..cc84b1cc 100644 --- a/backend/Inliningproof.v +++ b/backend/Inliningproof.v @@ -744,7 +744,7 @@ Lemma match_stacks_free_right: match_stacks F m m1' stk stk' sp. Proof. intros. eapply match_stacks_invariant; eauto. - intros. eapply Mem.perm_free_1; eauto. + intros. eapply Mem.perm_free_1; eauto with ordered_type. intros. eapply Mem.perm_free_3; eauto. Qed. @@ -755,13 +755,13 @@ Proof. assert (2 <= sz -> (2 | n)). intros. destruct (zle sz 1). omegaContradiction. destruct (zle sz 2). auto. - destruct (zle sz 4). apply Zdivides_trans with 4; auto. exists 2; auto. - apply Zdivides_trans with 8; auto. exists 4; auto. + destruct (zle sz 4). apply Z.divide_trans with 4; auto. exists 2; auto. + apply Z.divide_trans with 8; auto. exists 4; auto. assert (4 <= sz -> (4 | n)). intros. destruct (zle sz 1). omegaContradiction. destruct (zle sz 2). omegaContradiction. destruct (zle sz 4). auto. - apply Zdivides_trans with 8; auto. exists 2; auto. + apply Z.divide_trans with 8; auto. exists 2; auto. assert (8 <= sz -> (8 | n)). intros. destruct (zle sz 1). omegaContradiction. destruct (zle sz 2). omegaContradiction. @@ -1043,7 +1043,7 @@ Proof. eapply match_stacks_bound with (bound := sp'). eapply match_stacks_invariant; eauto. intros. eapply Mem.perm_free_3; eauto. - intros. eapply Mem.perm_free_1; eauto. + intros. eapply Mem.perm_free_1; eauto with ordered_type. intros. eapply Mem.perm_free_3; eauto. erewrite Mem.nextblock_free; eauto. red in VB; xomega. eapply agree_val_regs; eauto. @@ -1135,7 +1135,7 @@ Proof. eapply match_stacks_bound with (bound := sp'). eapply match_stacks_invariant; eauto. intros. eapply Mem.perm_free_3; eauto. - intros. eapply Mem.perm_free_1; eauto. + intros. eapply Mem.perm_free_1; eauto with ordered_type. intros. eapply Mem.perm_free_3; eauto. erewrite Mem.nextblock_free; eauto. red in VB; xomega. destruct or; simpl. apply agree_val_reg; auto. auto. @@ -1182,7 +1182,7 @@ Proof. subst b1. rewrite D in H8; inv H8. eelim Plt_strict; eauto. intros. eapply Mem.perm_alloc_1; eauto. intros. exploit Mem.perm_alloc_inv. eexact A. eauto. - rewrite dec_eq_false; auto. + rewrite dec_eq_false; auto with ordered_type. auto. auto. auto. eauto. auto. rewrite H5. apply agree_regs_init_regs. eauto. auto. inv H1; auto. congruence. auto. eapply Mem.valid_new_block; eauto. @@ -1249,7 +1249,7 @@ Proof. eapply external_call_nextblock; eauto. auto. auto. -- (* return fron noninlined function *) +- (* return from noninlined function *) inv MS0. + (* normal case *) left; econstructor; split. diff --git a/backend/Json.ml b/backend/Json.ml index b8f66c08..bd4d6ff9 100644 --- a/backend/Json.ml +++ b/backend/Json.ml @@ -10,7 +10,6 @@ (* *) (* *********************************************************************) -open Format open Camlcoq @@ -18,16 +17,21 @@ open Camlcoq (* Print a string as json string *) let pp_jstring oc s = - fprintf oc "\"%s\"" s + output_string oc "\""; + output_string oc s; + output_string oc "\"" (* Print a bool as json bool *) -let pp_jbool oc = fprintf oc "%B" +let pp_jbool oc b = output_string oc (string_of_bool b) (* Print an int as json int *) -let pp_jint oc = fprintf oc "%d" +let pp_jint oc i = output_string oc (string_of_int i) (* Print an int32 as json int *) -let pp_jint32 oc = fprintf oc "%ld" +let pp_jint32 oc i = output_string oc (Int32.to_string i) + +(* Print an int64 as json int *) +let pp_jint64 oc i = output_string oc (Int64.to_string i) (* Print optional value *) let pp_jopt pp_elem oc = function @@ -36,15 +40,19 @@ let pp_jopt pp_elem oc = function (* Print opening and closing curly braces for json dictionaries *) let pp_jobject_start pp = - fprintf pp "@[<v 1>{" + output_string pp "\n{" let pp_jobject_end pp = - fprintf pp "@;<0 -1>}@]" + output_string pp "}" (* Print a member of a json dictionary *) let pp_jmember ?(first=false) pp name pp_mem mem = - let sep = if first then "" else "," in - fprintf pp "%s@ \"%s\": %a" sep name pp_mem mem + if not first then output_string pp ","; + output_string pp " "; + pp_jstring pp name; + output_string pp " :"; + pp_mem pp mem; + output_string pp "\n" (* Print singleton object *) let pp_jsingle_object pp name pp_mem mem = @@ -54,29 +62,31 @@ let pp_jsingle_object pp name pp_mem mem = (* Print a list as json array *) let pp_jarray elem pp l = - match l with - | [] -> fprintf pp "[]"; + let pp_sep () = output_string pp ", " in + output_string pp "["; + begin match l with + | [] -> () | hd::tail -> - fprintf pp "@[<v 1>["; - fprintf pp "%a" elem hd; - List.iter (fun l -> fprintf pp ",@ %a" elem l) tail; - fprintf pp "@;<0 -1>]@]" + elem pp hd; + List.iter (fun l -> pp_sep (); elem pp l) tail; + end; + output_string pp "]" (* Helper functions for printing coq integer and floats *) let pp_int pp i = - fprintf pp "%ld" (camlint_of_coqint i) + pp_jint32 pp (camlint_of_coqint i) let pp_int64 pp i = - fprintf pp "%Ld" (camlint64_of_coqint i) + pp_jint64 pp (camlint64_of_coqint i) let pp_float32 pp f = - fprintf pp "%ld" (camlint_of_coqint (Floats.Float32.to_bits f)) + pp_jint32 pp (camlint_of_coqint (Floats.Float32.to_bits f)) let pp_float64 pp f = - fprintf pp "%Ld" (camlint64_of_coqint (Floats.Float.to_bits f)) + pp_jint64 pp (camlint64_of_coqint (Floats.Float.to_bits f)) let pp_z pp z = - fprintf pp "%s" (Z.to_string z) + output_string pp (Z.to_string z) (* Helper functions for printing assembler constructs *) let pp_atom pp a = @@ -106,4 +116,4 @@ let reset_id () = let pp_id_const pp () = let i = next_id () in - pp_jsingle_object pp "Integer" (fun pp i -> fprintf pp "%d" i) i + pp_jsingle_object pp "Integer" pp_jint i diff --git a/backend/JsonAST.ml b/backend/JsonAST.ml index 3469bdc6..8905e252 100644 --- a/backend/JsonAST.ml +++ b/backend/JsonAST.ml @@ -15,7 +15,6 @@ open Asm open AST open C2C open Json -open Format open Sections @@ -54,8 +53,8 @@ let pp_section pp sec = | Section_ais_annotation -> () (* There should be no info in the debug sections *) let pp_int_opt pp = function - | None -> fprintf pp "0" - | Some i -> fprintf pp "%d" i + | None -> output_string pp "0" + | Some i -> pp_jint pp i let pp_fundef pp_inst pp (name,fn) = let alignment = atom_alignof name @@ -119,19 +118,18 @@ let pp_program pp pp_inst prog = pp_jobject_end pp let pp_mnemonics pp mnemonic_names = - let mnemonic_names = List.sort (String.compare) mnemonic_names in - let new_line pp () = pp_print_string pp "\n" in - pp_print_list ~pp_sep:new_line pp_print_string pp mnemonic_names + let new_line pp () = Format.pp_print_string pp "\n" in + Format.pp_print_list ~pp_sep:new_line Format.pp_print_string pp mnemonic_names -let jdump_magic_number = "CompCertJDUMP" ^ Version.version +let jdump_magic_number = "CompCertJDUMPRelease: " ^ Version.version let pp_ast pp pp_inst ast sourcename = let get_args () = let buf = Buffer.create 100 in Buffer.add_string buf Sys.executable_name; - for i = 1 to (Array.length !Commandline.argv - 1) do + for i = 1 to (Array.length Commandline.argv - 1) do Buffer.add_string buf " "; - Buffer.add_string buf (Responsefile.gnu_quote !Commandline.argv.(i)); + Buffer.add_string buf (Responsefile.gnu_quote Commandline.argv.(i)); done; Buffer.contents buf in let dump_compile_info pp () = @@ -153,4 +151,4 @@ let pp_ast pp pp_inst ast sourcename = pp_jmember pp "Compilation Unit" pp_jstring sourcename; pp_jmember pp "Asm Ast" (fun pp prog -> pp_program pp pp_inst prog) ast; pp_jobject_end pp; - Format.pp_print_flush pp () + flush pp diff --git a/backend/JsonAST.mli b/backend/JsonAST.mli index 7afdce51..c32439e4 100644 --- a/backend/JsonAST.mli +++ b/backend/JsonAST.mli @@ -13,4 +13,4 @@ val pp_mnemonics : Format.formatter -> string list -> unit -val pp_ast : Format.formatter -> (Format.formatter -> Asm.code -> unit) -> (Asm.coq_function AST.fundef, 'a) AST.program -> string -> unit +val pp_ast : out_channel -> (out_channel -> Asm.code -> unit) -> (Asm.coq_function AST.fundef, 'a) AST.program -> string -> unit diff --git a/backend/Linearizeaux.ml b/backend/Linearizeaux.ml index 46d5c3f1..902724e0 100644 --- a/backend/Linearizeaux.ml +++ b/backend/Linearizeaux.ml @@ -106,7 +106,7 @@ let flatten_blocks blks = let cmp_minpc (mpc1, _) (mpc2, _) = if mpc1 = mpc2 then 0 else if mpc1 > mpc2 then -1 else 1 in - List.flatten (List.map Pervasives.snd (List.sort cmp_minpc blks)) + List.flatten (List.map snd (List.sort cmp_minpc blks)) (* Build the enumeration *) diff --git a/backend/Lineartyping.v b/backend/Lineartyping.v index fc163719..0e3b7c8e 100644 --- a/backend/Lineartyping.v +++ b/backend/Lineartyping.v @@ -39,7 +39,7 @@ Definition slot_valid (sl: slot) (ofs: Z) (ty: typ): bool := | Outgoing => zle 0 ofs | Incoming => In_dec Loc.eq (S Incoming ofs ty) (regs_of_rpairs (loc_parameters funct.(fn_sig))) end - && Zdivide_dec (typealign ty) ofs (typealign_pos ty). + && Zdivide_dec (typealign ty) ofs. Definition slot_writable (sl: slot) : bool := match sl with @@ -321,7 +321,7 @@ Local Opaque mreg_type. + (* other ops *) destruct (type_of_operation op) as [ty_args ty_res] eqn:TYOP. InvBooleans. econstructor; eauto. - apply wt_setreg; auto. eapply Val.has_subtype; eauto. + apply wt_setreg. eapply Val.has_subtype; eauto. change ty_res with (snd (ty_args, ty_res)). rewrite <- TYOP. eapply type_of_operation_sound; eauto. red; intros; subst op. simpl in ISMOVE. destruct args; try discriminate. destruct args; discriminate. diff --git a/backend/NeedDomain.v b/backend/NeedDomain.v index d431f3d8..3c2d8e20 100644 --- a/backend/NeedDomain.v +++ b/backend/NeedDomain.v @@ -16,6 +16,7 @@ Require Import Coqlib. Require Import Maps. Require Import IntvSets. Require Import AST. +Require Import Zbits. Require Import Integers. Require Import Floats. Require Import Values. @@ -300,13 +301,13 @@ Proof. rewrite Int.bits_ror. replace (((i - Int.unsigned amount) mod Int.zwordsize + Int.unsigned amount) mod Int.zwordsize) with i. auto. - apply Int.eqmod_small_eq with Int.zwordsize; auto. - apply Int.eqmod_trans with ((i - Int.unsigned amount) + Int.unsigned amount). - apply Int.eqmod_refl2; omega. - eapply Int.eqmod_trans. 2: apply Int.eqmod_mod; auto. - apply Int.eqmod_add. - apply Int.eqmod_mod; auto. - apply Int.eqmod_refl. + apply eqmod_small_eq with Int.zwordsize; auto. + apply eqmod_trans with ((i - Int.unsigned amount) + Int.unsigned amount). + apply eqmod_refl2; omega. + eapply eqmod_trans. 2: apply eqmod_mod; auto. + apply eqmod_add. + apply eqmod_mod; auto. + apply eqmod_refl. apply Z_mod_lt; auto. apply Z_mod_lt; auto. Qed. @@ -324,16 +325,16 @@ Qed. Lemma eqmod_iagree: forall m x y, - Int.eqmod (two_p (Int.size m)) x y -> + eqmod (two_p (Int.size m)) x y -> iagree (Int.repr x) (Int.repr y) m. Proof. - intros. set (p := nat_of_Z (Int.size m)). + intros. set (p := Z.to_nat (Int.size m)). generalize (Int.size_range m); intros RANGE. - assert (EQ: Int.size m = Z.of_nat p). { symmetry; apply nat_of_Z_eq. omega. } + assert (EQ: Int.size m = Z.of_nat p). { symmetry; apply Z2Nat.id. omega. } rewrite EQ in H; rewrite <- two_power_nat_two_p in H. red; intros. rewrite ! Int.testbit_repr by auto. destruct (zlt i (Int.size m)). - eapply Int.same_bits_eqmod; eauto. omega. + eapply same_bits_eqmod; eauto. omega. assert (Int.testbit m i = false) by (eapply Int.bits_size_2; omega). congruence. Qed. @@ -343,13 +344,13 @@ Definition complete_mask (m: int) := Int.zero_ext (Int.size m) Int.mone. Lemma iagree_eqmod: forall x y m, iagree x y (complete_mask m) -> - Int.eqmod (two_p (Int.size m)) (Int.unsigned x) (Int.unsigned y). + eqmod (two_p (Int.size m)) (Int.unsigned x) (Int.unsigned y). Proof. - intros. set (p := nat_of_Z (Int.size m)). + intros. set (p := Z.to_nat (Int.size m)). generalize (Int.size_range m); intros RANGE. - assert (EQ: Int.size m = Z.of_nat p). { symmetry; apply nat_of_Z_eq. omega. } + assert (EQ: Int.size m = Z.of_nat p). { symmetry; apply Z2Nat.id. omega. } rewrite EQ; rewrite <- two_power_nat_two_p. - apply Int.eqmod_same_bits. intros. apply H. omega. + apply eqmod_same_bits. intros. apply H. omega. unfold complete_mask. rewrite Int.bits_zero_ext by omega. rewrite zlt_true by omega. rewrite Int.bits_mone by omega. auto. Qed. @@ -362,7 +363,7 @@ Proof. + assert (Int.unsigned m <> 0). { red; intros; elim n. rewrite <- (Int.repr_unsigned m). rewrite H; auto. } assert (0 < Int.size m). - { apply Int.Zsize_pos'. generalize (Int.unsigned_range m); omega. } + { apply Zsize_pos'. generalize (Int.unsigned_range m); omega. } generalize (Int.size_range m); intros. f_equal. apply Int.bits_size_4. tauto. rewrite Int.bits_zero_ext by omega. rewrite zlt_true by omega. @@ -593,7 +594,8 @@ Proof. Qed. (** Modular arithmetic operations: add, mul, opposite. - (But not subtraction because of the pointer - pointer case. *) + Also subtraction, but only on 64-bit targets, otherwise + the pointer - pointer case does not fit. *) Definition modarith (x: nval) := match x with @@ -610,7 +612,20 @@ Proof. unfold modarith; intros. destruct x; simpl in *. - auto. - unfold Val.add; InvAgree. - apply eqmod_iagree. apply Int.eqmod_add; apply iagree_eqmod; auto. + apply eqmod_iagree. apply eqmod_add; apply iagree_eqmod; auto. +- inv H; auto. inv H0; auto. destruct w1; auto. +Qed. + +Lemma sub_sound: + forall v1 w1 v2 w2 x, + vagree v1 w1 (modarith x) -> vagree v2 w2 (modarith x) -> + Archi.ptr64 = true -> + vagree (Val.sub v1 v2) (Val.sub w1 w2) x. +Proof. + unfold modarith; intros. destruct x; simpl in *. +- auto. +- unfold Val.sub; rewrite H1; InvAgree. + apply eqmod_iagree. apply eqmod_sub; apply iagree_eqmod; auto. - inv H; auto. inv H0; auto. destruct w1; auto. Qed. @@ -626,7 +641,7 @@ Lemma mul_sound: Proof. unfold mul, add; intros. destruct x; simpl in *. - auto. -- unfold Val.mul; InvAgree. apply eqmod_iagree. apply Int.eqmod_mult; apply iagree_eqmod; auto. +- unfold Val.mul; InvAgree. apply eqmod_iagree. apply eqmod_mult; apply iagree_eqmod; auto. - inv H; auto. inv H0; auto. destruct w1; auto. Qed. @@ -638,7 +653,7 @@ Proof. intros; destruct x; simpl in *. - auto. - unfold Val.neg; InvAgree. - apply eqmod_iagree. apply Int.eqmod_neg. apply iagree_eqmod; auto. + apply eqmod_iagree. apply eqmod_neg. apply iagree_eqmod; auto. - inv H; simpl; auto. Qed. @@ -679,7 +694,7 @@ Definition sign_ext (n: Z) (x: nval) := Lemma sign_ext_sound: forall v w x n, vagree v w (sign_ext n x) -> - 0 < n < Int.zwordsize -> + 0 < n -> vagree (Val.sign_ext n v) (Val.sign_ext n w) x. Proof. unfold sign_ext; intros. destruct x; simpl in *. @@ -785,6 +800,34 @@ Proof. inv H0. rewrite iagree_and_eq in H. rewrite H. auto. Qed. +(** The needs of a select *) + +Lemma normalize_sound: + forall v w x ty, + vagree v w x -> + vagree (Val.normalize v ty) (Val.normalize w ty) x. +Proof. + intros. destruct x; simpl in *. +- auto. +- unfold Val.normalize. destruct v. + auto. + destruct w; try contradiction. destruct ty; auto. + destruct ty; auto. + destruct ty; auto. + destruct ty; auto. + destruct ty; destruct Archi.ptr64; auto. +- apply Val.normalize_lessdef; auto. +Qed. + +Lemma select_sound: + forall ob v1 v2 w1 w2 ty x, + vagree v1 w1 x -> vagree v2 w2 x -> + vagree (Val.select ob v1 v2 ty) (Val.select ob w1 w2 ty) x. +Proof. + unfold Val.select; intros. destruct ob as [b|]; auto with na. + apply normalize_sound. destruct b; auto. +Qed. + (** The default abstraction: if the result is unused, the arguments are unused; otherwise, the arguments are needed in full. *) @@ -860,7 +903,8 @@ Lemma default_needs_of_operation_sound: eval_operation ge (Vptr sp Ptrofs.zero) op args1 m1 = Some v1 -> vagree_list args1 args2 nil \/ vagree_list args1 args2 (default nv :: nil) - \/ vagree_list args1 args2 (default nv :: default nv :: nil) -> + \/ vagree_list args1 args2 (default nv :: default nv :: nil) + \/ vagree_list args1 args2 (default nv :: default nv :: default nv :: nil) -> nv <> Nothing -> exists v2, eval_operation ge (Vptr sp Ptrofs.zero) op args2 m2 = Some v2 @@ -872,7 +916,8 @@ Proof. { destruct H0. auto with na. destruct H0. inv H0; constructor; auto with na. - inv H0; constructor; auto with na. inv H8; constructor; auto with na. + destruct H0. inv H0. constructor. inv H8; constructor; auto with na. + inv H0; constructor; auto with na. inv H8; constructor; auto with na. inv H9; constructor; auto with na. } exploit (@eval_operation_inj _ _ _ _ ge ge inject_id). eassumption. auto. auto. auto. diff --git a/backend/PrintAsm.ml b/backend/PrintAsm.ml index 92d465d5..155f5e55 100644 --- a/backend/PrintAsm.ml +++ b/backend/PrintAsm.ml @@ -13,7 +13,6 @@ open AST open Camlcoq -open DwarfPrinter open PrintAsmaux open Printf open Sections @@ -40,6 +39,7 @@ module Printer(Target:TARGET) = let print_function oc name fn = Hashtbl.clear current_function_labels; + Debug.symbol_printed (extern_atom name); let (text, lit, jmptbl) = Target.get_section_names name in Target.section oc text; let alignment = @@ -117,7 +117,7 @@ module Printer(Target:TARGET) = match v.gvar_init with | [] -> () | _ -> - Debug.variable_printed (extern_atom name); + Debug.symbol_printed (extern_atom name); let sec = match C2C.atom_sections name with | [s] -> s @@ -176,7 +176,7 @@ module Printer(Target:TARGET) = let address = Target.address end - module DebugPrinter = DwarfPrinter (DwarfTarget) + module DebugPrinter = DwarfPrinter.DwarfPrinter (DwarfTarget) end let print_program oc p = diff --git a/backend/PrintAsmaux.ml b/backend/PrintAsmaux.ml index f9ed569f..d82e6f84 100644 --- a/backend/PrintAsmaux.ml +++ b/backend/PrintAsmaux.ml @@ -99,7 +99,7 @@ let exists_constants () = let current_function_stacksize = ref 0l let current_function_sig = - ref { sig_args = []; sig_res = None; sig_cc = cc_default } + ref { sig_args = []; sig_res = Tvoid; sig_cc = cc_default } (* Functions for printing of symbol names *) let elf_symbol oc symb = @@ -245,14 +245,15 @@ let print_debug_info comment print_line preg_string sp_name oc kind txt args = (** Inline assembly *) -let print_asm_argument print_preg oc modifier = function - | BA r -> print_preg oc r +let print_asm_argument print_preg oc modifier typ = function + | BA r -> print_preg oc typ r | BA_splitlong(BA hi, BA lo) -> begin match modifier with - | "R" -> print_preg oc hi - | "Q" -> print_preg oc lo - | _ -> fprintf oc "%a:%a" print_preg hi print_preg lo - (* Probably not what was intended *) + | "R" -> print_preg oc Tint hi + | "Q" -> print_preg oc Tint lo + | _ -> print_preg oc Tint hi; fprintf oc ":"; print_preg oc Tint lo + (* This case (printing a split long in full) should never + happen because of the checks done in ExtendedAsm.ml *) end | _ -> failwith "bad asm argument" @@ -265,8 +266,10 @@ let re_asm_param_1 = Str.regexp "%%\\|%[QR]?[0-9]+" let re_asm_param_2 = Str.regexp "%\\([QR]?\\)\\([0-9]+\\)" let print_inline_asm print_preg oc txt sg args res = - let operands = - if sg.sig_res = None then args else builtin_arg_of_res res :: args in + let (operands, ty_operands) = + match sg.sig_res with + | Tvoid -> (args, sg.sig_args) + | tres -> (builtin_arg_of_res res :: args, proj_rettype tres :: sg.sig_args) in let print_fragment = function | Str.Text s -> output_string oc s @@ -277,7 +280,9 @@ let print_inline_asm print_preg oc txt sg args res = let modifier = Str.matched_group 1 s and number = int_of_string (Str.matched_group 2 s) in try - print_asm_argument print_preg oc modifier (List.nth operands number) + print_asm_argument print_preg oc modifier + (List.nth ty_operands number) + (List.nth operands number) with Failure _ -> fprintf oc "<bad parameter %s>" s in List.iter print_fragment (Str.full_split re_asm_param_1 txt); @@ -289,12 +294,20 @@ let print_inline_asm print_preg oc txt sg args res = let print_version_and_options oc comment = let version_string = if Version.buildnr <> "" && Version.tag <> "" then - sprintf "%s, Build: %s, Tag: %s" Version.version Version.buildnr Version.tag + sprintf "Release: %s, Build: %s, Tag: %s" Version.version Version.buildnr Version.tag else Version.version in fprintf oc "%s File generated by CompCert %s\n" comment version_string; fprintf oc "%s Command line:" comment; - for i = 1 to Array.length Sys.argv - 1 do - fprintf oc " %s" Sys.argv.(i) + for i = 1 to Array.length Commandline.argv - 1 do + fprintf oc " %s" Commandline.argv.(i) done; fprintf oc "\n" +(** Get the name of the common section if it is used otherwise the given section + name, with bss as default *) + +let common_section ?(sec = ".bss") () = + if !Clflags.option_fcommon then + "COMM" + else + sec diff --git a/backend/PrintCminor.ml b/backend/PrintCminor.ml index f68c1267..c9a6d399 100644 --- a/backend/PrintCminor.ml +++ b/backend/PrintCminor.ml @@ -16,7 +16,7 @@ (** Pretty-printer for Cminor *) open Format -open Camlcoq +open! Camlcoq open Integers open AST open PrintAST @@ -193,9 +193,7 @@ let print_sig p sg = List.iter (fun t -> fprintf p "%s ->@ " (name_of_type t)) sg.sig_args; - match sg.sig_res with - | None -> fprintf p "void" - | Some ty -> fprintf p "%s" (name_of_type ty) + fprintf p "%s" (name_of_rettype sg.sig_res) let rec just_skips s = match s with diff --git a/backend/PrintLTL.ml b/backend/PrintLTL.ml index d0557073..1c449e74 100644 --- a/backend/PrintLTL.ml +++ b/backend/PrintLTL.ml @@ -112,7 +112,7 @@ let print_function pp id f = fprintf pp "%s() {\n" (extern_atom id); let instrs = List.sort - (fun (pc1, _) (pc2, _) -> Pervasives.compare pc2 pc1) + (fun (pc1, _) (pc2, _) -> compare pc2 pc1) (List.rev_map (fun (pc, i) -> (P.to_int pc, i)) (PTree.elements f.fn_code)) in diff --git a/backend/PrintLTLin.ml b/backend/PrintLTLin.ml deleted file mode 100644 index 4e8efd16..00000000 --- a/backend/PrintLTLin.ml +++ /dev/null @@ -1,115 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Xavier Leroy, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -(** Pretty-printer for LTLin code *) - -open Format -open Camlcoq -open Datatypes -open Maps -open AST -open Integers -open Locations -open Machregsaux -open LTLin -open PrintAST -open PrintOp - -let reg pp loc = - match loc with - | R r -> - begin match name_of_register r with - | Some s -> fprintf pp "%s" s - | None -> fprintf pp "<unknown reg>" - end - | S (Local(ofs, ty)) -> - fprintf pp "local(%s,%ld)" (name_of_type ty) (camlint_of_coqint ofs) - | S (Incoming(ofs, ty)) -> - fprintf pp "incoming(%s,%ld)" (name_of_type ty) (camlint_of_coqint ofs) - | S (Outgoing(ofs, ty)) -> - fprintf pp "outgoing(%s,%ld)" (name_of_type ty) (camlint_of_coqint ofs) - -let rec regs pp = function - | [] -> () - | [r] -> reg pp r - | r1::rl -> fprintf pp "%a, %a" reg r1 regs rl - -let ros pp = function - | Coq_inl r -> reg pp r - | Coq_inr s -> fprintf pp "\"%s\"" (extern_atom s) - -let print_instruction pp i = - match i with - | Lop(op, args, res) -> - fprintf pp "%a = %a@ " - reg res (PrintOp.print_operation reg) (op, args) - | Lload(chunk, addr, args, dst) -> - fprintf pp "%a = %s[%a]@ " - reg dst (name_of_chunk chunk) - (PrintOp.print_addressing reg) (addr, args) - | Lstore(chunk, addr, args, src) -> - fprintf pp "%s[%a] = %a@ " - (name_of_chunk chunk) - (PrintOp.print_addressing reg) (addr, args) - reg src - | Lcall(sg, fn, args, res) -> - fprintf pp "%a = %a(%a)@ " - reg res ros fn regs args - | Ltailcall(sg, fn, args) -> - fprintf pp "tailcall %a(%a)@ " - ros fn regs args - | Lbuiltin(ef, args, res) -> - fprintf pp "%a = builtin %s(%a)@ " - reg res (name_of_external ef) regs args - | Llabel lbl -> - fprintf pp "%ld:@ " (P.to_int32 lbl) - | Lgoto lbl -> - fprintf pp "goto %ld@ " (P.to_int32 lbl) - | Lcond(cond, args, lbl) -> - fprintf pp "if (%a) goto %ld@ " - (PrintOp.print_condition reg) (cond, args) - (P.to_int32 lbl) - | Ljumptable(arg, tbl) -> - let tbl = Array.of_list tbl in - fprintf pp "@[<v 2>jumptable (%a)" reg arg; - for i = 0 to Array.length tbl - 1 do - fprintf pp "@ case %d: goto %ld" i (P.to_int32 tbl.(i)) - done; - fprintf pp "@]@ " - | Lreturn None -> - fprintf pp "return@ " - | Lreturn (Some arg) -> - fprintf pp "return %a@ " reg arg - -let print_function pp id f = - fprintf pp "@[<v 2>%s(%a) {@ " (extern_atom id) regs f.fn_params; - List.iter (print_instruction pp) f.fn_code; - fprintf pp "@;<0 -2>}@]@." - -let print_globdef pp (id, gd) = - match gd with - | Gfun(Internal f) -> print_function pp id f - | _ -> () - -let print_program pp prog = - List.iter (print_globdef pp) prog.prog_defs - -let destination : string option ref = ref None - -let print_if prog = - match !destination with - | None -> () - | Some f -> - let oc = open_out f in - let pp = formatter_of_out_channel oc in - print_program pp prog; - close_out oc diff --git a/backend/PrintRTL.ml b/backend/PrintRTL.ml index ba336b0a..841540b6 100644 --- a/backend/PrintRTL.ml +++ b/backend/PrintRTL.ml @@ -93,7 +93,7 @@ let print_function pp id f = fprintf pp "%s(%a) {\n" (extern_atom id) regs f.fn_params; let instrs = List.sort - (fun (pc1, _) (pc2, _) -> Pervasives.compare pc2 pc1) + (fun (pc1, _) (pc2, _) -> compare pc2 pc1) (List.rev_map (fun (pc, i) -> (P.to_int pc, i)) (PTree.elements f.fn_code)) in diff --git a/backend/PrintXTL.ml b/backend/PrintXTL.ml index cc1f7d49..6432682a 100644 --- a/backend/PrintXTL.ml +++ b/backend/PrintXTL.ml @@ -138,7 +138,7 @@ let print_function pp ?alloc ?live f = fprintf pp "f() {\n"; let instrs = List.sort - (fun (pc1, _) (pc2, _) -> Pervasives.compare pc2 pc1) + (fun (pc1, _) (pc2, _) -> compare pc2 pc1) (List.map (fun (pc, i) -> (P.to_int pc, i)) (PTree.elements f.fn_code)) in diff --git a/backend/RTLgen.v b/backend/RTLgen.v index 9d7a8506..f7280c9e 100644 --- a/backend/RTLgen.v +++ b/backend/RTLgen.v @@ -410,12 +410,11 @@ Fixpoint convert_builtin_args {A: Type} (al: list (builtin_arg expr)) (rl: list a1' :: convert_builtin_args al rl1 end. -Definition convert_builtin_res (map: mapping) (oty: option typ) (r: builtin_res ident) : mon (builtin_res reg) := - match r, oty with - | BR id, _ => do r <- find_var map id; ret (BR r) - | BR_none, None => ret BR_none - | BR_none, Some _ => do r <- new_reg; ret (BR r) - | _, _ => error (Errors.msg "RTLgen: bad builtin_res") +Definition convert_builtin_res (map: mapping) (ty: rettype) (r: builtin_res ident) : mon (builtin_res reg) := + match r with + | BR id => do r <- find_var map id; ret (BR r) + | BR_none => if rettype_eq ty Tvoid then ret BR_none else (do r <- new_reg; ret (BR r)) + | _ => error (Errors.msg "RTLgen: bad builtin_res") end. (** Translation of an expression. [transl_expr map a rd nd] @@ -667,10 +666,7 @@ Fixpoint reserve_labels (s: stmt) (ms: labelmap * state) (** Translation of a CminorSel function. *) Definition ret_reg (sig: signature) (rd: reg) : option reg := - match sig.(sig_res) with - | None => None - | Some ty => Some rd - end. + if rettype_eq sig.(sig_res) Tvoid then None else Some rd. Definition transl_fun (f: CminorSel.function) (ngoto: labelmap): mon (node * list reg) := do (rparams, map1) <- add_vars init_mapping f.(CminorSel.fn_params); diff --git a/backend/RTLgenspec.v b/backend/RTLgenspec.v index 17022a7d..72693f63 100644 --- a/backend/RTLgenspec.v +++ b/backend/RTLgenspec.v @@ -639,8 +639,8 @@ Lemma new_reg_return_ok: map_valid map s1 -> return_reg_ok s2 map (ret_reg sig r). Proof. - intros. unfold ret_reg. destruct (sig_res sig); constructor. - eauto with rtlg. eauto with rtlg. + intros. unfold ret_reg. + destruct (rettype_eq (sig_res sig) Tvoid); constructor; eauto with rtlg. Qed. (** * Relational specification of the translation *) @@ -1224,9 +1224,9 @@ Lemma convert_builtin_res_charact: Proof. destruct res; simpl; intros. - monadInv TR. constructor. unfold find_var in EQ. destruct (map_vars map)!x; inv EQ; auto. -- destruct oty; monadInv TR. -+ constructor. eauto with rtlg. +- destruct (rettype_eq oty Tvoid); monadInv TR. + constructor. ++ constructor. eauto with rtlg. - monadInv TR. Qed. @@ -1350,7 +1350,7 @@ Proof. intros [C D]. eapply tr_function_intro; eauto with rtlg. eapply transl_stmt_charact; eauto with rtlg. - unfold ret_reg. destruct (sig_res (CminorSel.fn_sig f)). - constructor. eauto with rtlg. eauto with rtlg. + unfold ret_reg. destruct (rettype_eq (sig_res (CminorSel.fn_sig f)) Tvoid). constructor. + constructor; eauto with rtlg. Qed. diff --git a/backend/RTLtyping.v b/backend/RTLtyping.v index 8336d1bf..5b8646ea 100644 --- a/backend/RTLtyping.v +++ b/backend/RTLtyping.v @@ -151,11 +151,12 @@ Inductive wt_instr : instruction -> Prop := list_length_z tbl * 4 <= Int.max_unsigned -> wt_instr (Ijumptable arg tbl) | wt_Ireturn_none: - funct.(fn_sig).(sig_res) = None -> + funct.(fn_sig).(sig_res) = Tvoid -> wt_instr (Ireturn None) | wt_Ireturn_some: forall arg ty, - funct.(fn_sig).(sig_res) = Some ty -> + funct.(fn_sig).(sig_res) <> Tvoid -> + env arg = proj_sig_res funct.(fn_sig) -> env arg = ty -> wt_instr (Ireturn (Some arg)). @@ -298,7 +299,7 @@ Definition type_instr (e: S.typenv) (i: instruction) : res S.typenv := | Itailcall sig ros args => do e1 <- type_ros e ros; do e2 <- S.set_list e1 args sig.(sig_args); - if opt_typ_eq sig.(sig_res) f.(fn_sig).(sig_res) then + if rettype_eq sig.(sig_res) f.(fn_sig).(sig_res) then if tailcall_is_possible sig then OK e2 else Error(msg "tailcall not possible") @@ -323,9 +324,9 @@ Definition type_instr (e: S.typenv) (i: instruction) : res S.typenv := then OK e1 else Error(msg "jumptable too big") | Ireturn optres => - match optres, f.(fn_sig).(sig_res) with - | None, None => OK e - | Some r, Some t => S.set e r t + match optres, rettype_eq f.(fn_sig).(sig_res) Tvoid with + | None, left _ => OK e + | Some r, right _ => S.set e r (proj_sig_res f.(fn_sig)) | _, _ => Error(msg "bad return") end end. @@ -468,7 +469,7 @@ Proof. destruct l; try discriminate. destruct l; monadInv EQ0. eauto with ty. destruct (type_of_operation o) as [targs tres] eqn:TYOP. monadInv EQ0. eauto with ty. - (* tailcall *) - destruct (opt_typ_eq (sig_res s) (sig_res (fn_sig f))); try discriminate. + destruct (rettype_eq (sig_res s) (sig_res (fn_sig f))); try discriminate. destruct (tailcall_is_possible s) eqn:TCIP; inv EQ2. eauto with ty. - (* builtin *) @@ -477,7 +478,8 @@ Proof. destruct (zle (list_length_z l * 4) Int.max_unsigned); inv EQ2. eauto with ty. - (* return *) - simpl in H. destruct o as [r|] eqn: RET; destruct (sig_res (fn_sig f)) as [t|] eqn: RES; try discriminate. + simpl in H. + destruct o as [r|] eqn: RET; destruct (rettype_eq (sig_res (fn_sig f)) Tvoid); try discriminate. eauto with ty. inv H; auto with ty. Qed. @@ -519,7 +521,7 @@ Proof. eapply S.set_sound; eauto with ty. eauto with ty. - (* tailcall *) - destruct (opt_typ_eq (sig_res s) (sig_res (fn_sig f))); try discriminate. + destruct (rettype_eq (sig_res s) (sig_res (fn_sig f))); try discriminate. destruct (tailcall_is_possible s) eqn:TCIP; inv EQ2. constructor. eapply type_ros_sound; eauto with ty. @@ -543,8 +545,9 @@ Proof. eapply check_successors_sound; eauto. auto. - (* return *) - simpl in H. destruct o as [r|] eqn: RET; destruct (sig_res (fn_sig f)) as [t|] eqn: RES; try discriminate. - econstructor. eauto. eapply S.set_sound; eauto with ty. + simpl in H. + destruct o as [r|] eqn: RET; destruct (rettype_eq (sig_res (fn_sig f)) Tvoid); try discriminate. + econstructor. auto. eapply S.set_sound; eauto with ty. eauto. inv H. constructor. auto. Qed. @@ -721,9 +724,9 @@ Proof. rewrite check_successor_complete by auto; simpl. apply IHtbl0; intros; auto. - (* return none *) - rewrite H0. exists e; auto. + rewrite H0, dec_eq_true. exists e; auto. - (* return some *) - rewrite H0. apply S.set_complete; auto. + rewrite dec_eq_false by auto. apply S.set_complete; auto. Qed. Lemma type_code_complete: @@ -872,7 +875,7 @@ Qed. Inductive wt_stackframes: list stackframe -> signature -> Prop := | wt_stackframes_nil: forall sg, - sg.(sig_res) = Some Tint -> + sg.(sig_res) = Tint -> wt_stackframes nil sg | wt_stackframes_cons: forall s res f sp pc rs env sg, @@ -964,13 +967,13 @@ Proof. econstructor; eauto. (* Ireturn *) econstructor; eauto. - inv WTI; simpl. auto. unfold proj_sig_res; rewrite H2. auto. + inv WTI; simpl. auto. rewrite <- H3. auto. (* internal function *) simpl in *. inv H5. econstructor; eauto. inv H1. apply wt_init_regs; auto. rewrite wt_params0. auto. (* external function *) - econstructor; eauto. simpl. + econstructor; eauto. eapply external_call_well_typed; eauto. (* return *) inv H1. econstructor; eauto. diff --git a/backend/SelectDivproof.v b/backend/SelectDivproof.v index 9e24857a..c57d3652 100644 --- a/backend/SelectDivproof.v +++ b/backend/SelectDivproof.v @@ -12,7 +12,7 @@ (** Correctness of instruction selection for integer division *) -Require Import Zquot Coqlib. +Require Import Zquot Coqlib Zbits. Require Import AST Integers Floats Values Memory Globalenvs Events. Require Import Cminor Op CminorSel. Require Import SelectOp SelectOpproof SplitLong SplitLongproof SelectLong SelectLongproof SelectDiv. @@ -57,13 +57,13 @@ Proof. apply Z.mul_nonneg_nonneg; omega. assert (k * n <= two_p (N + l) - two_p l). apply Z.le_trans with (two_p l * n). - apply Zmult_le_compat_r. omega. omega. + apply Z.mul_le_mono_nonneg_r; omega. replace (N + l) with (l + N) by omega. rewrite two_p_is_exp. replace (two_p l * two_p N - two_p l) with (two_p l * (two_p N - 1)) by ring. - apply Zmult_le_compat_l. omega. exploit (two_p_gt_ZERO l). omega. omega. + apply Z.mul_le_mono_nonneg_l. omega. exploit (two_p_gt_ZERO l). omega. omega. omega. omega. assert (0 <= two_p (N + l) * r). apply Z.mul_nonneg_nonneg. @@ -72,7 +72,7 @@ Proof. assert (two_p (N + l) * r <= two_p (N + l) * d - two_p (N + l)). replace (two_p (N + l) * d - two_p (N + l)) with (two_p (N + l) * (d - 1)) by ring. - apply Zmult_le_compat_l. + apply Z.mul_le_mono_nonneg_l. omega. exploit (two_p_gt_ZERO (N + l)). omega. omega. assert (0 <= m * n - two_p (N + l) * q). @@ -138,13 +138,13 @@ Proof. rewrite H2. assert (k * n <= two_p (N + l)). rewrite Z.add_comm. rewrite two_p_is_exp; try omega. - apply Z.le_trans with (two_p l * n). apply Zmult_le_compat_r. omega. omega. - apply Zmult_le_compat_l. omega. exploit (two_p_gt_ZERO l). omega. omega. + apply Z.le_trans with (two_p l * n). apply Z.mul_le_mono_nonneg_r; omega. + apply Z.mul_le_mono_nonneg_l. omega. exploit (two_p_gt_ZERO l). omega. omega. assert (two_p (N + l) * r <= two_p (N + l) * d - two_p (N + l)). replace (two_p (N + l) * d - two_p (N + l)) with (two_p (N + l) * (d - 1)) by ring. - apply Zmult_le_compat_l. omega. exploit (two_p_gt_ZERO (N + l)). omega. omega. + apply Z.mul_le_mono_nonneg_l. exploit (two_p_gt_ZERO (N + l)). omega. omega. omega. omega. Qed. @@ -246,10 +246,11 @@ Proof. unfold Int.max_signed; omega. apply Zdiv_interval_1. generalize Int.min_signed_neg; omega. apply Int.half_modulus_pos. apply Int.modulus_pos. - split. apply Z.le_trans with (Int.min_signed * m). apply Zmult_le_compat_l_neg. omega. generalize Int.min_signed_neg; omega. - apply Zmult_le_compat_r. unfold n; generalize (Int.signed_range x); tauto. tauto. + split. apply Z.le_trans with (Int.min_signed * m). + apply Z.mul_le_mono_nonpos_l. generalize Int.min_signed_neg; omega. omega. + apply Z.mul_le_mono_nonneg_r. omega. unfold n; generalize (Int.signed_range x); tauto. apply Z.le_lt_trans with (Int.half_modulus * m). - apply Zmult_le_compat_r. generalize (Int.signed_range x); unfold n, Int.max_signed; omega. tauto. + apply Z.mul_le_mono_nonneg_r. tauto. generalize (Int.signed_range x); unfold n, Int.max_signed; omega. apply Zmult_lt_compat_l. generalize Int.half_modulus_pos; omega. tauto. assert (32 < Int.max_unsigned) by (compute; auto). omega. unfold Int.lt; fold n. rewrite Int.signed_zero. destruct (zlt n 0); apply Int.eqm_unsigned_repr. @@ -290,7 +291,7 @@ Proof. apply Int.eqm_sym. eapply Int.eqm_trans. apply Int.eqm_signed_unsigned. apply Int.eqm_unsigned_repr_l. apply Int.eqm_refl2. apply (f_equal (fun x => n * x / Int.modulus)). - rewrite Int.signed_repr_eq. rewrite Zmod_small by assumption. + rewrite Int.signed_repr_eq. rewrite Z.mod_small by assumption. apply zlt_false. assumption. Qed. @@ -377,7 +378,7 @@ Qed. Remark int64_shr'_div_two_p: forall x y, Int64.shr' x y = Int64.repr (Int64.signed x / two_p (Int.unsigned y)). Proof. - intros; unfold Int64.shr'. rewrite Int64.Zshiftr_div_two_p; auto. generalize (Int.unsigned_range y); omega. + intros; unfold Int64.shr'. rewrite Zshiftr_div_two_p; auto. generalize (Int.unsigned_range y); omega. Qed. Lemma divls_mul_shift_gen: @@ -400,8 +401,9 @@ Proof. unfold Int64.max_signed; omega. apply Zdiv_interval_1. generalize Int64.min_signed_neg; omega. apply Int64.half_modulus_pos. apply Int64.modulus_pos. - split. apply Z.le_trans with (Int64.min_signed * m). apply Zmult_le_compat_l_neg. omega. generalize Int64.min_signed_neg; omega. - apply Zmult_le_compat_r. unfold n; generalize (Int64.signed_range x); tauto. tauto. + split. apply Z.le_trans with (Int64.min_signed * m). + apply Z.mul_le_mono_nonpos_l. generalize Int64.min_signed_neg; omega. omega. + apply Z.mul_le_mono_nonneg_r. tauto. unfold n; generalize (Int64.signed_range x); tauto. apply Z.le_lt_trans with (Int64.half_modulus * m). apply Zmult_le_compat_r. generalize (Int64.signed_range x); unfold n, Int64.max_signed; omega. tauto. apply Zmult_lt_compat_l. generalize Int64.half_modulus_pos; omega. tauto. @@ -444,14 +446,14 @@ Proof. apply Int64.eqm_sym. eapply Int64.eqm_trans. apply Int64.eqm_signed_unsigned. apply Int64.eqm_unsigned_repr_l. apply Int64.eqm_refl2. apply (f_equal (fun x => n * x / Int64.modulus)). - rewrite Int64.signed_repr_eq. rewrite Zmod_small by assumption. + rewrite Int64.signed_repr_eq. rewrite Z.mod_small by assumption. apply zlt_false. assumption. Qed. Remark int64_shru'_div_two_p: forall x y, Int64.shru' x y = Int64.repr (Int64.unsigned x / two_p (Int.unsigned y)). Proof. - intros; unfold Int64.shru'. rewrite Int64.Zshiftr_div_two_p; auto. generalize (Int.unsigned_range y); omega. + intros; unfold Int64.shru'. rewrite Zshiftr_div_two_p; auto. generalize (Int.unsigned_range y); omega. Qed. Theorem divlu_mul_shift: @@ -761,8 +763,8 @@ Lemma eval_divlu_mull: Proof. intros. unfold divlu_mull. exploit (divlu_mul_shift x); eauto. intros [A B]. assert (A0: eval_expr ge sp e m le (Eletvar O) (Vlong x)) by (constructor; auto). - exploit eval_mullhu. eauto. eexact A0. instantiate (1 := Int64.repr M). intros (v1 & A1 & B1). - exploit eval_shrluimm. eauto. eexact A1. instantiate (1 := Int.repr p). intros (v2 & A2 & B2). + exploit eval_mullhu. try apply HELPERS. eexact A0. instantiate (1 := Int64.repr M). intros (v1 & A1 & B1). + exploit eval_shrluimm. try apply HELPERS. eexact A1. instantiate (1 := Int.repr p). intros (v2 & A2 & B2). simpl in B1; inv B1. simpl in B2. replace (Int.ltu (Int.repr p) Int64.iwordsize') with true in B2. inv B2. rewrite B. assumption. unfold Int.ltu. rewrite Int.unsigned_repr. rewrite zlt_true; auto. tauto. @@ -832,17 +834,17 @@ Proof. intros. unfold divls_mull. assert (A0: eval_expr ge sp e m le (Eletvar O) (Vlong x)). { constructor; auto. } - exploit eval_mullhs. eauto. eexact A0. instantiate (1 := Int64.repr M). intros (v1 & A1 & B1). - exploit eval_addl; auto; try apply HELPERS. eexact A1. eexact A0. intros (v2 & A2 & B2). - exploit eval_shrluimm. eauto. eexact A0. instantiate (1 := Int.repr 63). intros (v3 & A3 & B3). + exploit eval_mullhs. try apply HELPERS. eexact A0. instantiate (1 := Int64.repr M). intros (v1 & A1 & B1). + exploit eval_addl. auto. eexact A1. eexact A0. intros (v2 & A2 & B2). + exploit eval_shrluimm. try apply HELPERS. eexact A0. instantiate (1 := Int.repr 63). intros (v3 & A3 & B3). set (a4 := if zlt M Int64.half_modulus then mullhs (Eletvar 0) (Int64.repr M) else addl (mullhs (Eletvar 0) (Int64.repr M)) (Eletvar 0)). set (v4 := if zlt M Int64.half_modulus then v1 else v2). assert (A4: eval_expr ge sp e m le a4 v4). { unfold a4, v4; destruct (zlt M Int64.half_modulus); auto. } - exploit eval_shrlimm. eauto. eexact A4. instantiate (1 := Int.repr p). intros (v5 & A5 & B5). - exploit eval_addl; auto; try apply HELPERS. eexact A5. eexact A3. intros (v6 & A6 & B6). + exploit eval_shrlimm. try apply HELPERS. eexact A4. instantiate (1 := Int.repr p). intros (v5 & A5 & B5). + exploit eval_addl. auto. eexact A5. eexact A3. intros (v6 & A6 & B6). assert (RANGE: forall x, 0 <= x < 64 -> Int.ltu (Int.repr x) Int64.iwordsize' = true). { intros. unfold Int.ltu. rewrite Int.unsigned_repr. rewrite zlt_true by tauto. auto. assert (64 < Int.max_unsigned) by (compute; auto). omega. } @@ -946,8 +948,7 @@ Proof. intros until y. unfold divf. destruct (divf_match b); intros. - unfold divfimm. destruct (Float.exact_inverse n2) as [n2' | ] eqn:EINV. + inv H0. inv H4. simpl in H6. inv H6. econstructor; split. - EvalOp. constructor. eauto. constructor. EvalOp. simpl; eauto. constructor. - simpl; eauto. + repeat (econstructor; eauto). destruct x; simpl; auto. erewrite Float.div_mul_inverse; eauto. + TrivialExists. - TrivialExists. @@ -962,8 +963,7 @@ Proof. intros until y. unfold divfs. destruct (divfs_match b); intros. - unfold divfsimm. destruct (Float32.exact_inverse n2) as [n2' | ] eqn:EINV. + inv H0. inv H4. simpl in H6. inv H6. econstructor; split. - EvalOp. constructor. eauto. constructor. EvalOp. simpl; eauto. constructor. - simpl; eauto. + repeat (econstructor; eauto). destruct x; simpl; auto. erewrite Float32.div_mul_inverse; eauto. + TrivialExists. - TrivialExists. diff --git a/backend/Selection.v b/backend/Selection.v index 4520cb0c..c9771d99 100644 --- a/backend/Selection.v +++ b/backend/Selection.v @@ -24,9 +24,9 @@ Require String. Require Import Coqlib Maps. -Require Import AST Errors Integers Globalenvs Switch. +Require Import AST Errors Integers Globalenvs Builtins Switch. Require Cminor. -Require Import Op CminorSel. +Require Import Op CminorSel Cminortyping. Require Import SelectOp SplitLong SelectLong SelectDiv. Require Machregs. @@ -43,6 +43,12 @@ Function condexpr_of_expr (e: expr) : condexpr := | _ => CEcond (Ccompuimm Cne Int.zero) (e ::: Enil) end. +Function condition_of_expr (e: expr) : condition * exprlist := + match e with + | Eop (Ocmp c) el => (c, el) + | _ => (Ccompuimm Cne Int.zero, e ::: Enil) + end. + (** Conversion of loads and stores *) Definition load (chunk: memory_chunk) (e1: expr) := @@ -156,6 +162,13 @@ Definition sel_binop (op: Cminor.binary_operation) (arg1 arg2: expr) : expr := | Cminor.Ocmplu c => cmplu c arg1 arg2 end. +Definition sel_select (ty: typ) (cnd ifso ifnot: expr) : expr := + let (cond, args) := condition_of_expr cnd in + match SelectOp.select ty cond args ifso ifnot with + | Some a => a + | None => Econdition (condexpr_of_expr cnd) ifso ifnot + end. + (** Conversion from Cminor expression to Cminorsel expressions *) Fixpoint sel_expr (a: Cminor.expr) : expr := @@ -173,6 +186,10 @@ Fixpoint sel_exprlist (al: list Cminor.expr) : exprlist := | a :: bl => Econs (sel_expr a) (sel_exprlist bl) end. +Definition sel_select_opt (ty: typ) (arg1 arg2 arg3: Cminor.expr) : option expr := + let (cond, args) := condition_of_expr (sel_expr arg1) in + SelectOp.select ty cond args (sel_expr arg2) (sel_expr arg3). + (** Recognition of immediate calls and calls to built-in functions that should be inlined *) @@ -221,6 +238,43 @@ Definition sel_builtin_res (optid: option ident) : builtin_res ident := | Some id => BR id end. +(** Known builtin functions *) + +Function sel_known_builtin (bf: builtin_function) (args: exprlist) := + match bf, args with + | BI_platform b, _ => + SelectOp.platform_builtin b args + | BI_standard (BI_select ty), a1 ::: a2 ::: a3 ::: Enil => + Some (sel_select ty a1 a2 a3) + | BI_standard BI_fabs, a1 ::: Enil => + Some (SelectOp.absf a1) + | _, _ => + None + end. + +(** Builtin functions in general *) + +Definition sel_builtin_default (optid: option ident) (ef: external_function) + (args: list Cminor.expr) := + Sbuiltin (sel_builtin_res optid) ef + (sel_builtin_args args (Machregs.builtin_constraints ef)). + +Definition sel_builtin (optid: option ident) (ef: external_function) + (args: list Cminor.expr) := + match optid, ef with + | Some id, EF_builtin name sg => + match lookup_builtin_function name sg with + | Some bf => + match sel_known_builtin bf (sel_exprlist args) with + | Some a => Sassign id a + | None => sel_builtin_default optid ef args + end + | None => sel_builtin_default optid ef args + end + | _, _ => + sel_builtin_default optid ef args + end. + (** Conversion of Cminor [switch] statements to decision trees. *) Parameter compile_switch: Z -> nat -> table -> comptree. @@ -267,9 +321,63 @@ Definition sel_switch_long := (fun arg ofs => subl arg (longconst (Int64.repr ofs))) lowlong. +(** "If conversion": conversion of certain if-then-else statements + into branchless conditional move instructions. *) + +(** Recognition of "then" and "else" statements that support if-conversion. + Basically we are interested in assignments to local variables [id = e]. + However the front-end may have put [skip] statements around these + assignments. *) + +Inductive stmt_class : Type := + | SCskip + | SCassign (id: ident) (a: Cminor.expr) + | SCother. + +Function classify_stmt (s: Cminor.stmt) : stmt_class := + match s with + | Cminor.Sskip => SCskip + | Cminor.Sassign id a => SCassign id a + | Cminor.Sseq Cminor.Sskip s => classify_stmt s + | Cminor.Sseq s Cminor.Sskip => classify_stmt s + | _ => SCother + end. + +(** External heuristic to limit the amount of if-conversion performed. + Arguments are: the condition, the "then" and the "else" expressions, + and the type at which selection is done. *) + +Parameter if_conversion_heuristic: + Cminor.expr -> Cminor.expr -> Cminor.expr -> AST.typ -> bool. + +Definition if_conversion_base + (ki: known_idents) (env: typenv) + (cond: Cminor.expr) (id: ident) (ifso ifnot: Cminor.expr) : option stmt := + let ty := env id in + if is_known ki id + && safe_expr ki ifso && safe_expr ki ifnot + && if_conversion_heuristic cond ifso ifnot ty + then option_map + (fun sel => Sassign id sel) + (sel_select_opt ty cond ifso ifnot) + else None. + +Definition if_conversion + (ki: known_idents) (env: typenv) + (cond: Cminor.expr) (ifso ifnot: Cminor.stmt) : option stmt := + match classify_stmt ifso, classify_stmt ifnot with + | SCskip, SCassign id a => + if_conversion_base ki env cond id (Cminor.Evar id) a + | SCassign id a, SCskip => + if_conversion_base ki env cond id a (Cminor.Evar id) + | SCassign id1 a1, SCassign id2 a2 => + if ident_eq id1 id2 then if_conversion_base ki env cond id1 a1 a2 else None + | _, _ => None + end. + (** Conversion from Cminor statements to Cminorsel statements. *) -Fixpoint sel_stmt (s: Cminor.stmt) : res stmt := +Fixpoint sel_stmt (ki: known_idents) (env: typenv) (s: Cminor.stmt) : res stmt := match s with | Cminor.Sskip => OK Sskip | Cminor.Sassign id e => OK (Sassign id (sel_expr e)) @@ -278,28 +386,29 @@ Fixpoint sel_stmt (s: Cminor.stmt) : res stmt := OK (match classify_call fn with | Call_default => Scall optid sg (inl _ (sel_expr fn)) (sel_exprlist args) | Call_imm id => Scall optid sg (inr _ id) (sel_exprlist args) - | Call_builtin ef => Sbuiltin (sel_builtin_res optid) ef - (sel_builtin_args args - (Machregs.builtin_constraints ef)) + | Call_builtin ef => sel_builtin optid ef args end) | Cminor.Sbuiltin optid ef args => - OK (Sbuiltin (sel_builtin_res optid) ef - (sel_builtin_args args (Machregs.builtin_constraints ef))) + OK (sel_builtin optid ef args) | Cminor.Stailcall sg fn args => OK (match classify_call fn with | Call_imm id => Stailcall sg (inr _ id) (sel_exprlist args) | _ => Stailcall sg (inl _ (sel_expr fn)) (sel_exprlist args) end) | Cminor.Sseq s1 s2 => - do s1' <- sel_stmt s1; do s2' <- sel_stmt s2; + do s1' <- sel_stmt ki env s1; do s2' <- sel_stmt ki env s2; OK (Sseq s1' s2') | Cminor.Sifthenelse e ifso ifnot => - do ifso' <- sel_stmt ifso; do ifnot' <- sel_stmt ifnot; - OK (Sifthenelse (condexpr_of_expr (sel_expr e)) ifso' ifnot') + match if_conversion ki env e ifso ifnot with + | Some s => OK s + | None => + do ifso' <- sel_stmt ki env ifso; do ifnot' <- sel_stmt ki env ifnot; + OK (Sifthenelse (condexpr_of_expr (sel_expr e)) ifso' ifnot') + end | Cminor.Sloop body => - do body' <- sel_stmt body; OK (Sloop body') + do body' <- sel_stmt ki env body; OK (Sloop body') | Cminor.Sblock body => - do body' <- sel_stmt body; OK (Sblock body') + do body' <- sel_stmt ki env body; OK (Sblock body') | Cminor.Sexit n => OK (Sexit n) | Cminor.Sswitch false e cases dfl => let t := compile_switch Int.modulus dfl cases in @@ -314,7 +423,7 @@ Fixpoint sel_stmt (s: Cminor.stmt) : res stmt := | Cminor.Sreturn None => OK (Sreturn None) | Cminor.Sreturn (Some e) => OK (Sreturn (Some (sel_expr e))) | Cminor.Slabel lbl body => - do body' <- sel_stmt body; OK (Slabel lbl body') + do body' <- sel_stmt ki env body; OK (Slabel lbl body') | Cminor.Sgoto lbl => OK (Sgoto lbl) end. @@ -322,8 +431,15 @@ End SELECTION. (** Conversion of functions. *) +Definition known_id (f: Cminor.function) : known_idents := + let add (ki: known_idents) (id: ident) := PTree.set id tt ki in + List.fold_left add f.(Cminor.fn_vars) + (List.fold_left add f.(Cminor.fn_params) (PTree.empty unit)). + Definition sel_function (dm: PTree.t globdef) (hf: helper_functions) (f: Cminor.function) : res function := - do body' <- sel_stmt dm f.(Cminor.fn_body); + let ki := known_id f in + do env <- Cminortyping.type_function f; + do body' <- sel_stmt dm ki env f.(Cminor.fn_body); OK (mkfunction f.(Cminor.fn_sig) f.(Cminor.fn_params) diff --git a/backend/Selectionaux.ml b/backend/Selectionaux.ml new file mode 100644 index 00000000..8acae8f2 --- /dev/null +++ b/backend/Selectionaux.ml @@ -0,0 +1,115 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +open AST +open Cminor + +(* Heuristics to guide if conversion *) + +(* Estimate a cost for evaluating a safe expression. + Unsafe operators need not be estimated. + Basic integer operations (add, and, ...) have cost 1 by convention. + The other costs are rough estimates. *) + +let cost_unop = function + | Ocast8unsigned | Ocast8signed + | Ocast16unsigned | Ocast16signed + | Onegint | Onotint -> 1 + | Onegf | Oabsf -> 1 + | Onegfs | Oabsfs -> 1 + | Osingleoffloat | Ofloatofsingle -> 2 + | Ointoffloat | Ointuoffloat + | Ofloatofint | Ofloatofintu + | Ointofsingle | Ointuofsingle + | Osingleofint | Osingleofintu -> assert false + | Onegl | Onotl -> if Archi.splitlong then 2 else 1 + | Ointoflong | Olongofint | Olongofintu -> 1 + | Olongoffloat | Olonguoffloat + | Ofloatoflong | Ofloatoflongu + | Olongofsingle | Olonguofsingle + | Osingleoflong | Osingleoflongu -> assert false + +let cost_binop = function + | Oadd | Osub -> 1 + | Omul -> 2 + | Odiv | Odivu | Omod | Omodu -> assert false + | Oand | Oor | Oxor | Oshl | Oshr | Oshru -> 1 + | Oaddf | Osubf | Omulf -> 2 + | Odivf -> 10 + | Oaddfs| Osubfs| Omulfs -> 2 + | Odivfs -> 10 + | Oaddl | Osubl -> if Archi.splitlong then 3 else 1 + | Omull -> if Archi.splitlong then 6 else 2 + | Odivl | Odivlu | Omodl | Omodlu -> assert false + | Oandl | Oorl | Oxorl -> if Archi.splitlong then 2 else 1 + | Oshll | Oshrl | Oshrlu -> if Archi.splitlong then 4 else 1 + | Ocmp _ | Ocmpu _ -> 2 + | Ocmpf _ | Ocmpfs _ -> 2 + | Ocmpl _ | Ocmplu _ -> assert false + +let rec cost_expr = function + | Evar _ -> 0 + | Econst _ -> 1 + | Eunop(op, e1) -> cost_unop op + cost_expr e1 + | Ebinop(op, e1, e2) -> cost_binop op + cost_expr e1 + cost_expr e2 + | Eload(_, e1) -> assert false + +(* Does the target architecture support an efficient "conditional move" + at the given type? *) + +let fast_cmove ty = + match Configuration.arch, Configuration.model with + | "aarch64", _ -> + (match ty with Tint | Tlong | Tfloat | Tsingle -> true | _ -> false) + | "arm", _ -> + (match ty with Tint | Tfloat | Tsingle -> true | _ -> false) + | "powerpc", "e5500" -> + (match ty with Tint -> true | Tlong -> true | _ -> false) + | "powerpc", _ -> false + | "riscV", _ -> false + | "x86", _ -> + (match ty with Tint -> true | Tlong -> Archi.ptr64 | _ -> false) + | _, _ -> + assert false + +(* The if-conversion heuristic depend on the + -fif-conversion and -Obranchless flags. + +With [-fno-if-conversion] or [-0O], if-conversion is turned off entirely. +With [-Obranchless], if-conversion is performed whenever semantically +correct, regardless of how much it could cost. +Otherwise (and by default), optimization is performed when it seems beneficial. + +If-conversion seems beneficial if: +- the target architecture supports an efficient "conditional move" instruction + (not an emulation that takes several instructions) +- the total cost the "then" and "else" branches is not too high +- the cost difference between the "then" and "else" branches is low enough. + +Intuition: on a modern processor, the "then" and the "else" branches +can generally be computed in parallel, there is enough ILP for that. +So, the bad case is if the most taken branch is much cheaper than the +other branch. Another bad case is if both branches are big: since the +code for one branch precedes entirely the code for the other branch, +if the first branch contains a lot of instructions, +dynamic reordering of instructions will not look ahead far enough +to execute instructions from the other branch in parallel with +instructions from the first branch. +*) + +let if_conversion_heuristic cond ifso ifnot ty = + if not !Clflags.option_fifconversion then false else + if !Clflags.option_Obranchless then true else + if not (fast_cmove ty) then false else + let c1 = cost_expr ifso and c2 = cost_expr ifnot in + c1 + c2 <= 24 && abs (c1 - c2) <= 8 + diff --git a/backend/Selectionproof.v b/backend/Selectionproof.v index afc470b3..8a3aaae6 100644 --- a/backend/Selectionproof.v +++ b/backend/Selectionproof.v @@ -14,8 +14,9 @@ Require Import FunInd. Require Import Coqlib Maps. -Require Import AST Linking Errors Integers Values Memory Events Globalenvs Smallstep. -Require Import Switch Cminor Op CminorSel. +Require Import AST Linking Errors Integers. +Require Import Values Memory Builtins Events Globalenvs Smallstep. +Require Import Switch Cminor Op CminorSel Cminortyping. Require Import SelectOp SelectDiv SplitLong SelectLong Selection. Require Import SelectOpproof SelectDivproof SplitLongproof SelectLongproof. @@ -119,6 +120,16 @@ Let ge := Genv.globalenv prog. Let tge := Genv.globalenv tprog. Hypothesis TRANSF: match_prog prog tprog. +Lemma wt_prog : wt_program prog. +Proof. + red; intros. destruct TRANSF as [A _]. + exploit list_forall2_in_left; eauto. + intros ((i' & gd') & B & (C & D)). simpl in *. inv D. + destruct H2 as (hf & P & Q). destruct f; monadInv Q. +- monadInv EQ. econstructor; apply type_function_sound; eauto. +- constructor. +Qed. + Lemma symbols_preserved: forall (s: ident), Genv.find_symbol tge s = Genv.find_symbol ge s. Proof (Genv.find_symbol_match TRANSF). @@ -202,6 +213,22 @@ Proof. simpl. inv H0. auto. Qed. +Lemma eval_condition_of_expr: + forall a le v b, + eval_expr tge sp e m le a v -> + Val.bool_of_val v b -> + exists vl, + eval_exprlist tge sp e m le (snd (condition_of_expr a)) vl + /\ eval_condition (fst (condition_of_expr a)) vl m = Some b. +Proof. + intros a; functional induction (condition_of_expr a); intros; simpl. +- inv H. exists vl; split; auto. + simpl in H6. inv H6. apply Val.bool_of_val_of_optbool in H0. auto. +- exists (v :: nil); split. + constructor; auto; constructor. + inv H0; simpl; auto. +Qed. + Lemma eval_load: forall le a v chunk v', eval_expr tge sp e m le a v -> @@ -324,6 +351,52 @@ Proof. exists v; split; auto. eapply eval_cmplu; eauto. Qed. +Lemma eval_sel_select: + forall le a1 a2 a3 v1 v2 v3 b ty, + eval_expr tge sp e m le a1 v1 -> + eval_expr tge sp e m le a2 v2 -> + eval_expr tge sp e m le a3 v3 -> + Val.bool_of_val v1 b -> + exists v, eval_expr tge sp e m le (sel_select ty a1 a2 a3) v + /\ Val.lessdef (Val.select (Some b) v2 v3 ty) v. +Proof. + unfold sel_select; intros. + specialize (eval_condition_of_expr _ _ _ _ H H2). + destruct (condition_of_expr a1) as [cond args]; simpl fst; simpl snd. intros (vl & A & B). + destruct (select ty cond args a2 a3) as [a|] eqn:SEL. +- eapply eval_select; eauto. +- exists (if b then v2 else v3); split. + econstructor; eauto. eapply eval_condexpr_of_expr; eauto. destruct b; auto. + apply Val.lessdef_normalize. +Qed. + +(** Known built-in functions *) + +Lemma eval_sel_known_builtin: + forall bf args a vl v le, + sel_known_builtin bf args = Some a -> + eval_exprlist tge sp e m le args vl -> + builtin_function_sem bf vl = Some v -> + exists v', eval_expr tge sp e m le a v' /\ Val.lessdef v v'. +Proof. + intros until le; intros SEL ARGS SEM. + destruct bf as [bf|bf]; simpl in SEL. +- destruct bf; try discriminate. ++ (* select *) + inv ARGS; try discriminate. inv H0; try discriminate. inv H2; try discriminate. inv H3; try discriminate. + inv SEL. + simpl in SEM. destruct v1; inv SEM. + replace (Val.normalize (if Int.eq i Int.zero then v2 else v0) t) + with (Val.select (Some (negb (Int.eq i Int.zero))) v0 v2 t) + by (destruct (Int.eq i Int.zero); reflexivity). + eapply eval_sel_select; eauto. constructor. ++ (* fabs *) + inv ARGS; try discriminate. inv H0; try discriminate. + inv SEL. + simpl in SEM; inv SEM. apply eval_absf; auto. +- eapply eval_platform_builtin; eauto. +Qed. + End CMCONSTR. (** Recognition of calls to built-in functions *) @@ -506,7 +579,7 @@ Proof. unfold Int.sub. rewrite Int.unsigned_repr_eq. f_equal. f_equal. apply Int.unsigned_repr. unfold Int.max_unsigned; omega. - intros until i0; intros EVAL R. exists v; split; auto. - inv R. rewrite Zmod_small by (apply Int.unsigned_range). constructor. + inv R. rewrite Z.mod_small by (apply Int.unsigned_range). constructor. - constructor. - apply Int.unsigned_range. Qed. @@ -698,6 +771,29 @@ Proof. exists (v1' :: vl'); split; auto. constructor; eauto. Qed. +Lemma sel_select_opt_correct: + forall ty cond a1 a2 a sp e m vcond v1 v2 b e' m' le, + sel_select_opt ty cond a1 a2 = Some a -> + Cminor.eval_expr ge sp e m cond vcond -> + Cminor.eval_expr ge sp e m a1 v1 -> + Cminor.eval_expr ge sp e m a2 v2 -> + Val.bool_of_val vcond b -> + env_lessdef e e' -> Mem.extends m m' -> + exists v', eval_expr tge sp e' m' le a v' /\ Val.lessdef (Val.select (Some b) v1 v2 ty) v'. +Proof. + unfold sel_select_opt; intros. + destruct (condition_of_expr (sel_expr cond)) as [cnd args] eqn:C. + exploit sel_expr_correct. eexact H0. eauto. eauto. intros (vcond' & EVC & LDC). + exploit sel_expr_correct. eexact H1. eauto. eauto. intros (v1' & EV1 & LD1). + exploit sel_expr_correct. eexact H2. eauto. eauto. intros (v2' & EV2 & LD2). + assert (Val.bool_of_val vcond' b) by (inv H3; inv LDC; constructor). + exploit eval_condition_of_expr. eexact EVC. eauto. rewrite C. intros (vargs' & EVARGS & EVCOND). + exploit eval_select; eauto. intros (v' & X & Y). + exists v'; split; eauto. + eapply Val.lessdef_trans; [|eexact Y]. + apply Val.select_lessdef; auto. +Qed. + Lemma sel_builtin_arg_correct: forall sp e e' m m' a v c, env_lessdef e e' -> Mem.extends m m' -> @@ -741,37 +837,219 @@ Proof. intros. destruct oid; simpl; auto. apply set_var_lessdef; auto. Qed. +Lemma sel_builtin_default_correct: + forall optid ef al sp e1 m1 vl t v m2 e1' m1' f k, + Cminor.eval_exprlist ge sp e1 m1 al vl -> + external_call ef ge vl m1 t v m2 -> + env_lessdef e1 e1' -> Mem.extends m1 m1' -> + exists e2' m2', + step tge (State f (sel_builtin_default optid ef al) k sp e1' m1') + t (State f Sskip k sp e2' m2') + /\ env_lessdef (set_optvar optid v e1) e2' + /\ Mem.extends m2 m2'. +Proof. + intros. unfold sel_builtin_default. + exploit sel_builtin_args_correct; eauto. intros (vl' & A & B). + exploit external_call_mem_extends; eauto. intros (v' & m2' & D & E & F & _). + econstructor; exists m2'; split. + econstructor. eexact A. eapply external_call_symbols_preserved. eexact senv_preserved. eexact D. + split; auto. apply sel_builtin_res_correct; auto. +Qed. + +Lemma sel_builtin_correct: + forall optid ef al sp e1 m1 vl t v m2 e1' m1' f k, + Cminor.eval_exprlist ge sp e1 m1 al vl -> + external_call ef ge vl m1 t v m2 -> + env_lessdef e1 e1' -> Mem.extends m1 m1' -> + exists e2' m2', + step tge (State f (sel_builtin optid ef al) k sp e1' m1') + t (State f Sskip k sp e2' m2') + /\ env_lessdef (set_optvar optid v e1) e2' + /\ Mem.extends m2 m2'. +Proof. + intros. + exploit sel_exprlist_correct; eauto. intros (vl' & A & B). + exploit external_call_mem_extends; eauto. intros (v' & m2' & D & E & F & _). + unfold sel_builtin. + destruct optid as [id|]; eauto using sel_builtin_default_correct. + destruct ef; eauto using sel_builtin_default_correct. + destruct (lookup_builtin_function name sg) as [bf|] eqn:LKUP; eauto using sel_builtin_default_correct. + destruct (sel_known_builtin bf (sel_exprlist al)) as [a|] eqn:SKB; eauto using sel_builtin_default_correct. + simpl in D. red in D. rewrite LKUP in D. inv D. + exploit eval_sel_known_builtin; eauto. intros (v'' & U & V). + econstructor; exists m2'; split. + econstructor. eexact U. + split; auto. apply set_var_lessdef; auto. apply Val.lessdef_trans with v'; auto. +Qed. + +(** If-conversion *) + +Lemma classify_stmt_sound_1: + forall f sp e m s k, + classify_stmt s = SCskip -> + star Cminor.step ge (Cminor.State f s k sp e m) E0 (Cminor.State f Cminor.Sskip k sp e m). +Proof. + intros until s; functional induction (classify_stmt s); intros; try discriminate. + - apply star_refl. + - eapply star_trans; eauto. eapply star_two. constructor. constructor. + traceEq. traceEq. + - eapply star_left. constructor. + eapply star_right. eauto. constructor. + traceEq. traceEq. +Qed. + +Lemma classify_stmt_sound_2: + forall f sp e m a id v, + Cminor.eval_expr ge sp e m a v -> + forall s k, + classify_stmt s = SCassign id a -> + star Cminor.step ge (Cminor.State f s k sp e m) E0 (Cminor.State f Cminor.Sskip k sp (PTree.set id v e) m). +Proof. + intros until s; functional induction (classify_stmt s); intros; try discriminate. + - inv H0. apply star_one. constructor; auto. + - eapply star_trans; eauto. eapply star_two. constructor. constructor. + traceEq. traceEq. + - eapply star_left. constructor. + eapply star_right. eauto. constructor. + traceEq. traceEq. +Qed. + +Lemma classify_stmt_wt: + forall env tyret id a s, + classify_stmt s = SCassign id a -> + wt_stmt env tyret s -> + wt_expr env a (env id). +Proof. + intros until s; functional induction (classify_stmt s); intros CL WT; + try discriminate. +- inv CL; inv WT; auto. +- inv WT; eauto. +- inv WT; eauto. +Qed. + +Lemma eval_select_safe_exprs: + forall a1 a2 f env ty e e' m m' sp cond vb b id s, + safe_expr (known_id f) a1 = true -> + safe_expr (known_id f) a2 = true -> + option_map (fun sel => Sassign id sel) (sel_select_opt ty cond a1 a2) = Some s -> + Cminor.eval_expr ge sp e m cond vb -> Val.bool_of_val vb b -> + wt_expr env a1 ty -> + wt_expr env a2 ty -> + def_env f e -> wt_env env e -> + Cminor.eval_expr ge sp e m cond vb -> Val.bool_of_val vb b -> + env_lessdef e e' -> Mem.extends m m' -> + exists a' v1 v2 v', + s = Sassign id a' + /\ Cminor.eval_expr ge sp e m a1 v1 + /\ Cminor.eval_expr ge sp e m a2 v2 + /\ eval_expr tge sp e' m' nil a' v' + /\ Val.lessdef (if b then v1 else v2) v'. +Proof. + intros. + destruct (sel_select_opt ty cond a1 a2) as [a'|] eqn:SSO; simpl in H1; inv H1. + destruct (eval_safe_expr ge f sp e m a1) as (v1 & EV1); auto. + destruct (eval_safe_expr ge f sp e m a2) as (v2 & EV2); auto. + assert (TY1: Val.has_type v1 ty) by (eapply wt_eval_expr; eauto). + assert (TY2: Val.has_type v2 ty) by (eapply wt_eval_expr; eauto). + exploit sel_select_opt_correct; eauto. intros (v' & EV' & LD). + exists a', v1, v2, v'; intuition eauto. + apply Val.lessdef_trans with (Val.select (Some b) v1 v2 ty). + simpl. rewrite Val.normalize_idem; auto. destruct b; auto. + assumption. +Qed. + +Lemma if_conversion_correct: + forall f env tyret cond ifso ifnot s vb b k f' k' sp e m e' m', + if_conversion (known_id f) env cond ifso ifnot = Some s -> + def_env f e -> wt_env env e -> + wt_stmt env tyret ifso -> + wt_stmt env tyret ifnot -> + Cminor.eval_expr ge sp e m cond vb -> Val.bool_of_val vb b -> + env_lessdef e e' -> Mem.extends m m' -> + let s0 := if b then ifso else ifnot in + exists e1 e1', + step tge (State f' s k' sp e' m') E0 (State f' Sskip k' sp e1' m') + /\ star Cminor.step ge (Cminor.State f s0 k sp e m) E0 (Cminor.State f Cminor.Sskip k sp e1 m) + /\ env_lessdef e1 e1'. +Proof. + unfold if_conversion; intros until m'; intros IFC DE WTE WT1 WT2 EVC BOV ELD MEXT. + set (s0 := if b then ifso else ifnot). set (ki := known_id f) in *. + destruct (classify_stmt ifso) eqn:IFSO; try discriminate; + destruct (classify_stmt ifnot) eqn:IFNOT; try discriminate; + unfold if_conversion_base in IFC. +- destruct (is_known ki id && safe_expr ki (Cminor.Evar id) && safe_expr ki a + && if_conversion_heuristic cond (Cminor.Evar id) a (env id)) eqn:B; inv IFC. + InvBooleans. + exploit (eval_select_safe_exprs (Cminor.Evar id) a); eauto. + constructor. eapply classify_stmt_wt; eauto. + intros (a' & v1 & v2 & v' & A & B & C & D & E). + exists (PTree.set id (if b then v1 else v2) e), (PTree.set id v' e'). + split. subst s. constructor; auto. + split. unfold s0; destruct b. + rewrite PTree.gsident by (inv B; auto). apply classify_stmt_sound_1; auto. + eapply classify_stmt_sound_2; eauto. + apply set_var_lessdef; auto. +- destruct (is_known ki id && safe_expr ki a && safe_expr ki (Cminor.Evar id) + && if_conversion_heuristic cond a (Cminor.Evar id) (env id)) eqn:B; inv IFC. + InvBooleans. + exploit (eval_select_safe_exprs a (Cminor.Evar id)); eauto. + eapply classify_stmt_wt; eauto. constructor. + intros (a' & v1 & v2 & v' & A & B & C & D & E). + exists (PTree.set id (if b then v1 else v2) e), (PTree.set id v' e'). + split. subst s. constructor; auto. + split. unfold s0; destruct b. + eapply classify_stmt_sound_2; eauto. + rewrite PTree.gsident by (inv C; auto). apply classify_stmt_sound_1; auto. + apply set_var_lessdef; auto. +- destruct (ident_eq id id0); try discriminate. subst id0. + destruct (is_known ki id && safe_expr ki a && safe_expr ki a0 + && if_conversion_heuristic cond a a0 (env id)) eqn:B; inv IFC. + InvBooleans. + exploit (eval_select_safe_exprs a a0); eauto. + eapply classify_stmt_wt; eauto. eapply classify_stmt_wt; eauto. + intros (a' & v1 & v2 & v' & A & B & C & D & E). + exists (PTree.set id (if b then v1 else v2) e), (PTree.set id v' e'). + split. subst s. constructor; auto. + split. unfold s0; destruct b; eapply classify_stmt_sound_2; eauto. + apply set_var_lessdef; auto. +Qed. + End EXPRESSIONS. (** Semantic preservation for functions and statements. *) -Inductive match_cont: Cminor.program -> helper_functions -> Cminor.cont -> CminorSel.cont -> Prop := - | match_cont_stop: forall cunit hf, - match_cont cunit hf Cminor.Kstop Kstop - | match_cont_seq: forall cunit hf s s' k k', - sel_stmt (prog_defmap cunit) s = OK s' -> - match_cont cunit hf k k' -> - match_cont cunit hf (Cminor.Kseq s k) (Kseq s' k') - | match_cont_block: forall cunit hf k k', - match_cont cunit hf k k' -> - match_cont cunit hf (Cminor.Kblock k) (Kblock k') - | match_cont_call: forall cunit' hf' cunit hf id f sp e k f' e' k', +Inductive match_cont: Cminor.program -> helper_functions -> known_idents -> typenv -> Cminor.cont -> CminorSel.cont -> Prop := + | match_cont_seq: forall cunit hf ki env s s' k k', + sel_stmt (prog_defmap cunit) ki env s = OK s' -> + match_cont cunit hf ki env k k' -> + match_cont cunit hf ki env (Cminor.Kseq s k) (Kseq s' k') + | match_cont_block: forall cunit hf ki env k k', + match_cont cunit hf ki env k k' -> + match_cont cunit hf ki env (Cminor.Kblock k) (Kblock k') + | match_cont_other: forall cunit hf ki env k k', + match_call_cont k k' -> + match_cont cunit hf ki env k k' + +with match_call_cont: Cminor.cont -> CminorSel.cont -> Prop := + | match_cont_stop: + match_call_cont Cminor.Kstop Kstop + | match_cont_call: forall cunit hf env id f sp e k f' e' k', linkorder cunit prog -> helper_functions_declared cunit hf -> sel_function (prog_defmap cunit) hf f = OK f' -> - match_cont cunit hf k k' -> env_lessdef e e' -> - match_cont cunit' hf' (Cminor.Kcall id f sp e k) (Kcall id f' sp e' k'). - -Definition match_call_cont (k: Cminor.cont) (k': CminorSel.cont) : Prop := - forall cunit hf, match_cont cunit hf k k'. + type_function f = OK env -> + match_cont cunit hf (known_id f) env k k' -> + env_lessdef e e' -> + match_call_cont (Cminor.Kcall id f sp e k) (Kcall id f' sp e' k'). Inductive match_states: Cminor.state -> CminorSel.state -> Prop := - | match_state: forall cunit hf f f' s k s' k' sp e m e' m' + | match_state: forall cunit hf f f' s k s' k' sp e m e' m' env (LINK: linkorder cunit prog) (HF: helper_functions_declared cunit hf) (TF: sel_function (prog_defmap cunit) hf f = OK f') - (TS: sel_stmt (prog_defmap cunit) s = OK s') - (MC: match_cont cunit hf k k') + (TYF: type_function f = OK env) + (TS: sel_stmt (prog_defmap cunit) (known_id f) env s = OK s') + (MC: match_cont cunit hf (known_id f) env k k') (LD: env_lessdef e e') (ME: Mem.extends m m'), match_states @@ -793,48 +1071,49 @@ Inductive match_states: Cminor.state -> CminorSel.state -> Prop := match_states (Cminor.Returnstate v k m) (Returnstate v' k' m') - | match_builtin_1: forall cunit hf ef args args' optid f sp e k m al f' e' k' m' + | match_builtin_1: forall cunit hf ef args optid f sp e k m al f' e' k' m' env (LINK: linkorder cunit prog) (HF: helper_functions_declared cunit hf) (TF: sel_function (prog_defmap cunit) hf f = OK f') - (MC: match_cont cunit hf k k') - (LDA: Val.lessdef_list args args') + (TYF: type_function f = OK env) + (MC: match_cont cunit hf (known_id f) env k k') + (EA: Cminor.eval_exprlist ge sp e m al args) (LDE: env_lessdef e e') - (ME: Mem.extends m m') - (EA: list_forall2 (CminorSel.eval_builtin_arg tge sp e' m') al args'), + (ME: Mem.extends m m'), match_states (Cminor.Callstate (External ef) args (Cminor.Kcall optid f sp e k) m) - (State f' (Sbuiltin (sel_builtin_res optid) ef al) k' sp e' m') - | match_builtin_2: forall cunit hf v v' optid f sp e k m f' e' m' k' + (State f' (sel_builtin optid ef al) k' sp e' m') + | match_builtin_2: forall cunit hf v v' optid f sp e k m f' e' m' k' env (LINK: linkorder cunit prog) (HF: helper_functions_declared cunit hf) (TF: sel_function (prog_defmap cunit) hf f = OK f') - (MC: match_cont cunit hf k k') + (TYF: type_function f = OK env) + (MC: match_cont cunit hf (known_id f) env k k') (LDV: Val.lessdef v v') - (LDE: env_lessdef e e') + (LDE: env_lessdef (set_optvar optid v e) e') (ME: Mem.extends m m'), match_states (Cminor.Returnstate v (Cminor.Kcall optid f sp e k) m) - (State f' Sskip k' sp (set_builtin_res (sel_builtin_res optid) v' e') m'). + (State f' Sskip k' sp e' m'). Remark call_cont_commut: - forall cunit hf k k', match_cont cunit hf k k' -> match_call_cont (Cminor.call_cont k) (call_cont k'). + forall cunit hf ki env k k', + match_cont cunit hf ki env k k' -> match_call_cont (Cminor.call_cont k) (call_cont k'). Proof. - induction 1; simpl; auto; red; intros. -- constructor. -- eapply match_cont_call with (hf := hf); eauto. + induction 1; simpl; auto. inversion H; subst; auto. Qed. Remark match_is_call_cont: - forall cunit hf k k', match_cont cunit hf k k' -> Cminor.is_call_cont k -> match_call_cont k k'. + forall cunit hf ki env k k', + match_cont cunit ki env hf k k' -> Cminor.is_call_cont k -> + match_call_cont k k' /\ is_call_cont k'. Proof. - destruct 1; intros; try contradiction; red; intros. -- constructor. -- eapply match_cont_call with (hf := hf); eauto. + destruct 1; intros; try contradiction. split; auto. inv H; auto. Qed. +(* Remark match_call_cont_cont: - forall k k', match_call_cont k k' -> exists cunit hf, match_cont cunit hf k k'. + forall k k', match_call_cont k k' -> exists cunit hf ki env, match_cont cunit hf ki env k k'. Proof. intros. simple refine (let cunit : Cminor.program := _ in _). econstructor. apply nil. apply nil. apply xH. @@ -842,14 +1121,66 @@ Proof. econstructor; apply xH. exists cunit, hf; auto. Qed. +*) + +Definition nolabel (s: Cminor.stmt) : Prop := + forall lbl k, Cminor.find_label lbl s k = None. +Definition nolabel' (s: stmt) : Prop := + forall lbl k, find_label lbl s k = None. + +Lemma classify_stmt_nolabel: + forall s, classify_stmt s <> SCother -> nolabel s. +Proof. + intros s. functional induction (classify_stmt s); intros. +- red; auto. +- red; auto. +- apply IHs0 in H. red; intros; simpl. apply H. +- apply IHs0 in H. red; intros; simpl. rewrite H; auto. +- congruence. +Qed. + +Lemma if_conversion_base_nolabel: forall (hf: helper_functions) ki env a id a1 a2 s, + if_conversion_base ki env a id a1 a2 = Some s -> + nolabel' s. +Proof. + unfold if_conversion_base; intros. + destruct (is_known ki id && safe_expr ki a1 && safe_expr ki a2 && + if_conversion_heuristic a a1 a2 (env id)); try discriminate. + destruct (sel_select_opt (env id) a a1 a2); inv H. + red; auto. +Qed. + +Lemma if_conversion_nolabel: forall (hf: helper_functions) ki env a s1 s2 s, + if_conversion ki env a s1 s2 = Some s -> + nolabel s1 /\ nolabel s2 /\ nolabel' s. +Proof. + unfold if_conversion; intros. + Ltac conclude := + split; [apply classify_stmt_nolabel;congruence + |split; [apply classify_stmt_nolabel;congruence + |eapply if_conversion_base_nolabel; eauto]]. + destruct (classify_stmt s1) eqn:C1; try discriminate; + destruct (classify_stmt s2) eqn:C2; try discriminate. + conclude. + conclude. + destruct (ident_eq id id0). conclude. discriminate. +Qed. + +Remark sel_builtin_nolabel: + forall (hf: helper_functions) optid ef args, nolabel' (sel_builtin optid ef args). +Proof. + unfold sel_builtin; intros; red; intros. + destruct optid; auto. destruct ef; auto. destruct lookup_builtin_function; auto. + destruct sel_known_builtin; auto. +Qed. Remark find_label_commut: - forall cunit hf lbl s k s' k', - match_cont cunit hf k k' -> - sel_stmt (prog_defmap cunit) s = OK s' -> + forall cunit hf ki env lbl s k s' k', + match_cont cunit hf ki env k k' -> + sel_stmt (prog_defmap cunit) ki env s = OK s' -> match Cminor.find_label lbl s k, find_label lbl s' k' with | None, None => True - | Some(s1, k1), Some(s1', k1') => sel_stmt (prog_defmap cunit) s1 = OK s1' /\ match_cont cunit hf k1 k1' + | Some(s1, k1), Some(s1', k1') => sel_stmt (prog_defmap cunit) ki env s1 = OK s1' /\ match_cont cunit hf ki env k1 k1' | _, _ => False end. Proof. @@ -858,14 +1189,21 @@ Proof. unfold store. destruct (addressing m (sel_expr e)); simpl; auto. (* call *) destruct (classify_call (prog_defmap cunit) e); simpl; auto. + rewrite sel_builtin_nolabel; auto. (* tailcall *) destruct (classify_call (prog_defmap cunit) e); simpl; auto. +(* builtin *) + rewrite sel_builtin_nolabel; auto. (* seq *) exploit (IHs1 (Cminor.Kseq s2 k)). constructor; eauto. eauto. destruct (Cminor.find_label lbl s1 (Cminor.Kseq s2 k)) as [[sx kx] | ]; destruct (find_label lbl x (Kseq x0 k')) as [[sy ky] | ]; intuition. apply IHs2; auto. (* ifthenelse *) + destruct (if_conversion ki env e s1 s2) as [s|] eqn:IFC. + inv SE. exploit if_conversion_nolabel; eauto. intros (A & B & C). + rewrite A, B, C. auto. + monadInv SE; simpl. exploit (IHs1 k); eauto. destruct (Cminor.find_label lbl s1 k) as [[sx kx] | ]; destruct (find_label lbl x k') as [[sy ky] | ]; @@ -895,20 +1233,22 @@ Definition measure (s: Cminor.state) : nat := Lemma sel_step_correct: forall S1 t S2, Cminor.step ge S1 t S2 -> - forall T1, match_states S1 T1 -> + forall T1, match_states S1 T1 -> wt_state S1 -> (exists T2, step tge T1 t T2 /\ match_states S2 T2) - \/ (measure S2 < measure S1 /\ t = E0 /\ match_states S2 T1)%nat. + \/ (measure S2 < measure S1 /\ t = E0 /\ match_states S2 T1)%nat + \/ (exists S3 T2, star Cminor.step ge S2 E0 S3 /\ step tge T1 t T2 /\ match_states S3 T2). Proof. - induction 1; intros T1 ME; inv ME; try (monadInv TS). + induction 1; intros T1 ME WTS; inv ME; try (monadInv TS). - (* skip seq *) inv MC. left; econstructor; split. econstructor. econstructor; eauto. + inv H. - (* skip block *) inv MC. left; econstructor; split. econstructor. econstructor; eauto. + inv H. - (* skip call *) exploit Mem.free_parallel_extends; eauto. intros [m2' [A B]]. left; econstructor; split. - econstructor. inv MC; simpl in H; simpl; auto. - eauto. + econstructor. eapply match_is_call_cont; eauto. erewrite stackspace_function_translated; eauto. econstructor; eauto. eapply match_is_call_cont; eauto. - (* assign *) @@ -917,8 +1257,8 @@ Proof. econstructor; eauto. econstructor; eauto. apply set_var_lessdef; auto. - (* store *) - exploit sel_expr_correct. eauto. eauto. eexact H. eauto. eauto. intros [vaddr' [A B]]. - exploit sel_expr_correct. eauto. eauto. eexact H0. eauto. eauto. intros [v' [C D]]. + exploit sel_expr_correct. try apply LINK. try apply HF. eexact H. eauto. eauto. intros [vaddr' [A B]]. + exploit sel_expr_correct. try apply LINK. try apply HF. eexact H0. eauto. eauto. intros [v' [C D]]. exploit Mem.storev_extends; eauto. intros [m2' [P Q]]. left; econstructor; split. eapply eval_store; eauto. @@ -934,7 +1274,7 @@ Proof. econstructor; eauto. econstructor; eauto. eapply sig_function_translated; eauto. eapply match_callstate with (cunit := cunit'); eauto. - red; intros. eapply match_cont_call with (cunit := cunit) (hf := hf); eauto. + eapply match_cont_call with (cunit := cunit) (hf := hf); eauto. + (* direct *) intros [b [U V]]. exploit sel_exprlist_correct; eauto. intros [vargs' [C D]]. @@ -944,12 +1284,10 @@ Proof. subst vf. econstructor; eauto. rewrite symbols_preserved; eauto. eapply sig_function_translated; eauto. eapply match_callstate with (cunit := cunit'); eauto. - red; intros; eapply match_cont_call with (cunit := cunit) (hf := hf); eauto. + eapply match_cont_call with (cunit := cunit) (hf := hf); eauto. + (* turned into Sbuiltin *) intros EQ. subst fd. - exploit sel_builtin_args_correct; eauto. intros [vargs' [C D]]. - right; split. simpl. omega. split. auto. - econstructor; eauto. + right; left; split. simpl; omega. split; auto. econstructor; eauto. - (* Stailcall *) exploit Mem.free_parallel_extends; eauto. intros [m2' [P Q]]. erewrite <- stackspace_function_translated in P by eauto. @@ -966,18 +1304,20 @@ Proof. eapply match_callstate with (cunit := cunit'); eauto. eapply call_cont_commut; eauto. - (* Sbuiltin *) - exploit sel_builtin_args_correct; eauto. intros [vargs' [P Q]]. - exploit external_call_mem_extends; eauto. - intros [vres' [m2 [A [B [C D]]]]]. - left; econstructor; split. - econstructor. eauto. eapply external_call_symbols_preserved; eauto. apply senv_preserved. - econstructor; eauto. apply sel_builtin_res_correct; auto. + exploit sel_builtin_correct; eauto. intros (e2' & m2' & P & Q & R). + left; econstructor; split. eexact P. econstructor; eauto. - (* Seq *) left; econstructor; split. constructor. econstructor; eauto. constructor; auto. - (* Sifthenelse *) - exploit sel_expr_correct; eauto. intros [v' [A B]]. + simpl in TS. destruct (if_conversion (known_id f) env a s1 s2) as [s|] eqn:IFC; monadInv TS. ++ inv WTS. inv WT_FN. assert (env0 = env) by congruence. subst env0. inv WT_STMT. + exploit if_conversion_correct; eauto. + set (s0 := if b then s1 else s2). intros (e1 & e1' & A & B & C). + right; right. econstructor; econstructor. + split. eexact B. split. eexact A. econstructor; eauto. ++ exploit sel_expr_correct; eauto. intros [v' [A B]]. assert (Val.bool_of_val v' b). inv B. auto. inv H0. left; exists (State f' (if b then x else x0) k' sp e' m'); split. econstructor; eauto. eapply eval_condexpr_of_expr; eauto. @@ -989,10 +1329,13 @@ Proof. left; econstructor; split. constructor. econstructor; eauto. constructor; auto. - (* Sexit seq *) inv MC. left; econstructor; split. constructor. econstructor; eauto. + inv H. - (* Sexit0 block *) inv MC. left; econstructor; split. constructor. econstructor; eauto. + inv H. - (* SexitS block *) inv MC. left; econstructor; split. constructor. econstructor; eauto. + inv H. - (* Sswitch *) inv H0; simpl in TS. + set (ct := compile_switch Int.modulus default cases) in *. @@ -1023,10 +1366,10 @@ Proof. - (* Slabel *) left; econstructor; split. constructor. econstructor; eauto. - (* Sgoto *) - assert (sel_stmt (prog_defmap cunit) (Cminor.fn_body f) = OK (fn_body f')). - { monadInv TF; simpl; auto. } - exploit (find_label_commut cunit hf lbl (Cminor.fn_body f) (Cminor.call_cont k)). - eapply call_cont_commut; eauto. eauto. + assert (sel_stmt (prog_defmap cunit) (known_id f) env (Cminor.fn_body f) = OK (fn_body f')). + { monadInv TF; simpl. congruence. } + exploit (find_label_commut cunit hf (known_id f) env lbl (Cminor.fn_body f) (Cminor.call_cont k)). + apply match_cont_other. eapply call_cont_commut; eauto. eauto. rewrite H. destruct (find_label lbl (fn_body f') (call_cont k'0)) as [[s'' k'']|] eqn:?; intros; try contradiction. @@ -1035,13 +1378,15 @@ Proof. econstructor; eauto. econstructor; eauto. - (* internal function *) - destruct TF as (hf & HF & TF). specialize (MC cunit hf). + destruct TF as (hf & HF & TF). monadInv TF. generalize EQ; intros TF; monadInv TF. exploit Mem.alloc_extends. eauto. eauto. apply Z.le_refl. apply Z.le_refl. intros [m2' [A B]]. left; econstructor; split. econstructor; simpl; eauto. - econstructor; simpl; eauto. apply set_locals_lessdef. apply set_params_lessdef; auto. + econstructor; simpl; eauto. + apply match_cont_other; auto. + apply set_locals_lessdef. apply set_params_lessdef; auto. - (* external call *) destruct TF as (hf & HF & TF). monadInv TF. @@ -1051,20 +1396,15 @@ Proof. econstructor. eapply external_call_symbols_preserved; eauto. apply senv_preserved. econstructor; eauto. - (* external call turned into a Sbuiltin *) - exploit external_call_mem_extends; eauto. - intros [vres' [m2 [A [B [C D]]]]]. - left; econstructor; split. - econstructor. eauto. eapply external_call_symbols_preserved; eauto. apply senv_preserved. - econstructor; eauto. + exploit sel_builtin_correct; eauto. intros (e2' & m2' & P & Q & R). + left; econstructor; split. eexact P. econstructor; eauto. - (* return *) - apply match_call_cont_cont in MC. destruct MC as (cunit0 & hf0 & MC). inv MC. left; econstructor; split. econstructor. econstructor; eauto. destruct optid; simpl; auto. apply set_var_lessdef; auto. - (* return of an external call turned into a Sbuiltin *) - right; split. simpl; omega. split. auto. econstructor; eauto. - apply sel_builtin_res_correct; auto. + right; left; split. simpl; omega. split. auto. econstructor; eauto. Qed. Lemma sel_initial_states: @@ -1079,26 +1419,35 @@ Proof. rewrite (match_program_main TRANSF). fold tge. rewrite symbols_preserved. eauto. eexact A. rewrite <- H2. eapply sig_function_translated; eauto. - econstructor; eauto. red; intros; constructor. apply Mem.extends_refl. + econstructor; eauto. constructor. apply Mem.extends_refl. Qed. Lemma sel_final_states: forall S R r, match_states S R -> Cminor.final_state S r -> final_state R r. Proof. - intros. inv H0. inv H. - apply match_call_cont_cont in MC. destruct MC as (cunit0 & hf0 & MC). - inv MC. inv LD. constructor. + intros. inv H0. inv H. inv MC. inv LD. constructor. Qed. Theorem transf_program_correct: forward_simulation (Cminor.semantics prog) (CminorSel.semantics tprog). Proof. - apply forward_simulation_opt with (match_states := match_states) (measure := measure). - apply senv_preserved. - apply sel_initial_states; auto. - apply sel_final_states; auto. - apply sel_step_correct; auto. + set (MS := fun S T => match_states S T /\ wt_state S). + apply forward_simulation_determ_star with (match_states := MS) (measure := measure). +- apply Cminor.semantics_determinate. +- apply senv_preserved. +- intros. exploit sel_initial_states; eauto. intros (T & P & Q). + exists T; split; auto; split; auto. eapply wt_initial_state. eexact wt_prog. auto. +- intros. destruct H. eapply sel_final_states; eauto. +- intros S1 t S2 A T1 [B C]. + assert (wt_state S2) by (eapply subject_reduction; eauto using wt_prog). + unfold MS. + exploit sel_step_correct; eauto. + intros [(T2 & D & E) | [(D & E & F) | (S3 & T2 & D & E & F)]]. ++ exists S2, T2. intuition auto using star_refl, plus_one. ++ subst t. exists S2, T1. intuition auto using star_refl. ++ assert (wt_state S3) by (eapply subject_reduction_star; eauto using wt_prog). + exists S3, T2. intuition auto using plus_one. Qed. End PRESERVATION. diff --git a/backend/SplitLong.vp b/backend/SplitLong.vp index de954482..694bb0e2 100644 --- a/backend/SplitLong.vp +++ b/backend/SplitLong.vp @@ -43,13 +43,13 @@ Class helper_functions := mk_helper_functions { i64_smulh: ident; (**r signed multiply high *) }. -Definition sig_l_l := mksignature (Tlong :: nil) (Some Tlong) cc_default. -Definition sig_l_f := mksignature (Tlong :: nil) (Some Tfloat) cc_default. -Definition sig_l_s := mksignature (Tlong :: nil) (Some Tsingle) cc_default. -Definition sig_f_l := mksignature (Tfloat :: nil) (Some Tlong) cc_default. -Definition sig_ll_l := mksignature (Tlong :: Tlong :: nil) (Some Tlong) cc_default. -Definition sig_li_l := mksignature (Tlong :: Tint :: nil) (Some Tlong) cc_default. -Definition sig_ii_l := mksignature (Tint :: Tint :: nil) (Some Tlong) cc_default. +Definition sig_l_l := mksignature (Tlong :: nil) Tlong cc_default. +Definition sig_l_f := mksignature (Tlong :: nil) Tfloat cc_default. +Definition sig_l_s := mksignature (Tlong :: nil) Tsingle cc_default. +Definition sig_f_l := mksignature (Tfloat :: nil) Tlong cc_default. +Definition sig_ll_l := mksignature (Tlong :: Tlong :: nil) Tlong cc_default. +Definition sig_li_l := mksignature (Tlong :: Tint :: nil) Tlong cc_default. +Definition sig_ii_l := mksignature (Tint :: Tint :: nil) Tlong cc_default. Section SELECT. diff --git a/backend/SplitLongproof.v b/backend/SplitLongproof.v index f1e8b590..18c1f18d 100644 --- a/backend/SplitLongproof.v +++ b/backend/SplitLongproof.v @@ -15,42 +15,13 @@ Require Import String. Require Import Coqlib Maps. Require Import AST Errors Integers Floats. -Require Import Values Memory Globalenvs Events Cminor Op CminorSel. +Require Import Values Memory Globalenvs Builtins Events Cminor Op CminorSel. Require Import SelectOp SelectOpproof SplitLong. Local Open Scope cminorsel_scope. Local Open Scope string_scope. -(** * Axiomatization of the helper functions *) - -Definition external_implements (name: string) (sg: signature) (vargs: list val) (vres: val) : Prop := - forall F V (ge: Genv.t F V) m, - external_call (EF_runtime name sg) ge vargs m E0 vres m. - -Definition builtin_implements (name: string) (sg: signature) (vargs: list val) (vres: val) : Prop := - forall F V (ge: Genv.t F V) m, - external_call (EF_builtin name sg) ge vargs m E0 vres m. - -Axiom i64_helpers_correct : - (forall x z, Val.longoffloat x = Some z -> external_implements "__compcert_i64_dtos" sig_f_l (x::nil) z) - /\ (forall x z, Val.longuoffloat x = Some z -> external_implements "__compcert_i64_dtou" sig_f_l (x::nil) z) - /\ (forall x z, Val.floatoflong x = Some z -> external_implements "__compcert_i64_stod" sig_l_f (x::nil) z) - /\ (forall x z, Val.floatoflongu x = Some z -> external_implements "__compcert_i64_utod" sig_l_f (x::nil) z) - /\ (forall x z, Val.singleoflong x = Some z -> external_implements "__compcert_i64_stof" sig_l_s (x::nil) z) - /\ (forall x z, Val.singleoflongu x = Some z -> external_implements "__compcert_i64_utof" sig_l_s (x::nil) z) - /\ (forall x, builtin_implements "__builtin_negl" sig_l_l (x::nil) (Val.negl x)) - /\ (forall x y, builtin_implements "__builtin_addl" sig_ll_l (x::y::nil) (Val.addl x y)) - /\ (forall x y, builtin_implements "__builtin_subl" sig_ll_l (x::y::nil) (Val.subl x y)) - /\ (forall x y, builtin_implements "__builtin_mull" sig_ii_l (x::y::nil) (Val.mull' x y)) - /\ (forall x y z, Val.divls x y = Some z -> external_implements "__compcert_i64_sdiv" sig_ll_l (x::y::nil) z) - /\ (forall x y z, Val.divlu x y = Some z -> external_implements "__compcert_i64_udiv" sig_ll_l (x::y::nil) z) - /\ (forall x y z, Val.modls x y = Some z -> external_implements "__compcert_i64_smod" sig_ll_l (x::y::nil) z) - /\ (forall x y z, Val.modlu x y = Some z -> external_implements "__compcert_i64_umod" sig_ll_l (x::y::nil) z) - /\ (forall x y, external_implements "__compcert_i64_shl" sig_li_l (x::y::nil) (Val.shll x y)) - /\ (forall x y, external_implements "__compcert_i64_shr" sig_li_l (x::y::nil) (Val.shrlu x y)) - /\ (forall x y, external_implements "__compcert_i64_sar" sig_li_l (x::y::nil) (Val.shrl x y)) - /\ (forall x y, external_implements "__compcert_i64_umulh" sig_ll_l (x::y::nil) (Val.mullhu x y)) - /\ (forall x y, external_implements "__compcert_i64_smulh" sig_ll_l (x::y::nil) (Val.mullhs x y)). +(** * Properties of the helper functions *) Definition helper_declared {F V: Type} (p: AST.program (AST.fundef F) V) (id: ident) (name: string) (sg: signature) : Prop := (prog_defmap p)!id = Some (Gfun (External (EF_runtime name sg))). @@ -84,60 +55,67 @@ Variable sp: val. Variable e: env. Variable m: mem. -Ltac UseHelper := decompose [Logic.and] i64_helpers_correct; eauto. Ltac DeclHelper := red in HELPERS; decompose [Logic.and] HELPERS; eauto. Lemma eval_helper: - forall le id name sg args vargs vres, + forall bf le id name sg args vargs vres, eval_exprlist ge sp e m le args vargs -> helper_declared prog id name sg -> - external_implements name sg vargs vres -> + lookup_builtin_function name sg = Some bf -> + builtin_function_sem bf vargs = Some vres -> eval_expr ge sp e m le (Eexternal id sg args) vres. Proof. intros. red in H0. apply Genv.find_def_symbol in H0. destruct H0 as (b & P & Q). rewrite <- Genv.find_funct_ptr_iff in Q. - econstructor; eauto. + econstructor; eauto. + simpl. red. rewrite H1. constructor; auto. Qed. Corollary eval_helper_1: - forall le id name sg arg1 varg1 vres, + forall bf le id name sg arg1 varg1 vres, eval_expr ge sp e m le arg1 varg1 -> helper_declared prog id name sg -> - external_implements name sg (varg1::nil) vres -> + lookup_builtin_function name sg = Some bf -> + builtin_function_sem bf (varg1 :: nil) = Some vres -> eval_expr ge sp e m le (Eexternal id sg (arg1 ::: Enil)) vres. Proof. intros. eapply eval_helper; eauto. constructor; auto. constructor. Qed. Corollary eval_helper_2: - forall le id name sg arg1 arg2 varg1 varg2 vres, + forall bf le id name sg arg1 arg2 varg1 varg2 vres, eval_expr ge sp e m le arg1 varg1 -> eval_expr ge sp e m le arg2 varg2 -> helper_declared prog id name sg -> - external_implements name sg (varg1::varg2::nil) vres -> + lookup_builtin_function name sg = Some bf -> + builtin_function_sem bf (varg1 :: varg2 :: nil) = Some vres -> eval_expr ge sp e m le (Eexternal id sg (arg1 ::: arg2 ::: Enil)) vres. Proof. intros. eapply eval_helper; eauto. constructor; auto. constructor; auto. constructor. Qed. Remark eval_builtin_1: - forall le id sg arg1 varg1 vres, + forall bf le id sg arg1 varg1 vres, eval_expr ge sp e m le arg1 varg1 -> - builtin_implements id sg (varg1::nil) vres -> + lookup_builtin_function id sg = Some bf -> + builtin_function_sem bf (varg1 :: nil) = Some vres -> eval_expr ge sp e m le (Ebuiltin (EF_builtin id sg) (arg1 ::: Enil)) vres. Proof. - intros. econstructor. econstructor. eauto. constructor. apply H0. + intros. econstructor. econstructor. eauto. constructor. + simpl. red. rewrite H0. constructor. auto. Qed. Remark eval_builtin_2: - forall le id sg arg1 arg2 varg1 varg2 vres, + forall bf le id sg arg1 arg2 varg1 varg2 vres, eval_expr ge sp e m le arg1 varg1 -> eval_expr ge sp e m le arg2 varg2 -> - builtin_implements id sg (varg1::varg2::nil) vres -> + lookup_builtin_function id sg = Some bf -> + builtin_function_sem bf (varg1 :: varg2 :: nil) = Some vres -> eval_expr ge sp e m le (Ebuiltin (EF_builtin id sg) (arg1 ::: arg2 ::: Enil)) vres. Proof. - intros. econstructor. constructor; eauto. constructor; eauto. constructor. apply H1. + intros. econstructor. constructor; eauto. constructor; eauto. constructor. + simpl. red. rewrite H1. constructor. auto. Qed. Definition unary_constructor_sound (cstr: expr -> expr) (sem: val -> val) : Prop := @@ -386,9 +364,10 @@ Qed. Theorem eval_negl: unary_constructor_sound negl Val.negl. Proof. unfold negl; red; intros. destruct (is_longconst a) eqn:E. - econstructor; split. apply eval_longconst. +- econstructor; split. apply eval_longconst. exploit is_longconst_sound; eauto. intros EQ; subst x. simpl. auto. - econstructor; split. eapply eval_builtin_1; eauto. UseHelper. auto. +- exists (Val.negl x); split; auto. + eapply (eval_builtin_1 (BI_standard BI_negl)); eauto. Qed. Theorem eval_notl: unary_constructor_sound notl Val.notl. @@ -410,7 +389,7 @@ Theorem eval_longoffloat: exists v, eval_expr ge sp e m le (longoffloat a) v /\ Val.lessdef y v. Proof. intros; unfold longoffloat. econstructor; split. - eapply eval_helper_1; eauto. DeclHelper. UseHelper. auto. + eapply (eval_helper_1 (BI_standard BI_i64_dtos)); eauto. DeclHelper. auto. auto. Qed. Theorem eval_longuoffloat: @@ -420,7 +399,7 @@ Theorem eval_longuoffloat: exists v, eval_expr ge sp e m le (longuoffloat a) v /\ Val.lessdef y v. Proof. intros; unfold longuoffloat. econstructor; split. - eapply eval_helper_1; eauto. DeclHelper. UseHelper. auto. + eapply (eval_helper_1 (BI_standard BI_i64_dtou)); eauto. DeclHelper. auto. auto. Qed. Theorem eval_floatoflong: @@ -429,8 +408,9 @@ Theorem eval_floatoflong: Val.floatoflong x = Some y -> exists v, eval_expr ge sp e m le (floatoflong a) v /\ Val.lessdef y v. Proof. - intros; unfold floatoflong. econstructor; split. - eapply eval_helper_1; eauto. DeclHelper. UseHelper. auto. + intros; unfold floatoflong. exists y; split; auto. + eapply (eval_helper_1 (BI_standard BI_i64_stod)); eauto. DeclHelper. auto. + simpl. destruct x; simpl in H0; inv H0; auto. Qed. Theorem eval_floatoflongu: @@ -439,8 +419,9 @@ Theorem eval_floatoflongu: Val.floatoflongu x = Some y -> exists v, eval_expr ge sp e m le (floatoflongu a) v /\ Val.lessdef y v. Proof. - intros; unfold floatoflongu. econstructor; split. - eapply eval_helper_1; eauto. DeclHelper. UseHelper. auto. + intros; unfold floatoflongu. exists y; split; auto. + eapply (eval_helper_1 (BI_standard BI_i64_utod)); eauto. DeclHelper. auto. + simpl. destruct x; simpl in H0; inv H0; auto. Qed. Theorem eval_longofsingle: @@ -477,8 +458,9 @@ Theorem eval_singleoflong: Val.singleoflong x = Some y -> exists v, eval_expr ge sp e m le (singleoflong a) v /\ Val.lessdef y v. Proof. - intros; unfold singleoflong. econstructor; split. - eapply eval_helper_1; eauto. DeclHelper. UseHelper. auto. + intros; unfold singleoflong. exists y; split; auto. + eapply (eval_helper_1 (BI_standard BI_i64_stof)); eauto. DeclHelper. auto. + simpl. destruct x; simpl in H0; inv H0; auto. Qed. Theorem eval_singleoflongu: @@ -487,8 +469,9 @@ Theorem eval_singleoflongu: Val.singleoflongu x = Some y -> exists v, eval_expr ge sp e m le (singleoflongu a) v /\ Val.lessdef y v. Proof. - intros; unfold singleoflongu. econstructor; split. - eapply eval_helper_1; eauto. DeclHelper. UseHelper. auto. + intros; unfold singleoflongu. exists y; split; auto. + eapply (eval_helper_1 (BI_standard BI_i64_utof)); eauto. DeclHelper. auto. + simpl. destruct x; simpl in H0; inv H0; auto. Qed. Theorem eval_andl: binary_constructor_sound andl Val.andl. @@ -615,7 +598,9 @@ Proof. simpl. erewrite <- Int64.decompose_shl_2. instantiate (1 := Int64.hiword i). rewrite Int64.ofwords_recompose. auto. auto. + (* n >= 64 *) - econstructor; split. eapply eval_helper_2; eauto. EvalOp. DeclHelper. UseHelper. auto. + econstructor; split. + eapply eval_helper_2; eauto. EvalOp. DeclHelper. reflexivity. reflexivity. + auto. Qed. Theorem eval_shll: binary_constructor_sound shll Val.shll. @@ -626,7 +611,7 @@ Proof. exploit is_intconst_sound; eauto. intros EQ; subst y; clear H0. eapply eval_shllimm; eauto. - (* General case *) - econstructor; split. eapply eval_helper_2; eauto. DeclHelper. UseHelper. auto. + econstructor; split. eapply eval_helper_2; eauto. DeclHelper. reflexivity. reflexivity. auto. Qed. Lemma eval_shrluimm: @@ -660,7 +645,9 @@ Proof. simpl. erewrite <- Int64.decompose_shru_2. instantiate (1 := Int64.loword i). rewrite Int64.ofwords_recompose. auto. auto. + (* n >= 64 *) - econstructor; split. eapply eval_helper_2; eauto. EvalOp. DeclHelper. UseHelper. auto. + econstructor; split. + eapply eval_helper_2; eauto. EvalOp. DeclHelper. reflexivity. reflexivity. + auto. Qed. Theorem eval_shrlu: binary_constructor_sound shrlu Val.shrlu. @@ -671,7 +658,7 @@ Proof. exploit is_intconst_sound; eauto. intros EQ; subst y; clear H0. eapply eval_shrluimm; eauto. - (* General case *) - econstructor; split. eapply eval_helper_2; eauto. DeclHelper. UseHelper. auto. + econstructor; split. eapply eval_helper_2; eauto. DeclHelper. reflexivity. reflexivity. auto. Qed. Lemma eval_shrlimm: @@ -709,7 +696,9 @@ Proof. erewrite <- Int64.decompose_shr_2. instantiate (1 := Int64.loword i). rewrite Int64.ofwords_recompose. auto. auto. + (* n >= 64 *) - econstructor; split. eapply eval_helper_2; eauto. EvalOp. DeclHelper. UseHelper. auto. + econstructor; split. + eapply eval_helper_2; eauto. EvalOp. DeclHelper. reflexivity. reflexivity. + auto. Qed. Theorem eval_shrl: binary_constructor_sound shrl Val.shrl. @@ -720,7 +709,7 @@ Proof. exploit is_intconst_sound; eauto. intros EQ; subst y; clear H0. eapply eval_shrlimm; eauto. - (* General case *) - econstructor; split. eapply eval_helper_2; eauto. DeclHelper. UseHelper. auto. + econstructor; split. eapply eval_helper_2; eauto. DeclHelper. reflexivity. reflexivity. auto. Qed. Theorem eval_addl: Archi.ptr64 = false -> binary_constructor_sound addl Val.addl. @@ -730,7 +719,7 @@ Proof. assert (DEFAULT: exists v, eval_expr ge sp e m le default v /\ Val.lessdef (Val.addl x y) v). { - econstructor; split. eapply eval_builtin_2; eauto. UseHelper. auto. + econstructor; split. eapply eval_builtin_2; eauto. reflexivity. reflexivity. auto. } destruct (is_longconst a) as [p|] eqn:LC1; destruct (is_longconst b) as [q|] eqn:LC2. @@ -753,7 +742,7 @@ Proof. assert (DEFAULT: exists v, eval_expr ge sp e m le default v /\ Val.lessdef (Val.subl x y) v). { - econstructor; split. eapply eval_builtin_2; eauto. UseHelper. auto. + econstructor; split. eapply eval_builtin_2; eauto. reflexivity. reflexivity. auto. } destruct (is_longconst a) as [p|] eqn:LC1; destruct (is_longconst b) as [q|] eqn:LC2. @@ -784,7 +773,7 @@ Proof. exploit eval_add. eexact E2. eexact E3. intros [v5 [E5 L5]]. exploit eval_add. eexact E5. eexact E4. intros [v6 [E6 L6]]. exists (Val.longofwords v6 (Val.loword p)); split. - EvalOp. eapply eval_builtin_2; eauto. UseHelper. + EvalOp. eapply eval_builtin_2; eauto. reflexivity. reflexivity. intros. unfold le1, p in *; subst; simpl in *. inv L3. inv L4. inv L5. simpl in L6. inv L6. simpl. f_equal. symmetry. apply Int64.decompose_mul. @@ -832,14 +821,14 @@ Theorem eval_mullhu: forall n, unary_constructor_sound (fun a => mullhu a n) (fun v => Val.mullhu v (Vlong n)). Proof. unfold mullhu; intros; red; intros. econstructor; split; eauto. - eapply eval_helper_2; eauto. apply eval_longconst. DeclHelper; eauto. UseHelper. + eapply eval_helper_2; eauto. apply eval_longconst. DeclHelper. reflexivity. reflexivity. Qed. Theorem eval_mullhs: forall n, unary_constructor_sound (fun a => mullhs a n) (fun v => Val.mullhs v (Vlong n)). Proof. unfold mullhs; intros; red; intros. econstructor; split; eauto. - eapply eval_helper_2; eauto. apply eval_longconst. DeclHelper; eauto. UseHelper. + eapply eval_helper_2; eauto. apply eval_longconst. DeclHelper. reflexivity. reflexivity. Qed. Theorem eval_shrxlimm: @@ -881,7 +870,7 @@ Theorem eval_divlu_base: exists v, eval_expr ge sp e m le (divlu_base a b) v /\ Val.lessdef z v. Proof. intros; unfold divlu_base. - econstructor; split. eapply eval_helper_2; eauto. DeclHelper. UseHelper. auto. + econstructor; split. eapply eval_helper_2; eauto. DeclHelper. reflexivity. eassumption. auto. Qed. Theorem eval_modlu_base: @@ -892,7 +881,7 @@ Theorem eval_modlu_base: exists v, eval_expr ge sp e m le (modlu_base a b) v /\ Val.lessdef z v. Proof. intros; unfold modlu_base. - econstructor; split. eapply eval_helper_2; eauto. DeclHelper. UseHelper. auto. + econstructor; split. eapply eval_helper_2; eauto. DeclHelper. reflexivity. eassumption. auto. Qed. Theorem eval_divls_base: @@ -903,7 +892,7 @@ Theorem eval_divls_base: exists v, eval_expr ge sp e m le (divls_base a b) v /\ Val.lessdef z v. Proof. intros; unfold divls_base. - econstructor; split. eapply eval_helper_2; eauto. DeclHelper. UseHelper. auto. + econstructor; split. eapply eval_helper_2; eauto. DeclHelper. reflexivity. eassumption. auto. Qed. Theorem eval_modls_base: @@ -914,7 +903,7 @@ Theorem eval_modls_base: exists v, eval_expr ge sp e m le (modls_base a b) v /\ Val.lessdef z v. Proof. intros; unfold modls_base. - econstructor; split. eapply eval_helper_2; eauto. DeclHelper. UseHelper. auto. + econstructor; split. eapply eval_helper_2; eauto. DeclHelper. reflexivity. eassumption. auto. Qed. Remark decompose_cmpl_eq_zero: diff --git a/backend/Stackingproof.v b/backend/Stackingproof.v index ffd9b227..d8b81689 100644 --- a/backend/Stackingproof.v +++ b/backend/Stackingproof.v @@ -149,7 +149,7 @@ Lemma contains_get_stack: Proof. intros. unfold load_stack. replace (Val.offset_ptr (Vptr sp Ptrofs.zero) (Ptrofs.repr ofs)) with (Vptr sp (Ptrofs.repr ofs)). - eapply loadv_rule; eauto. + eapply loadv_rule; eauto using perm_F_any. simpl. rewrite Ptrofs.add_zero_l; auto. Qed. @@ -171,7 +171,7 @@ Lemma contains_set_stack: Proof. intros. unfold store_stack. replace (Val.offset_ptr (Vptr sp Ptrofs.zero) (Ptrofs.repr ofs)) with (Vptr sp (Ptrofs.repr ofs)). - eapply storev_rule; eauto. + eapply storev_rule; eauto using perm_F_any. simpl. rewrite Ptrofs.add_zero_l; auto. Qed. @@ -936,7 +936,7 @@ Local Opaque mreg_type. apply range_drop_left with (mid := pos1) in SEP; [ | omega ]. apply range_split with (mid := pos1 + sz) in SEP; [ | omega ]. unfold sz at 1 in SEP. rewrite <- size_type_chunk in SEP. - apply range_contains in SEP; auto. + apply range_contains in SEP; auto with mem. exploit (contains_set_stack (fun v' => Val.inject j (ls (R r)) v') (rs r)). eexact SEP. apply load_result_inject; auto. apply wt_ls. @@ -1087,14 +1087,14 @@ Local Opaque b fe. apply (frame_env_separated b) in SEP. replace (make_env b) with fe in SEP by auto. (* Store of parent *) rewrite sep_swap3 in SEP. - apply (range_contains Mptr) in SEP; [|tauto]. + apply range_contains in SEP;[|apply perm_F_any|tauto]. exploit (contains_set_stack (fun v' => v' = parent) parent (fun _ => True) m2' Tptr). rewrite chunk_of_Tptr; eexact SEP. apply Val.load_result_same; auto. clear SEP; intros (m3' & STORE_PARENT & SEP). rewrite sep_swap3 in SEP. (* Store of return address *) rewrite sep_swap4 in SEP. - apply (range_contains Mptr) in SEP; [|tauto]. + apply range_contains in SEP; [|apply perm_F_any|tauto]. exploit (contains_set_stack (fun v' => v' = ra) ra (fun _ => True) m3' Tptr). rewrite chunk_of_Tptr; eexact SEP. apply Val.load_result_same; auto. clear SEP; intros (m4' & STORE_RETADDR & SEP). diff --git a/backend/Tailcall.v b/backend/Tailcall.v index 939abeea..b7a62d74 100644 --- a/backend/Tailcall.v +++ b/backend/Tailcall.v @@ -82,7 +82,7 @@ Definition transf_instr (f: function) (pc: node) (instr: instruction) := | Icall sig ros args res s => if is_return niter f s res && tailcall_is_possible sig - && opt_typ_eq sig.(sig_res) f.(fn_sig).(sig_res) + && rettype_eq sig.(sig_res) f.(fn_sig).(sig_res) then Itailcall sig ros args else instr | _ => instr diff --git a/backend/Tailcallproof.v b/backend/Tailcallproof.v index 06e314f3..9ec89553 100644 --- a/backend/Tailcallproof.v +++ b/backend/Tailcallproof.v @@ -157,12 +157,10 @@ Lemma transf_instr_charact: transf_instr_spec f instr (transf_instr f pc instr). Proof. intros. unfold transf_instr. destruct instr; try constructor. - caseEq (is_return niter f n r && tailcall_is_possible s && - opt_typ_eq (sig_res s) (sig_res (fn_sig f))); intros. - destruct (andb_prop _ _ H0). destruct (andb_prop _ _ H1). - eapply transf_instr_tailcall; eauto. - eapply is_return_charact; eauto. - constructor. + destruct (is_return niter f n r && tailcall_is_possible s && + rettype_eq (sig_res s) (sig_res (fn_sig f))) eqn:B. +- InvBooleans. eapply transf_instr_tailcall; eauto. eapply is_return_charact; eauto. +- constructor. Qed. Lemma transf_instr_lookup: diff --git a/backend/Unusedglob.v b/backend/Unusedglob.v index 916e111b..8ac7c4ce 100644 --- a/backend/Unusedglob.v +++ b/backend/Unusedglob.v @@ -107,7 +107,7 @@ Definition used_globals (p: program) (pm: prog_map) : option IS.t := (** * Elimination of unreferenced global definitions *) -(** We also eliminate multiple definitions of the same global name, keeping ony +(** We also eliminate multiple definitions of the same global name, keeping only the last definition (in program definition order). *) Fixpoint filter_globdefs (used: IS.t) (accu defs: list (ident * globdef fundef unit)) := diff --git a/backend/Unusedglobproof.v b/backend/Unusedglobproof.v index d5f871a0..680daba7 100644 --- a/backend/Unusedglobproof.v +++ b/backend/Unusedglobproof.v @@ -1160,10 +1160,10 @@ Local Transparent Mem.loadbytes. generalize (S1 NO). unfold Mem.loadbytes. destruct Mem.range_perm_dec; intros E1; inv E1. generalize (S2 NO). unfold Mem.loadbytes. destruct Mem.range_perm_dec; intros E2; inv E2. rewrite Z.add_0_r. - apply Mem_getN_forall2 with (p := 0) (n := nat_of_Z (init_data_list_size (gvar_init v))). + apply Mem_getN_forall2 with (p := 0) (n := Z.to_nat (init_data_list_size (gvar_init v))). rewrite H3, H4. apply bytes_of_init_inject. auto. omega. - rewrite nat_of_Z_eq by (apply init_data_list_size_pos). omega. + rewrite Z2Nat.id by (apply Z.ge_le; apply init_data_list_size_pos). omega. Qed. Lemma init_mem_inj_2: diff --git a/backend/ValueAnalysis.v b/backend/ValueAnalysis.v index 1f80c293..b0ce019c 100644 --- a/backend/ValueAnalysis.v +++ b/backend/ValueAnalysis.v @@ -13,7 +13,7 @@ Require Import FunInd. Require Import Coqlib Maps Integers Floats Lattice Kildall. Require Import Compopts AST Linking. -Require Import Values Memory Globalenvs Events. +Require Import Values Memory Globalenvs Builtins Events. Require Import Registers Op RTL. Require Import ValueDomain ValueAOp Liveness. @@ -78,6 +78,15 @@ Definition transfer_builtin_default let (av, am') := analyze_call am (map (abuiltin_arg ae am rm) args) in VA.State (set_builtin_res res av ae) am'. +Definition eval_static_builtin_function + (ae: aenv) (am: amem) (rm: romem) + (bf: builtin_function) (args: list (builtin_arg reg)) := + match builtin_function_sem bf + (map val_of_aval (map (abuiltin_arg ae am rm) args)) with + | Some v => aval_of_val v + | None => None + end. + Definition transfer_builtin (ae: aenv) (am: amem) (rm: romem) (ef: external_function) (args: list (builtin_arg reg)) (res: builtin_res reg) := @@ -105,6 +114,15 @@ Definition transfer_builtin | EF_annot_val _ _ _, v :: nil => let av := abuiltin_arg ae am rm v in VA.State (set_builtin_res res av ae) am + | EF_builtin name sg, _ => + match lookup_builtin_function name sg with + | Some bf => + match eval_static_builtin_function ae am rm bf args with + | Some av => VA.State (set_builtin_res res av ae) am + | None => transfer_builtin_default ae am rm args res + end + | None => transfer_builtin_default ae am rm args res + end | _, _ => transfer_builtin_default ae am rm args res end. @@ -372,6 +390,31 @@ Proof. intros. destruct res; simpl; auto. apply ematch_update; auto. Qed. +Lemma eval_static_builtin_function_sound: + forall bc ge rs sp m ae rm am (bf: builtin_function) al vl v va, + ematch bc rs ae -> + romatch bc m rm -> + mmatch bc m am -> + genv_match bc ge -> + bc sp = BCstack -> + eval_builtin_args ge (fun r => rs#r) (Vptr sp Ptrofs.zero) m al vl -> + eval_static_builtin_function ae am rm bf al = Some va -> + builtin_function_sem bf vl = Some v -> + vmatch bc v va. +Proof. + unfold eval_static_builtin_function; intros. + exploit abuiltin_args_sound; eauto. + set (vla := map (abuiltin_arg ae am rm) al) in *. intros VMA. + destruct (builtin_function_sem bf (map val_of_aval vla)) as [v0|] eqn:A; try discriminate. + assert (LD: Val.lessdef v0 v). + { apply val_inject_lessdef. + exploit (bs_inject _ (builtin_function_sem bf)). + apply val_inject_list_lessdef. eapply list_val_of_aval_sound; eauto. + rewrite A, H6; simpl. auto. + } + inv LD. apply aval_of_val_sound; auto. discriminate. +Qed. + (** ** Constructing block classifications *) Definition bc_nostack (bc: block_classification) : Prop := @@ -996,9 +1039,8 @@ Proof. red; simpl; intros. destruct (plt b (Mem.nextblock m)). exploit RO; eauto. intros (R & P & Q). split; auto. - split. apply bmatch_incr with bc; auto. apply bmatch_inv with m; auto. - intros. eapply Mem.loadbytes_unchanged_on_1. eapply external_call_readonly; eauto. - auto. intros; red. apply Q. + split. apply bmatch_incr with bc; auto. apply bmatch_ext with m; auto. + intros. eapply external_call_readonly with (m2 := m'); eauto. intros; red; intros; elim (Q ofs). eapply external_call_max_perm with (m2 := m'); eauto. destruct (j' b); congruence. @@ -1105,10 +1147,10 @@ Proof. - constructor. - assert (Plt sp bound') by eauto with va. eapply sound_stack_public_call; eauto. apply IHsound_stack; intros. - apply INV. xomega. rewrite SAME; auto. xomega. auto. auto. + apply INV. xomega. rewrite SAME; auto with ordered_type. xomega. auto. auto. - assert (Plt sp bound') by eauto with va. eapply sound_stack_private_call; eauto. apply IHsound_stack; intros. - apply INV. xomega. rewrite SAME; auto. xomega. auto. auto. + apply INV. xomega. rewrite SAME; auto with ordered_type. xomega. auto. auto. apply bmatch_ext with m; auto. intros. apply INV. xomega. auto. auto. auto. Qed. @@ -1319,7 +1361,7 @@ Proof. apply sound_stack_exten with bc. apply sound_stack_inv with m. auto. intros. apply Q. red. eapply Plt_trans; eauto. - rewrite C; auto. + rewrite C; auto with ordered_type. exact AA. * (* public builtin call *) exploit anonymize_stack; eauto. @@ -1338,11 +1380,18 @@ Proof. apply sound_stack_exten with bc. apply sound_stack_inv with m. auto. intros. apply Q. red. eapply Plt_trans; eauto. - rewrite C; auto. + rewrite C; auto with ordered_type. exact AA. } unfold transfer_builtin in TR. destruct ef; auto. ++ (* builtin function *) + destruct (lookup_builtin_function name sg) as [bf|] eqn:LK; auto. + destruct (eval_static_builtin_function ae am rm bf args) as [av|] eqn:ES; auto. + simpl in H1. red in H1. rewrite LK in H1. inv H1. + eapply sound_succ_state; eauto. simpl; auto. + apply set_builtin_res_sound; auto. + eapply eval_static_builtin_function_sound; eauto. + (* volatile load *) inv H0; auto. inv H3; auto. inv H1. exploit abuiltin_arg_sound; eauto. intros VM1. diff --git a/backend/ValueDomain.v b/backend/ValueDomain.v index e7e44e29..c132ce7c 100644 --- a/backend/ValueDomain.v +++ b/backend/ValueDomain.v @@ -11,9 +11,9 @@ (* *********************************************************************) Require Import FunInd. -Require Import Zwf Coqlib Maps Integers Floats Lattice. +Require Import Zwf Coqlib Maps Zbits Integers Floats Lattice. Require Import Compopts AST. -Require Import Values Memory Globalenvs Events. +Require Import Values Memory Globalenvs Builtins Events. Require Import Registers RTL. (** The abstract domains for value analysis *) @@ -1492,12 +1492,12 @@ Proof. inv H; auto with va. - apply vmatch_uns. red; intros. rewrite Int.bits_rol by auto. generalize (Int.unsigned_range n); intros. - rewrite Zmod_small by omega. + rewrite Z.mod_small by omega. apply H1. omega. omega. - destruct (zlt n0 Int.zwordsize); auto with va. apply vmatch_sgn. red; intros. rewrite ! Int.bits_rol by omega. generalize (Int.unsigned_range n); intros. - rewrite ! Zmod_small by omega. + rewrite ! Z.mod_small by omega. rewrite H1 by omega. symmetry. rewrite H1 by omega. auto. - destruct (zlt n0 Int.zwordsize); auto with va. Qed. @@ -1670,7 +1670,7 @@ Proof. assert (UNS: forall i j, j <> Int.zero -> is_uns (usize j) (Int.modu i j)). { intros. apply is_uns_mon with (usize (Int.modu i j)); auto with va. - unfold usize, Int.size. apply Int.Zsize_monotone. + unfold usize, Int.size. apply Zsize_monotone. generalize (Int.unsigned_range_2 j); intros RANGE. assert (Int.unsigned j <> 0). { red; intros; elim H. rewrite <- (Int.repr_unsigned j). rewrite H0. auto. } @@ -2093,6 +2093,7 @@ Proof. Qed. Definition sign_ext (nbits: Z) (v: aval) := + if zle nbits 0 then Uns (provenance v) 0 else match v with | I i => I (Int.sign_ext nbits i) | Uns p n => if zlt n nbits then Uns p n else sgn p nbits @@ -2101,20 +2102,39 @@ Definition sign_ext (nbits: Z) (v: aval) := end. Lemma sign_ext_sound: - forall nbits v x, 0 < nbits -> vmatch v x -> vmatch (Val.sign_ext nbits v) (sign_ext nbits x). + forall nbits v x, vmatch v x -> vmatch (Val.sign_ext nbits v) (sign_ext nbits x). Proof. assert (DFL: forall p nbits i, 0 < nbits -> vmatch (Vint (Int.sign_ext nbits i)) (sgn p nbits)). { intros. apply vmatch_sgn. apply is_sign_ext_sgn; auto with va. } - intros. inv H0; simpl; auto with va. -- destruct (zlt n nbits); eauto with va. + intros. unfold sign_ext. destruct (zle nbits 0). +- destruct v; simpl; auto with va. constructor. omega. + rewrite Int.sign_ext_below by auto. red; intros; apply Int.bits_zero. +- inv H; simpl; auto with va. ++ destruct (zlt n nbits); eauto with va. constructor; auto. eapply is_sign_ext_uns; eauto with va. -- destruct (zlt n nbits); auto with va. -- apply vmatch_sgn. apply is_sign_ext_sgn; auto with va. ++ destruct (zlt n nbits); auto with va. ++ apply vmatch_sgn. apply is_sign_ext_sgn; auto with va. apply Z.min_case; auto with va. Qed. +Definition zero_ext_l (s: Z) := unop_long (Int64.zero_ext s). + +Lemma zero_ext_l_sound: + forall s v x, vmatch v x -> vmatch (Val.zero_ext_l s v) (zero_ext_l s x). +Proof. + intros s. exact (unop_long_sound (Int64.zero_ext s)). +Qed. + +Definition sign_ext_l (s: Z) := unop_long (Int64.sign_ext s). + +Lemma sign_ext_l_sound: + forall s v x, vmatch v x -> vmatch (Val.sign_ext_l s v) (sign_ext_l s x). +Proof. + intros s. exact (unop_long_sound (Int64.sign_ext s)). +Qed. + Definition longofint (v: aval) := match v with | I i => L (Int64.repr (Int.signed i)) @@ -2824,6 +2844,64 @@ Proof. intros. inv H; simpl in H0; congruence. Qed. +(** Select either returns one of its arguments, or Vundef. *) + +Definition add_undef (x: aval) := + match x with + | Vbot => ntop + | I i => + if Int.lt i Int.zero + then sgn Pbot (ssize i) + else uns Pbot (usize i) + | L _ | F _ | FS _ => ntop + | _ => x + end. + +Lemma add_undef_sound: + forall v x, vmatch v x -> vmatch v (add_undef x). +Proof. + destruct 1; simpl; auto with va. + destruct (Int.lt i Int.zero). + apply vmatch_sgn; apply is_sgn_ssize. + apply vmatch_uns; apply is_uns_usize. +Qed. + +Lemma add_undef_undef: + forall x, vmatch Vundef (add_undef x). +Proof. + destruct x; simpl; auto with va. + destruct (Int.lt n Int.zero); auto with va. +Qed. + +Lemma add_undef_normalize: + forall v x ty, vmatch v x -> vmatch (Val.normalize v ty) (add_undef x). +Proof. + intros. destruct (Val.lessdef_normalize v ty); + auto using add_undef_sound, add_undef_undef. +Qed. + +Definition select (ab: abool) (x y: aval) := + match ab with + | Bnone => ntop + | Just b | Maybe b => add_undef (if b then x else y) + | Btop => add_undef (vlub x y) + end. + +Lemma select_sound: + forall ob v w ab x y ty, + cmatch ob ab -> vmatch v x -> vmatch w y -> + vmatch (Val.select ob v w ty) (select ab x y). +Proof. + unfold Val.select, select; intros. inv H. +- auto with va. +- apply add_undef_normalize; destruct b; auto. +- apply add_undef_undef. +- apply add_undef_normalize; destruct b; auto. +- destruct ob as [b|]. ++ apply add_undef_normalize. destruct b; [apply vmatch_lub_l|apply vmatch_lub_r]; auto. ++ apply add_undef_undef. +Qed. + (** Normalization at load time *) Definition vnormalize (chunk: memory_chunk) (v: aval) := @@ -2980,7 +3058,47 @@ Proof with (auto using provenance_monotone with va). - destruct (zlt n 16)... Qed. -(** Abstracting memory blocks *) +(** Analysis of known builtin functions. All we have is a dynamic semantics + as a function [list val -> option val], but we can still perform + some constant propagation. *) + +Definition val_of_aval (a: aval) : val := + match a with + | I n => Vint n + | L n => Vlong n + | F f => Vfloat f + | FS f => Vsingle f + | _ => Vundef + end. + +Definition aval_of_val (v: val) : option aval := + match v with + | Vint n => Some (I n) + | Vlong n => Some (L n) + | Vfloat f => Some (F f) + | Vsingle f => Some (FS f) + | _ => None + end. + +Lemma val_of_aval_sound: + forall v a, vmatch v a -> Val.lessdef (val_of_aval a) v. +Proof. + destruct 1; simpl; auto. +Qed. + +Corollary list_val_of_aval_sound: + forall vl al, list_forall2 vmatch vl al -> Val.lessdef_list (map val_of_aval al) vl. +Proof. + induction 1; simpl; constructor; auto using val_of_aval_sound. +Qed. + +Lemma aval_of_val_sound: + forall v a, aval_of_val v = Some a -> vmatch v a. +Proof. + intros v a E; destruct v; simpl in E; inv E; constructor. +Qed. + +(** * Abstracting memory blocks *) Inductive acontent : Type := | ACval (chunk: memory_chunk) (av: aval). @@ -3134,7 +3252,7 @@ Proof. omega. intros (bytes1 & bytes2 & LOAD1 & LOAD2 & CONCAT). subst bytes. - exploit Mem.loadbytes_length. eexact LOAD1. change (nat_of_Z 1) with 1%nat. intros LENGTH1. + exploit Mem.loadbytes_length. eexact LOAD1. change (Z.to_nat 1) with 1%nat. intros LENGTH1. rewrite in_app_iff in IN. destruct IN. * destruct bytes1; try discriminate. destruct bytes1; try discriminate. simpl in H. destruct H; try contradiction. subst m0. @@ -3492,7 +3610,7 @@ Qed. Lemma ablock_storebytes_sound: forall m b ofs bytes m' p ab sz, Mem.storebytes m b ofs bytes = Some m' -> - length bytes = nat_of_Z sz -> + length bytes = Z.to_nat sz -> (forall b' ofs' q i, In (Fragment (Vptr b' ofs') q i) bytes -> pmatch b' ofs' p) -> bmatch m b ab -> bmatch m' b (ablock_storebytes ab p ofs sz). @@ -3509,7 +3627,7 @@ Proof. exploit ablock_storebytes_contents; eauto. intros [A B]. assert (Mem.load chunk' m b ofs' = Some v'). { rewrite <- LOAD'; symmetry. eapply Mem.load_storebytes_other; eauto. - rewrite U. rewrite LENGTH. rewrite nat_of_Z_max. right; omega. } + rewrite U. rewrite LENGTH. rewrite Z_to_nat_max. right; omega. } exploit BM2; eauto. unfold ablock_load. rewrite A. rewrite COMPAT. auto. Qed. @@ -3992,7 +4110,7 @@ Theorem storebytes_sound: Mem.storebytes m b (Ptrofs.unsigned ofs) bytes = Some m' -> mmatch m am -> pmatch b ofs p -> - length bytes = nat_of_Z sz -> + length bytes = Z.to_nat sz -> (forall b' ofs' qt i, In (Fragment (Vptr b' ofs') qt i) bytes -> pmatch b' ofs' q) -> mmatch m' (storebytes am p sz q). Proof. @@ -4614,6 +4732,7 @@ Hint Resolve cnot_sound symbol_address_sound negfs_sound absfs_sound addfs_sound subfs_sound mulfs_sound divfs_sound zero_ext_sound sign_ext_sound longofint_sound longofintu_sound + zero_ext_l_sound sign_ext_l_sound singleoffloat_sound floatofsingle_sound intoffloat_sound intuoffloat_sound floatofint_sound floatofintu_sound intofsingle_sound intuofsingle_sound singleofint_sound singleofintu_sound diff --git a/cfrontend/C2C.ml b/cfrontend/C2C.ml index 206ba421..7f796fe3 100644 --- a/cfrontend/C2C.ml +++ b/cfrontend/C2C.ml @@ -33,6 +33,7 @@ type inline_status = type atom_info = { a_storage: C.storage; (* storage class *) + a_size: int64 option; (* size in bytes *) a_alignment: int option; (* alignment *) a_sections: Sections.section_name list; (* in which section to put it *) (* 1 section for data, 3 sections (code/lit/jumptbl) for functions *) @@ -61,15 +62,25 @@ let atom_alignof a = with Not_found -> None +let atom_is_aligned a sz = + match atom_alignof a with + | None -> false + | Some align -> align mod (Z.to_int sz) = 0 + let atom_sections a = try (Hashtbl.find decl_atom a).a_sections with Not_found -> [] -let atom_is_small_data a ofs = +let atom_is_small_data a ofs = try - (Hashtbl.find decl_atom a).a_access = Sections.Access_near + let info = Hashtbl.find decl_atom a in + info.a_access = Sections.Access_near + && (match info.a_size with + | None -> false + | Some sz -> + let ofs = camlint64_of_ptrofs ofs in 0L <= ofs && ofs < sz) with Not_found -> false @@ -109,7 +120,7 @@ let atom_location a = let comp_env : composite_env ref = ref Maps.PTree.empty -(** Hooks -- overriden in machine-dependent CPragmas module *) +(** Hooks -- overridden in machine-dependent CPragmas module *) let process_pragma_hook = ref (fun (_: string) -> false) @@ -155,13 +166,15 @@ let ais_annot_functions = [] let builtins_generic = { - Builtins.typedefs = []; - Builtins.functions = + builtin_typedefs = []; + builtin_functions = ais_annot_functions @ [ (* Integer arithmetic *) - "__builtin_bswap", + "__builtin_bswap64", + (TInt(IULongLong, []), [TInt(IULongLong, [])], false); + "__builtin_bswap", (TInt(IUInt, []), [TInt(IUInt, [])], false); "__builtin_bswap32", (TInt(IUInt, []), [TInt(IUInt, [])], false); @@ -181,6 +194,11 @@ let builtins_generic = { TInt(IULong, []); TInt(IULong, [])], false); + (* Selection *) + "__builtin_sel", + (TVoid [], + [TInt(C.IBool, [])], + true); (* Annotations *) "__builtin_annot", (TVoid [], @@ -300,9 +318,12 @@ let builtins_generic = { (* Add processor-dependent builtins *) -let builtins = - Builtins.({ typedefs = builtins_generic.typedefs @ CBuiltins.builtins.typedefs; - functions = builtins_generic.Builtins.functions @ CBuiltins.builtins.functions }) +let builtins = { + builtin_typedefs = + builtins_generic.builtin_typedefs @ CBuiltins.builtins.builtin_typedefs; + builtin_functions = + builtins_generic.builtin_functions @ CBuiltins.builtins.builtin_functions +} (** ** The known attributes *) @@ -337,6 +358,7 @@ let name_for_string_literal s = Hashtbl.add decl_atom id { a_storage = C.Storage_static; a_alignment = Some 1; + a_size = Some (Int64.of_int (String.length s + 1)); a_sections = [Sections.for_stringlit()]; a_access = Sections.Access_default; a_inline = No_specifier; @@ -364,9 +386,12 @@ let name_for_wide_string_literal s = incr stringNum; let name = Printf.sprintf "__stringlit_%d" !stringNum in let id = intern_string name in + let wchar_size = Machine.((!config).sizeof_wchar) in Hashtbl.add decl_atom id { a_storage = C.Storage_static; - a_alignment = Some Machine.((!config).sizeof_wchar); + a_alignment = Some wchar_size; + a_size = Some (Int64.(mul (of_int (List.length s + 1)) + (of_int wchar_size))); a_sections = [Sections.for_stringlit()]; a_access = Sections.Access_default; a_inline = No_specifier; @@ -596,6 +621,12 @@ and convertParams env = function | [] -> Tnil | (id, ty) :: rem -> Tcons(convertTyp env ty, convertParams env rem) +(* Convert types for the arguments to a function call. The types for + fixed arguments are taken from the function prototype. The types + for other arguments (variable-argument function or unprototyped K&R + functions) are taken from the types of the function arguments, + after default argument conversion. *) + let rec convertTypArgs env tl el = match tl, el with | _, [] -> Tnil @@ -605,6 +636,20 @@ let rec convertTypArgs env tl el = | (id, t1) :: tl, e1 :: el -> Tcons(convertTyp env t1, convertTypArgs env tl el) +(* Convert types for the arguments to inline asm statements and to + the special built-in functions __builtin_annot, __builtin_ais_annot_ + and __builtin_debug. The types are taken from the types of the + arguments, after performing the usual unary conversions. + Hence char becomes int but float remains float and is not promoted + to double. The goal is to preserve the representation of the arguments + and avoid inserting compiled code to convert the arguments. *) + +let rec convertTypAnnotArgs env = function + | [] -> Tnil + | e1 :: el -> + Tcons(convertTyp env (Cutil.unary_conversion env e1.etyp), + convertTypAnnotArgs env el) + let convertField env f = if f.fld_bitfield <> None then unsupported "bit field in struct or union (consider adding option [-fbitfields])"; @@ -845,7 +890,7 @@ let rec convertExpr env e = | {edesc = C.EVar id} :: args2 -> (id.name, args2) | _::args2 -> error "argument 2 of '__builtin_debug' must be either a string literal or a variable"; ("", args2) | [] -> assert false (* catched earlier *) in - let targs2 = convertTypArgs env [] args2 in + let targs2 = convertTypAnnotArgs env args2 in Ebuiltin( AST.EF_debug(P.of_int64 kind, intern_string text, typlist_of_typelist targs2), @@ -854,7 +899,7 @@ let rec convertExpr env e = | C.ECall({edesc = C.EVar {name = "__builtin_annot"}}, args) -> begin match args with | {edesc = C.EConst(CStr txt)} :: args1 -> - let targs1 = convertTypArgs env [] args1 in + let targs1 = convertTypAnnotArgs env args1 in Ebuiltin( AST.EF_annot(P.of_int 1,coqstring_of_camlstring txt, typlist_of_typelist targs1), targs1, convertExprList env args1, convertTyp env e.etyp) @@ -882,7 +927,7 @@ let rec convertExpr env e = let file,line = !currentLocation in let fun_name = !currentFunction in let loc_string = Printf.sprintf "# file:%s line:%d function:%s\n" file line fun_name in - let targs1 = convertTypArgs env [] args1 in + let targs1 = convertTypAnnotArgs env args1 in AisAnnot.validate_ais_annot env !currentLocation txt args1; Ebuiltin( AST.EF_annot(P.of_int 2,coqstring_of_camlstring (loc_string ^ txt), typlist_of_typelist targs1), @@ -918,6 +963,10 @@ let rec convertExpr env e = Econs(va_list_ptr dst, Econs(va_list_ptr src, Enil)), Tvoid) + | C.ECall({edesc = C.EVar {name = "__builtin_sel"}}, [arg1; arg2; arg3]) -> + ewrap (Ctyping.eselection (convertExpr env arg1) + (convertExpr env arg2) (convertExpr env arg3)) + | C.ECall({edesc = C.EVar {name = "printf"}}, args) when !Clflags.option_interp -> let targs = convertTypArgs env [] args @@ -983,14 +1032,14 @@ let convertAsm loc env txt outputs inputs clobber = match output' with None -> TVoid [] | Some e -> e.etyp in (* Build the Ebuiltin expression *) let e = - let tinputs = convertTypArgs env [] inputs' in + let tinputs = convertTypAnnotArgs env inputs' in let toutput = convertTyp env ty_res in Ebuiltin( AST.EF_inline_asm(coqstring_of_camlstring txt', signature_of_type tinputs toutput AST.cc_default, clobber'), tinputs, convertExprList env inputs', - convertTyp env ty_res) in + toutput) in (* Add an assignment to the output, if any *) match output' with | None -> e @@ -1184,7 +1233,8 @@ let convertFundef loc env fd = Hashtbl.add decl_atom id' { a_storage = fd.fd_storage; a_alignment = None; - a_sections = Sections.for_function env id' fd.fd_attrib; + a_size = None; + a_sections = Sections.for_function env loc id' fd.fd_attrib; a_access = Sections.Access_default; a_inline = inline; a_loc = loc }; @@ -1213,7 +1263,7 @@ let convertFundecl env (sto, id, ty, optinit) = if id.name = "free" then AST.EF_free else if Str.string_match re_runtime id.name 0 then AST.EF_runtime(id'', sg) else if Str.string_match re_builtin id.name 0 - && List.mem_assoc id.name builtins.Builtins.functions + && List.mem_assoc id.name builtins.builtin_functions then AST.EF_builtin(id'', sg) else AST.EF_external(id'', sg) in (id', AST.Gfun(Ctypes.External(ef, args, res, cconv))) @@ -1261,7 +1311,7 @@ let convertGlobvar loc env (sto, id, ty, optinit) = | Some i -> convertInitializer env ty i in let (section, access) = - Sections.for_variable env id' ty (optinit <> None) in + Sections.for_variable env loc id' ty (optinit <> None) in if Z.gt sz (Z.of_uint64 0xFFFF_FFFFL) then error "'%s' is too big (%s bytes)" id.name (Z.to_string sz); @@ -1270,6 +1320,7 @@ let convertGlobvar loc env (sto, id, ty, optinit) = Hashtbl.add decl_atom id' { a_storage = sto; a_alignment = Some (Z.to_int al); + a_size = Some (Z.to_int64 sz); a_sections = [section]; a_access = access; a_inline = No_specifier; @@ -1412,7 +1463,7 @@ let convertProgram p = Hashtbl.clear decl_atom; Hashtbl.clear stringTable; Hashtbl.clear wstringTable; - let p = cleanupGlobals (Builtins.declarations() @ p) in + let p = cleanupGlobals (Env.initial_declarations() @ p) in try let env = translEnv Env.empty p in let typs = convertCompositedefs env [] p in diff --git a/cfrontend/Cexec.v b/cfrontend/Cexec.v index 823d2542..b08c3ad7 100644 --- a/cfrontend/Cexec.v +++ b/cfrontend/Cexec.v @@ -16,7 +16,7 @@ Require Import FunInd. Require Import Axioms Classical. Require Import String Coqlib Decidableplus. Require Import Errors Maps Integers Floats. -Require Import AST Values Memory Events Globalenvs Determinism. +Require Import AST Values Memory Events Globalenvs Builtins Determinism. Require Import Ctypes Cop Csyntax Csem. Require Cstrategy. @@ -292,7 +292,6 @@ Remark check_assign_copy: { assign_copy_ok ty b ofs b' ofs' } + {~ assign_copy_ok ty b ofs b' ofs' }. Proof with try (right; intuition omega). intros. unfold assign_copy_ok. - assert (alignof_blockcopy ge ty > 0) by apply alignof_blockcopy_pos. destruct (Zdivide_dec (alignof_blockcopy ge ty) (Ptrofs.unsigned ofs')); auto... destruct (Zdivide_dec (alignof_blockcopy ge ty) (Ptrofs.unsigned ofs)); auto... assert (Y: {b' <> b \/ @@ -461,6 +460,14 @@ Definition do_ef_free check (zlt 0 (Ptrofs.unsigned sz)); do m' <- Mem.free m b (Ptrofs.unsigned lo - size_chunk Mptr) (Ptrofs.unsigned lo + Ptrofs.unsigned sz); Some(w, E0, Vundef, m') + | Vint n :: nil => + if Int.eq_dec n Int.zero && negb Archi.ptr64 + then Some(w, E0, Vundef, m) + else None + | Vlong n :: nil => + if Int64.eq_dec n Int64.zero && Archi.ptr64 + then Some(w, E0, Vundef, m) + else None | _ => None end. @@ -502,12 +509,19 @@ Definition do_ef_debug (kind: positive) (text: ident) (targs: list typ) (w: world) (vargs: list val) (m: mem) : option (world * trace * val * mem) := Some(w, E0, Vundef, m). +Definition do_builtin_or_external (name: string) (sg: signature) + (w: world) (vargs: list val) (m: mem) : option (world * trace * val * mem) := + match lookup_builtin_function name sg with + | Some bf => match builtin_function_sem bf vargs with Some v => Some(w, E0, v, m) | None => None end + | None => do_external_function name sg ge w vargs m + end. + Definition do_external (ef: external_function): world -> list val -> mem -> option (world * trace * val * mem) := match ef with | EF_external name sg => do_external_function name sg ge - | EF_builtin name sg => do_external_function name sg ge - | EF_runtime name sg => do_external_function name sg ge + | EF_builtin name sg => do_builtin_or_external name sg + | EF_runtime name sg => do_builtin_or_external name sg | EF_vload chunk => do_ef_volatile_load chunk | EF_vstore chunk => do_ef_volatile_store chunk | EF_malloc => do_ef_malloc @@ -524,50 +538,65 @@ Lemma do_ef_external_sound: do_external ef w vargs m = Some(w', t, vres, m') -> external_call ef ge vargs m t vres m' /\ possible_trace w t w'. Proof with try congruence. + intros until m'. assert (SIZE: forall v sz, do_alloc_size v = Some sz -> v = Vptrofs sz). { intros until sz; unfold Vptrofs; destruct v; simpl; destruct Archi.ptr64 eqn:SF; intros EQ; inv EQ; f_equal; symmetry; eauto with ptrofs. } - intros until m'. + assert (BF_EX: forall name sg, + do_builtin_or_external name sg w vargs m = Some (w', t, vres, m') -> + builtin_or_external_sem name sg ge vargs m t vres m' /\ possible_trace w t w'). + { unfold do_builtin_or_external, builtin_or_external_sem; intros. + destruct (lookup_builtin_function name sg ) as [bf|]. + - destruct (builtin_function_sem bf vargs) as [vres1|] eqn:BF; inv H. + split. constructor; auto. constructor. + - eapply do_external_function_sound; eauto. + } destruct ef; simpl. -(* EF_external *) - eapply do_external_function_sound; eauto. -(* EF_builtin *) +- (* EF_external *) eapply do_external_function_sound; eauto. -(* EF_runtime *) - eapply do_external_function_sound; eauto. -(* EF_vload *) +- (* EF_builtin *) + eapply BF_EX; eauto. +- (* EF_runtime *) + eapply BF_EX; eauto. +- (* EF_vload *) unfold do_ef_volatile_load. destruct vargs... destruct v... destruct vargs... mydestr. destruct p as [[w'' t''] v]; mydestr. exploit do_volatile_load_sound; eauto. intuition. econstructor; eauto. - auto. -(* EF_vstore *) +- (* EF_vstore *) unfold do_ef_volatile_store. destruct vargs... destruct v... destruct vargs... destruct vargs... mydestr. destruct p as [[w'' t''] m'']. mydestr. exploit do_volatile_store_sound; eauto. intuition. econstructor; eauto. - auto. -(* EF_malloc *) +- (* EF_malloc *) unfold do_ef_malloc. destruct vargs... destruct vargs... mydestr. destruct (Mem.alloc m (- size_chunk Mptr) (Ptrofs.unsigned i)) as [m1 b] eqn:?. mydestr. split. apply SIZE in Heqo. subst v. econstructor; eauto. constructor. -(* EF_free *) - unfold do_ef_free. destruct vargs... destruct v... destruct vargs... - mydestr. split. apply SIZE in Heqo0. econstructor; eauto. congruence. omega. constructor. -(* EF_memcpy *) +- (* EF_free *) + unfold do_ef_free. destruct vargs... destruct v... ++ destruct vargs... mydestr; InvBooleans; subst i. + replace (Vint Int.zero) with Vnullptr. split; constructor. + apply negb_true_iff in H0. unfold Vnullptr; rewrite H0; auto. ++ destruct vargs... mydestr; InvBooleans; subst i. + replace (Vlong Int64.zero) with Vnullptr. split; constructor. + unfold Vnullptr; rewrite H0; auto. ++ destruct vargs... mydestr. + split. apply SIZE in Heqo0. econstructor; eauto. congruence. omega. + constructor. +- (* EF_memcpy *) unfold do_ef_memcpy. destruct vargs... destruct v... destruct vargs... destruct v... destruct vargs... mydestr. apply Decidable_sound in Heqb1. red in Heqb1. split. econstructor; eauto; tauto. constructor. -(* EF_annot *) +- (* EF_annot *) unfold do_ef_annot. mydestr. split. constructor. apply list_eventval_of_val_sound; auto. econstructor. constructor; eauto. constructor. -(* EF_annot_val *) +- (* EF_annot_val *) unfold do_ef_annot_val. destruct vargs... destruct vargs... mydestr. split. constructor. apply eventval_of_val_sound; auto. econstructor. constructor; eauto. constructor. -(* EF_inline_asm *) +- (* EF_inline_asm *) eapply do_inline_assembly_sound; eauto. -(* EF_debug *) +- (* EF_debug *) unfold do_ef_debug. mydestr. split; constructor. Qed. @@ -576,42 +605,52 @@ Lemma do_ef_external_complete: external_call ef ge vargs m t vres m' -> possible_trace w t w' -> do_external ef w vargs m = Some(w', t, vres, m'). Proof. + intros. assert (SIZE: forall n, do_alloc_size (Vptrofs n) = Some n). { unfold Vptrofs, do_alloc_size; intros; destruct Archi.ptr64 eqn:SF. rewrite Ptrofs.of_int64_to_int64; auto. rewrite Ptrofs.of_int_to_int; auto. } - intros. destruct ef; simpl in *. -(* EF_external *) - eapply do_external_function_complete; eauto. -(* EF_builtin *) - eapply do_external_function_complete; eauto. -(* EF_runtime *) + assert (BF_EX: forall name sg, + builtin_or_external_sem name sg ge vargs m t vres m' -> + do_builtin_or_external name sg w vargs m = Some (w', t, vres, m')). + { unfold do_builtin_or_external, builtin_or_external_sem; intros. + destruct (lookup_builtin_function name sg) as [bf|]. + - inv H1. inv H0. rewrite H2. auto. + - eapply do_external_function_complete; eauto. + } + destruct ef; simpl in *. +- (* EF_external *) eapply do_external_function_complete; eauto. -(* EF_vload *) +- (* EF_builtin *) + eapply BF_EX; eauto. +- (* EF_runtime *) + eapply BF_EX; eauto. +- (* EF_vload *) inv H; unfold do_ef_volatile_load. exploit do_volatile_load_complete; eauto. intros EQ; rewrite EQ; auto. -(* EF_vstore *) +- (* EF_vstore *) inv H; unfold do_ef_volatile_store. exploit do_volatile_store_complete; eauto. intros EQ; rewrite EQ; auto. -(* EF_malloc *) +- (* EF_malloc *) inv H; unfold do_ef_malloc. inv H0. erewrite SIZE by eauto. rewrite H1, H2. auto. -(* EF_free *) +- (* EF_free *) inv H; unfold do_ef_free. - inv H0. rewrite H1. erewrite SIZE by eauto. rewrite zlt_true. rewrite H3. auto. omega. -(* EF_memcpy *) ++ inv H0. rewrite H1. erewrite SIZE by eauto. rewrite zlt_true. rewrite H3. auto. omega. ++ inv H0. unfold Vnullptr; destruct Archi.ptr64; auto. +- (* EF_memcpy *) inv H; unfold do_ef_memcpy. inv H0. rewrite Decidable_complete. rewrite H7; rewrite H8; auto. red. tauto. -(* EF_annot *) +- (* EF_annot *) inv H; unfold do_ef_annot. inv H0. inv H6. inv H4. rewrite (list_eventval_of_val_complete _ _ _ H1). auto. -(* EF_annot_val *) +- (* EF_annot_val *) inv H; unfold do_ef_annot_val. inv H0. inv H6. inv H4. rewrite (eventval_of_val_complete _ _ _ H1). auto. -(* EF_inline_asm *) +- (* EF_inline_asm *) eapply do_inline_assembly_complete; eauto. -(* EF_debug *) +- (* EF_debug *) inv H. inv H0. reflexivity. Qed. @@ -1100,8 +1139,8 @@ Proof. induction 1; intros; constructor; eauto. Qed. -Hint Constructors context contextlist. -Hint Resolve context_compose contextlist_compose. +Local Hint Constructors context contextlist : core. +Local Hint Resolve context_compose contextlist_compose : core. Definition reduction_ok (k: kind) (a: expr) (m: mem) (rd: reduction) : Prop := match k, rd with @@ -1667,8 +1706,9 @@ Proof. change (In (f (C0, rd)) (map f res2)). apply in_map; auto. Qed. -Hint Resolve reducts_incl_val reducts_incl_loc reducts_incl_listval - reducts_incl_incontext reducts_incl_incontext2_left reducts_incl_incontext2_right. +Local Hint Resolve reducts_incl_val reducts_incl_loc reducts_incl_listval + reducts_incl_incontext reducts_incl_incontext2_left + reducts_incl_incontext2_right : core. Lemma step_expr_context: forall from to C, context from to C -> @@ -2053,7 +2093,7 @@ Ltac myinv := | _ => idtac end. -Hint Extern 3 => exact I. +Local Hint Extern 3 => exact I : core. Theorem do_step_sound: forall w S rule t S', diff --git a/cfrontend/Clight.v b/cfrontend/Clight.v index 7a4c49a2..8ab29fe9 100644 --- a/cfrontend/Clight.v +++ b/cfrontend/Clight.v @@ -84,7 +84,7 @@ Definition typeof (e: expr) : type := (** ** Statements *) (** Clight statements are similar to those of Compcert C, with the addition - of assigment (of a rvalue to a lvalue), assignment to a temporary, + of assignment (of a rvalue to a lvalue), assignment to a temporary, and function call (with assignment of the result to a temporary). The three C loops are replaced by a single infinite loop [Sloop s1 s2] that executes [s1] then [s2] repeatedly. A [continue] in [s1] diff --git a/cfrontend/ClightBigstep.v b/cfrontend/ClightBigstep.v index 92457586..2bccf60a 100644 --- a/cfrontend/ClightBigstep.v +++ b/cfrontend/ClightBigstep.v @@ -74,6 +74,8 @@ Definition outcome_result_value (out: outcome) (t: type) (v: val) (m: mem): Prop [t] is the trace of input/output events performed during this evaluation. *) +Variable function_entry: function -> list val -> mem -> env -> temp_env -> mem -> Prop. + Inductive exec_stmt: env -> temp_env -> mem -> statement -> trace -> temp_env -> mem -> outcome -> Prop := | exec_Sskip: forall e le m, exec_stmt e le m Sskip @@ -163,14 +165,12 @@ Inductive exec_stmt: env -> temp_env -> mem -> statement -> trace -> temp_env -> by the call. *) with eval_funcall: mem -> fundef -> list val -> trace -> mem -> val -> Prop := - | eval_funcall_internal: forall le m f vargs t e m1 m2 m3 out vres m4, - alloc_variables ge empty_env m (f.(fn_params) ++ f.(fn_vars)) e m1 -> - list_norepet (var_names f.(fn_params) ++ var_names f.(fn_vars)) -> - bind_parameters ge e m1 f.(fn_params) vargs m2 -> - exec_stmt e (create_undef_temps f.(fn_temps)) m2 f.(fn_body) t le m3 out -> - outcome_result_value out f.(fn_return) vres m3 -> - Mem.free_list m3 (blocks_of_env ge e) = Some m4 -> - eval_funcall m (Internal f) vargs t m4 vres + | eval_funcall_internal: forall m f vargs t e le1 le2 m1 m2 out vres m3, + function_entry f vargs m e le1 m1 -> + exec_stmt e le1 m1 f.(fn_body) t le2 m2 out -> + outcome_result_value out f.(fn_return) vres m2 -> + Mem.free_list m2 (blocks_of_env ge e) = Some m3 -> + eval_funcall m (Internal f) vargs t m3 vres | eval_funcall_external: forall m ef targs tres cconv vargs t vres m', external_call ef ge vargs m t vres m' -> eval_funcall m (External ef targs tres cconv) vargs t m' vres. @@ -231,17 +231,19 @@ CoInductive execinf_stmt: env -> temp_env -> mem -> statement -> traceinf -> Pro [fd] on arguments [args] diverges, with observable trace [t]. *) with evalinf_funcall: mem -> fundef -> list val -> traceinf -> Prop := - | evalinf_funcall_internal: forall m f vargs t e m1 m2, - alloc_variables ge empty_env m (f.(fn_params) ++ f.(fn_vars)) e m1 -> - list_norepet (var_names f.(fn_params) ++ var_names f.(fn_vars)) -> - bind_parameters ge e m1 f.(fn_params) vargs m2 -> - execinf_stmt e (create_undef_temps f.(fn_temps)) m2 f.(fn_body) t -> + | evalinf_funcall_internal: forall m f vargs t e m1 le1, + function_entry f vargs m e le1 m1 -> + execinf_stmt e le1 m1 f.(fn_body) t -> evalinf_funcall m (Internal f) vargs t. End BIGSTEP. (** Big-step execution of a whole program. *) +Section ENTRY. + +Variable function_entry: genv -> function -> list val -> mem -> env -> temp_env -> mem -> Prop. + Inductive bigstep_program_terminates (p: program): trace -> int -> Prop := | bigstep_program_terminates_intro: forall b f m0 m1 t r, let ge := globalenv p in @@ -249,7 +251,7 @@ Inductive bigstep_program_terminates (p: program): trace -> int -> Prop := Genv.find_symbol ge p.(prog_main) = Some b -> Genv.find_funct_ptr ge b = Some f -> type_of_fundef f = Tfunction Tnil type_int32s cc_default -> - eval_funcall ge m0 f nil t m1 (Vint r) -> + eval_funcall ge (function_entry ge) m0 f nil t m1 (Vint r) -> bigstep_program_terminates p t r. Inductive bigstep_program_diverges (p: program): traceinf -> Prop := @@ -259,12 +261,14 @@ Inductive bigstep_program_diverges (p: program): traceinf -> Prop := Genv.find_symbol ge p.(prog_main) = Some b -> Genv.find_funct_ptr ge b = Some f -> type_of_fundef f = Tfunction Tnil type_int32s cc_default -> - evalinf_funcall ge m0 f nil t -> + evalinf_funcall ge (function_entry ge) m0 f nil t -> bigstep_program_diverges p t. Definition bigstep_semantics (p: program) := Bigstep_semantics (bigstep_program_terminates p) (bigstep_program_diverges p). +End ENTRY. + (** * Implication from big-step semantics to transition semantics *) Section BIGSTEP_TO_TRANSITIONS. @@ -296,18 +300,31 @@ Proof. destruct k; simpl; intros; contradiction || auto. Qed. +Variable function_entry: genv -> function -> list val -> mem -> env -> temp_env -> mem -> Prop. + +Definition exec_stmt_fe (ge: genv) := exec_stmt ge (function_entry ge). +Definition eval_funcall_fe (ge: genv) := eval_funcall ge (function_entry ge). +Definition execinf_stmt_fe (ge: genv) := execinf_stmt ge (function_entry ge). +Definition evalinf_funcall_fe (ge: genv) := evalinf_funcall ge (function_entry ge). +Definition bigstep_semantics_fe := bigstep_semantics function_entry. + +Definition step_fe (ge: genv) := step ge (function_entry ge). +Definition semantics_fe (p: program) := + let ge := globalenv p in + Semantics_gen step_fe (initial_state p) final_state ge ge. + Lemma exec_stmt_eval_funcall_steps: (forall e le m s t le' m' out, - exec_stmt ge e le m s t le' m' out -> + exec_stmt_fe ge e le m s t le' m' out -> forall f k, exists S, - star step1 ge (State f s k e le m) t S + star step_fe ge (State f s k e le m) t S /\ outcome_state_match e le' m' f k out S) /\ (forall m fd args t m' res, - eval_funcall ge m fd args t m' res -> + eval_funcall_fe ge m fd args t m' res -> forall k, is_call_cont k -> - star step1 ge (Callstate fd args k m) t (Returnstate res k m')). + star step_fe ge (Callstate fd args k m) t (Returnstate res k m')). Proof. apply exec_stmt_funcall_ind; intros. @@ -450,23 +467,23 @@ Proof. unfold S2. inv B1; simpl; econstructor; eauto. (* call internal *) - destruct (H3 f k) as [S1 [A1 B1]]. - eapply star_left. eapply step_internal_function; eauto. econstructor; eauto. + destruct (H1 f k) as [S1 [A1 B1]]. + eapply star_left. eapply step_internal_function; eauto. eapply star_right. eexact A1. inv B1; simpl in H4; try contradiction. (* Out_normal *) assert (fn_return f = Tvoid /\ vres = Vundef). destruct (fn_return f); auto || contradiction. - destruct H7. subst vres. apply step_skip_call; auto. + destruct H5. subst vres. apply step_skip_call; auto. (* Out_return None *) assert (fn_return f = Tvoid /\ vres = Vundef). destruct (fn_return f); auto || contradiction. - destruct H8. subst vres. - rewrite <- (is_call_cont_call_cont k H6). rewrite <- H7. + destruct H6. subst vres. + rewrite <- (is_call_cont_call_cont k H4). rewrite <- H5. apply step_return_0; auto. (* Out_return Some *) - destruct H4. - rewrite <- (is_call_cont_call_cont k H6). rewrite <- H7. + destruct H2. + rewrite <- (is_call_cont_call_cont k H4). rewrite <- H5. eapply step_return_1; eauto. reflexivity. traceEq. @@ -476,31 +493,31 @@ Qed. Lemma exec_stmt_steps: forall e le m s t le' m' out, - exec_stmt ge e le m s t le' m' out -> + exec_stmt_fe ge e le m s t le' m' out -> forall f k, exists S, - star step1 ge (State f s k e le m) t S + star step_fe ge (State f s k e le m) t S /\ outcome_state_match e le' m' f k out S. Proof (proj1 exec_stmt_eval_funcall_steps). Lemma eval_funcall_steps: forall m fd args t m' res, - eval_funcall ge m fd args t m' res -> + eval_funcall_fe ge m fd args t m' res -> forall k, is_call_cont k -> - star step1 ge (Callstate fd args k m) t (Returnstate res k m'). + star step_fe ge (Callstate fd args k m) t (Returnstate res k m'). Proof (proj2 exec_stmt_eval_funcall_steps). Definition order (x y: unit) := False. Lemma evalinf_funcall_forever: forall m fd args T k, - evalinf_funcall ge m fd args T -> - forever_N step1 order ge tt (Callstate fd args k m) T. + evalinf_funcall_fe ge m fd args T -> + forever_N step_fe order ge tt (Callstate fd args k m) T. Proof. cofix CIH_FUN. assert (forall e le m s T f k, - execinf_stmt ge e le m s T -> - forever_N step1 order ge tt (State f s k e le m) T). + execinf_stmt_fe ge e le m s T -> + forever_N step_fe order ge tt (State f s k e le m) T). cofix CIH_STMT. intros. inv H. @@ -559,13 +576,13 @@ Proof. (* call internal *) intros. inv H0. eapply forever_N_plus. - eapply plus_one. econstructor; eauto. econstructor; eauto. + eapply plus_one. econstructor; eauto. apply H; eauto. traceEq. Qed. Theorem bigstep_semantics_sound: - bigstep_sound (bigstep_semantics prog) (semantics1 prog). + bigstep_sound (bigstep_semantics_fe prog) (semantics_fe prog). Proof. constructor; simpl; intros. (* termination *) diff --git a/cfrontend/Cminorgenproof.v b/cfrontend/Cminorgenproof.v index ffafc5d2..5acb996d 100644 --- a/cfrontend/Cminorgenproof.v +++ b/cfrontend/Cminorgenproof.v @@ -923,7 +923,7 @@ Remark inj_offset_aligned_block: Mem.inj_offset_aligned (align stacksize (block_alignment sz)) sz. Proof. intros; red; intros. - apply Zdivides_trans with (block_alignment sz). + apply Z.divide_trans with (block_alignment sz). unfold align_chunk. unfold block_alignment. generalize Z.divide_1_l; intro. generalize Z.divide_refl; intro. diff --git a/cfrontend/Cop.v b/cfrontend/Cop.v index c395a2cb..143e87a3 100644 --- a/cfrontend/Cop.v +++ b/cfrontend/Cop.v @@ -140,8 +140,8 @@ Definition classify_cast (tfrom tto: type) : classify_cast_cases := | Tfloat F64 _, Tfloat F32 _ => cast_case_s2f | Tfloat F32 _, Tfloat F64 _ => cast_case_f2s (* To pointer types *) - | Tpointer _ _, Tint _ _ _ => - if Archi.ptr64 then cast_case_i2l Unsigned else cast_case_pointer + | Tpointer _ _, Tint _ si _ => + if Archi.ptr64 then cast_case_i2l si else cast_case_pointer | Tpointer _ _, Tlong _ _ => if Archi.ptr64 then cast_case_pointer else cast_case_l2i I32 Unsigned | Tpointer _ _, (Tpointer _ _ | Tarray _ _ _ | Tfunction _ _ _) => cast_case_pointer @@ -1131,7 +1131,7 @@ Qed. Remark val_inject_vptrofs: forall n, Val.inject f (Vptrofs n) (Vptrofs n). Proof. intros. unfold Vptrofs. destruct Archi.ptr64; auto. Qed. -Hint Resolve val_inject_vtrue val_inject_vfalse val_inject_of_bool val_inject_vptrofs. +Local Hint Resolve val_inject_vtrue val_inject_vfalse val_inject_of_bool val_inject_vptrofs : core. Ltac TrivialInject := match goal with @@ -1517,7 +1517,7 @@ Inductive val_casted: val -> type -> Prop := | val_casted_void: forall v, val_casted v Tvoid. -Hint Constructors val_casted. +Local Hint Constructors val_casted : core. Remark cast_int_int_idem: forall sz sg i, cast_int_int sz sg (cast_int_int sz sg i) = cast_int_int sz sg i. @@ -1580,6 +1580,27 @@ Proof. intros. apply cast_val_casted. eapply cast_val_is_casted; eauto. Qed. +(** Moreover, casted values belong to the machine type corresponding to the + C type. *) + +Lemma val_casted_has_type: + forall v ty, val_casted v ty -> ty <> Tvoid -> Val.has_type v (typ_of_type ty). +Proof. + intros. inv H; simpl typ_of_type. +- exact I. +- exact I. +- exact I. +- exact I. +- apply Val.Vptr_has_type. +- red; unfold Tptr; rewrite H1; auto. +- red; unfold Tptr; rewrite H1; auto. +- red; unfold Tptr; rewrite H1; auto. +- red; unfold Tptr; rewrite H1; auto. +- apply Val.Vptr_has_type. +- apply Val.Vptr_has_type. +- congruence. +Qed. + (** Relation with the arithmetic conversions of ISO C99, section 6.3.1 *) Module ArithConv. diff --git a/cfrontend/Csem.v b/cfrontend/Csem.v index 0c3e00de..6d2b470f 100644 --- a/cfrontend/Csem.v +++ b/cfrontend/Csem.v @@ -15,19 +15,9 @@ (** Dynamic semantics for the Compcert C language *) -Require Import Coqlib. -Require Import Errors. -Require Import Maps. -Require Import Integers. -Require Import Floats. -Require Import Values. -Require Import AST. -Require Import Memory. -Require Import Events. -Require Import Globalenvs. -Require Import Ctypes. -Require Import Cop. -Require Import Csyntax. +Require Import Coqlib Errors Maps. +Require Import Integers Floats Values AST Memory Builtins Events Globalenvs. +Require Import Ctypes Cop Csyntax. Require Import Smallstep. (** * Operational semantics *) @@ -437,6 +427,59 @@ Definition not_stuck (e: expr) (m: mem) : Prop := forall k C e' , context k RV C -> e = C e' -> imm_safe k e' m. +(** ** Derived forms. *) + +(** The following are admissible reduction rules for some derived forms + of the CompCert C language. They help showing that the derived forms + make sense. *) + +Lemma red_selection: + forall v1 ty1 v2 ty2 v3 ty3 ty m b v2' v3', + ty <> Tvoid -> + bool_val v1 ty1 m = Some b -> + sem_cast v2 ty2 ty m = Some v2' -> + sem_cast v3 ty3 ty m = Some v3' -> + rred (Eselection (Eval v1 ty1) (Eval v2 ty2) (Eval v3 ty3) ty) m + E0 (Eval (if b then v2' else v3') ty) m. +Proof. + intros. unfold Eselection. + set (t := typ_of_type ty). + set (sg := mksignature (AST.Tint :: t :: t :: nil) t cc_default). + assert (LK: lookup_builtin_function "__builtin_sel"%string sg = Some (BI_standard (BI_select t))). + { unfold sg, t; destruct ty as [ | ? ? ? | ? | [] ? | ? ? | ? ? ? | ? ? ? | ? ? | ? ? ]; + simpl; unfold Tptr; destruct Archi.ptr64; reflexivity. } + set (v' := if b then v2' else v3'). + assert (C: val_casted v' ty). + { unfold v'; destruct b; eapply cast_val_is_casted; eauto. } + assert (EQ: Val.normalize v' t = v'). + { apply Val.normalize_idem. apply val_casted_has_type; auto. } + econstructor. +- constructor. rewrite cast_bool_bool_val, H0. eauto. + constructor. eauto. + constructor. eauto. + constructor. +- red. red. rewrite LK. constructor. simpl. rewrite <- EQ. + destruct b; auto. +Qed. + +Lemma ctx_selection_1: + forall k C r2 r3 ty, context k RV C -> context k RV (fun x => Eselection (C x) r2 r3 ty). +Proof. + intros. apply ctx_builtin. constructor; auto. +Qed. + +Lemma ctx_selection_2: + forall k r1 C r3 ty, context k RV C -> context k RV (fun x => Eselection r1 (C x) r3 ty). +Proof. + intros. apply ctx_builtin. constructor; constructor; auto. +Qed. + +Lemma ctx_selection_3: + forall k r1 r2 C ty, context k RV C -> context k RV (fun x => Eselection r1 r2 (C x) ty). +Proof. + intros. apply ctx_builtin. constructor; constructor; constructor; auto. +Qed. + End EXPR. (** ** Transition semantics. *) diff --git a/cfrontend/Cshmgen.v b/cfrontend/Cshmgen.v index 792a73f9..5bd12d00 100644 --- a/cfrontend/Cshmgen.v +++ b/cfrontend/Cshmgen.v @@ -23,6 +23,7 @@ Require Import Coqlib Maps Errors Integers Floats. Require Import AST Linking. Require Import Ctypes Cop Clight Cminor Csharpminor. +Require Import Conventions1. Local Open Scope string_scope. Local Open Scope error_monad_scope. @@ -558,6 +559,34 @@ Fixpoint typlist_of_arglist (al: list Clight.expr) (tyl: typelist) typ_of_type (default_argument_conversion (typeof a1)) :: typlist_of_arglist a2 Tnil end. +(** Translate a function call. + Depending on the ABI, it may be necessary to normalize the value + returned by casting it to the return type of the function. + For example, in the x86 ABI, a return value of type "char" is + returned in register AL, leaving the top 24 bits of EAX + unspecified. Hence, a cast to type "char" is needed to sign- or + zero-extend the returned integer before using it. *) + +Definition make_normalization (t: type) (a: expr) := + match t with + | Tint IBool _ _ => Eunop Ocast8unsigned a + | Tint I8 Signed _ => Eunop Ocast8signed a + | Tint I8 Unsigned _ => Eunop Ocast8unsigned a + | Tint I16 Signed _ => Eunop Ocast16signed a + | Tint I16 Unsigned _ => Eunop Ocast16unsigned a + | _ => a + end. + +Definition make_funcall (x: option ident) (tres: type) (sg: signature) + (fn: expr) (args: list expr): stmt := + match x, return_value_needs_normalization sg.(sig_res) with + | Some id, true => + Sseq (Scall x sg fn args) + (Sset id (make_normalization tres (Evar id))) + | _, _ => + Scall x sg fn args + end. + (** * Translation of statements *) (** [transl_statement nbrk ncnt s] returns a Csharpminor statement @@ -601,10 +630,10 @@ Fixpoint transl_statement (ce: composite_env) (tyret: type) (nbrk ncnt: nat) | fun_case_f args res cconv => do tb <- transl_expr ce b; do tcl <- transl_arglist ce cl args; - OK(Scall x {| sig_args := typlist_of_arglist cl args; - sig_res := opttyp_of_type res; - sig_cc := cconv |} - tb tcl) + let sg := {| sig_args := typlist_of_arglist cl args; + sig_res := rettype_of_type res; + sig_cc := cconv |} in + OK (make_funcall x res sg tb tcl) | _ => Error(msg "Cshmgen.transl_stmt(call)") end | Clight.Sbuiltin x ef tyargs bl => @@ -667,7 +696,7 @@ Definition transl_var (ce: composite_env) (v: ident * type) := Definition signature_of_function (f: Clight.function) := {| sig_args := map typ_of_type (map snd (Clight.fn_params f)); - sig_res := opttyp_of_type (Clight.fn_return f); + sig_res := rettype_of_type (Clight.fn_return f); sig_cc := Clight.fn_callconv f |}. Definition transl_function (ce: composite_env) (f: Clight.function) : res function := diff --git a/cfrontend/Cshmgenproof.v b/cfrontend/Cshmgenproof.v index 09e31cb2..1ceb8e4d 100644 --- a/cfrontend/Cshmgenproof.v +++ b/cfrontend/Cshmgenproof.v @@ -15,7 +15,7 @@ Require Import Coqlib Errors Maps Integers Floats. Require Import AST Linking. Require Import Values Events Memory Globalenvs Smallstep. -Require Import Ctypes Cop Clight Cminor Csharpminor. +Require Import Ctypes Ctyping Cop Clight Cminor Csharpminor. Require Import Cshmgen. (** * Relational specification of the transformation *) @@ -996,6 +996,26 @@ Proof. eapply make_memcpy_correct with (b := b) (v := Vptr b' ofs'); eauto. Qed. +Lemma make_normalization_correct: + forall e le m a v t, + eval_expr ge e le m a v -> + wt_val v t -> + eval_expr ge e le m (make_normalization t a) v. +Proof. + intros. destruct t; simpl; auto. inv H0. +- destruct i; simpl in H3. + + destruct s; econstructor; eauto; simpl; congruence. + + destruct s; econstructor; eauto; simpl; congruence. + + auto. + + econstructor; eauto; simpl; congruence. +- auto. +- destruct i. + + destruct s; econstructor; eauto. + + destruct s; econstructor; eauto. + + auto. + + econstructor; eauto. +Qed. + End CONSTRUCTORS. (** * Basic preservation invariants *) @@ -1360,7 +1380,16 @@ Inductive match_cont: composite_env -> type -> nat -> nat -> Clight.cont -> Csha match_cont cu.(prog_comp_env) (Clight.fn_return f) nbrk' ncnt' k tk -> match_cont ce tyret nbrk ncnt (Clight.Kcall id f e le k) - (Kcall id tf te le tk). + (Kcall id tf te le tk) + | match_Kcall_normalize: forall ce tyret nbrk ncnt nbrk' ncnt' f e k id a tf te le tk cu, + linkorder cu prog -> + transl_function cu.(prog_comp_env) f = OK tf -> + match_env e te -> + match_cont cu.(prog_comp_env) (Clight.fn_return f) nbrk' ncnt' k tk -> + (forall v e le m, wt_val v tyret -> le!id = Some v -> eval_expr tge e le m a v) -> + match_cont ce tyret nbrk ncnt + (Clight.Kcall (Some id) f e le k) + (Kcall (Some id) tf te le (Kseq (Sset id a) tk)). Inductive match_states: Clight.state -> Csharpminor.state -> Prop := | match_state: @@ -1377,14 +1406,15 @@ Inductive match_states: Clight.state -> Csharpminor.state -> Prop := forall fd args k m tfd tk targs tres cconv cu ce (LINK: linkorder cu prog) (TR: match_fundef cu fd tfd) - (MK: match_cont ce Tvoid 0%nat 0%nat k tk) + (MK: match_cont ce tres 0%nat 0%nat k tk) (ISCC: Clight.is_call_cont k) (TY: type_of_fundef fd = Tfunction targs tres cconv), match_states (Clight.Callstate fd args k m) (Callstate tfd args tk m) | match_returnstate: - forall res k m tk ce - (MK: match_cont ce Tvoid 0%nat 0%nat k tk), + forall res tres k m tk ce + (MK: match_cont ce tres 0%nat 0%nat k tk) + (WT: wt_val res tres), match_states (Clight.Returnstate res k m) (Returnstate res tk m). @@ -1442,7 +1472,9 @@ Proof. - (* set *) auto. - (* call *) - simpl in TR. destruct (classify_fun (typeof e)); monadInv TR. auto. + simpl in TR. destruct (classify_fun (typeof e)); monadInv TR. + unfold make_funcall. + destruct o; auto; destruct Conventions1.return_value_needs_normalization; auto. - (* builtin *) auto. - (* seq *) @@ -1500,24 +1532,26 @@ End FIND_LABEL. (** Properties of call continuations *) Lemma match_cont_call_cont: - forall ce' tyret' nbrk' ncnt' ce tyret nbrk ncnt k tk, + forall ce' nbrk' ncnt' ce tyret nbrk ncnt k tk, match_cont ce tyret nbrk ncnt k tk -> - match_cont ce' tyret' nbrk' ncnt' (Clight.call_cont k) (call_cont tk). + match_cont ce' tyret nbrk' ncnt' (Clight.call_cont k) (call_cont tk). Proof. induction 1; simpl; auto. - constructor. - econstructor; eauto. +- apply match_Kstop. +- eapply match_Kcall; eauto. +- eapply match_Kcall_normalize; eauto. Qed. Lemma match_cont_is_call_cont: - forall ce tyret nbrk ncnt k tk ce' tyret' nbrk' ncnt', + forall ce tyret nbrk ncnt k tk ce' nbrk' ncnt', match_cont ce tyret nbrk ncnt k tk -> Clight.is_call_cont k -> - match_cont ce' tyret' nbrk' ncnt' k tk /\ is_call_cont tk. + match_cont ce' tyret nbrk' ncnt' k tk /\ is_call_cont tk. Proof. intros. inv H; simpl in H0; try contradiction; simpl. - split; auto; constructor. - split; auto; econstructor; eauto. + split; auto; apply match_Kstop. + split; auto; eapply match_Kcall; eauto. + split; auto; eapply match_Kcall_normalize; eauto. Qed. (** The simulation proof *) @@ -1549,19 +1583,44 @@ Proof. - (* call *) revert TR. simpl. case_eq (classify_fun (typeof a)); try congruence. - intros targs tres cc CF TR. monadInv TR. inv MTR. + intros targs tres cc CF TR. monadInv TR. exploit functions_translated; eauto. intros (cu' & tfd & FIND & TFD & LINK'). rewrite H in CF. simpl in CF. inv CF. - econstructor; split. - apply plus_one. econstructor; eauto. - eapply transl_expr_correct with (cunit := cu); eauto. - eapply transl_arglist_correct with (cunit := cu); eauto. - erewrite typlist_of_arglist_eq by eauto. - eapply transl_fundef_sig1; eauto. - rewrite H3. auto. - econstructor; eauto. - eapply match_Kcall with (ce := prog_comp_env cu') (cu := cu); eauto. - simpl. auto. + set (sg := {| sig_args := typlist_of_arglist al targs; + sig_res := rettype_of_type tres; + sig_cc := cc |}) in *. + assert (SIG: funsig tfd = sg). + { unfold sg; erewrite typlist_of_arglist_eq by eauto. + eapply transl_fundef_sig1; eauto. rewrite H3; auto. } + assert (EITHER: tk' = tk /\ ts' = Scall optid sg x x0 + \/ exists id, optid = Some id /\ + tk' = tk /\ ts' = Sseq (Scall optid sg x x0) + (Sset id (make_normalization tres (Evar id)))). + { unfold make_funcall in MTR. + destruct optid. destruct Conventions1.return_value_needs_normalization. + inv MTR. right; exists i; auto. + inv MTR; auto. + inv MTR; auto. } + destruct EITHER as [(EK & ES) | (id & EI & EK & ES)]; rewrite EK, ES. + + (* without normalization of return value *) + econstructor; split. + apply plus_one. eapply step_call; eauto. + eapply transl_expr_correct with (cunit := cu); eauto. + eapply transl_arglist_correct with (cunit := cu); eauto. + econstructor; eauto. + eapply match_Kcall with (ce := prog_comp_env cu') (cu := cu); eauto. + exact I. + + (* with normalization of return value *) + subst optid. + econstructor; split. + eapply plus_two. apply step_seq. eapply step_call; eauto. + eapply transl_expr_correct with (cunit := cu); eauto. + eapply transl_arglist_correct with (cunit := cu); eauto. + traceEq. + econstructor; eauto. + eapply match_Kcall_normalize with (ce := prog_comp_env cu') (cu := cu); eauto. + intros. eapply make_normalization_correct; eauto. constructor; eauto. + exact I. - (* builtin *) monadInv TR. inv MTR. @@ -1658,6 +1717,7 @@ Proof. eapply match_env_free_blocks; eauto. eapply match_returnstate with (ce := prog_comp_env cu); eauto. eapply match_cont_call_cont. eauto. + constructor. - (* return some *) monadInv TR. inv MTR. @@ -1667,6 +1727,7 @@ Proof. eapply match_env_free_blocks; eauto. eapply match_returnstate with (ce := prog_comp_env cu); eauto. eapply match_cont_call_cont. eauto. + apply wt_val_casted. eapply cast_val_is_casted; eauto. - (* skip call *) monadInv TR. inv MTR. @@ -1675,6 +1736,7 @@ Proof. apply plus_one. apply step_skip_call. auto. eapply match_env_free_blocks; eauto. eapply match_returnstate with (ce := prog_comp_env cu); eauto. + constructor. - (* switch *) monadInv TR. @@ -1738,20 +1800,33 @@ Proof. simpl. econstructor; eauto. unfold transl_function. rewrite EQ; simpl. rewrite EQ1; simpl. auto. constructor. + replace (fn_return f) with tres. eassumption. + simpl in TY. unfold type_of_function in TY. congruence. - (* external function *) inv TR. exploit match_cont_is_call_cont; eauto. intros [A B]. econstructor; split. - apply plus_one. constructor. eauto. + apply plus_one. constructor. eapply external_call_symbols_preserved; eauto. apply senv_preserved. eapply match_returnstate with (ce := ce); eauto. + apply has_rettype_wt_val. + replace (rettype_of_type tres0) with (sig_res (ef_sig ef)). + eapply external_call_well_typed_gen; eauto. + rewrite H5. simpl. simpl in TY. congruence. - (* returnstate *) inv MK. - econstructor; split. - apply plus_one. constructor. - econstructor; eauto. simpl; reflexivity. constructor. + + (* without normalization *) + econstructor; split. + apply plus_one. constructor. + econstructor; eauto. simpl; reflexivity. constructor. + + (* with normalization *) + econstructor; split. + eapply plus_three. econstructor. econstructor. constructor. + simpl. apply H13. eauto. apply PTree.gss. + traceEq. + simpl. rewrite PTree.set2. econstructor; eauto. simpl; reflexivity. constructor. Qed. Lemma transl_initial_states: diff --git a/cfrontend/Cstrategy.v b/cfrontend/Cstrategy.v index 28c8eeb8..c235031f 100644 --- a/cfrontend/Cstrategy.v +++ b/cfrontend/Cstrategy.v @@ -222,7 +222,7 @@ Proof. induction 1; constructor; auto. Qed. -Hint Resolve leftcontext_context. +Local Hint Resolve leftcontext_context : core. (** Strategy for reducing expressions. We reduce the leftmost innermost non-simple subexpression, evaluating its arguments (which are necessarily @@ -398,8 +398,8 @@ Proof. induction 1; intros; constructor; eauto. Qed. -Hint Constructors context contextlist. -Hint Resolve context_compose contextlist_compose. +Local Hint Constructors context contextlist : core. +Local Hint Resolve context_compose contextlist_compose : core. (** * Safe executions. *) @@ -975,7 +975,7 @@ Proof. apply extensionality; intros. f_equal. f_equal. apply exprlist_app_assoc. Qed. -Hint Resolve contextlist'_head contextlist'_tail. +Local Hint Resolve contextlist'_head contextlist'_tail : core. Lemma eval_simple_list_steps: forall rl vl, eval_simple_list' rl vl -> @@ -1049,7 +1049,7 @@ Scheme expr_ind2 := Induction for expr Sort Prop with exprlist_ind2 := Induction for exprlist Sort Prop. Combined Scheme expr_expr_list_ind from expr_ind2, exprlist_ind2. -Hint Constructors leftcontext leftcontextlist. +Local Hint Constructors leftcontext leftcontextlist : core. Lemma decompose_expr: (forall a from C, diff --git a/cfrontend/Csyntax.v b/cfrontend/Csyntax.v index 914328be..e3e2c1e9 100644 --- a/cfrontend/Csyntax.v +++ b/cfrontend/Csyntax.v @@ -41,7 +41,7 @@ Inductive expr : Type := (**r binary arithmetic operation *) | Ecast (r: expr) (ty: type) (**r type cast [(ty)r] *) | Eseqand (r1 r2: expr) (ty: type) (**r sequential "and" [r1 && r2] *) - | Eseqor (r1 r2: expr) (ty: type) (**r sequential "or" [r1 && r2] *) + | Eseqor (r1 r2: expr) (ty: type) (**r sequential "or" [r1 || r2] *) | Econdition (r1 r2 r3: expr) (ty: type) (**r conditional [r1 ? r2 : r3] *) | Esizeof (ty': type) (ty: type) (**r size of a type *) | Ealignof (ty': type) (ty: type) (**r natural alignment of a type *) @@ -100,6 +100,18 @@ Definition Epreincr (id: incr_or_decr) (l: expr) (ty: type) := Eassignop (match id with Incr => Oadd | Decr => Osub end) l (Eval (Vint Int.one) type_int32s) (typeconv ty) ty. +(** Selection is a conditional expression that always evaluates both arms + and can be implemented by "conditional move" instructions. + It is expressed as an invocation of a builtin function. *) + +Definition Eselection (r1 r2 r3: expr) (ty: type) := + let t := typ_of_type ty in + let sg := mksignature (AST.Tint :: t :: t :: nil) t cc_default in + Ebuiltin (EF_builtin "__builtin_sel"%string sg) + (Tcons type_bool (Tcons ty (Tcons ty Tnil))) + (Econs r1 (Econs r2 (Econs r3 Enil))) + ty. + (** Extract the type part of a type-annotated expression. *) Definition typeof (a: expr) : type := diff --git a/cfrontend/Ctypes.v b/cfrontend/Ctypes.v index bfc5daa9..664a60c5 100644 --- a/cfrontend/Ctypes.v +++ b/cfrontend/Ctypes.v @@ -732,8 +732,21 @@ Definition typ_of_type (t: type) : AST.typ := | Tpointer _ _ | Tarray _ _ _ | Tfunction _ _ _ | Tstruct _ _ | Tunion _ _ => AST.Tptr end. -Definition opttyp_of_type (t: type) : option AST.typ := - if type_eq t Tvoid then None else Some (typ_of_type t). +Definition rettype_of_type (t: type) : AST.rettype := + match t with + | Tvoid => AST.Tvoid + | Tint I32 _ _ => AST.Tint + | Tint I8 Signed _ => AST.Tint8signed + | Tint I8 Unsigned _ => AST.Tint8unsigned + | Tint I16 Signed _ => AST.Tint16signed + | Tint I16 Unsigned _ => AST.Tint16unsigned + | Tint IBool _ _ => AST.Tint8unsigned + | Tlong _ _ => AST.Tlong + | Tfloat F32 _ => AST.Tsingle + | Tfloat F64 _ => AST.Tfloat + | Tpointer _ _ => AST.Tptr + | Tarray _ _ _ | Tfunction _ _ _ | Tstruct _ _ | Tunion _ _ => AST.Tvoid + end. Fixpoint typlist_of_typelist (tl: typelist) : list AST.typ := match tl with @@ -742,7 +755,7 @@ Fixpoint typlist_of_typelist (tl: typelist) : list AST.typ := end. Definition signature_of_type (args: typelist) (res: type) (cc: calling_convention): signature := - mksignature (typlist_of_typelist args) (opttyp_of_type res) cc. + mksignature (typlist_of_typelist args) (rettype_of_type res) cc. (** * Construction of the composite environment *) diff --git a/cfrontend/Ctyping.v b/cfrontend/Ctyping.v index ba1d34fb..00fcf8ab 100644 --- a/cfrontend/Ctyping.v +++ b/cfrontend/Ctyping.v @@ -18,7 +18,7 @@ Require Import String. Require Import Coqlib Maps Integers Floats Errors. Require Import AST Linking. -Require Import Values Memory Globalenvs Events. +Require Import Values Memory Globalenvs Builtins Events. Require Import Ctypes Cop Csyntax Csem. Local Open Scope error_monad_scope. @@ -392,13 +392,17 @@ Inductive wt_rvalue : expr -> Prop := classify_fun (typeof r1) = fun_case_f tyargs tyres cconv -> wt_arguments rargs tyargs -> wt_rvalue (Ecall r1 rargs tyres) - | wt_Ebuiltin: forall ef tyargs rargs, + | wt_Ebuiltin: forall ef tyargs rargs ty, wt_exprlist rargs -> wt_arguments rargs tyargs -> - (* This is specialized to builtins returning void, the only - case generated by C2C. *) - sig_res (ef_sig ef) = None -> - wt_rvalue (Ebuiltin ef tyargs rargs Tvoid) + (* This typing rule is specialized to the builtin invocations generated + by C2C, which are either __builtin_sel or builtins returning void. *) + (ty = Tvoid /\ sig_res (ef_sig ef) = AST.Tvoid) + \/ (tyargs = Tcons type_bool (Tcons ty (Tcons ty Tnil)) + /\ let t := typ_of_type ty in + let sg := mksignature (AST.Tint :: t :: t :: nil) t cc_default in + ef = EF_builtin "__builtin_sel"%string sg) -> + wt_rvalue (Ebuiltin ef tyargs rargs ty) | wt_Eparen: forall r tycast ty, wt_rvalue r -> wt_cast (typeof r) tycast -> subtype tycast ty -> @@ -517,11 +521,20 @@ Fixpoint bind_globdef (e: typenv) (l: list (ident * globdef fundef type)) : type | (id, Gvar v) :: l => bind_globdef (PTree.set id v.(gvar_info) e) l end. +Inductive wt_fundef (ce: composite_env) (e: typenv) : fundef -> Prop := + | wt_fundef_internal: forall f, + wt_function ce e f -> + wt_fundef ce e (Internal f) + | wt_fundef_external: forall ef targs tres cc, + (ef_sig ef).(sig_res) = rettype_of_type tres -> + wt_fundef ce e (External ef targs tres cc). + Inductive wt_program : program -> Prop := | wt_program_intro: forall p, let e := bind_globdef (PTree.empty _) p.(prog_defs) in - (forall id f, In (id, Gfun (Internal f)) p.(prog_defs) -> - wt_function p.(prog_comp_env) e f) -> + (forall id fd, + In (id, Gfun fd) p.(prog_defs) -> + wt_fundef p.(prog_comp_env) e fd) -> wt_program p. Hint Constructors wt_val wt_rvalue wt_lvalue wt_stmt wt_lblstmts: ty. @@ -741,10 +754,16 @@ Definition ebuiltin (ef: external_function) (tyargs: typelist) (args: exprlist) do x1 <- check_rvals args; do x2 <- check_arguments args tyargs; if type_eq tyres Tvoid - && opt_typ_eq (sig_res (ef_sig ef)) None + && AST.rettype_eq (sig_res (ef_sig ef)) AST.Tvoid then OK (Ebuiltin ef tyargs args tyres) else Error (msg "builtin: wrong type decoration"). +Definition eselection (r1 r2 r3: expr) : res expr := + do x1 <- check_rval r1; do x2 <- check_rval r2; do x3 <- check_rval r3; + do y1 <- check_bool (typeof r1); + do ty <- type_conditional (typeof r2) (typeof r3); + OK (Eselection r1 r2 r3 ty). + Definition sdo (a: expr) : res statement := do x <- check_rval a; OK (Sdo a). @@ -905,7 +924,8 @@ Definition retype_function (ce: composite_env) (e: typenv) (f: function) : res f Definition retype_fundef (ce: composite_env) (e: typenv) (fd: fundef) : res fundef := match fd with | Internal f => do f' <- retype_function ce e f; OK (Internal f') - | External id args res cc => OK fd + | External ef args res cc => + assertion (rettype_eq (ef_sig ef).(sig_res) (rettype_of_type res)); OK fd end. Definition typecheck_program (p: program) : res program := @@ -977,10 +997,20 @@ Proof. classify_cast (Tint i s a) t2 <> cast_case_default). { unfold classify_cast. destruct t2; try congruence. destruct f; congruence. + destruct Archi.ptr64; congruence. } destruct i; auto. Qed. +Lemma wt_bool_cast: + forall ty, wt_bool ty -> wt_cast ty type_bool. +Proof. + unfold wt_bool, wt_cast; unfold classify_bool; intros. + destruct ty; simpl in *; try congruence; + try (destruct Archi.ptr64; congruence). + destruct f; congruence. +Qed. + Lemma wt_cast_int: forall i1 s1 a1 i2 s2 a2, wt_cast (Tint i1 s1 a1) (Tint i2 s2 a2). Proof. @@ -1221,10 +1251,21 @@ Lemma ebuiltin_sound: Proof. intros. monadInv H. destruct (type_eq tyres Tvoid); simpl in EQ2; try discriminate. - destruct (opt_typ_eq (sig_res (ef_sig ef)) None); inv EQ2. + destruct (rettype_eq (sig_res (ef_sig ef)) AST.Tvoid); inv EQ2. econstructor; eauto. eapply check_arguments_sound; eauto. Qed. +Lemma eselection_sound: + forall r1 r2 r3 a, eselection r1 r2 r3 = OK a -> + wt_expr ce e r1 -> wt_expr ce e r2 -> wt_expr ce e r3 -> wt_expr ce e a. +Proof. + intros. monadInv H. apply type_conditional_cast in EQ3. destruct EQ3. + eapply wt_Ebuiltin. + repeat (constructor; eauto with ty). + repeat (constructor; eauto with ty). apply wt_bool_cast; eauto with ty. + right; auto. +Qed. + Lemma sdo_sound: forall a s, sdo a = OK s -> wt_expr ce e a -> wt_stmt ce e rt s. Proof. @@ -1342,6 +1383,14 @@ Proof. intros. monadInv H. constructor; simpl. eapply retype_stmt_sound; eauto. Qed. +Lemma retype_fundef_sound: + forall ce e fd fd', retype_fundef ce e fd = OK fd' -> wt_fundef ce e fd'. +Proof. + intros. destruct fd; monadInv H. +- constructor; eapply retype_function_sound; eauto. +- constructor; auto. +Qed. + Theorem typecheck_program_sound: forall p p', typecheck_program p = OK p' -> wt_program p'. Proof. @@ -1364,11 +1413,11 @@ Proof. inv H1. simpl. auto. } rewrite ENVS. - intros id f. revert MATCH; generalize (prog_defs p) (AST.prog_defs tp). + intros id fd. revert MATCH; generalize (prog_defs p) (AST.prog_defs tp). induction 1; simpl; intros. contradiction. destruct H0; auto. subst b1; inv H. simpl in H1. inv H1. - destruct f1; monadInv H4. eapply retype_function_sound; eauto. + eapply retype_fundef_sound; eauto. Qed. (** * Subject reduction *) @@ -1632,15 +1681,6 @@ Proof. destruct f; discriminate. Qed. -Lemma wt_bool_cast: - forall ty, wt_bool ty -> wt_cast ty type_bool. -Proof. - unfold wt_bool, wt_cast; unfold classify_bool; intros. - destruct ty; simpl in *; try congruence; - try (destruct Archi.ptr64; congruence). - destruct f; congruence. -Qed. - Lemma wt_cast_self: forall t1 t2, wt_cast t1 t2 -> wt_cast t2 t2. Proof. @@ -1689,6 +1729,26 @@ Proof. inv H; auto. Qed. +Lemma has_rettype_wt_val: + forall v ty, + Val.has_rettype v (rettype_of_type ty) -> wt_val v ty. +Proof. + unfold rettype_of_type, Val.has_rettype, Val.has_type; destruct ty; intros. +- destruct v; contradiction || constructor. +- destruct i. + + destruct s; destruct v; try contradiction; constructor; red; auto. + + destruct s; destruct v; try contradiction; constructor; red; auto. + + destruct v; try contradiction; constructor; auto. + + destruct v; try contradiction; constructor; red; auto. +- destruct v; try contradiction; constructor; auto. +- destruct f; destruct v; try contradiction; constructor. +- unfold Tptr in *; destruct v; destruct Archi.ptr64 eqn:P64; try contradiction; constructor; auto. +- destruct v; contradiction || constructor. +- destruct v; contradiction || constructor. +- destruct v; contradiction || constructor. +- destruct v; contradiction || constructor. +Qed. + Lemma wt_rred: forall ge tenv a m t a' m', rred ge a m t a' m' -> wt_rvalue ge tenv a -> wt_rvalue ge tenv a'. @@ -1725,7 +1785,27 @@ Proof. constructor; auto. - (* comma *) auto. - (* paren *) inv H3. constructor. apply H5. eapply pres_sem_cast; eauto. -- (* builtin *) subst. auto with ty. +- (* builtin *) subst. destruct H7 as [(A & B) | (A & B)]. ++ subst ty. auto with ty. ++ simpl in B. set (T := typ_of_type ty) in *. + set (sg := mksignature (AST.Tint :: T :: T :: nil) T cc_default) in *. + assert (LK: lookup_builtin_function "__builtin_sel"%string sg = Some (BI_standard (BI_select T))). + { unfold sg, T; destruct ty as [ | ? ? ? | ? | [] ? | ? ? | ? ? ? | ? ? ? | ? ? | ? ? ]; + simpl; unfold Tptr; destruct Archi.ptr64; reflexivity. } + subst ef. red in H0. red in H0. rewrite LK in H0. inv H0. + inv H. inv H8. inv H9. inv H10. simpl in H1. + assert (A: val_casted v1 type_bool) by (eapply cast_val_is_casted; eauto). + inv A. + set (v' := if Int.eq n Int.zero then v4 else v2) in *. + constructor. + destruct (type_eq ty Tvoid). + subst. constructor. + inv H1. + assert (C: val_casted v' ty). + { unfold v'; destruct (Int.eq n Int.zero); eapply cast_val_is_casted; eauto. } + assert (EQ: Val.normalize v' T = v'). + { apply Val.normalize_idem. apply val_casted_has_type; auto. } + rewrite EQ. apply wt_val_casted; auto. Qed. Lemma wt_lred: @@ -1767,8 +1847,8 @@ with wt_subexprlist: wt_exprlist cenv tenv (C a) -> wt_expr_kind cenv tenv from a. Proof. - destruct 1; intros WT; auto; inv WT; eauto. - destruct 1; intros WT; inv WT; eauto. +- destruct 1; intros WT; auto; inv WT; eauto. +- destruct 1; intros WT; inv WT; eauto. Qed. Lemma typeof_context: @@ -1854,12 +1934,6 @@ Hypothesis WTPROG: wt_program prog. Let ge := globalenv prog. Let gtenv := bind_globdef (PTree.empty _) prog.(prog_defs). -Hypothesis WT_EXTERNAL: - forall id ef args res cc vargs m t vres m', - In (id, Gfun (External ef args res cc)) prog.(prog_defs) -> - external_call ef ge vargs m t vres m' -> - wt_val vres res. - Inductive wt_expr_cont: typenv -> function -> cont -> Prop := | wt_Kdo: forall te f k, wt_stmt_cont te f k -> @@ -1958,12 +2032,6 @@ Proof. induction 1; simpl; auto; econstructor; eauto. Qed. -Definition wt_fundef (fd: fundef) := - match fd with - | Internal f => wt_function ge gtenv f - | External ef targs tres cc => True - end. - Definition fundef_return (fd: fundef) : type := match fd with | Internal f => f.(fn_return) @@ -1971,10 +2039,10 @@ Definition fundef_return (fd: fundef) : type := end. Lemma wt_find_funct: - forall v fd, Genv.find_funct ge v = Some fd -> wt_fundef fd. + forall v fd, Genv.find_funct ge v = Some fd -> wt_fundef ge gtenv fd. Proof. intros. apply Genv.find_funct_prop with (p := prog) (v := v); auto. - intros. inv WTPROG. destruct f; simpl; auto. apply H1 with id; auto. + intros. inv WTPROG. apply H1 with id; auto. Qed. Inductive wt_state: state -> Prop := @@ -1990,7 +2058,7 @@ Inductive wt_state: state -> Prop := wt_state (ExprState f r k e m) | wt_call_state: forall b fd vargs k m (WTK: wt_call_cont k (fundef_return fd)) - (WTFD: wt_fundef fd) + (WTFD: wt_fundef ge gtenv fd) (FIND: Genv.find_funct ge b = Some fd), wt_state (Callstate fd vargs k m) | wt_return_state: forall v k m ty @@ -2047,7 +2115,6 @@ Qed. End WT_FIND_LABEL. - Lemma preservation_estep: forall S t S', estep ge S t S' -> wt_state S -> wt_state S'. Proof. @@ -2122,9 +2189,10 @@ Proof. - inv WTS; eauto with ty. - exploit wt_find_label. eexact WTB. eauto. eapply call_cont_wt'; eauto. intros [A B]. eauto with ty. -- simpl in WTFD; inv WTFD. econstructor; eauto. apply wt_call_cont_stmt_cont; auto. -- exploit (Genv.find_funct_inversion prog); eauto. intros (id & A). - econstructor; eauto. +- inv WTFD. inv H3. econstructor; eauto. apply wt_call_cont_stmt_cont; auto. +- inv WTFD. econstructor; eauto. + apply has_rettype_wt_val. simpl; rewrite <- H1. + eapply external_call_well_typed_gen; eauto. - inv WTK. eauto with ty. Qed. @@ -2139,7 +2207,7 @@ Theorem wt_initial_state: Proof. intros. inv H. econstructor. constructor. apply Genv.find_funct_ptr_prop with (p := prog) (b := b); auto. - intros. inv WTPROG. destruct f0; simpl; auto. apply H4 with id; auto. + intros. inv WTPROG. apply H4 with id; auto. instantiate (1 := (Vptr b Ptrofs.zero)). rewrite Genv.find_funct_find_funct_ptr. auto. Qed. diff --git a/cfrontend/PrintClight.ml b/cfrontend/PrintClight.ml index ca378c11..7d4c47e5 100644 --- a/cfrontend/PrintClight.ml +++ b/cfrontend/PrintClight.ml @@ -23,9 +23,14 @@ open Cop open PrintCsyntax open Clight -(* Naming temporaries *) +(* Naming temporaries. + Some temporaries are obtained by lifting variables in SimplLocals. + For these we use a meaningful name "$var", as found in the table of + atoms. Other temporaries are generated during SimplExpr, and are + not in the table of atoms. We print them as "$NNN" (a unique + integer). *) -let temp_name (id: AST.ident) = "$" ^ Z.to_string (Z.Zpos id) +let temp_name (id: AST.ident) = (* "$" ^ Z.to_string (Z.Zpos id) *)extern_atom id (* Declarator (identifier + type) -- reuse from PrintCsyntax *) @@ -236,10 +241,20 @@ and print_stmt_for p s = | _ -> fprintf p "({ %a })" print_stmt s -let print_function p id f = +(* There are two versions of Clight, Clight1 and Clight2, that differ + only in the meaning of function parameters: + - in Clight1, function parameters are variables + - in Clight2, function parameters are temporaries. +*) + +type clight_version = Clight1 | Clight2 + +let name_param = function Clight1 -> extern_atom | Clight2 -> temp_name + +let print_function ver p id f = fprintf p "%s@ " - (name_cdecl (name_function_parameters (extern_atom id) - f.fn_params f.fn_callconv) + (name_cdecl (name_function_parameters (name_param ver) + (extern_atom id) f.fn_params f.fn_callconv) f.fn_return); fprintf p "@[<v 2>{@ "; List.iter @@ -253,12 +268,12 @@ let print_function p id f = print_stmt p f.fn_body; fprintf p "@;<0 -2>}@]@ @ " -let print_fundef p id fd = +let print_fundef ver p id fd = match fd with | Ctypes.External(_, _, _, _) -> () | Internal f -> - print_function p id f + print_function ver p id f let print_fundecl p id fd = match fd with @@ -271,9 +286,9 @@ let print_fundecl p id fd = fprintf p "%s;@ " (name_cdecl (extern_atom id) (Clight.type_of_function f)) -let print_globdef p (id, gd) = +let print_globdef var p (id, gd) = match gd with - | AST.Gfun f -> print_fundef p id f + | AST.Gfun f -> print_fundef var p id f | AST.Gvar v -> print_globvar p id v (* from PrintCsyntax *) let print_globdecl p (id, gd) = @@ -281,20 +296,29 @@ let print_globdecl p (id, gd) = | AST.Gfun f -> print_fundecl p id f | AST.Gvar v -> () -let print_program p prog = +let print_program ver p prog = fprintf p "@[<v 0>"; List.iter (declare_composite p) prog.prog_types; List.iter (define_composite p) prog.prog_types; List.iter (print_globdecl p) prog.prog_defs; - List.iter (print_globdef p) prog.prog_defs; + List.iter (print_globdef ver p) prog.prog_defs; fprintf p "@]@." let destination : string option ref = ref None -let print_if prog = +let print_if_gen ver prog = match !destination with | None -> () | Some f -> let oc = open_out f in - print_program (formatter_of_out_channel oc) prog; + print_program ver (formatter_of_out_channel oc) prog; close_out oc + +(* print_if is called from driver/Compiler.v between the SimplExpr + and SimplLocals passes. It receives Clight1 syntax. *) +let print_if prog = print_if_gen Clight1 prog + +(* print_if_2 is called from clightgen/Clightgen.ml, after the + SimplLocals pass. It receives Clight2 syntax. *) +let print_if_2 prog = print_if_gen Clight2 prog + diff --git a/cfrontend/PrintCsyntax.ml b/cfrontend/PrintCsyntax.ml index 3a44796c..03dc5837 100644 --- a/cfrontend/PrintCsyntax.ml +++ b/cfrontend/PrintCsyntax.ml @@ -19,7 +19,7 @@ open Format open Camlcoq open Values open AST -open Ctypes +open! Ctypes open Cop open Csyntax @@ -85,7 +85,7 @@ let name_optid id = let rec name_cdecl id ty = match ty with - | Tvoid -> + | Ctypes.Tvoid -> "void" ^ name_optid id | Ctypes.Tint(sz, sg, a) -> name_inttype sz sg ^ attributes a ^ name_optid id @@ -391,7 +391,7 @@ and print_stmt_for p s = | _ -> fprintf p "({ %a })" print_stmt s -let name_function_parameters fun_name params cconv = +let name_function_parameters name_param fun_name params cconv = let b = Buffer.create 20 in Buffer.add_string b fun_name; Buffer.add_char b '('; @@ -404,7 +404,7 @@ let name_function_parameters fun_name params cconv = if cconv.cc_vararg then Buffer.add_string b ",..." | (id, ty) :: rem -> if not first then Buffer.add_string b ", "; - Buffer.add_string b (name_cdecl (extern_atom id) ty); + Buffer.add_string b (name_cdecl (name_param id) ty); add_params false rem in add_params true params end; @@ -413,8 +413,8 @@ let name_function_parameters fun_name params cconv = let print_function p id f = fprintf p "%s@ " - (name_cdecl (name_function_parameters (extern_atom id) - f.fn_params f.fn_callconv) + (name_cdecl (name_function_parameters extern_atom + (extern_atom id) f.fn_params f.fn_callconv) f.fn_return); fprintf p "@[<v 2>{@ "; List.iter diff --git a/cfrontend/SimplExprspec.v b/cfrontend/SimplExprspec.v index 37e2cd96..e7d57a1c 100644 --- a/cfrontend/SimplExprspec.v +++ b/cfrontend/SimplExprspec.v @@ -687,7 +687,7 @@ Hint Resolve gensym_within within_widen contained_widen in_eq in_cons Ple_trans Ple_refl: gensym. -Hint Resolve dest_for_val_below dest_for_effect_below. +Local Hint Resolve dest_for_val_below dest_for_effect_below : core. (** ** Correctness of the translation functions *) diff --git a/cfrontend/SimplLocalsproof.v b/cfrontend/SimplLocalsproof.v index 26d3d347..2dd34389 100644 --- a/cfrontend/SimplLocalsproof.v +++ b/cfrontend/SimplLocalsproof.v @@ -1055,7 +1055,7 @@ Proof. assert (RPDST: Mem.range_perm m bdst (Ptrofs.unsigned odst) (Ptrofs.unsigned odst + sizeof tge ty) Cur Nonempty). replace (sizeof tge ty) with (Z.of_nat (List.length bytes)). eapply Mem.range_perm_implies. eapply Mem.storebytes_range_perm; eauto. auto with mem. - rewrite LEN. apply nat_of_Z_eq. omega. + rewrite LEN. apply Z2Nat.id. omega. assert (PSRC: Mem.perm m bsrc (Ptrofs.unsigned osrc) Cur Nonempty). apply RPSRC. omega. assert (PDST: Mem.perm m bdst (Ptrofs.unsigned odst) Cur Nonempty). diff --git a/common/AST.v b/common/AST.v index 145f4919..fcbf0b34 100644 --- a/common/AST.v +++ b/common/AST.v @@ -45,9 +45,6 @@ Lemma typ_eq: forall (t1 t2: typ), {t1=t2} + {t1<>t2}. Proof. decide equality. Defined. Global Opaque typ_eq. -Definition opt_typ_eq: forall (t1 t2: option typ), {t1=t2} + {t1<>t2} - := option_eq typ_eq. - Definition list_typ_eq: forall (l1 l2: list typ), {l1=l2} + {l1<>l2} := list_eq_dec typ_eq. @@ -91,10 +88,34 @@ Fixpoint subtype_list (tyl1 tyl2: list typ) : bool := | _, _ => false end. +(** To describe the values returned by functions, we use the more precise + types below. *) + +Inductive rettype : Type := + | Tret (t: typ) (**r like type [t] *) + | Tint8signed (**r 8-bit signed integer *) + | Tint8unsigned (**r 8-bit unsigned integer *) + | Tint16signed (**r 16-bit signed integer *) + | Tint16unsigned (**r 16-bit unsigned integer *) + | Tvoid. (**r no value returned *) + +Coercion Tret: typ >-> rettype. + +Lemma rettype_eq: forall (t1 t2: rettype), {t1=t2} + {t1<>t2}. +Proof. generalize typ_eq; decide equality. Defined. +Global Opaque rettype_eq. + +Fixpoint proj_rettype (r: rettype) : typ := + match r with + | Tret t => t + | Tint8signed | Tint8unsigned | Tint16signed | Tint16unsigned => Tint + | Tvoid => Tint + end. + (** Additionally, function definitions and function calls are annotated by function signatures indicating: - the number and types of arguments; -- the type of the returned value, if any; +- the type of the returned value; - additional information on which calling convention to use. These signatures are used in particular to determine appropriate @@ -117,24 +138,20 @@ Global Opaque calling_convention_eq. Record signature : Type := mksignature { sig_args: list typ; - sig_res: option typ; + sig_res: rettype; sig_cc: calling_convention }. -Definition proj_sig_res (s: signature) : typ := - match s.(sig_res) with - | None => Tint - | Some t => t - end. +Definition proj_sig_res (s: signature) : typ := proj_rettype s.(sig_res). Definition signature_eq: forall (s1 s2: signature), {s1=s2} + {s1<>s2}. Proof. - generalize opt_typ_eq, list_typ_eq, calling_convention_eq; decide equality. + generalize rettype_eq, list_typ_eq, calling_convention_eq; decide equality. Defined. Global Opaque signature_eq. Definition signature_main := - {| sig_args := nil; sig_res := Some Tint; sig_cc := cc_default |}. + {| sig_args := nil; sig_res := Tint; sig_cc := cc_default |}. (** Memory accesses (load and store instructions) are annotated by a ``memory chunk'' indicating the type, size and signedness of the @@ -177,6 +194,28 @@ Definition type_of_chunk (c: memory_chunk) : typ := Lemma type_of_Mptr: type_of_chunk Mptr = Tptr. Proof. unfold Mptr, Tptr; destruct Archi.ptr64; auto. Qed. +(** Same, as a return type. *) + +Definition rettype_of_chunk (c: memory_chunk) : rettype := + match c with + | Mint8signed => Tint8signed + | Mint8unsigned => Tint8unsigned + | Mint16signed => Tint16signed + | Mint16unsigned => Tint16unsigned + | Mint32 => Tint + | Mint64 => Tlong + | Mfloat32 => Tsingle + | Mfloat64 => Tfloat + | Many32 => Tany32 + | Many64 => Tany64 + end. + +Lemma proj_rettype_of_chunk: + forall chunk, proj_rettype (rettype_of_chunk chunk) = type_of_chunk chunk. +Proof. + destruct chunk; auto. +Qed. + (** The chunk that is appropriate to store and reload a value of the given type, without losing information. *) @@ -432,12 +471,12 @@ Inductive external_function : Type := (** A function from the run-time library. Behaves like an external, but must not be redefined. *) | EF_vload (chunk: memory_chunk) - (** A volatile read operation. If the adress given as first argument + (** A volatile read operation. If the address given as first argument points within a volatile global variable, generate an event and return the value found in this event. Otherwise, produce no event and behave like a regular memory load. *) | EF_vstore (chunk: memory_chunk) - (** A volatile store operation. If the adress given as first argument + (** A volatile store operation. If the address given as first argument points within a volatile global variable, generate an event. Otherwise, produce no event and behave like a regular memory store. *) | EF_malloc @@ -477,15 +516,15 @@ Definition ef_sig (ef: external_function): signature := | EF_external name sg => sg | EF_builtin name sg => sg | EF_runtime name sg => sg - | EF_vload chunk => mksignature (Tptr :: nil) (Some (type_of_chunk chunk)) cc_default - | EF_vstore chunk => mksignature (Tptr :: type_of_chunk chunk :: nil) None cc_default - | EF_malloc => mksignature (Tptr :: nil) (Some Tptr) cc_default - | EF_free => mksignature (Tptr :: nil) None cc_default - | EF_memcpy sz al => mksignature (Tptr :: Tptr :: nil) None cc_default - | EF_annot kind text targs => mksignature targs None cc_default - | EF_annot_val kind text targ => mksignature (targ :: nil) (Some targ) cc_default + | EF_vload chunk => mksignature (Tptr :: nil) (rettype_of_chunk chunk) cc_default + | EF_vstore chunk => mksignature (Tptr :: type_of_chunk chunk :: nil) Tvoid cc_default + | EF_malloc => mksignature (Tptr :: nil) Tptr cc_default + | EF_free => mksignature (Tptr :: nil) Tvoid cc_default + | EF_memcpy sz al => mksignature (Tptr :: Tptr :: nil) Tvoid cc_default + | EF_annot kind text targs => mksignature targs Tvoid cc_default + | EF_annot_val kind text targ => mksignature (targ :: nil) targ cc_default | EF_inline_asm text sg clob => sg - | EF_debug kind text targs => mksignature targs None cc_default + | EF_debug kind text targs => mksignature targs Tvoid cc_default end. (** Whether an external function should be inlined by the compiler. *) diff --git a/common/Builtins.v b/common/Builtins.v new file mode 100644 index 00000000..476b541e --- /dev/null +++ b/common/Builtins.v @@ -0,0 +1,58 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Known built-in functions *) + +Require Import String Coqlib. +Require Import AST Integers Floats Values. +Require Export Builtins0 Builtins1. + +Inductive builtin_function : Type := + | BI_standard (b: standard_builtin) + | BI_platform (b: platform_builtin). + +Definition builtin_function_sig (b: builtin_function) : signature := + match b with + | BI_standard b => standard_builtin_sig b + | BI_platform b => platform_builtin_sig b + end. + +Definition builtin_function_sem (b: builtin_function) : builtin_sem (sig_res (builtin_function_sig b)) := + match b with + | BI_standard b => standard_builtin_sem b + | BI_platform b => platform_builtin_sem b + end. + +Definition lookup_builtin_function (name: string) (sg: signature) : option builtin_function := + match lookup_builtin standard_builtin_sig name sg standard_builtin_table with + | Some b => Some (BI_standard b) + | None => + match lookup_builtin platform_builtin_sig name sg platform_builtin_table with + | Some b => Some (BI_platform b) + | None => None + end end. + +Lemma lookup_builtin_function_sig: + forall name sg b, lookup_builtin_function name sg = Some b -> builtin_function_sig b = sg. +Proof. + unfold lookup_builtin_function; intros. + destruct (lookup_builtin standard_builtin_sig name sg standard_builtin_table) as [bs|] eqn:E. + inv H. simpl. eapply lookup_builtin_sig; eauto. + destruct (lookup_builtin platform_builtin_sig name sg platform_builtin_table) as [bp|] eqn:E'. + inv H. simpl. eapply lookup_builtin_sig; eauto. + discriminate. +Qed. + + diff --git a/common/Builtins0.v b/common/Builtins0.v new file mode 100644 index 00000000..8da98314 --- /dev/null +++ b/common/Builtins0.v @@ -0,0 +1,531 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Associating semantics to built-in functions *) + +Require Import String Coqlib. +Require Import AST Integers Floats Values Memdata. + +(** This module provides definitions and mechanisms to associate semantics + with names of built-in functions. + + This module is independent of the target architecture. Each target + provides a [Builtins1] module that lists the built-ins semantics + appropriate for the target. +*) + +Definition val_opt_has_rettype (ov: option val) (t: rettype) : Prop := + match ov with Some v => Val.has_rettype v t | None => True end. + +Definition val_opt_inject (j: meminj) (ov ov': option val) : Prop := + match ov, ov' with + | None, _ => True + | Some v, Some v' => Val.inject j v v' + | _, None => False + end. + +(** The semantics of a built-in function is a partial function + from list of values to values. + It must agree with the return type stated in the signature, + and be compatible with value injections. +*) + +Record builtin_sem (tret: rettype) : Type := mkbuiltin { + bs_sem :> list val -> option val; + bs_well_typed: forall vl, + val_opt_has_rettype (bs_sem vl) tret; + bs_inject: forall j vl vl', + Val.inject_list j vl vl' -> val_opt_inject j (bs_sem vl) (bs_sem vl') +}. + +(** Builtin functions can be created from functions over values, such as those + from the [Values.Val] module. Proofs of well-typedness and compatibility with + injections must be provided. The constructor functions have names +- [mkbuiltin_vNt] for a [t]otal function of [N] arguments that are [v]alues, or +- [mkbuiltin_vNp] for a [p]artial function of [N] arguments that are [v]alues. +*) + +Local Unset Program Cases. + +Program Definition mkbuiltin_v1t + (tret: rettype) (f: val -> val) + (WT: forall v1, Val.has_rettype (f v1) tret) + (INJ: forall j v1 v1', Val.inject j v1 v1' -> Val.inject j (f v1) (f v1')) := + mkbuiltin tret (fun vl => match vl with v1 :: nil => Some (f v1) | _ => None end) _ _. +Next Obligation. + red; destruct vl; auto. destruct vl; auto. +Qed. +Next Obligation. + red; inv H; auto. inv H1; auto. +Qed. + +Program Definition mkbuiltin_v2t + (tret: rettype) (f: val -> val -> val) + (WT: forall v1 v2, Val.has_rettype (f v1 v2) tret) + (INJ: forall j v1 v1' v2 v2', + Val.inject j v1 v1' -> Val.inject j v2 v2' -> + Val.inject j (f v1 v2) (f v1' v2')) := + mkbuiltin tret (fun vl => match vl with v1 :: v2 :: nil => Some (f v1 v2) | _ => None end) _ _. +Next Obligation. + red; destruct vl; auto. destruct vl; auto. destruct vl; auto. +Qed. +Next Obligation. + red; inv H; auto. inv H1; auto. inv H2; auto. +Qed. + +Program Definition mkbuiltin_v3t + (tret: rettype) (f: val -> val -> val -> val) + (WT: forall v1 v2 v3, Val.has_rettype (f v1 v2 v3) tret) + (INJ: forall j v1 v1' v2 v2' v3 v3', + Val.inject j v1 v1' -> Val.inject j v2 v2' -> Val.inject j v3 v3' -> + Val.inject j (f v1 v2 v3) (f v1' v2' v3')) := + mkbuiltin tret (fun vl => match vl with v1 :: v2 :: v3 :: nil => Some (f v1 v2 v3) | _ => None end) _ _. +Next Obligation. + red; destruct vl; auto. destruct vl; auto. destruct vl; auto. destruct vl; auto. +Qed. +Next Obligation. + red; inv H; auto. inv H1; auto. inv H2; auto. inv H3; auto. +Qed. + +Program Definition mkbuiltin_v1p + (tret: rettype) (f: val -> option val) + (WT: forall v1, val_opt_has_rettype (f v1) tret) + (INJ: forall j v1 v1', + Val.inject j v1 v1' -> val_opt_inject j (f v1) (f v1')) := + mkbuiltin tret (fun vl => match vl with v1 :: nil => f v1 | _ => None end) _ _. +Next Obligation. + red; destruct vl; auto. destruct vl; auto. apply WT. +Qed. +Next Obligation. + red; inv H; auto. inv H1; auto. apply INJ; auto. +Qed. + +Program Definition mkbuiltin_v2p + (tret: rettype) (f: val -> val -> option val) + (WT: forall v1 v2, val_opt_has_rettype (f v1 v2) tret) + (INJ: forall j v1 v1' v2 v2', + Val.inject j v1 v1' -> Val.inject j v2 v2' -> + val_opt_inject j (f v1 v2) (f v1' v2')) := + mkbuiltin tret (fun vl => match vl with v1 :: v2 :: nil => f v1 v2 | _ => None end) _ _. +Next Obligation. + red; destruct vl; auto. destruct vl; auto. destruct vl; auto. apply WT. +Qed. +Next Obligation. + red; inv H; auto. inv H1; auto. inv H2; auto. apply INJ; auto. +Qed. + +(** For numerical functions, involving only integers and floating-point numbers + but no pointer values, we can automate the proofs of well-typedness and + of compatibility with injections. *) + +(** First we define a mapping from syntactic Cminor types ([Tint], [Tfloat], etc) to semantic Coq types. *) + +Definition valty (t: typ) : Type := + match t with + | Tint => int + | Tlong => int64 + | Tfloat => float + | Tsingle => float32 + | Tany32 | Tany64 => Empty_set (**r no clear semantic meaning *) + end. + +(** We can inject and project between the numerical type [valty t] and the type [val]. *) + +Definition inj_num (t: typ) : valty t -> val := + match t with + | Tint => Vint + | Tlong => Vlong + | Tfloat => Vfloat + | Tsingle => Vsingle + | Tany32 | Tany64 => fun _ => Vundef + end. + +Definition proj_num {A: Type} (t: typ) (k0: A) (v: val): (valty t -> A) -> A := + match t with + | Tint => fun k1 => match v with Vint n => k1 n | _ => k0 end + | Tlong => fun k1 => match v with Vlong n => k1 n | _ => k0 end + | Tfloat => fun k1 => match v with Vfloat n => k1 n | _ => k0 end + | Tsingle => fun k1 => match v with Vsingle n => k1 n | _ => k0 end + | Tany32 | Tany64 => fun k1 => k0 + end. + +Lemma inj_num_wt: forall t x, Val.has_type (inj_num t x) t. +Proof. + destruct t; intros; exact I. +Qed. + +Lemma inj_num_inject: forall j t x, Val.inject j (inj_num t x) (inj_num t x). +Proof. + destruct t; intros; constructor. +Qed. + +Lemma inj_num_opt_wt: forall t x, val_opt_has_rettype (option_map (inj_num t) x) t. +Proof. + intros. destruct x; simpl. apply inj_num_wt. auto. +Qed. + +Lemma inj_num_opt_inject: forall j t x, + val_opt_inject j (option_map (inj_num t) x) (option_map (inj_num t) x). +Proof. + destruct x; simpl. apply inj_num_inject. auto. +Qed. + +Lemma proj_num_wt: + forall tres t k1 v, + (forall x, Val.has_type (k1 x) tres) -> + Val.has_type (proj_num t Vundef v k1) tres. +Proof. + intros. destruct t; simpl; destruct v; auto; exact I. +Qed. + +Lemma proj_num_inject: + forall j t k1 v k1' v', + (forall x, Val.inject j (k1 x) (k1' x)) -> + Val.inject j v v' -> + Val.inject j (proj_num t Vundef v k1) (proj_num t Vundef v' k1'). +Proof. + intros. destruct t; simpl; inv H0; auto. +Qed. + +Lemma proj_num_opt_wt: + forall (tres: typ) t k0 k1 v, + k0 = None \/ k0 = Some Vundef -> + (forall x, val_opt_has_rettype (k1 x) tres) -> + val_opt_has_rettype (proj_num t k0 v k1) tres. +Proof. + intros. + assert (val_opt_has_rettype k0 tres). { destruct H; subst k0; exact I. } + destruct t; simpl; destruct v; auto. +Qed. + +Lemma proj_num_opt_inject: + forall j k0 t k1 v k1' v', + (forall ov, val_opt_inject j k0 ov) -> + (forall x, val_opt_inject j (k1 x) (k1' x)) -> + Val.inject j v v' -> + val_opt_inject j (proj_num t k0 v k1) (proj_num t k0 v' k1'). +Proof. + intros. destruct t; simpl; inv H1; auto. +Qed. + +(** Wrapping numerical functions as built-ins. The constructor functions + have names +- [mkbuiltin_nNt] for a [t]otal function of [N] numbers, or +- [mkbuiltin_vNp] for a [p]artial function of [N] numbers. + *) + +Program Definition mkbuiltin_n1t + (targ1: typ) (tres: typ) + (f: valty targ1 -> valty tres) := + mkbuiltin_v1t tres + (fun v1 => proj_num targ1 Vundef v1 (fun x => inj_num tres (f x))) + _ _. +Next Obligation. + auto using proj_num_wt, inj_num_wt. +Qed. +Next Obligation. + auto using proj_num_inject, inj_num_inject. +Qed. + +Program Definition mkbuiltin_n2t + (targ1 targ2: typ) (tres: typ) + (f: valty targ1 -> valty targ2 -> valty tres) := + mkbuiltin_v2t tres + (fun v1 v2 => + proj_num targ1 Vundef v1 (fun x1 => + proj_num targ2 Vundef v2 (fun x2 => inj_num tres (f x1 x2)))) + _ _. +Next Obligation. + auto using proj_num_wt, inj_num_wt. +Qed. +Next Obligation. + auto using proj_num_inject, inj_num_inject. +Qed. + +Program Definition mkbuiltin_n3t + (targ1 targ2 targ3: typ) (tres: typ) + (f: valty targ1 -> valty targ2 -> valty targ3 -> valty tres) := + mkbuiltin_v3t tres + (fun v1 v2 v3 => + proj_num targ1 Vundef v1 (fun x1 => + proj_num targ2 Vundef v2 (fun x2 => + proj_num targ3 Vundef v3 (fun x3 => inj_num tres (f x1 x2 x3))))) + _ _. +Next Obligation. + auto using proj_num_wt, inj_num_wt. +Qed. +Next Obligation. + auto using proj_num_inject, inj_num_inject. +Qed. + +Program Definition mkbuiltin_n1p + (targ1: typ) (tres: typ) + (f: valty targ1 -> option (valty tres)) := + mkbuiltin_v1p tres + (fun v1 => proj_num targ1 None v1 (fun x => option_map (inj_num tres) (f x))) + _ _. +Next Obligation. + auto using proj_num_opt_wt, inj_num_opt_wt. +Qed. +Next Obligation. + apply proj_num_opt_inject; auto. intros; red; auto. intros; apply inj_num_opt_inject. +Qed. + +Program Definition mkbuiltin_n2p + (targ1 targ2: typ) (tres: typ) + (f: valty targ1 -> valty targ2 -> option (valty tres)) := + mkbuiltin_v2p tres + (fun v1 v2 => + proj_num targ1 None v1 (fun x1 => + proj_num targ2 None v2 (fun x2 => option_map (inj_num tres) (f x1 x2)))) + _ _. +Next Obligation. + auto using proj_num_opt_wt, inj_num_opt_wt. +Qed. +Next Obligation. + apply proj_num_opt_inject; auto. intros; red; auto. intros. + apply proj_num_opt_inject; auto. intros; red; auto. intros. + apply inj_num_opt_inject. +Qed. + +(** Looking up builtins by name and signature *) + +Section LOOKUP. + +Context {A: Type} (sig_of: A -> signature). + +Fixpoint lookup_builtin (name: string) (sg: signature) (l: list (string * A)) : option A := + match l with + | nil => None + | (n, b) :: l => + if string_dec name n && signature_eq sg (sig_of b) + then Some b + else lookup_builtin name sg l + end. + +Lemma lookup_builtin_sig: forall name sg b l, + lookup_builtin name sg l = Some b -> sig_of b = sg. +Proof. + induction l as [ | [n b'] l ]; simpl; intros. +- discriminate. +- destruct (string_dec name n && signature_eq sg (sig_of b')) eqn:E. ++ InvBooleans. congruence. ++ auto. +Qed. + +End LOOKUP. + +(** The standard, platform-independent built-ins *) + +Inductive standard_builtin : Type := + | BI_select (t: typ) + | BI_fabs + | BI_fsqrt + | BI_negl + | BI_addl + | BI_subl + | BI_mull + | BI_i16_bswap + | BI_i32_bswap + | BI_i64_bswap + | BI_i64_umulh + | BI_i64_smulh + | BI_i64_sdiv + | BI_i64_udiv + | BI_i64_smod + | BI_i64_umod + | BI_i64_shl + | BI_i64_shr + | BI_i64_sar + | BI_i64_dtos + | BI_i64_dtou + | BI_i64_stod + | BI_i64_utod + | BI_i64_stof + | BI_i64_utof. + +Local Open Scope string_scope. + +Definition standard_builtin_table : list (string * standard_builtin) := + ("__builtin_sel", BI_select Tint) + :: ("__builtin_sel", BI_select Tlong) + :: ("__builtin_sel", BI_select Tfloat) + :: ("__builtin_sel", BI_select Tsingle) + :: ("__builtin_fabs", BI_fabs) + :: ("__builtin_fsqrt", BI_fsqrt) + :: ("__builtin_negl", BI_negl) + :: ("__builtin_addl", BI_addl) + :: ("__builtin_subl", BI_subl) + :: ("__builtin_mull", BI_mull) + :: ("__builtin_bswap16", BI_i16_bswap) + :: ("__builtin_bswap", BI_i32_bswap) + :: ("__builtin_bswap32", BI_i32_bswap) + :: ("__builtin_bswap64", BI_i64_bswap) + :: ("__compcert_i64_umulh", BI_i64_umulh) + :: ("__compcert_i64_smulh", BI_i64_smulh) + :: ("__compcert_i64_sdiv", BI_i64_sdiv) + :: ("__compcert_i64_udiv", BI_i64_udiv) + :: ("__compcert_i64_smod", BI_i64_smod) + :: ("__compcert_i64_umod", BI_i64_umod) + :: ("__compcert_i64_shl", BI_i64_shl) + :: ("__compcert_i64_shr", BI_i64_shr) + :: ("__compcert_i64_sar", BI_i64_sar) + :: ("__compcert_i64_dtos", BI_i64_dtos) + :: ("__compcert_i64_dtou", BI_i64_dtou) + :: ("__compcert_i64_stod", BI_i64_stod) + :: ("__compcert_i64_utod", BI_i64_utod) + :: ("__compcert_i64_stof", BI_i64_stof) + :: ("__compcert_i64_utof", BI_i64_utof) + :: nil. + +Definition standard_builtin_sig (b: standard_builtin) : signature := + match b with + | BI_select t => + mksignature (Tint :: t :: t :: nil) t cc_default + | BI_fabs | BI_fsqrt => + mksignature (Tfloat :: nil) Tfloat cc_default + | BI_negl => + mksignature (Tlong :: nil) Tlong cc_default + | BI_addl | BI_subl | BI_i64_umulh| BI_i64_smulh + | BI_i64_sdiv | BI_i64_udiv | BI_i64_smod | BI_i64_umod => + mksignature (Tlong :: Tlong :: nil) Tlong cc_default + | BI_mull => + mksignature (Tint :: Tint :: nil) Tlong cc_default + | BI_i32_bswap => + mksignature (Tint :: nil) Tint cc_default + | BI_i64_bswap => + mksignature (Tlong :: nil) Tlong cc_default + | BI_i16_bswap => + mksignature (Tint :: nil) Tint cc_default + | BI_i64_shl | BI_i64_shr | BI_i64_sar => + mksignature (Tlong :: Tint :: nil) Tlong cc_default + | BI_i64_dtos | BI_i64_dtou => + mksignature (Tfloat :: nil) Tlong cc_default + | BI_i64_stod | BI_i64_utod => + mksignature (Tlong :: nil) Tfloat cc_default + | BI_i64_stof | BI_i64_utof => + mksignature (Tlong :: nil) Tsingle cc_default + end. + +Program Definition standard_builtin_sem (b: standard_builtin) : builtin_sem (sig_res (standard_builtin_sig b)) := + match b with + | BI_select t => + mkbuiltin t + (fun vargs => match vargs with + | Vint n :: v1 :: v2 :: nil => Some (Val.normalize (if Int.eq n Int.zero then v2 else v1) t) + | _ => None + end) _ _ + | BI_fabs => mkbuiltin_n1t Tfloat Tfloat Float.abs + | BI_fsqrt => mkbuiltin_n1t Tfloat Tfloat Float.sqrt + | BI_negl => mkbuiltin_n1t Tlong Tlong Int64.neg + | BI_addl => mkbuiltin_v2t Tlong Val.addl _ _ + | BI_subl => mkbuiltin_v2t Tlong Val.subl _ _ + | BI_mull => mkbuiltin_v2t Tlong Val.mull' _ _ + | BI_i16_bswap => + mkbuiltin_n1t Tint Tint + (fun n => Int.repr (decode_int (List.rev (encode_int 2%nat (Int.unsigned n))))) + | BI_i32_bswap => + mkbuiltin_n1t Tint Tint + (fun n => Int.repr (decode_int (List.rev (encode_int 4%nat (Int.unsigned n))))) + | BI_i64_bswap => + mkbuiltin_n1t Tlong Tlong + (fun n => Int64.repr (decode_int (List.rev (encode_int 8%nat (Int64.unsigned n))))) + | BI_i64_umulh => mkbuiltin_n2t Tlong Tlong Tlong Int64.mulhu + | BI_i64_smulh => mkbuiltin_n2t Tlong Tlong Tlong Int64.mulhs + | BI_i64_sdiv => mkbuiltin_v2p Tlong Val.divls _ _ + | BI_i64_udiv => mkbuiltin_v2p Tlong Val.divlu _ _ + | BI_i64_smod => mkbuiltin_v2p Tlong Val.modls _ _ + | BI_i64_umod => mkbuiltin_v2p Tlong Val.modlu _ _ + | BI_i64_shl => mkbuiltin_v2t Tlong Val.shll _ _ + | BI_i64_shr => mkbuiltin_v2t Tlong Val.shrlu _ _ + | BI_i64_sar => mkbuiltin_v2t Tlong Val.shrl _ _ + | BI_i64_dtos => mkbuiltin_n1p Tfloat Tlong Float.to_long + | BI_i64_dtou => mkbuiltin_n1p Tfloat Tlong Float.to_longu + | BI_i64_stod => mkbuiltin_n1t Tlong Tfloat Float.of_long + | BI_i64_utod => mkbuiltin_n1t Tlong Tfloat Float.of_longu + | BI_i64_stof => mkbuiltin_n1t Tlong Tsingle Float32.of_long + | BI_i64_utof => mkbuiltin_n1t Tlong Tsingle Float32.of_longu + end. +Next Obligation. + red. destruct vl; auto. destruct v; auto. + destruct vl; auto. destruct vl; auto. destruct vl; auto. + apply Val.normalize_type. +Qed. +Next Obligation. + red. inv H; auto. inv H0; auto. inv H1; auto. inv H0; auto. inv H2; auto. + apply Val.normalize_inject. destruct (Int.eq i Int.zero); auto. +Qed. +Next Obligation. + unfold Val.addl, Val.has_type; destruct v1; auto; destruct v2; auto; destruct Archi.ptr64; auto. +Qed. +Next Obligation. + apply Val.addl_inject; auto. +Qed. +Next Obligation. + unfold Val.subl, Val.has_type, negb; destruct v1; auto; destruct v2; auto; + destruct Archi.ptr64; auto; destruct (eq_block b0 b1); auto. +Qed. +Next Obligation. + apply Val.subl_inject; auto. +Qed. +Next Obligation. + unfold Val.mull', Val.has_type; destruct v1; simpl; auto; destruct v2; auto. +Qed. +Next Obligation. + inv H; simpl; auto. inv H0; auto. +Qed. +Next Obligation. + red. destruct v1; simpl; auto. destruct v2; auto. destruct orb; exact I. +Qed. +Next Obligation. + red. inv H; simpl; auto. inv H0; auto. destruct orb; auto. +Qed. +Next Obligation. + red. destruct v1; simpl; auto. destruct v2; auto. destruct Int64.eq; exact I. +Qed. +Next Obligation. + red. inv H; simpl; auto. inv H0; auto. destruct Int64.eq; auto. +Qed. +Next Obligation. + red. destruct v1; simpl; auto. destruct v2; auto. destruct orb; exact I. +Qed. +Next Obligation. + red. inv H; simpl; auto. inv H0; auto. destruct orb; auto. +Qed. +Next Obligation. + red. destruct v1; simpl; auto. destruct v2; auto. destruct Int64.eq; exact I. +Qed. +Next Obligation. + red. inv H; simpl; auto. inv H0; auto. destruct Int64.eq; auto. +Qed. +Next Obligation. + red. destruct v1; simpl; auto. destruct v2; auto. destruct Int.ltu; auto. +Qed. +Next Obligation. + inv H; simpl; auto. inv H0; auto. destruct Int.ltu; auto. +Qed. +Next Obligation. + red. destruct v1; simpl; auto. destruct v2; auto. destruct Int.ltu; auto. +Qed. +Next Obligation. + inv H; simpl; auto. inv H0; auto. destruct Int.ltu; auto. +Qed. +Next Obligation. + red. destruct v1; simpl; auto. destruct v2; auto. destruct Int.ltu; auto. +Qed. +Next Obligation. + inv H; simpl; auto. inv H0; auto. destruct Int.ltu; auto. +Qed. + diff --git a/common/Errors.v b/common/Errors.v index 28933313..6807735a 100644 --- a/common/Errors.v +++ b/common/Errors.v @@ -164,7 +164,7 @@ Ltac monadInv1 H := | (match ?X with left _ => _ | right _ => assertion_failed end = OK _) => destruct X; [try (monadInv1 H) | discriminate] | (match (negb ?X) with true => _ | false => assertion_failed end = OK _) => - destruct X as [] eqn:?; [discriminate | try (monadInv1 H)] + destruct X as [] eqn:?; simpl negb in H; [discriminate | try (monadInv1 H)] | (match ?X with true => _ | false => assertion_failed end = OK _) => destruct X as [] eqn:?; [try (monadInv1 H) | discriminate] | (mmap ?F ?L = OK ?M) => diff --git a/common/Events.v b/common/Events.v index b2335b96..28bb992a 100644 --- a/common/Events.v +++ b/common/Events.v @@ -24,6 +24,7 @@ Require Import Floats. Require Import Values. Require Import Memory. Require Import Globalenvs. +Require Import Builtins. (** * Events and traces *) @@ -622,7 +623,7 @@ Record extcall_properties (sem: extcall_sem) (sg: signature) : Prop := ec_well_typed: forall ge vargs m1 t vres m2, sem ge vargs m1 t vres m2 -> - Val.has_type vres (proj_sig_res sg); + Val.has_rettype vres sg.(sig_res); (** The semantics is invariant under change of global environment that preserves symbols. *) ec_symbols_preserved: @@ -648,9 +649,12 @@ Record extcall_properties (sem: extcall_sem) (sg: signature) : Prop := (** External call cannot modify memory unless they have [Max, Writable] permissions. *) ec_readonly: - forall ge vargs m1 t vres m2, + forall ge vargs m1 t vres m2 b ofs n bytes, sem ge vargs m1 t vres m2 -> - Mem.unchanged_on (loc_not_writable m1) m1 m2; + Mem.valid_block m1 b -> + Mem.loadbytes m2 b ofs n = Some bytes -> + (forall i, ofs <= i < ofs + n -> ~Mem.perm m1 b i Max Writable) -> + Mem.loadbytes m1 b ofs n = Some bytes; (** External calls must commute with memory extensions, in the following sense. *) @@ -770,12 +774,12 @@ Qed. Lemma volatile_load_ok: forall chunk, extcall_properties (volatile_load_sem chunk) - (mksignature (Tptr :: nil) (Some (type_of_chunk chunk)) cc_default). + (mksignature (Tptr :: nil) (rettype_of_chunk chunk) cc_default). Proof. intros; constructor; intros. (* well typed *) -- unfold proj_sig_res; simpl. inv H. inv H0. apply Val.load_result_type. - eapply Mem.load_type; eauto. +- inv H. inv H0. apply Val.load_result_rettype. + eapply Mem.load_rettype; eauto. (* symbols *) - inv H0. constructor. eapply volatile_load_preserved; eauto. (* valid blocks *) @@ -783,7 +787,7 @@ Proof. (* max perms *) - inv H; auto. (* readonly *) -- inv H. apply Mem.unchanged_on_refl. +- inv H; auto. (* mem extends *) - inv H. inv H1. inv H6. inv H4. exploit volatile_load_extends; eauto. intros [v' [A B]]. @@ -832,14 +836,27 @@ Proof. rewrite C; auto. Qed. +Lemma unchanged_on_readonly: + forall m1 m2 b ofs n bytes, + Mem.unchanged_on (loc_not_writable m1) m1 m2 -> + Mem.valid_block m1 b -> + Mem.loadbytes m2 b ofs n = Some bytes -> + (forall i, ofs <= i < ofs + n -> ~Mem.perm m1 b i Max Writable) -> + Mem.loadbytes m1 b ofs n = Some bytes. +Proof. + intros. + rewrite <- H1. symmetry. + apply Mem.loadbytes_unchanged_on_1 with (P := loc_not_writable m1); auto. +Qed. + Lemma volatile_store_readonly: forall ge chunk1 m1 b1 ofs1 v t m2, volatile_store ge chunk1 m1 b1 ofs1 v t m2 -> Mem.unchanged_on (loc_not_writable m1) m1 m2. Proof. intros. inv H. - apply Mem.unchanged_on_refl. - eapply Mem.store_unchanged_on; eauto. +- apply Mem.unchanged_on_refl. +- eapply Mem.store_unchanged_on; eauto. exploit Mem.store_valid_access_3; eauto. intros [P Q]. intros. unfold loc_not_writable. red; intros. elim H2. apply Mem.perm_cur_max. apply P. auto. @@ -921,7 +938,7 @@ Qed. Lemma volatile_store_ok: forall chunk, extcall_properties (volatile_store_sem chunk) - (mksignature (Tptr :: type_of_chunk chunk :: nil) None cc_default). + (mksignature (Tptr :: type_of_chunk chunk :: nil) Tvoid cc_default). Proof. intros; constructor; intros. (* well typed *) @@ -933,7 +950,7 @@ Proof. (* perms *) - inv H. inv H2. auto. eauto with mem. (* readonly *) -- inv H. eapply volatile_store_readonly; eauto. +- inv H. eapply unchanged_on_readonly; eauto. eapply volatile_store_readonly; eauto. (* mem extends*) - inv H. inv H1. inv H6. inv H7. inv H4. exploit volatile_store_extends; eauto. intros [m2' [A [B C]]]. @@ -966,7 +983,7 @@ Inductive extcall_malloc_sem (ge: Senv.t): Lemma extcall_malloc_ok: extcall_properties extcall_malloc_sem - (mksignature (Tptr :: nil) (Some Tptr) cc_default). + (mksignature (Tptr :: nil) Tptr cc_default). Proof. assert (UNCHANGED: forall (P: block -> Z -> Prop) m lo hi v m' b m'', @@ -983,7 +1000,7 @@ Proof. } constructor; intros. (* well typed *) -- inv H. unfold proj_sig_res, Tptr; simpl. destruct Archi.ptr64; auto. +- inv H. simpl. unfold Tptr; destruct Archi.ptr64; auto. (* symbols preserved *) - inv H0; econstructor; eauto. (* valid block *) @@ -993,7 +1010,7 @@ Proof. rewrite dec_eq_false. auto. apply Mem.valid_not_valid_diff with m1; eauto with mem. (* readonly *) -- inv H. eapply UNCHANGED; eauto. +- inv H. eapply unchanged_on_readonly; eauto. (* mem extends *) - inv H. inv H1. inv H7. assert (SZ: v2 = Vptrofs sz). @@ -1044,38 +1061,43 @@ Qed. Inductive extcall_free_sem (ge: Senv.t): list val -> mem -> trace -> val -> mem -> Prop := - | extcall_free_sem_intro: forall b lo sz m m', + | extcall_free_sem_ptr: forall b lo sz m m', Mem.load Mptr m b (Ptrofs.unsigned lo - size_chunk Mptr) = Some (Vptrofs sz) -> Ptrofs.unsigned sz > 0 -> Mem.free m b (Ptrofs.unsigned lo - size_chunk Mptr) (Ptrofs.unsigned lo + Ptrofs.unsigned sz) = Some m' -> - extcall_free_sem ge (Vptr b lo :: nil) m E0 Vundef m'. + extcall_free_sem ge (Vptr b lo :: nil) m E0 Vundef m' + | extcall_free_sem_null: forall m, + extcall_free_sem ge (Vnullptr :: nil) m E0 Vundef m. Lemma extcall_free_ok: extcall_properties extcall_free_sem - (mksignature (Tptr :: nil) None cc_default). + (mksignature (Tptr :: nil) Tvoid cc_default). Proof. constructor; intros. (* well typed *) -- inv H. unfold proj_sig_res. simpl. auto. +- inv H; simpl; auto. (* symbols preserved *) - inv H0; econstructor; eauto. (* valid block *) -- inv H. eauto with mem. +- inv H; eauto with mem. (* perms *) -- inv H. eapply Mem.perm_free_3; eauto. +- inv H; eauto using Mem.perm_free_3. (* readonly *) -- inv H. eapply Mem.free_unchanged_on; eauto. - intros. red; intros. elim H3. +- eapply unchanged_on_readonly; eauto. inv H. ++ eapply Mem.free_unchanged_on; eauto. + intros. red; intros. elim H6. apply Mem.perm_cur_max. apply Mem.perm_implies with Freeable; auto with mem. eapply Mem.free_range_perm; eauto. ++ apply Mem.unchanged_on_refl. (* mem extends *) -- inv H. inv H1. inv H8. inv H6. +- inv H. ++ inv H1. inv H8. inv H6. exploit Mem.load_extends; eauto. intros [v' [A B]]. assert (v' = Vptrofs sz). { unfold Vptrofs in *; destruct Archi.ptr64; inv B; auto. } subst v'. exploit Mem.free_parallel_extends; eauto. intros [m2' [C D]]. - exists Vundef; exists m2'; intuition. + exists Vundef; exists m2'; intuition auto. econstructor; eauto. eapply Mem.free_unchanged_on; eauto. unfold loc_out_of_bounds; intros. @@ -1083,8 +1105,14 @@ Proof. { apply Mem.perm_cur_max. apply Mem.perm_implies with Freeable; auto with mem. eapply Mem.free_range_perm. eexact H4. eauto. } tauto. ++ inv H1. inv H5. replace v2 with Vnullptr. + exists Vundef; exists m1'; intuition auto. + constructor. + apply Mem.unchanged_on_refl. + unfold Vnullptr in *; destruct Archi.ptr64; inv H3; auto. (* mem inject *) -- inv H0. inv H2. inv H7. inv H9. +- inv H0. ++ inv H2. inv H7. inv H9. exploit Mem.load_inject; eauto. intros [v' [A B]]. assert (v' = Vptrofs sz). { unfold Vptrofs in *; destruct Archi.ptr64; inv B; auto. } @@ -1098,7 +1126,7 @@ Proof. intro EQ. exploit Mem.free_parallel_inject; eauto. intros (m2' & C & D). exists f, Vundef, m2'; split. - apply extcall_free_sem_intro with (sz := sz) (m' := m2'). + apply extcall_free_sem_ptr with (sz := sz) (m' := m2'). rewrite EQ. rewrite <- A. f_equal. omega. auto. auto. rewrite ! EQ. rewrite <- C. f_equal; omega. @@ -1111,14 +1139,19 @@ Proof. apply P. omega. split. auto. red; intros. congruence. ++ inv H2. inv H6. replace v' with Vnullptr. + exists f, Vundef, m1'; intuition auto using Mem.unchanged_on_refl. + constructor. + red; intros; congruence. + unfold Vnullptr in *; destruct Archi.ptr64; inv H4; auto. (* trace length *) - inv H; simpl; omega. (* receptive *) -- assert (t1 = t2). inv H; inv H0; auto. subst t2. +- assert (t1 = t2) by (inv H; inv H0; auto). subst t2. exists vres1; exists m1; auto. (* determ *) -- inv H; inv H0. - assert (EQ1: Vptrofs sz0 = Vptrofs sz) by congruence. +- inv H; inv H0; try (unfold Vnullptr in *; destruct Archi.ptr64; discriminate). ++ assert (EQ1: Vptrofs sz0 = Vptrofs sz) by congruence. assert (EQ2: sz0 = sz). { unfold Vptrofs in EQ1; destruct Archi.ptr64 eqn:SF. rewrite <- (Ptrofs.of_int64_to_int64 SF sz0), <- (Ptrofs.of_int64_to_int64 SF sz). congruence. @@ -1126,6 +1159,7 @@ Proof. } subst sz0. split. constructor. intuition congruence. ++ split. constructor. intuition auto. Qed. (** ** Semantics of [memcpy] operations. *) @@ -1146,11 +1180,11 @@ Inductive extcall_memcpy_sem (sz al: Z) (ge: Senv.t): Lemma extcall_memcpy_ok: forall sz al, extcall_properties (extcall_memcpy_sem sz al) - (mksignature (Tptr :: Tptr :: nil) None cc_default). + (mksignature (Tptr :: Tptr :: nil) Tvoid cc_default). Proof. intros. constructor. - (* return type *) - intros. inv H. constructor. + intros. inv H. exact I. - (* change of globalenv *) intros. inv H0. econstructor; eauto. - (* valid blocks *) @@ -1158,8 +1192,9 @@ Proof. - (* perms *) intros. inv H. eapply Mem.perm_storebytes_2; eauto. - (* readonly *) - intros. inv H. eapply Mem.storebytes_unchanged_on; eauto. - intros; red; intros. elim H8. + intros. inv H. eapply unchanged_on_readonly; eauto. + eapply Mem.storebytes_unchanged_on; eauto. + intros; red; intros. elim H11. apply Mem.perm_cur_max. eapply Mem.storebytes_range_perm; eauto. - (* extensions *) intros. inv H. @@ -1208,7 +1243,7 @@ Proof. assert (RPDST: Mem.range_perm m1 bdst (Ptrofs.unsigned odst) (Ptrofs.unsigned odst + sz) Cur Nonempty). replace sz with (Z.of_nat (length bytes)). eapply Mem.range_perm_implies. eapply Mem.storebytes_range_perm; eauto. auto with mem. - rewrite LEN. apply nat_of_Z_eq. omega. + rewrite LEN. apply Z2Nat.id. omega. assert (PSRC: Mem.perm m1 bsrc (Ptrofs.unsigned osrc) Cur Nonempty). apply RPSRC. omega. assert (PDST: Mem.perm m1 bdst (Ptrofs.unsigned odst) Cur Nonempty). @@ -1257,7 +1292,7 @@ Inductive extcall_annot_sem (text: string) (targs: list typ) (ge: Senv.t): Lemma extcall_annot_ok: forall text targs, extcall_properties (extcall_annot_sem text targs) - (mksignature targs None cc_default). + (mksignature targs Tvoid cc_default). Proof. intros; constructor; intros. (* well typed *) @@ -1270,7 +1305,7 @@ Proof. (* perms *) - inv H; auto. (* readonly *) -- inv H. apply Mem.unchanged_on_refl. +- inv H; auto. (* mem extends *) - inv H. exists Vundef; exists m1'; intuition. @@ -1302,11 +1337,11 @@ Inductive extcall_annot_val_sem (text: string) (targ: typ) (ge: Senv.t): Lemma extcall_annot_val_ok: forall text targ, extcall_properties (extcall_annot_val_sem text targ) - (mksignature (targ :: nil) (Some targ) cc_default). + (mksignature (targ :: nil) targ cc_default). Proof. intros; constructor; intros. (* well typed *) -- inv H. unfold proj_sig_res; simpl. eapply eventval_match_type; eauto. +- inv H. eapply eventval_match_type; eauto. (* symbols *) - destruct H as (A & B & C). inv H0. econstructor; eauto. eapply eventval_match_preserved; eauto. @@ -1315,7 +1350,7 @@ Proof. (* perms *) - inv H; auto. (* readonly *) -- inv H. apply Mem.unchanged_on_refl. +- inv H; auto. (* mem extends *) - inv H. inv H1. inv H6. exists v2; exists m1'; intuition. @@ -1346,7 +1381,7 @@ Inductive extcall_debug_sem (ge: Senv.t): Lemma extcall_debug_ok: forall targs, extcall_properties extcall_debug_sem - (mksignature targs None cc_default). + (mksignature targs Tvoid cc_default). Proof. intros; constructor; intros. (* well typed *) @@ -1358,7 +1393,7 @@ Proof. (* perms *) - inv H; auto. (* readonly *) -- inv H. apply Mem.unchanged_on_refl. +- inv H; auto. (* mem extends *) - inv H. exists Vundef; exists m1'; intuition. @@ -1377,12 +1412,68 @@ Proof. split. constructor. auto. Qed. +(** ** Semantics of known built-in functions. *) + +(** Some built-in functions and runtime support functions have known semantics + as defined in the [Builtin] modules. + These built-in functions have no observable effects and do not access memory. *) + +Inductive known_builtin_sem (bf: builtin_function) (ge: Senv.t): + list val -> mem -> trace -> val -> mem -> Prop := + | known_builtin_sem_intro: forall vargs vres m, + builtin_function_sem bf vargs = Some vres -> + known_builtin_sem bf ge vargs m E0 vres m. + +Lemma known_builtin_ok: forall bf, + extcall_properties (known_builtin_sem bf) (builtin_function_sig bf). +Proof. + intros. set (bsem := builtin_function_sem bf). constructor; intros. +(* well typed *) +- inv H. + specialize (bs_well_typed _ bsem vargs). + unfold val_opt_has_rettype, bsem; rewrite H0. + auto. +(* symbols *) +- inv H0. econstructor; eauto. +(* valid blocks *) +- inv H; auto. +(* perms *) +- inv H; auto. +(* readonly *) +- inv H; auto. +(* mem extends *) +- inv H. fold bsem in H2. apply val_inject_list_lessdef in H1. + specialize (bs_inject _ bsem _ _ _ H1). + unfold val_opt_inject; rewrite H2; intros. + destruct (bsem vargs') as [vres'|] eqn:?; try contradiction. + exists vres', m1'; intuition auto using Mem.extends_refl, Mem.unchanged_on_refl. + constructor; auto. + apply val_inject_lessdef; auto. +(* mem injects *) +- inv H0. fold bsem in H3. + specialize (bs_inject _ bsem _ _ _ H2). + unfold val_opt_inject; rewrite H3; intros. + destruct (bsem vargs') as [vres'|] eqn:?; try contradiction. + exists f, vres', m1'; intuition auto using Mem.extends_refl, Mem.unchanged_on_refl. + constructor; auto. + red; intros; congruence. +(* trace length *) +- inv H; simpl; omega. +(* receptive *) +- inv H; inv H0. exists vres1, m1; constructor; auto. +(* determ *) +- inv H; inv H0. + split. constructor. intuition congruence. +Qed. + (** ** Semantics of external functions. *) -(** For functions defined outside the program ([EF_external], - [EF_builtin] and [EF_runtime]), we do not define their - semantics, but only assume that it satisfies - [extcall_properties]. *) +(** For functions defined outside the program ([EF_external]), + we do not define their semantics, but only assume that it satisfies + [extcall_properties]. + We do the same for built-in functions and runtime support functions that + are not described in [Builtins]. +*) Parameter external_functions_sem: String.string -> signature -> extcall_sem. @@ -1398,6 +1489,22 @@ Axiom inline_assembly_properties: (** ** Combined semantics of external calls *) +Definition builtin_or_external_sem name sg := + match lookup_builtin_function name sg with + | Some bf => known_builtin_sem bf + | None => external_functions_sem name sg + end. + +Lemma builtin_or_external_sem_ok: forall name sg, + extcall_properties (builtin_or_external_sem name sg) sg. +Proof. + unfold builtin_or_external_sem; intros. + destruct (lookup_builtin_function name sg) as [bf|] eqn:L. +- exploit lookup_builtin_function_sig; eauto. intros EQ; subst sg. + apply known_builtin_ok. +- apply external_functions_properties. +Qed. + (** Combining the semantics given above for the various kinds of external calls, we define the predicate [external_call] that relates: - the external function being invoked @@ -1412,8 +1519,8 @@ This predicate is used in the semantics of all CompCert languages. *) Definition external_call (ef: external_function): extcall_sem := match ef with | EF_external name sg => external_functions_sem name sg - | EF_builtin name sg => external_functions_sem name sg - | EF_runtime name sg => external_functions_sem name sg + | EF_builtin name sg => builtin_or_external_sem name sg + | EF_runtime name sg => builtin_or_external_sem name sg | EF_vload chunk => volatile_load_sem chunk | EF_vstore chunk => volatile_store_sem chunk | EF_malloc => extcall_malloc_sem @@ -1431,8 +1538,8 @@ Theorem external_call_spec: Proof. intros. unfold external_call, ef_sig; destruct ef. apply external_functions_properties. - apply external_functions_properties. - apply external_functions_properties. + apply builtin_or_external_sem_ok. + apply builtin_or_external_sem_ok. apply volatile_load_ok. apply volatile_store_ok. apply extcall_malloc_ok. @@ -1444,7 +1551,7 @@ Proof. apply extcall_debug_ok. Qed. -Definition external_call_well_typed ef := ec_well_typed (external_call_spec ef). +Definition external_call_well_typed_gen ef := ec_well_typed (external_call_spec ef). Definition external_call_symbols_preserved ef := ec_symbols_preserved (external_call_spec ef). Definition external_call_valid_block ef := ec_valid_block (external_call_spec ef). Definition external_call_max_perm ef := ec_max_perm (external_call_spec ef). @@ -1455,6 +1562,16 @@ Definition external_call_trace_length ef := ec_trace_length (external_call_spec Definition external_call_receptive ef := ec_receptive (external_call_spec ef). Definition external_call_determ ef := ec_determ (external_call_spec ef). +(** Corollary of [external_call_well_typed_gen]. *) + +Lemma external_call_well_typed: + forall ef ge vargs m1 t vres m2, + external_call ef ge vargs m1 t vres m2 -> + Val.has_type vres (proj_sig_res (ef_sig ef)). +Proof. + intros. apply Val.has_proj_rettype. eapply external_call_well_typed_gen; eauto. +Qed. + (** Corollary of [external_call_valid_block]. *) Lemma external_call_nextblock: diff --git a/common/Globalenvs.v b/common/Globalenvs.v index d37fbd46..462a4ec1 100644 --- a/common/Globalenvs.v +++ b/common/Globalenvs.v @@ -22,10 +22,8 @@ Global environments, along with the initial memory state at the beginning of program execution, are built from the program of interest, as follows: - A distinct memory address is assigned to each function of the program. - These function addresses use negative numbers to distinguish them from - addresses of memory blocks. The associations of function name to function - address and function address to function description are recorded in - the global environment. + The associations of function name to function address and function address + to function description are recorded in the global environment. - For each global variable, a memory block is allocated and associated to the name of the variable. diff --git a/common/Memdata.v b/common/Memdata.v index a9ed48b4..f3016efe 100644 --- a/common/Memdata.v +++ b/common/Memdata.v @@ -17,6 +17,7 @@ (** In-memory representation of values. *) Require Import Coqlib. +Require Import Zbits. Require Archi. Require Import AST. Require Import Integers. @@ -50,7 +51,7 @@ Proof. Qed. Definition size_chunk_nat (chunk: memory_chunk) : nat := - nat_of_Z(size_chunk chunk). + Z.to_nat(size_chunk chunk). Lemma size_chunk_conv: forall chunk, size_chunk chunk = Z.of_nat (size_chunk_nat chunk). @@ -258,21 +259,21 @@ Lemma decode_encode_int_4: forall x, Int.repr (decode_int (encode_int 4 (Int.unsigned x))) = x. Proof. intros. rewrite decode_encode_int. transitivity (Int.repr (Int.unsigned x)). - decEq. apply Zmod_small. apply Int.unsigned_range. apply Int.repr_unsigned. + decEq. apply Z.mod_small. apply Int.unsigned_range. apply Int.repr_unsigned. Qed. Lemma decode_encode_int_8: forall x, Int64.repr (decode_int (encode_int 8 (Int64.unsigned x))) = x. Proof. intros. rewrite decode_encode_int. transitivity (Int64.repr (Int64.unsigned x)). - decEq. apply Zmod_small. apply Int64.unsigned_range. apply Int64.repr_unsigned. + decEq. apply Z.mod_small. apply Int64.unsigned_range. apply Int64.repr_unsigned. Qed. (** A length-[n] encoding depends only on the low [8*n] bits of the integer. *) Lemma bytes_of_int_mod: forall n x y, - Int.eqmod (two_p (Z.of_nat n * 8)) x y -> + eqmod (two_p (Z.of_nat n * 8)) x y -> bytes_of_int n x = bytes_of_int n y. Proof. induction n. @@ -284,7 +285,7 @@ Proof. intro EQM. simpl; decEq. apply Byte.eqm_samerepr. red. - eapply Int.eqmod_divides; eauto. apply Z.divide_factor_r. + eapply eqmod_divides; eauto. apply Z.divide_factor_r. apply IHn. destruct EQM as [k EQ]. exists k. rewrite EQ. rewrite <- Z_div_plus_full_l. decEq. change (two_p 8) with 256. ring. omega. @@ -292,7 +293,7 @@ Qed. Lemma encode_int_8_mod: forall x y, - Int.eqmod (two_p 8) x y -> + eqmod (two_p 8) x y -> encode_int 1%nat x = encode_int 1%nat y. Proof. intros. unfold encode_int. decEq. apply bytes_of_int_mod. auto. @@ -300,7 +301,7 @@ Qed. Lemma encode_int_16_mod: forall x y, - Int.eqmod (two_p 16) x y -> + eqmod (two_p 16) x y -> encode_int 2%nat x = encode_int 2%nat y. Proof. intros. unfold encode_int. decEq. apply bytes_of_int_mod. auto. @@ -546,18 +547,26 @@ Proof. destruct v1; auto. Qed. -Lemma decode_val_type: +Lemma decode_val_rettype: forall chunk cl, - Val.has_type (decode_val chunk cl) (type_of_chunk chunk). + Val.has_rettype (decode_val chunk cl) (rettype_of_chunk chunk). Proof. intros. unfold decode_val. destruct (proj_bytes cl). - destruct chunk; simpl; auto. -Local Opaque Val.load_result. +- destruct chunk; simpl; rewrite ? Int.sign_ext_idem, ? Int.zero_ext_idem by omega; auto. +- Local Opaque Val.load_result. destruct chunk; simpl; (exact I || apply Val.load_result_type || destruct Archi.ptr64; (exact I || apply Val.load_result_type)). Qed. +Lemma decode_val_type: + forall chunk cl, + Val.has_type (decode_val chunk cl) (type_of_chunk chunk). +Proof. + intros. rewrite <- proj_rettype_of_chunk. + apply Val.has_proj_rettype. apply decode_val_rettype. +Qed. + Lemma encode_val_int8_signed_unsigned: forall v, encode_val Mint8signed v = encode_val Mint8unsigned v. Proof. @@ -606,11 +615,9 @@ Lemma decode_val_cast: | _ => True end. Proof. - unfold decode_val; intros; destruct chunk; auto; destruct (proj_bytes l); auto. - unfold Val.sign_ext. rewrite Int.sign_ext_idem; auto. omega. - unfold Val.zero_ext. rewrite Int.zero_ext_idem; auto. omega. - unfold Val.sign_ext. rewrite Int.sign_ext_idem; auto. omega. - unfold Val.zero_ext. rewrite Int.zero_ext_idem; auto. omega. + intros. + assert (A: Val.has_rettype v (rettype_of_chunk chunk)) by apply decode_val_rettype. + destruct chunk; auto; simpl in A; destruct v; try contradiction; simpl; congruence. Qed. (** Pointers cannot be forged. *) diff --git a/common/Memory.v b/common/Memory.v index 2cf1c3ab..9f9934c2 100644 --- a/common/Memory.v +++ b/common/Memory.v @@ -284,7 +284,7 @@ Lemma valid_access_dec: Proof. intros. destruct (range_perm_dec m b ofs (ofs + size_chunk chunk) Cur p). - destruct (Zdivide_dec (align_chunk chunk) ofs (align_chunk_pos chunk)). + destruct (Zdivide_dec (align_chunk chunk) ofs). left; constructor; auto. right; red; intro V; inv V; contradiction. right; red; intro V; inv V; contradiction. @@ -460,7 +460,7 @@ Definition loadv (chunk: memory_chunk) (m: mem) (addr: val) : option val := Definition loadbytes (m: mem) (b: block) (ofs n: Z): option (list memval) := if range_perm_dec m b ofs (ofs + n) Cur Readable - then Some (getN (nat_of_Z n) ofs (m.(mem_contents)#b)) + then Some (getN (Z.to_nat n) ofs (m.(mem_contents)#b)) else None. (** Memory stores. *) @@ -682,6 +682,15 @@ Proof. apply decode_val_type. Qed. +Theorem load_rettype: + forall m chunk b ofs v, + load chunk m b ofs = Some v -> + Val.has_rettype v (rettype_of_chunk chunk). +Proof. + intros. exploit load_result; eauto; intros. rewrite H0. + apply decode_val_rettype. +Qed. + Theorem load_cast: forall m chunk b ofs v, load chunk m b ofs = Some v -> @@ -780,7 +789,7 @@ Qed. Theorem loadbytes_length: forall m b ofs n bytes, loadbytes m b ofs n = Some bytes -> - length bytes = nat_of_Z n. + length bytes = Z.to_nat n. Proof. unfold loadbytes; intros. destruct (range_perm_dec m b ofs (ofs + n) Cur Readable); try congruence. @@ -791,7 +800,7 @@ Theorem loadbytes_empty: forall m b ofs n, n <= 0 -> loadbytes m b ofs n = Some nil. Proof. - intros. unfold loadbytes. rewrite pred_dec_true. rewrite nat_of_Z_neg; auto. + intros. unfold loadbytes. rewrite pred_dec_true. rewrite Z_to_nat_neg; auto. red; intros. omegaContradiction. Qed. @@ -816,8 +825,8 @@ Proof. unfold loadbytes; intros. destruct (range_perm_dec m b ofs (ofs + n1) Cur Readable); try congruence. destruct (range_perm_dec m b (ofs + n1) (ofs + n1 + n2) Cur Readable); try congruence. - rewrite pred_dec_true. rewrite nat_of_Z_plus; auto. - rewrite getN_concat. rewrite nat_of_Z_eq; auto. + rewrite pred_dec_true. rewrite Z2Nat.inj_add by omega. + rewrite getN_concat. rewrite Z2Nat.id by omega. congruence. red; intros. assert (ofs0 < ofs + n1 \/ ofs0 >= ofs + n1) by omega. @@ -836,8 +845,8 @@ Proof. unfold loadbytes; intros. destruct (range_perm_dec m b ofs (ofs + (n1 + n2)) Cur Readable); try congruence. - rewrite nat_of_Z_plus in H; auto. rewrite getN_concat in H. - rewrite nat_of_Z_eq in H; auto. + rewrite Z2Nat.inj_add in H by omega. rewrite getN_concat in H. + rewrite Z2Nat.id in H by omega. repeat rewrite pred_dec_true. econstructor; econstructor. split. reflexivity. split. reflexivity. congruence. @@ -887,11 +896,11 @@ Proof. intros (bytes1 & bytes2 & LB1 & LB2 & APP). change 4 with (size_chunk Mint32) in LB1. exploit loadbytes_load. eexact LB1. - simpl. apply Zdivides_trans with 8; auto. exists 2; auto. + simpl. apply Z.divide_trans with 8; auto. exists 2; auto. intros L1. change 4 with (size_chunk Mint32) in LB2. exploit loadbytes_load. eexact LB2. - simpl. apply Z.divide_add_r. apply Zdivides_trans with 8; auto. exists 2; auto. exists 1; auto. + simpl. apply Z.divide_add_r. apply Z.divide_trans with 8; auto. exists 2; auto. exists 1; auto. intros L2. exists (decode_val Mint32 (if Archi.big_endian then bytes1 else bytes2)); exists (decode_val Mint32 (if Archi.big_endian then bytes2 else bytes1)). @@ -1106,7 +1115,7 @@ Proof. assert (valid_access m2 chunk b ofs Readable) by eauto with mem. unfold loadbytes. rewrite pred_dec_true. rewrite store_mem_contents; simpl. rewrite PMap.gss. - replace (nat_of_Z (size_chunk chunk)) with (length (encode_val chunk v)). + replace (Z.to_nat (size_chunk chunk)) with (length (encode_val chunk v)). rewrite getN_setN_same. auto. rewrite encode_val_length. auto. apply H. @@ -1127,10 +1136,10 @@ Proof. rewrite PMap.gsspec. destruct (peq b' b). subst b'. destruct H. congruence. destruct (zle n 0) as [z | n0]. - rewrite (nat_of_Z_neg _ z). auto. + rewrite (Z_to_nat_neg _ z). auto. destruct H. omegaContradiction. apply getN_setN_outside. rewrite encode_val_length. rewrite <- size_chunk_conv. - rewrite nat_of_Z_eq. auto. omega. + rewrite Z2Nat.id. auto. omega. auto. red; intros. eauto with mem. rewrite pred_dec_false. auto. @@ -1523,7 +1532,7 @@ Proof. destruct (range_perm_dec m1 b ofs (ofs + Z.of_nat (length bytes)) Cur Writable); try discriminate. rewrite pred_dec_true. - decEq. inv STORE2; simpl. rewrite PMap.gss. rewrite nat_of_Z_of_nat. + decEq. inv STORE2; simpl. rewrite PMap.gss. rewrite Nat2Z.id. apply getN_setN_same. red; eauto with mem. Qed. @@ -1539,7 +1548,7 @@ Proof. rewrite pred_dec_true. rewrite storebytes_mem_contents. decEq. rewrite PMap.gsspec. destruct (peq b' b). subst b'. - apply getN_setN_disjoint. rewrite nat_of_Z_eq; auto. intuition congruence. + apply getN_setN_disjoint. rewrite Z2Nat.id by omega. intuition congruence. auto. red; auto with mem. apply pred_dec_false. @@ -1644,9 +1653,9 @@ Proof. rewrite encode_val_length in SB2. simpl in SB2. exists m1; split. apply storebytes_store. exact SB1. - simpl. apply Zdivides_trans with 8; auto. exists 2; auto. + simpl. apply Z.divide_trans with 8; auto. exists 2; auto. apply storebytes_store. exact SB2. - simpl. apply Z.divide_add_r. apply Zdivides_trans with 8; auto. exists 2; auto. exists 1; auto. + simpl. apply Z.divide_add_r. apply Z.divide_trans with 8; auto. exists 2; auto. exists 1; auto. Qed. Theorem storev_int64_split: @@ -1867,7 +1876,7 @@ Proof. unfold loadbytes; intros. destruct (range_perm_dec m2 b ofs (ofs + n) Cur Readable); inv H. revert H0. injection ALLOC; intros A B. rewrite <- A; rewrite <- B; simpl. rewrite PMap.gss. - generalize (nat_of_Z n) ofs. induction n0; simpl; intros. + generalize (Z.to_nat n) ofs. induction n0; simpl; intros. contradiction. rewrite ZMap.gi in H0. destruct H0; eauto. Qed. @@ -2342,13 +2351,13 @@ Lemma loadbytes_inj: Proof. intros. unfold loadbytes in *. destruct (range_perm_dec m1 b1 ofs (ofs + len) Cur Readable); inv H0. - exists (getN (nat_of_Z len) (ofs + delta) (m2.(mem_contents)#b2)). + exists (getN (Z.to_nat len) (ofs + delta) (m2.(mem_contents)#b2)). split. apply pred_dec_true. replace (ofs + delta + len) with ((ofs + len) + delta) by omega. eapply range_perm_inj; eauto with mem. apply getN_inj; auto. - destruct (zle 0 len). rewrite nat_of_Z_eq; auto. omega. - rewrite nat_of_Z_neg. simpl. red; intros; omegaContradiction. omega. + destruct (zle 0 len). rewrite Z2Nat.id by omega. auto. + rewrite Z_to_nat_neg by omega. simpl. red; intros; omegaContradiction. Qed. (** Preservation of stores. *) @@ -4340,7 +4349,7 @@ Proof. + unfold loadbytes. destruct H. destruct (range_perm_dec m b ofs (ofs + n) Cur Readable). rewrite pred_dec_true. f_equal. - apply getN_exten. intros. rewrite nat_of_Z_eq in H by omega. + apply getN_exten. intros. rewrite Z2Nat.id in H by omega. apply unchanged_on_contents0; auto. red; intros. apply unchanged_on_perm0; auto. rewrite pred_dec_false. auto. diff --git a/common/Memtype.v b/common/Memtype.v index 03dc1499..ca9c6f1f 100644 --- a/common/Memtype.v +++ b/common/Memtype.v @@ -300,6 +300,11 @@ Axiom load_type: load chunk m b ofs = Some v -> Val.has_type v (type_of_chunk chunk). +Axiom load_rettype: + forall m chunk b ofs v, + load chunk m b ofs = Some v -> + Val.has_rettype v (rettype_of_chunk chunk). + (** For a small integer or float type, the value returned by [load] is invariant under the corresponding cast. *) Axiom load_cast: @@ -358,7 +363,7 @@ Axiom load_loadbytes: Axiom loadbytes_length: forall m b ofs n bytes, loadbytes m b ofs n = Some bytes -> - length bytes = nat_of_Z n. + length bytes = Z.to_nat n. Axiom loadbytes_empty: forall m b ofs n, diff --git a/common/PrintAST.ml b/common/PrintAST.ml index e477957a..cf3a17d5 100644 --- a/common/PrintAST.ml +++ b/common/PrintAST.ml @@ -27,6 +27,14 @@ let name_of_type = function | Tany32 -> "any32" | Tany64 -> "any64" +let name_of_rettype = function + | Tret t -> name_of_type t + | Tvoid -> "void" + | Tint8signed -> "int8s" + | Tint8unsigned -> "int8u" + | Tint16signed -> "int16s" + | Tint16unsigned -> "int16u" + let name_of_chunk = function | Mint8signed -> "int8s" | Mint8unsigned -> "int8u" diff --git a/common/Sections.ml b/common/Sections.ml index 30be9e69..839128a5 100644 --- a/common/Sections.ml +++ b/common/Sections.ml @@ -160,9 +160,22 @@ let gcc_section name readonly exec = sec_writable = not readonly; sec_executable = exec; sec_access = Access_default } +(* Check and extract whether a section was given as attribute *) + +let get_attr_section loc attr = + match Cutil.find_custom_attributes ["section"; "__section__"] attr with + | [] -> None + | [[C.AString name]] -> Some name + | [[_]] -> + Diagnostics.error loc "'section' attribute requires a string"; + None + | _ -> + Diagnostics.error loc "ambiguous 'section' attribute"; + None + (* Determine section for a variable definition *) -let for_variable env id ty init = +let for_variable env loc id ty init = let attr = Cutil.attributes_of_type env ty in let readonly = List.mem C.AConst attr && not(List.mem C.AVolatile attr) in let si = @@ -170,11 +183,11 @@ let for_variable env id ty init = (* 1- Section explicitly associated with #use_section *) Hashtbl.find use_section_table id with Not_found -> - match Cutil.find_custom_attributes ["section"; "__section__"] attr with - | [[C.AString name]] -> + match get_attr_section loc attr with + | Some name -> (* 2- Section given as an attribute, gcc-style *) gcc_section name readonly false - | _ -> + | None -> (* 3- Default section appropriate for size and const-ness *) let size = match Cutil.sizeof env ty with Some sz -> sz | None -> max_int in @@ -190,17 +203,17 @@ let for_variable env id ty init = (* Determine sections for a function definition *) -let for_function env id attr = +let for_function env loc id attr = let si_code = try (* 1- Section explicitly associated with #use_section *) Hashtbl.find use_section_table id with Not_found -> - match Cutil.find_custom_attributes ["section"; "__section__"] attr with - | [[C.AString name]] -> + match get_attr_section loc attr with + | Some name -> (* 2- Section given as an attribute, gcc-style *) gcc_section name true true - | _ -> + | None -> (* 3- Default section *) try Hashtbl.find current_section_table "CODE" diff --git a/common/Sections.mli b/common/Sections.mli index bc97814d..d9fd9239 100644 --- a/common/Sections.mli +++ b/common/Sections.mli @@ -46,7 +46,7 @@ val define_section: -> ?writable:bool -> ?executable:bool -> ?access:access_mode -> unit -> unit val use_section_for: AST.ident -> string -> bool -val for_variable: Env.t -> AST.ident -> C.typ -> bool -> +val for_variable: Env.t -> C.location -> AST.ident -> C.typ -> bool -> section_name * access_mode -val for_function: Env.t -> AST.ident -> C.attributes -> section_name list +val for_function: Env.t -> C.location -> AST.ident -> C.attributes -> section_name list val for_stringlit: unit -> section_name diff --git a/common/Separation.v b/common/Separation.v index a9642d72..9aee633f 100644 --- a/common/Separation.v +++ b/common/Separation.v @@ -29,7 +29,7 @@ frame rule; instead, a weak form of the frame rule is provided by the lemmas that help us reason about the logical assertions. *) -Require Import Setoid Program.Basics. +Require Import Setoid Morphisms Program.Basics. Require Import Coqlib Decidableplus. Require Import AST Integers Values Memory Events Globalenvs. @@ -113,7 +113,36 @@ Proof. intros P Q [[A B] [C D]]. split; auto. Qed. -Hint Resolve massert_imp_refl massert_eqv_refl. +Instance massert_imp_eqv_Proper: + Proper (massert_eqv ==> massert_eqv ==> iff) massert_imp. +Proof. + intros p q Hpq r s Hrs. + split; destruct 1 as [HR1 HR2]; + constructor; intros; + apply Hrs || apply Hpq; + apply HR1 || apply HR2; + apply Hpq || apply Hrs; + assumption. +Qed. + +Hint Resolve massert_imp_refl massert_eqv_refl : core. + +Instance footprint_massert_imp_Proper: + Proper (massert_imp --> eq ==> eq ==> Basics.impl) m_footprint. +Proof. + destruct 1. repeat intro. subst. intuition. +Qed. + +Instance footprint_massert_eqv_Proper: + Proper (massert_eqv ==> eq ==> eq ==> iff) m_footprint. +Proof. + intros P Q HPQ b' b Hbeq ofs' ofs Hoeq. + subst. + destruct HPQ as [HPQ HQP]. + split; intro HH. + now rewrite HQP. + now rewrite HPQ. +Qed. (** * Separating conjunction *) @@ -143,6 +172,21 @@ Proof. - intuition auto. Qed. +Add Morphism disjoint_footprint + with signature massert_eqv ==> massert_eqv ==> iff + as disjoint_footprint_morph_1. +Proof. + intros p q Hpq r s Hrs. + unfold disjoint_footprint. + split; intro HH; intros b ofs Hf1 Hf2. + - rewrite <-Hpq in Hf1. + rewrite <-Hrs in Hf2. + now specialize (HH _ _ Hf1 Hf2). + - rewrite Hpq in Hf1. + rewrite Hrs in Hf2. + now specialize (HH _ _ Hf1 Hf2). +Qed. + Add Morphism sepconj with signature massert_eqv ==> massert_eqv ==> massert_eqv as sepconj_morph_2. @@ -161,6 +205,15 @@ Proof. intros. rewrite <- H0, <- H1; auto. Qed. +Lemma sep_imp': + forall P P' Q Q', + massert_imp P P' -> + massert_imp Q Q' -> + massert_imp (P ** Q) (P' ** Q'). +Proof. + intros * HP HQ. rewrite HP, HQ. reflexivity. +Qed. + Lemma sep_comm_1: forall P Q, massert_imp (P ** Q) (Q ** P). Proof. @@ -240,15 +293,17 @@ Proof. Qed. Lemma sep_drop: - forall P Q m, m |= P ** Q -> m |= Q. + forall P Q, massert_imp (P ** Q) Q. Proof. - simpl; intros. tauto. + constructor. + - simpl; intros. tauto. + - intros. now constructor 2. Qed. Lemma sep_drop2: - forall P Q R m, m |= P ** Q ** R -> m |= P ** R. + forall P Q R, massert_imp (P ** Q ** R) (P ** R). Proof. - intros. rewrite sep_swap in H. eapply sep_drop; eauto. + intros. rewrite sep_swap, sep_drop. reflexivity. Qed. Lemma sep_proj1: @@ -259,7 +314,9 @@ Qed. Lemma sep_proj2: forall P Q m, m |= P ** Q -> m |= Q. -Proof sep_drop. +Proof. + apply sep_drop. +Qed. Definition sep_pick1 := sep_proj1. @@ -315,19 +372,40 @@ Proof. simpl; intros. intuition auto. red; simpl; tauto. Qed. -(** A range of bytes, with full permissions and unspecified contents. *) +Lemma sep_pure': + forall P m, m |= pure P <-> P. +Proof. + simpl; intros. intuition auto. +Qed. + +(** A range of bytes with given permissions unspecified contents *) -Program Definition range (b: block) (lo hi: Z) : massert := {| +Program Definition range' (p: permission) (b: block) (lo hi: Z) : massert := {| m_pred := fun m => 0 <= lo /\ hi <= Ptrofs.modulus - /\ (forall i k p, lo <= i < hi -> Mem.perm m b i k p); + /\ (forall i k, lo <= i < hi -> Mem.perm m b i k p); m_footprint := fun b' ofs' => b' = b /\ lo <= ofs' < hi |}. Next Obligation. split; auto. split; auto. intros. eapply Mem.perm_unchanged_on; eauto. simpl; auto. Qed. Next Obligation. - apply Mem.perm_valid_block with ofs Cur Freeable; auto. + eapply Mem.perm_valid_block with ofs Cur _; auto. +Qed. + +Notation range := (range' Freeable). +Notation range_w := (range' Writable). + +Lemma range'_imp: + forall p p' b lo hi, + perm_order p p' -> + massert_imp (range' p b lo hi) (range' p' b lo hi). +Proof. + constructor; auto. + destruct 1 as (Hlo & Hhi & Hperm). + repeat split; auto. + intros i k Hoff. + eapply Mem.perm_implies; eauto. Qed. Lemma alloc_rule: @@ -346,47 +424,95 @@ Proof. eelim Mem.fresh_block_alloc; eauto. eapply (m_valid P); eauto. Qed. +Lemma free_rule: +forall P m b lo hi, + m |= range b lo hi ** P -> + exists m', + Mem.free m b lo hi = Some m' /\ m' |= P. +Proof. + intros P m b lo hi Hr. + destruct Hr as ((Hlo & Hhi & Hperm) & HP & Hdj). + assert (Mem.range_perm m b lo hi Cur Freeable) as Hrp + by (intros ? ?; now apply Hperm). + apply Mem.range_perm_free in Hrp. + destruct Hrp as (m2 & Hfree). + exists m2. + split; [assumption|]. + apply Mem.free_unchanged_on with (P:=m_footprint P) in Hfree. + now apply m_invar with (1:=HP) (2:=Hfree). + intros i Hr HfP. + apply Hdj with (2:=HfP). + now split. +Qed. + +Lemma range_split': + forall p b lo hi mid, + lo <= mid <= hi -> + massert_eqv (range' p b lo hi) + (range' p b lo mid ** range' p b mid hi). +Proof. + intros * HR. + constructor; constructor. + - intros m HH. + inversion HH as [Hlo [Hhi Hperm]]. + split; constructor; repeat split. + + assumption. + + omega. + + intros. apply Hperm. omega. + + omega. + + exact Hhi. + + intros. apply Hperm. omega. + + red; simpl; intros; omega. + - intros. simpl in *. intuition. + - intros m HH. + inversion_clear HH as [Hlm [Hmh Hdisj]]. + inversion_clear Hlm as [Hlo [Hmid Hperm]]. + inversion_clear Hmh as [Hmid' [Hhi Hperm']]. + constructor; repeat split. + + assumption. + + assumption. + + intros i k Hi. + destruct (Z.lt_ge_cases i mid); intuition. + - intros * Hfoot. simpl in *. + destruct (Z.lt_ge_cases ofs mid); intuition. +Qed. + Lemma range_split: - forall b lo hi P mid m, + forall p b lo hi P mid m, lo <= mid <= hi -> - m |= range b lo hi ** P -> - m |= range b lo mid ** range b mid hi ** P. + m |= range' p b lo hi ** P -> + m |= range' p b lo mid ** range' p b mid hi ** P. Proof. - intros. rewrite <- sep_assoc. eapply sep_imp; eauto. - split; simpl; intros. -- intuition auto. -+ omega. -+ apply H5; omega. -+ omega. -+ apply H5; omega. -+ red; simpl; intros; omega. -- intuition omega. + intros. + rewrite <-sep_assoc. + rewrite range_split' with (1:=H) in H0. + assumption. Qed. Lemma range_drop_left: - forall b lo hi P mid m, + forall p b lo hi P mid m, lo <= mid <= hi -> - m |= range b lo hi ** P -> - m |= range b mid hi ** P. + m |= range' p b lo hi ** P -> + m |= range' p b mid hi ** P. Proof. - intros. apply sep_drop with (range b lo mid). apply range_split; auto. + intros. apply sep_drop with (range' p b lo mid). apply range_split; auto. Qed. Lemma range_drop_right: - forall b lo hi P mid m, + forall p b lo hi P mid m, lo <= mid <= hi -> - m |= range b lo hi ** P -> - m |= range b lo mid ** P. + m |= range' p b lo hi ** P -> + m |= range' p b lo mid ** P. Proof. - intros. apply sep_drop2 with (range b mid hi). apply range_split; auto. + intros. apply sep_drop2 with (range' p b mid hi). apply range_split; auto. Qed. Lemma range_split_2: - forall b lo hi P mid al m, + forall p b lo hi P mid al m, lo <= align mid al <= hi -> al > 0 -> - m |= range b lo hi ** P -> - m |= range b lo mid ** range b (align mid al) hi ** P. + m |= range' p b lo hi ** P -> + m |= range' p b lo mid ** range' p b (align mid al) hi ** P. Proof. intros. rewrite <- sep_assoc. eapply sep_imp; eauto. assert (mid <= align mid al) by (apply align_le; auto). @@ -401,67 +527,89 @@ Proof. Qed. Lemma range_preserved: - forall m m' b lo hi, - m |= range b lo hi -> + forall p m m' b lo hi, + m |= range' p b lo hi -> (forall i k p, lo <= i < hi -> Mem.perm m b i k p -> Mem.perm m' b i k p) -> - m' |= range b lo hi. + m' |= range' p b lo hi. Proof. intros. destruct H as (A & B & C). simpl; intuition auto. Qed. -(** A memory area that contains a value sastifying a given predicate *) +(** A memory area that contains a value satisfying a given predicate. *) -Program Definition contains (chunk: memory_chunk) (b: block) (ofs: Z) (spec: val -> Prop) : massert := {| +Program Definition contains' (p: permission) (chunk: memory_chunk) (b: block) (ofs: Z) (spec: val -> Prop) : massert := {| m_pred := fun m => - 0 <= ofs <= Ptrofs.max_unsigned - /\ Mem.valid_access m chunk b ofs Freeable + 0 <= ofs /\ ofs + size_chunk chunk <= Ptrofs.modulus + /\ Mem.valid_access m chunk b ofs p /\ exists v, Mem.load chunk m b ofs = Some v /\ spec v; m_footprint := fun b' ofs' => b' = b /\ ofs <= ofs' < ofs + size_chunk chunk |}. Next Obligation. - rename H2 into v. split;[|split]. + rename H3 into v. split;[|split;[|split]]. +- auto. - auto. -- destruct H1; split; auto. red; intros; eapply Mem.perm_unchanged_on; eauto. simpl; auto. +- destruct H2; split; auto. red; intros; eapply Mem.perm_unchanged_on; eauto. simpl; auto. - exists v. split; auto. eapply Mem.load_unchanged_on; eauto. simpl; auto. Qed. Next Obligation. eauto with mem. Qed. +Notation contains := (contains' Freeable). +Notation contains_w := (contains' Writable). + +Lemma contains'_imp: + forall p p' chunk b ofs spec, + perm_order p p' -> + massert_imp (contains' p chunk b ofs spec) (contains' p' chunk b ofs spec). +Proof. + constructor; auto. + inversion 1 as (Hlo & Hhi & Hac & v & Hload & Hspec). + eapply Mem.valid_access_implies in Hac; eauto. + repeat (split; eauto). +Qed. + Lemma contains_no_overflow: - forall spec m chunk b ofs, - m |= contains chunk b ofs spec -> + forall p spec m chunk b ofs, + m |= contains' p chunk b ofs spec -> 0 <= ofs <= Ptrofs.max_unsigned. Proof. - intros. simpl in H. tauto. + intros. simpl in H. + destruct H as (H1 & H2 & H3). + split; [assumption|]. + generalize (size_chunk_pos chunk). + unfold Ptrofs.max_unsigned. omega. Qed. Lemma load_rule: - forall spec m chunk b ofs, - m |= contains chunk b ofs spec -> + forall p spec m chunk b ofs, + perm_order p Readable -> + m |= contains' p chunk b ofs spec -> exists v, Mem.load chunk m b ofs = Some v /\ spec v. Proof. - intros. destruct H as (D & E & v & F & G). + intros * Hp Hc. destruct Hc as (D & E & F & v & G & H). exists v; auto. Qed. Lemma loadv_rule: - forall spec m chunk b ofs, - m |= contains chunk b ofs spec -> + forall p spec m chunk b ofs, + perm_order p Readable -> + m |= contains' p chunk b ofs spec -> exists v, Mem.loadv chunk m (Vptr b (Ptrofs.repr ofs)) = Some v /\ spec v. Proof. - intros. exploit load_rule; eauto. intros (v & A & B). exists v; split; auto. + intros. exploit load_rule; eauto with mem. intros (v & A & B). exists v; split; auto. simpl. rewrite Ptrofs.unsigned_repr; auto. eapply contains_no_overflow; eauto. Qed. Lemma store_rule: - forall chunk m b ofs v (spec1 spec: val -> Prop) P, - m |= contains chunk b ofs spec1 ** P -> + forall p chunk m b ofs v (spec1 spec: val -> Prop) P, + perm_order p Writable -> + m |= contains' p chunk b ofs spec1 ** P -> spec (Val.load_result chunk v) -> exists m', - Mem.store chunk m b ofs v = Some m' /\ m' |= contains chunk b ofs spec ** P. + Mem.store chunk m b ofs v = Some m' /\ m' |= contains' p chunk b ofs spec ** P. Proof. - intros. destruct H as (A & B & C). destruct A as (D & E & v0 & F & G). + intros * Hp Hc Hs. destruct Hc as (A & B & C). destruct A as (D & E & v0 & F & G). assert (H: Mem.valid_access m chunk b ofs Writable) by eauto with mem. destruct (Mem.valid_access_store _ _ _ _ v H) as [m' STORE]. exists m'; split; auto. simpl. intuition auto. @@ -473,64 +621,132 @@ Proof. Qed. Lemma storev_rule: - forall chunk m b ofs v (spec1 spec: val -> Prop) P, - m |= contains chunk b ofs spec1 ** P -> + forall p chunk m b ofs v (spec1 spec: val -> Prop) P, + perm_order p Writable -> + m |= contains' p chunk b ofs spec1 ** P -> spec (Val.load_result chunk v) -> exists m', - Mem.storev chunk m (Vptr b (Ptrofs.repr ofs)) v = Some m' /\ m' |= contains chunk b ofs spec ** P. + Mem.storev chunk m (Vptr b (Ptrofs.repr ofs)) v = Some m' /\ m' |= contains' p chunk b ofs spec ** P. Proof. intros. exploit store_rule; eauto. intros (m' & A & B). exists m'; split; auto. simpl. rewrite Ptrofs.unsigned_repr; auto. eapply contains_no_overflow. eapply sep_pick1; eauto. Qed. -Lemma range_contains: - forall chunk b ofs P m, - m |= range b ofs (ofs + size_chunk chunk) ** P -> - (align_chunk chunk | ofs) -> - m |= contains chunk b ofs (fun v => True) ** P. +Lemma storev_rule2: + forall p chunk m m' b ofs v (spec1 spec: val -> Prop) P, + perm_order p Writable -> + m |= contains' p chunk b ofs spec1 ** P -> + spec (Val.load_result chunk v) -> + Memory.Mem.storev chunk m (Vptr b (Ptrofs.repr ofs)) v = Some m' -> + m' |= contains' p chunk b ofs spec ** P. Proof. - intros. destruct H as (A & B & C). destruct A as (D & E & F). - split; [|split]. -- assert (Mem.valid_access m chunk b ofs Freeable). + intros * Hp Hm Hspec Hstore. + eapply storev_rule with (2:=Hm) in Hspec; eauto. + destruct Hspec as [m'' [Hmem Hspec]]. + rewrite Hmem in Hstore. injection Hstore. + intro; subst. assumption. +Qed. + +Lemma range_contains': + forall p chunk b ofs, + perm_order p Readable -> + (align_chunk chunk | ofs) -> + massert_imp (range' p b ofs (ofs + size_chunk chunk)) + (contains' p chunk b ofs (fun v => True)). +Proof. + intros. constructor. + intros * Hr. destruct Hr as (D & E & F). + assert (Mem.valid_access m chunk b ofs p). { split; auto. red; auto. } - split. generalize (size_chunk_pos chunk). unfold Ptrofs.max_unsigned. omega. - split. auto. -+ destruct (Mem.valid_access_load m chunk b ofs) as [v LOAD]. + split; [|split]. +- generalize (size_chunk_pos chunk). omega. +- assumption. +- split; [assumption|]. + destruct (Mem.valid_access_load m chunk b ofs) as [v LOAD]. eauto with mem. exists v; auto. - auto. -- auto. +Qed. + +Lemma range_contains: + forall p chunk b ofs P m, + perm_order p Readable -> + m |= range' p b ofs (ofs + size_chunk chunk) ** P -> + (align_chunk chunk | ofs) -> + m |= contains' p chunk b ofs (fun v => True) ** P. +Proof. + intros * Hp Hr Hc. + rewrite range_contains' in Hr; assumption. +Qed. + +Lemma contains_range': + forall p chunk b ofs spec, + massert_imp (contains' p chunk b ofs spec) + (range' p b ofs (ofs + size_chunk chunk)). +Proof. + intros. + split. +- intros. destruct H as (A & B & C & D). + split; [|split]; try assumption. + destruct C as (C1 & C2). + intros i k Hr. + specialize (C1 _ Hr). + eauto with mem. +- trivial. +Qed. + +Lemma contains_range: + forall p chunk b ofs spec P m, + m |= contains' p chunk b ofs spec ** P -> + m |= range' p b ofs (ofs + size_chunk chunk) ** P. +Proof. + intros. + rewrite contains_range' in H; assumption. Qed. Lemma contains_imp: - forall (spec1 spec2: val -> Prop) chunk b ofs, + forall p (spec1 spec2: val -> Prop) chunk b ofs, (forall v, spec1 v -> spec2 v) -> - massert_imp (contains chunk b ofs spec1) (contains chunk b ofs spec2). + massert_imp (contains' p chunk b ofs spec1) (contains' p chunk b ofs spec2). Proof. intros; split; simpl; intros. - intuition auto. destruct H4 as (v & A & B). exists v; auto. - auto. Qed. -(** A memory area that contains a given value *) +(** A memory area that contains a given value. *) -Definition hasvalue (chunk: memory_chunk) (b: block) (ofs: Z) (v: val) : massert := - contains chunk b ofs (fun v' => v' = v). +Definition hasvalue' (p: permission) (chunk: memory_chunk) (b: block) (ofs: Z) (v: val) : massert := + contains' p chunk b ofs (fun v' => v' = v). + +Notation hasvalue := (hasvalue' Freeable). +Notation hasvalue_w := (hasvalue' Writable). + +Lemma hasvalue'_imp: + forall p p' chunk b ofs v, + perm_order p p' -> + massert_imp (hasvalue' p chunk b ofs v) (hasvalue' p' chunk b ofs v). +Proof. + constructor; auto. + now apply contains'_imp. +Qed. Lemma store_rule': - forall chunk m b ofs v (spec1: val -> Prop) P, - m |= contains chunk b ofs spec1 ** P -> + forall p chunk m b ofs v (spec1: val -> Prop) P, + perm_order p Writable -> + m |= contains' p chunk b ofs spec1 ** P -> exists m', - Mem.store chunk m b ofs v = Some m' /\ m' |= hasvalue chunk b ofs (Val.load_result chunk v) ** P. + Mem.store chunk m b ofs v = Some m' /\ m' |= hasvalue' p chunk b ofs (Val.load_result chunk v) ** P. Proof. intros. eapply store_rule; eauto. Qed. Lemma storev_rule': - forall chunk m b ofs v (spec1: val -> Prop) P, - m |= contains chunk b ofs spec1 ** P -> + forall p chunk m b ofs v (spec1: val -> Prop) P, + perm_order p Writable -> + m |= contains' p chunk b ofs spec1 ** P -> exists m', - Mem.storev chunk m (Vptr b (Ptrofs.repr ofs)) v = Some m' /\ m' |= hasvalue chunk b ofs (Val.load_result chunk v) ** P. + Mem.storev chunk m (Vptr b (Ptrofs.repr ofs)) v = Some m' /\ m' |= hasvalue' p chunk b ofs (Val.load_result chunk v) ** P. Proof. intros. eapply storev_rule; eauto. Qed. @@ -702,7 +918,7 @@ Proof. - intros. assert (0 <= ofs < sz2) by (eapply Mem.perm_alloc_3; eauto). omega. - intros. apply Mem.perm_implies with Freeable; auto with mem. eapply Mem.perm_alloc_2; eauto. xomega. -- red; intros. apply Zdivides_trans with 8; auto. +- red; intros. apply Z.divide_trans with 8; auto. exists (8 / align_chunk chunk). destruct chunk; reflexivity. - intros. elim FRESH2. eapply Mem.valid_block_inject_2; eauto. - intros (j' & INJ' & J1 & J2 & J3). diff --git a/common/Smallstep.v b/common/Smallstep.v index c269013b..27ad0a2d 100644 --- a/common/Smallstep.v +++ b/common/Smallstep.v @@ -872,6 +872,14 @@ Proof. intros. eapply sd_determ; eauto. Qed. +Lemma sd_determ_3: + forall s t s1 s2, + Step L s t s1 -> Step L s E0 s2 -> t = E0 /\ s1 = s2. +Proof. + intros. exploit (sd_determ DET). eexact H. eexact H0. + intros [A B]. inv A. auto. +Qed. + Lemma star_determinacy: forall s t s', Star L s t s' -> forall s'', Star L s t s'' -> Star L s' E0 s'' \/ Star L s'' E0 s'. @@ -895,6 +903,171 @@ Qed. End DETERMINACY. +(** Extra simulation diagrams for determinate languages. *) + +Section FORWARD_SIMU_DETERM. + +Variable L1: semantics. +Variable L2: semantics. + +Hypothesis L1det: determinate L1. + +Variable index: Type. +Variable order: index -> index -> Prop. +Hypothesis wf_order: well_founded order. + +Variable match_states: index -> state L1 -> state L2 -> Prop. + +Hypothesis match_initial_states: + forall s1, initial_state L1 s1 -> + exists i s2, initial_state L2 s2 /\ match_states i s1 s2. + +Hypothesis match_final_states: + forall i s1 s2 r, + match_states i s1 s2 -> + final_state L1 s1 r -> + final_state L2 s2 r. + +Hypothesis simulation: + forall s1 t s1', Step L1 s1 t s1' -> + forall i s2, match_states i s1 s2 -> + exists s1'' i' s2', + Star L1 s1' E0 s1'' + /\ (Plus L2 s2 t s2' \/ (Star L2 s2 t s2' /\ order i' i)) + /\ match_states i' s1'' s2'. + +Hypothesis public_preserved: + forall id, Senv.public_symbol (symbolenv L2) id = Senv.public_symbol (symbolenv L1) id. + +Inductive match_states_later: index * nat -> state L1 -> state L2 -> Prop := +| msl_now: forall i s1 s2, + match_states i s1 s2 -> match_states_later (i, O) s1 s2 +| msl_later: forall i n s1 s1' s2, + Step L1 s1 E0 s1' -> match_states_later (i, n) s1' s2 -> match_states_later (i, S n) s1 s2. + +Lemma star_match_states_later: + forall s1 s1', Star L1 s1 E0 s1' -> + forall i s2, match_states i s1' s2 -> + exists n, match_states_later (i, n) s1 s2. +Proof. + intros s10 s10' STAR0. pattern s10, s10'; eapply star_E0_ind; eauto. + - intros s1 i s2 M. exists O; constructor; auto. + - intros s1 s1' s1'' STEP IH i s2 M. + destruct (IH i s2 M) as (n & MS). + exists (S n); econstructor; eauto. +Qed. + +Lemma forward_simulation_determ: forward_simulation L1 L2. +Proof. + apply Forward_simulation with (order0 := lex_ord order lt) (match_states0 := match_states_later); + constructor. +- apply wf_lex_ord. apply wf_order. apply lt_wf. +- intros. exploit match_initial_states; eauto. intros (i & s2 & A & B). + exists (i, O), s2; auto using msl_now. +- intros. inv H. + + eapply match_final_states; eauto. + + eelim (sd_final_nostep L1det); eauto. +- intros s1 t s1' A; destruct 1. + + exploit simulation; eauto. intros (s1'' & i' & s2' & B & C & D). + exploit star_match_states_later; eauto. intros (n & E). + exists (i', n), s2'; split; auto. + destruct C as [P | [P Q]]; auto using lex_ord_left. + + exploit sd_determ_3. eauto. eexact A. eauto. intros [P Q]; subst t s1'0. + exists (i, n), s2; split; auto. + right; split. apply star_refl. apply lex_ord_right. omega. +- exact public_preserved. +Qed. + +End FORWARD_SIMU_DETERM. + +(** A few useful special cases. *) + +Section FORWARD_SIMU_DETERM_DIAGRAMS. + +Variable L1: semantics. +Variable L2: semantics. + +Hypothesis L1det: determinate L1. + +Variable match_states: state L1 -> state L2 -> Prop. + +Hypothesis public_preserved: + forall id, Senv.public_symbol (symbolenv L2) id = Senv.public_symbol (symbolenv L1) id. + +Hypothesis match_initial_states: + forall s1, initial_state L1 s1 -> + exists s2, initial_state L2 s2 /\ match_states s1 s2. + +Hypothesis match_final_states: + forall s1 s2 r, + match_states s1 s2 -> + final_state L1 s1 r -> + final_state L2 s2 r. + +Section SIMU_DETERM_STAR. + +Variable measure: state L1 -> nat. + +Hypothesis simulation: + forall s1 t s1', Step L1 s1 t s1' -> + forall s2, match_states s1 s2 -> + exists s1'' s2', + Star L1 s1' E0 s1'' + /\ (Plus L2 s2 t s2' \/ (Star L2 s2 t s2' /\ measure s1'' < measure s1))%nat + /\ match_states s1'' s2'. + +Lemma forward_simulation_determ_star: forward_simulation L1 L2. +Proof. + apply forward_simulation_determ with + (match_states := fun i s1 s2 => i = s1 /\ match_states s1 s2) + (order := ltof _ measure). +- assumption. +- apply well_founded_ltof. +- intros. exploit match_initial_states; eauto. intros (s2 & A & B). + exists s1, s2; auto. +- intros. destruct H. eapply match_final_states; eauto. +- intros. destruct H0; subst i. + exploit simulation; eauto. intros (s1'' & s2' & A & B & C). + exists s1'', s1'', s2'. auto. +- assumption. +Qed. + +End SIMU_DETERM_STAR. + +Section SIMU_DETERM_PLUS. + +Hypothesis simulation: + forall s1 t s1', Step L1 s1 t s1' -> + forall s2, match_states s1 s2 -> + exists s1'' s2', Star L1 s1' E0 s1'' /\ Plus L2 s2 t s2' /\ match_states s1'' s2'. + +Lemma forward_simulation_determ_plus: forward_simulation L1 L2. +Proof. + apply forward_simulation_determ_star with (measure := fun _ => O). + intros. exploit simulation; eauto. intros (s1'' & s2' & A & B & C). + exists s1'', s2'; auto. +Qed. + +End SIMU_DETERM_PLUS. + +Section SIMU_DETERM_ONE. + +Hypothesis simulation: + forall s1 t s1', Step L1 s1 t s1' -> + forall s2, match_states s1 s2 -> + exists s1'' s2', Star L1 s1' E0 s1'' /\ Step L2 s2 t s2' /\ match_states s1'' s2'. + +Lemma forward_simulation_determ_one: forward_simulation L1 L2. +Proof. + apply forward_simulation_determ_plus. + intros. exploit simulation; eauto. intros (s1'' & s2' & A & B & C). + exists s1'', s2'; auto using plus_one. +Qed. + +End SIMU_DETERM_ONE. + +End FORWARD_SIMU_DETERM_DIAGRAMS. + (** * Backward simulations between two transition semantics. *) Definition safe (L: semantics) (s: state L) : Prop := diff --git a/common/Switch.v b/common/Switch.v index 0ef91d60..5a6d4c63 100644 --- a/common/Switch.v +++ b/common/Switch.v @@ -288,10 +288,10 @@ Lemma validate_jumptable_correct: Proof. intros. rewrite (validate_jumptable_correct_rec cases tbl ofs); auto. -- f_equal. f_equal. rewrite Zmod_small. omega. +- f_equal. f_equal. rewrite Z.mod_small. omega. destruct (zle ofs v). omega. assert (M: ((v - ofs) + 1 * modulus) mod modulus = (v - ofs) + modulus). - { rewrite Zmod_small. omega. omega. } + { rewrite Z.mod_small. omega. omega. } rewrite Z_mod_plus in M by auto. rewrite M in H0. omega. - generalize (Z_mod_lt (v - ofs) modulus modulus_pos). omega. Qed. @@ -331,7 +331,7 @@ Proof. rewrite (split_between_prop v _ _ _ _ _ _ EQ). assert (0 <= (v - ofs) mod modulus < modulus) by (apply Z_mod_lt; omega). destruct (zlt ((v - ofs) mod modulus) sz). - rewrite Zmod_small by omega. eapply validate_jumptable_correct; eauto. + rewrite Z.mod_small by omega. eapply validate_jumptable_correct; eauto. eapply IHt; eauto. Qed. diff --git a/common/Values.v b/common/Values.v index a20dd567..68a2054b 100644 --- a/common/Values.v +++ b/common/Values.v @@ -132,6 +132,40 @@ Proof. simpl in *. InvBooleans. destruct H0. split; auto. eapply has_subtype; eauto. Qed. +Definition has_type_dec (v: val) (t: typ) : { has_type v t } + { ~ has_type v t }. +Proof. + unfold has_type; destruct v. +- auto. +- destruct t; auto. +- destruct t; auto. +- destruct t; auto. +- destruct t; auto. +- destruct t. + apply bool_dec. + auto. + apply bool_dec. + auto. + apply bool_dec. + auto. +Defined. + +Definition has_rettype (v: val) (r: rettype) : Prop := + match r, v with + | Tret t, _ => has_type v t + | Tint8signed, Vint n => n = Int.sign_ext 8 n + | Tint8unsigned, Vint n => n = Int.zero_ext 8 n + | Tint16signed, Vint n => n = Int.sign_ext 16 n + | Tint16unsigned, Vint n => n = Int.zero_ext 16 n + | _, Vundef => True + | _, _ => False + end. + +Lemma has_proj_rettype: forall v r, + has_rettype v r -> has_type v (proj_rettype r). +Proof. + destruct r; simpl; intros; auto; destruct v; try contradiction; exact I. +Qed. + (** Truth values. Non-zero integers are treated as [True]. The integer 0 (also used to represent the null pointer) is [False]. Other values are neither true nor false. *) @@ -766,6 +800,18 @@ Definition rolml (v: val) (amount: int) (mask: int64): val := | _ => Vundef end. +Definition zero_ext_l (nbits: Z) (v: val) : val := + match v with + | Vlong n => Vlong(Int64.zero_ext nbits n) + | _ => Vundef + end. + +Definition sign_ext_l (nbits: Z) (v: val) : val := + match v with + | Vlong n => Vlong(Int64.sign_ext nbits n) + | _ => Vundef + end. + (** Comparisons *) Section COMPARISONS. @@ -898,6 +944,55 @@ Definition offset_ptr (v: val) (delta: ptrofs) : val := | _ => Vundef end. +(** Normalize a value to the given type, turning it into Vundef if it does not + match the type. *) + +Definition normalize (v: val) (ty: typ) : val := + match v, ty with + | Vundef, _ => Vundef + | Vint _, Tint => v + | Vlong _, Tlong => v + | Vfloat _, Tfloat => v + | Vsingle _, Tsingle => v + | Vptr _ _, (Tint | Tany32) => if Archi.ptr64 then Vundef else v + | Vptr _ _, Tlong => if Archi.ptr64 then v else Vundef + | (Vint _ | Vsingle _), Tany32 => v + | _, Tany64 => v + | _, _ => Vundef + end. + +Lemma normalize_type: + forall v ty, has_type (normalize v ty) ty. +Proof. + intros; destruct v; simpl. +- auto. +- destruct ty; exact I. +- destruct ty; exact I. +- destruct ty; exact I. +- destruct ty; exact I. +- unfold has_type; destruct ty, Archi.ptr64; auto. +Qed. + +Lemma normalize_idem: + forall v ty, has_type v ty -> normalize v ty = v. +Proof. + unfold has_type, normalize; intros. destruct v. +- auto. +- destruct ty; intuition auto. +- destruct ty; intuition auto. +- destruct ty; intuition auto. +- destruct ty; intuition auto. +- destruct ty, Archi.ptr64; intuition congruence. +Qed. + +(** Select between two values based on the result of a comparison. *) + +Definition select (cmp: option bool) (v1 v2: val) (ty: typ) := + match cmp with + | Some b => normalize (if b then v1 else v2) ty + | None => Vundef + end. + (** [load_result] reflects the effect of storing a value with a given memory chunk, then reading it back with the same chunk. Depending on the chunk and the type of the value, some normalization occurs. @@ -925,10 +1020,24 @@ Definition load_result (chunk: memory_chunk) (v: val) := | _, _ => Vundef end. +Lemma load_result_rettype: + forall chunk v, has_rettype (load_result chunk v) (rettype_of_chunk chunk). +Proof. + intros. unfold has_rettype; destruct chunk; destruct v; simpl; auto. +- rewrite Int.sign_ext_idem by omega; auto. +- rewrite Int.zero_ext_idem by omega; auto. +- rewrite Int.sign_ext_idem by omega; auto. +- rewrite Int.zero_ext_idem by omega; auto. +- destruct Archi.ptr64 eqn:SF; simpl; auto. +- destruct Archi.ptr64 eqn:SF; simpl; auto. +- destruct Archi.ptr64 eqn:SF; simpl; auto. +Qed. + Lemma load_result_type: forall chunk v, has_type (load_result chunk v) (type_of_chunk chunk). Proof. - intros. unfold has_type; destruct chunk; destruct v; simpl; auto; destruct Archi.ptr64 eqn:SF; simpl; auto. + intros. rewrite <- proj_rettype_of_chunk. apply has_proj_rettype. + apply load_result_rettype. Qed. Lemma load_result_same: @@ -1832,10 +1941,18 @@ Qed. Lemma zero_ext_and: forall n v, - 0 < n < Int.zwordsize -> + 0 <= n -> Val.zero_ext n v = Val.and v (Vint (Int.repr (two_p n - 1))). Proof. - intros. destruct v; simpl; auto. decEq. apply Int.zero_ext_and; auto. omega. + intros. destruct v; simpl; auto. decEq. apply Int.zero_ext_and; auto. +Qed. + +Lemma zero_ext_andl: + forall n v, + 0 <= n -> + Val.zero_ext_l n v = Val.andl v (Vlong (Int64.repr (two_p n - 1))). +Proof. + intros. destruct v; simpl; auto. decEq. apply Int64.zero_ext_and; auto. Qed. Lemma rolm_lt_zero: @@ -1883,7 +2000,7 @@ Inductive lessdef_list: list val -> list val -> Prop := lessdef v1 v2 -> lessdef_list vl1 vl2 -> lessdef_list (v1 :: vl1) (v2 :: vl2). -Hint Resolve lessdef_refl lessdef_undef lessdef_list_nil lessdef_list_cons. +Hint Resolve lessdef_refl lessdef_undef lessdef_list_nil lessdef_list_cons : core. Lemma lessdef_list_inv: forall vl1 vl2, lessdef_list vl1 vl2 -> vl1 = vl2 \/ In Vundef vl1. @@ -2044,6 +2161,36 @@ Proof. intros. destruct v; simpl; auto. f_equal. apply Ptrofs.add_assoc. Qed. +Lemma lessdef_normalize: + forall v ty, lessdef (normalize v ty) v. +Proof. + intros. destruct v; simpl. + - auto. + - destruct ty; auto. + - destruct ty; auto. + - destruct ty; auto. + - destruct ty; auto. + - destruct ty, Archi.ptr64; auto. +Qed. + +Lemma normalize_lessdef: + forall v v' ty, lessdef v v' -> lessdef (normalize v ty) (normalize v' ty). +Proof. + intros. inv H; auto. +Qed. + +Lemma select_lessdef: + forall ob ob' v1 v1' v2 v2' ty, + ob = None \/ ob = ob' -> + lessdef v1 v1' -> lessdef v2 v2' -> + lessdef (select ob v1 v2 ty) (select ob' v1' v2' ty). +Proof. + intros; unfold select. destruct H. +- subst ob; auto. +- subst ob'; destruct ob as [b|]; auto. + apply normalize_lessdef. destruct b; auto. +Qed. + (** * Values and memory injections *) (** A memory injection [f] is a function from addresses to either [None] @@ -2078,7 +2225,7 @@ Inductive inject (mi: meminj): val -> val -> Prop := | val_inject_undef: forall v, inject mi Vundef v. -Hint Constructors inject. +Hint Constructors inject : core. Inductive inject_list (mi: meminj): list val -> list val-> Prop:= | inject_list_nil : @@ -2087,7 +2234,7 @@ Inductive inject_list (mi: meminj): list val -> list val-> Prop:= inject mi v v' -> inject_list mi vl vl'-> inject_list mi (v :: vl) (v' :: vl'). -Hint Resolve inject_list_nil inject_list_cons. +Hint Resolve inject_list_nil inject_list_cons : core. Lemma inject_ptrofs: forall mi i, inject mi (Vptrofs i) (Vptrofs i). @@ -2095,7 +2242,7 @@ Proof. unfold Vptrofs; intros. destruct Archi.ptr64; auto. Qed. -Hint Resolve inject_ptrofs. +Hint Resolve inject_ptrofs : core. Section VAL_INJ_OPS. @@ -2328,6 +2475,36 @@ Proof. intros. unfold Val.hiword; inv H; auto. Qed. +Lemma normalize_inject: + forall v v' ty, inject f v v' -> inject f (normalize v ty) (normalize v' ty). +Proof. + intros. inv H. +- destruct ty; constructor. +- destruct ty; constructor. +- destruct ty; constructor. +- destruct ty; constructor. +- simpl. destruct ty. ++ destruct Archi.ptr64; econstructor; eauto. ++ auto. ++ destruct Archi.ptr64; econstructor; eauto. ++ auto. ++ destruct Archi.ptr64; econstructor; eauto. ++ econstructor; eauto. +- constructor. +Qed. + +Lemma select_inject: + forall ob ob' v1 v1' v2 v2' ty, + ob = None \/ ob = ob' -> + inject f v1 v1' -> inject f v2 v2' -> + inject f (select ob v1 v2 ty) (select ob' v1' v2' ty). +Proof. + intros; unfold select. destruct H. +- subst ob; auto. +- subst ob'; destruct ob as [b|]; auto. + apply normalize_inject. destruct b; auto. +Qed. + End VAL_INJ_OPS. End Val. @@ -2368,7 +2545,7 @@ Proof. constructor. eapply val_inject_incr; eauto. auto. Qed. -Hint Resolve inject_incr_refl val_inject_incr val_inject_list_incr. +Hint Resolve inject_incr_refl val_inject_incr val_inject_list_incr : core. Lemma val_inject_lessdef: forall v1 v2, Val.lessdef v1 v2 <-> Val.inject (fun b => Some(b, 0)) v1 v2. @@ -55,10 +55,12 @@ Supported targets: x86_64-macosx (x86 64 bits, MacOS X) rv32-linux (RISC-V 32 bits, Linux) rv64-linux (RISC-V 64 bits, Linux) + aarch64-linux (AArch64, i.e. ARMv8 in 64-bit mode, Linux) manual (edit configuration file by hand) For x86 targets, the "x86_32-" prefix can also be written "ia32-" or "i386-". For x86 targets, the "x86_64-" prefix can also be written "amd64-". +For AArch64 targets, the "aarch64-" prefix can also be written "arm64-". For PowerPC targets, the "ppc-" prefix can be refined into: ppc64- PowerPC 64 bits @@ -175,6 +177,8 @@ case "$target" in arch="riscV"; model="32"; endianness="little"; bitsize=32;; rv64-*) arch="riscV"; model="64"; endianness="little"; bitsize=64;; + aarch64-*|arm64-*) + arch="aarch64"; model="default"; endianness="little"; bitsize=64;; manual) ;; "") @@ -428,6 +432,29 @@ if test "$arch" = "riscV"; then system="linux" fi +# +# AArch64 (ARMv8 64 bits) Target Configuration +# +if test "$arch" = "aarch64"; then + case "$target" in + linux) + abi="standard" + casm="${toolprefix}gcc" + casm_options="-c" + cc="${toolprefix}gcc" + clinker="${toolprefix}gcc" + clinker_options="" + cprepro="${toolprefix}gcc" + cprepro_options="-std=c99 -U__GNUC__ -E" + libmath="-lm" + system="linux";; + *) + echo "Error: invalid eabi/system '$target' for architecture AArch64." 1>&2 + echo "$usage" 1>&2 + exit 2;; + esac +fi + # # Finalize Target Configuration @@ -503,43 +530,38 @@ missingtools=false echo "Testing Coq... " | tr -d '\n' coq_ver=$(${COQBIN}coqc -v 2>/dev/null | sed -n -e 's/The Coq Proof Assistant, version \([^ ]*\).*$/\1/p') case "$coq_ver" in - 8.7.0|8.7.1|8.7.2|8.8.0|8.8.1|8.8.2|8.9.0) + 8.8.0|8.8.1|8.8.2|8.9.0|8.9.1|8.10.0|8.10.1|8.10.2|8.11.0) echo "version $coq_ver -- good!";; ?*) echo "version $coq_ver -- UNSUPPORTED" if $ignore_coq_version; then echo "Warning: this version of Coq is unsupported, proceed at your own risks." else - echo "Error: CompCert requires one of the following Coq versions: 8.9.0, 8.8.2, 8.8.1, 8.8.0, 8.7.2, 8.7.1, 8.7.0" + echo "Error: CompCert requires one of the following Coq versions: 8.11.0, 8.10.2, 8.10.1, 8.10.0, 8.9.1, 8.9.0, 8.8.2, 8.8.1, 8.8.0" missingtools=true fi;; "") echo "NOT FOUND" - echo "Error: make sure Coq version 8.9.0 is installed." + echo "Error: make sure Coq version 8.9.1 is installed." missingtools=true;; esac echo "Testing OCaml... " | tr -d '\n' ocaml_ver=`ocamlopt -version 2>/dev/null` case "$ocaml_ver" in - 4.00.*|4.01.*) + 4.00.*|4.01.*| 4.02.*|4.03.*|4.04.*) echo "version $ocaml_ver -- UNSUPPORTED" - echo "Error: CompCert requires OCaml version 4.02 or later." + echo "Error: CompCert requires OCaml version 4.05 or later." missingtools=true;; - 4.02.*|4.03.*|4.04.*) - echo "version $ocaml_ver -- good!" - echo "WARNING: some Intel processors of the Skylake and Kaby Lake generations" - echo "have a hardware bug that can be triggered by this version of OCaml." - echo "To avoid this risk, it is recommended to use OCaml 4.05 or later.";; - 4.0*) + 4.*) echo "version $ocaml_ver -- good!";; ?.*) echo "version $ocaml_ver -- UNSUPPORTED" - echo "Error: CompCert requires OCaml version 4.02 or later." + echo "Error: CompCert requires OCaml version 4.05 or later." missingtools=true;; *) echo "NOT FOUND" - echo "Error: make sure OCaml version 4.02 or later is installed." + echo "Error: make sure OCaml version 4.05 or later is installed." missingtools=true;; esac @@ -553,29 +575,23 @@ else ocaml_opt_comp=false fi -MENHIR_REQUIRED=20161201 -MENHIR_NEW_API=20180530 -MENHIR_MAX=20181113 -menhir_flags='' +MENHIR_REQUIRED=20190626 echo "Testing Menhir... " | tr -d '\n' menhir_ver=`menhir --version 2>/dev/null | sed -n -e 's/^.*version \([0-9]*\).*$/\1/p'` case "$menhir_ver" in 20[0-9][0-9][0-9][0-9][0-9][0-9]) - if test "$menhir_ver" -ge $MENHIR_REQUIRED -a "$menhir_ver" -le $MENHIR_MAX; then + if test "$menhir_ver" -ge $MENHIR_REQUIRED; then echo "version $menhir_ver -- good!" - menhir_include_dir=`menhir --suggest-menhirLib` - if test -z "$menhir_include_dir"; then + menhir_dir=$(menhir --suggest-menhirLib | tr -d '\r' | tr '\\' '/') + if test -z "$menhir_dir"; then echo "Error: cannot determine the location of the Menhir API library." echo "This can be due to an incorrect Menhir package." echo "Consider using the OPAM package for Menhir." missingtools=true fi - if test "$menhir_ver" -ge $MENHIR_NEW_API; then - menhir_flags="--coq-lib-path compcert.cparser.MenhirLib" - fi else echo "version $menhir_ver -- UNSUPPORTED" - echo "Error: CompCert requires a version of Menhir between $MENHIR_REQUIRED and $MENHIR_MAX, included." + echo "Error: CompCert requires a version greater or equal to $MENHIR_REQUIRED." missingtools=true fi;; *) @@ -639,7 +655,8 @@ echo "-R lib compcert.lib \ -R driver compcert.driver \ -R flocq compcert.flocq \ -R exportclight compcert.exportclight \ --R cparser compcert.cparser" > _CoqProject +-R cparser compcert.cparser \ +-R MenhirLib compcert.MenhirLib" > _CoqProject case $arch in x86) echo "-R x86_${bitsize} compcert.x86_${bitsize}" >> _CoqProject @@ -660,8 +677,7 @@ MANDIR=$sharedir/man SHAREDIR=$sharedir COQDEVDIR=$coqdevdir OCAML_OPT_COMP=$ocaml_opt_comp -MENHIR_INCLUDES=-I "$menhir_include_dir" -MENHIR_FLAGS=$menhir_flags +MENHIR_DIR=$menhir_dir COMPFLAGS=-bin-annot EOF @@ -696,6 +712,8 @@ cat >> Makefile.config <<'EOF' # ARCH=powerpc # ARCH=arm # ARCH=x86 +# ARCH=riscV +# ARCH=aarch6 ARCH= # Hardware variant @@ -709,23 +727,24 @@ ARCH= # MODEL=armv7m # for ARM # MODEL=32sse2 # for x86 in 32-bit mode # MODEL=64 # for x86 in 64-bit mode +# MODEL=default # for others MODEL= # Target ABI # ABI=eabi # for PowerPC / Linux and other SVR4 or EABI platforms # ABI=eabi # for ARM # ABI=hardfloat # for ARM -# ABI=standard # for x86 +# ABI=standard # for others ABI= # Target bit width -# BITSIZE=64 # for x86 in 64-bit mode +# BITSIZE=64 # for x86 in 64-bit mode, RiscV in 64-bit mode, AArch64 # BITSIZE=32 # otherwise BITSIZE= # Target endianness # ENDIANNESS=big # for ARM or PowerPC -# ENDIANNESS=little # for ARM or x86 +# ENDIANNESS=little # for ARM or x86 or RiscV or AArch64 ENDIANNESS= # Target operating system and development environment @@ -734,7 +753,7 @@ ENDIANNESS= # SYSTEM=linux # SYSTEM=diab # -# Possible choices for ARM: +# Possible choices for ARM, AArch64, RiscV: # SYSTEM=linux # # Possible choices for x86: @@ -12,4 +12,4 @@ make -q ${1}o || { done) } -"${COQBIN}coqide" $INCLUDES $1 && make ${1}o +"${COQBIN}coqide" -async-proofs off $INCLUDES $1 && make ${1}o diff --git a/cparser/Builtins.ml b/cparser/Builtins.ml deleted file mode 100644 index 8eb1abfd..00000000 --- a/cparser/Builtins.ml +++ /dev/null @@ -1,54 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Xavier Leroy, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -(* Compiler built-ins *) - -open C -open Cutil - -let env = ref Env.empty -let idents = ref [] -let decls = ref [] - -let environment () = !env -let identifiers () = !idents -let declarations () = List.rev !decls - -let add_typedef (s, ty) = - let (id, env') = Env.enter_typedef !env s ty in - env := env'; - idents := id :: !idents; - decls := {gdesc = Gtypedef(id, ty); gloc = no_loc} :: !decls - -let add_function (s, (res, args, va)) = - let ty = - TFun(res, - Some (List.map (fun ty -> (Env.fresh_ident "", ty)) args), - va, []) in - let (id, env') = Env.enter_ident !env s Storage_extern ty in - env := env'; - idents := id :: !idents; - decls := {gdesc = Gdecl(Storage_extern, id, ty, None); gloc = no_loc} :: !decls - -type t = { - typedefs: (string * C.typ) list; - functions: (string * (C.typ * C.typ list * bool)) list -} - -let set blt = - env := Env.empty; - idents := []; - List.iter add_typedef blt.typedefs; - List.iter add_function blt.functions diff --git a/cparser/C.mli b/cparser/C.mli index cc8d4065..15717565 100644 --- a/cparser/C.mli +++ b/cparser/C.mli @@ -264,3 +264,10 @@ and globdecl_desc = | Gpragma of string (* #pragma directive *) type program = globdecl list + +(** Builtin types and functions *) + +type builtins = { + builtin_typedefs: (string * typ) list; + builtin_functions: (string * (typ * typ list * bool)) list +} diff --git a/cparser/Cabs.v b/cparser/Cabs.v index 5865ab69..5f12e8a1 100644 --- a/cparser/Cabs.v +++ b/cparser/Cabs.v @@ -20,7 +20,7 @@ Parameter string : Type. (* OCaml's int64 type, used to represent individual characters in literals. *) Parameter char_code : Type. (* Context information. *) -Parameter cabsloc : Type. +Parameter loc : Type. Record floatInfo := { isHex_FI:bool; @@ -51,7 +51,7 @@ Inductive typeSpecifier := (* Merge all specifiers into one type *) * They also have a list of __attribute__s that appeared between the * keyword and the type name (definitions only) *) | Tstruct_union : structOrUnion -> option string -> option (list field_group) -> list attribute -> typeSpecifier - | Tenum : option string -> option (list (string * option expression * cabsloc)) -> list attribute -> typeSpecifier + | Tenum : option string -> option (list (string * option expression * loc)) -> list attribute -> typeSpecifier with storage := AUTO | STATIC | EXTERN | REGISTER | TYPEDEF @@ -87,18 +87,18 @@ with decl_type := | PROTO_OLD : decl_type -> list string -> decl_type with parameter := - | PARAM : list spec_elem -> option string -> decl_type -> list attribute -> cabsloc -> parameter + | PARAM : list spec_elem -> option string -> decl_type -> list attribute -> loc -> parameter (* The optional expression is the bitfield *) with field_group := - | Field_group : list spec_elem -> list (option name * option expression) -> cabsloc -> field_group + | Field_group : list spec_elem -> list (option name * option expression) -> loc -> field_group (* The decl_type is in the order in which they are printed. Only the name of * the declared identifier is pulled out. *) (* e.g: in "int *x", "*x" is the declarator; "x" will be pulled out as *) (* the string, and decl_type will be PTR([], JUSTBASE) *) with name := - | Name : string -> decl_type -> list attribute -> cabsloc -> name + | Name : string -> decl_type -> list attribute -> loc -> name (* A variable declarator ("name") with an initializer *) with init_name := @@ -161,9 +161,9 @@ with initwhat := | ATINDEX_INIT : expression -> initwhat with attribute := - | GCC_ATTR : list gcc_attribute -> cabsloc -> attribute - | PACKED_ATTR : list expression -> cabsloc -> attribute - | ALIGNAS_ATTR : list expression -> cabsloc -> attribute + | GCC_ATTR : list gcc_attribute -> loc -> attribute + | PACKED_ATTR : list expression -> loc -> attribute + | ALIGNAS_ATTR : list expression -> loc -> attribute with gcc_attribute := | GCC_ATTR_EMPTY @@ -194,31 +194,31 @@ Definition asm_flag := (bool * list char_code)%type. ** Declaration definition (at toplevel) *) Inductive definition := - | FUNDEF : list spec_elem -> name -> list definition -> statement -> cabsloc -> definition - | DECDEF : init_name_group -> cabsloc -> definition (* global variable(s), or function prototype *) - | PRAGMA : string -> cabsloc -> definition + | FUNDEF : list spec_elem -> name -> list definition -> statement -> loc -> definition + | DECDEF : init_name_group -> loc -> definition (* global variable(s), or function prototype *) + | PRAGMA : string -> loc -> definition (* ** statements *) with statement := - | NOP : cabsloc -> statement - | COMPUTATION : expression -> cabsloc -> statement - | BLOCK : list statement -> cabsloc -> statement - | If : expression -> statement -> option statement -> cabsloc -> statement - | WHILE : expression -> statement -> cabsloc -> statement - | DOWHILE : expression -> statement -> cabsloc -> statement - | FOR : option for_clause -> option expression -> option expression -> statement -> cabsloc -> statement - | BREAK : cabsloc -> statement - | CONTINUE : cabsloc -> statement - | RETURN : option expression -> cabsloc -> statement - | SWITCH : expression -> statement -> cabsloc -> statement - | CASE : expression -> statement -> cabsloc -> statement - | DEFAULT : statement -> cabsloc -> statement - | LABEL : string -> statement -> cabsloc -> statement - | GOTO : string -> cabsloc -> statement - | ASM : list cvspec -> bool -> list char_code -> list asm_operand -> list asm_operand -> list asm_flag -> cabsloc -> statement + | NOP : loc -> statement + | COMPUTATION : expression -> loc -> statement + | BLOCK : list statement -> loc -> statement + | If : expression -> statement -> option statement -> loc -> statement + | WHILE : expression -> statement -> loc -> statement + | DOWHILE : expression -> statement -> loc -> statement + | FOR : option for_clause -> option expression -> option expression -> statement -> loc -> statement + | BREAK : loc -> statement + | CONTINUE : loc -> statement + | RETURN : option expression -> loc -> statement + | SWITCH : expression -> statement -> loc -> statement + | CASE : expression -> statement -> loc -> statement + | DEFAULT : statement -> loc -> statement + | LABEL : string -> statement -> loc -> statement + | GOTO : string -> loc -> statement + | ASM : list cvspec -> bool -> list char_code -> list asm_operand -> list asm_operand -> list asm_flag -> loc -> statement | DEFINITION : definition -> statement (*definition or declaration of a variable or type*) with for_clause := diff --git a/cparser/Cabshelper.ml b/cparser/Cabshelper.ml index 958f242c..22f3b3c7 100644 --- a/cparser/Cabshelper.ml +++ b/cparser/Cabshelper.ml @@ -16,11 +16,6 @@ open Cabs -let cabslu = {lineno = -10; - filename = "cabs loc unknown"; - byteno = -10; - ident = 0} - (*********** HELPER FUNCTIONS **********) let rec isStatic = function @@ -44,13 +39,13 @@ let rec isTypedef = function | _ :: rest -> isTypedef rest -let get_definitionloc (d : definition) : cabsloc = +let get_definitionloc (d : definition) : loc = match d with | FUNDEF(_, _, _, _, l) -> l | DECDEF(_, l) -> l | PRAGMA(_, l) -> l -let get_statementloc (s : statement) : cabsloc = +let get_statementloc (s : statement) : loc = begin match s with | NOP(loc) -> loc @@ -72,8 +67,8 @@ begin | ASM(_,_,_,_,_,_,loc) -> loc end -let string_of_cabsloc l = +let string_of_loc l = Printf.sprintf "%s:%d" l.filename l.lineno -let format_cabsloc pp l = +let format_loc pp l = Format.fprintf pp "%s:%d" l.filename l.lineno diff --git a/cparser/Ceval.ml b/cparser/Ceval.ml index 58dea5f4..ecf83779 100644 --- a/cparser/Ceval.ml +++ b/cparser/Ceval.ml @@ -271,7 +271,7 @@ let constant_expr env ty e = try match unroll env ty, cast env ty (expr env e) with | TInt(ik, _), I n -> Some(CInt(n, ik, "")) - | TPtr(_, _), I n -> Some(CInt(n, IInt, "")) + | TPtr(_, _), I n -> Some(CInt(n, ptr_t_ikind (), "")) | (TArray(_, _, _) | TPtr(_, _)), S s -> Some(CStr s) | (TArray(_, _, _) | TPtr(_, _)), WS s -> Some(CWStr s) | TEnum(_, _), I n -> Some(CInt(n, enum_ikind, "")) diff --git a/cparser/Checks.ml b/cparser/Checks.ml index a30cde7d..17caf19a 100644 --- a/cparser/Checks.ml +++ b/cparser/Checks.ml @@ -18,44 +18,68 @@ open Diagnostics open Cutil open Env -let unknown_attrs loc attrs = - let unknown attr = - let attr_class = class_of_attribute attr in - if attr_class = Attr_unknown then - warning loc Unknown_attribute - "unknown attribute '%s' ignored" (name_of_attribute attr) in - List.iter unknown attrs +(* AST traversal functions *) -let unknown_attrs_typ env loc ty = - let attr = attributes_of_type env ty in - unknown_attrs loc attr +let fold_over_stmt_loc ~(expr: 'a -> location -> exp -> 'a) + ~(decl: 'a -> location -> decl -> 'a) + (a: 'a) (s: stmt) : 'a = + let rec fold a s = + match s.sdesc with + | Sskip -> a + | Sbreak -> a + | Scontinue -> a + | Slabeled(_, s1) -> fold a s1 + | Sgoto _ -> a + | Sreturn None -> a + | Sreturn (Some e) -> expr a s.sloc e + | Sasm(_, _, outs, ins, _) -> asm_operands (asm_operands a s.sloc outs) s.sloc ins + | Sdo e -> expr a s.sloc e + | Sif (e, s1, s2) -> fold (fold (expr a s.sloc e) s1) s2 + | Sseq (s1, s2) -> fold (fold a s1) s2 + | Sfor (s1, e, s2, s3) -> fold (fold (expr (fold a s1) s.sloc e) s2) s3 + | Swhile(e, s1) -> fold (expr a s.sloc e) s1 + | Sdowhile (s1, e) -> expr (fold a s1) s.sloc e + | Sswitch (e, s1) -> fold (expr a s.sloc e) s1 + | Sblock sl -> List.fold_left fold a sl + | Sdecl d -> decl a s.sloc d + and asm_operands a loc l = + List.fold_left (fun a (_, _, e) -> expr a loc e) a l + in fold a s -let unknown_attrs_decl env loc (sto, id, ty, init) = - unknown_attrs_typ env loc ty +let iter_over_stmt_loc + ?(expr = fun loc e -> ()) + ?(decl = fun loc decl -> ()) + (s: stmt) : unit = + fold_over_stmt_loc ~expr: (fun () loc e -> expr loc e) + ~decl: (fun () loc d -> decl loc d) + () s + +let fold_over_stmt ~(expr: 'a -> exp -> 'a) + ~(decl: 'a -> location -> decl -> 'a) + (a: 'a) (s: stmt) : 'a = + fold_over_stmt_loc ~expr:(fun a _ e -> expr a e) ~decl:decl a s + +let iter_over_stmt ?(expr = fun e -> ()) + ?(decl = fun loc decl -> ()) + (s:stmt) : unit = + fold_over_stmt_loc ~expr:(fun () _ e -> expr e) + ~decl:(fun () loc d -> decl loc d) () s + +let fold_over_init ~(expr: 'a -> exp -> 'a) (a: 'a) (i: init) : 'a = + let rec fold a = function + | Init_single e -> expr a e + | Init_array il -> List.fold_left fold a il + | Init_struct (_, sl) -> List.fold_left (fun a (_,i) -> fold a i) a sl + | Init_union (_, _, ui) -> fold a ui + in fold a i -let rec unknown_attrs_stmt env s = - match s.sdesc with - | Sskip - | Sbreak - | Scontinue - | Slabeled _ - | Sgoto _ - | Sreturn _ - | Sasm _ - | Sdo _ -> () - | Sif (_,s1,s2) - | Sseq (s1,s2) -> - unknown_attrs_stmt env s1; - unknown_attrs_stmt env s2 - | Sfor (s1,e,s2,s3) -> - unknown_attrs_stmt env s1; - unknown_attrs_stmt env s2; - unknown_attrs_stmt env s3 - | Swhile(_,s) - | Sdowhile (s,_) - | Sswitch (_,s) -> unknown_attrs_stmt env s - | Sblock sl -> List.iter (unknown_attrs_stmt env) sl - | Sdecl d -> unknown_attrs_decl env s.sloc d +let iter_over_init ~(expr: exp -> unit) (i:init) : unit = + fold_over_init ~expr:(fun () e -> expr e) () i + +let fold_over_decl ~(expr: 'a -> exp -> 'a) (a: 'a) loc (sto, id, ty, init) : 'a= + match init with + | Some i -> fold_over_init ~expr a i + | None -> a let traverse_program ?(decl = fun env loc d -> ()) @@ -93,7 +117,27 @@ let traverse_program pragma env g.gloc s; env in traverse env gl in - traverse (Builtins.environment ()) p + traverse (Env.initial ()) p + +(* Unknown attributes warning *) + +let unknown_attrs loc attrs = + let unknown attr = + let attr_class = class_of_attribute attr in + if attr_class = Attr_unknown then + warning loc Unknown_attribute + "unknown attribute '%s' ignored" (name_of_attribute attr) in + List.iter unknown attrs + +let unknown_attrs_typ env loc ty = + let attr = attributes_of_type env ty in + unknown_attrs loc attr + +let unknown_attrs_decl env loc (sto, id, ty, init) = + unknown_attrs_typ env loc ty + +let unknown_attrs_stmt env s = + iter_over_stmt ~decl:(unknown_attrs_decl env) s let unknown_attrs_program p = let decl env loc d = @@ -122,6 +166,7 @@ let unknown_attrs_program p = ~enum:enum p +(* Unused variables and parameters warning *) let rec vars_used_expr env e = match e.edesc with @@ -143,83 +188,21 @@ let rec vars_used_expr env e = let env = vars_used_expr env e in List.fold_left vars_used_expr env p -and vars_used_init env = function - | Init_single e -> vars_used_expr env e - | Init_array al -> List.fold_left vars_used_init env al - | Init_struct (_,sl) -> List.fold_left (fun env (_,i) -> vars_used_init env i) env sl - | Init_union (_,_,ui) -> vars_used_init env ui - -let rec vars_used_stmt env s = - match s.sdesc with - | Sbreak - | Scontinue - | Sgoto _ - | Sreturn None - | Sskip -> env - | Sreturn (Some e) - | Sdo e -> (vars_used_expr env e) - | Sseq (s1,s2) -> - let env = vars_used_stmt env s1 in - vars_used_stmt env s2 - | Sif (e,s1,s2) -> - let env = vars_used_expr env e in - let env = vars_used_stmt env s1 in - vars_used_stmt env s2 - | Sfor (s1,e,s2,s3) -> - let env = vars_used_expr env e in - let env = vars_used_stmt env s1 in - let env = vars_used_stmt env s2 in - vars_used_stmt env s3 - | Sswitch (e,s) - | Swhile (e,s) - | Sdowhile (s,e) -> - let env = vars_used_expr env e in - vars_used_stmt env s - | Sblock sl -> List.fold_left vars_used_stmt env sl - | Sdecl (sto,id,ty,init) -> - let env = match init with - | Some init ->vars_used_init env init - | None -> env in - env - | Slabeled (lbl,s) -> vars_used_stmt env s - | Sasm (attr,str,op,op2,constr) -> - let vars_asm_op env (_,_,e) = - vars_used_expr env e in - let env = List.fold_left vars_asm_op env op in - let env = List.fold_left vars_asm_op env op2 in - env - -let unused_variable env used loc (id,ty) = +and vars_used_init env init = + fold_over_init ~expr:vars_used_expr env init + +let vars_used_stmt env s = + fold_over_stmt ~expr: vars_used_expr + ~decl: (fold_over_decl ~expr: vars_used_expr) env s + +let unused_variable env used loc (id, ty) = let attr = attributes_of_type env ty in let unused_attr = find_custom_attributes ["unused";"__unused__"] attr <> [] in if not ((IdentSet.mem id used) || unused_attr) then warning loc Unused_variable "unused variable '%s'" id.name -let rec unused_variables_stmt env used s = - match s.sdesc with - | Sbreak - | Scontinue - | Sgoto _ - | Sreturn _ - | Sskip - | Sasm _ - | Sdo _ -> () - | Sseq (s1,s2) - | Sif (_,s1,s2) -> - unused_variables_stmt env used s1; - unused_variables_stmt env used s2 - | Sfor (s1,e,s2,s3) -> - unused_variables_stmt env used s1; - unused_variables_stmt env used s2; - unused_variables_stmt env used s3 - | Slabeled (_,s) - | Sswitch (_,s) - | Swhile (_,s) - | Sdowhile (s,_) -> - unused_variables_stmt env used s - | Sblock sl -> List.iter (unused_variables_stmt env used) sl - | Sdecl (sto,id,ty,init) -> - unused_variable env used s.sloc (id,ty) +let unused_variables_stmt env used s = + iter_over_stmt ~decl:(fun loc (sto, id, ty, init) -> unused_variable env used loc (id,ty)) s let unused_variables p = let fundef env loc fd = @@ -229,3 +212,166 @@ let unused_variables p = traverse_program ~fundef:fundef p + +(* Warning for conditionals that cannot be transformed into linear code *) + +(* Compute the set of local variables that do not have their address taken *) + +let rec non_stack_locals_expr vars e = + match e.edesc with + | ECast (_,e) -> non_stack_locals_expr vars e + | EUnop (Oaddrof,e) -> + begin match e.edesc with + | EVar id -> + IdentSet.remove id vars + | _ -> vars + end + | EUnop (Oderef, e) -> + (* Special optimization *(& ...) is removed in SimplExpr *) + begin match e.edesc with + | EUnop (Oaddrof,e) -> non_stack_locals_expr vars e + | _ -> non_stack_locals_expr vars e + end + | EUnop (_, e) -> + non_stack_locals_expr vars e + | EBinop (_,e1,e2,_) -> + let vars = non_stack_locals_expr vars e1 in + non_stack_locals_expr vars e2 + | EConditional (e1,e2,e3) -> + let vars = non_stack_locals_expr vars e1 in + let vars = non_stack_locals_expr vars e2 in + non_stack_locals_expr vars e3 + | ECompound (_,init) -> non_stack_locals_init vars init + | ECall (e,p) -> + let vars = non_stack_locals_expr vars e in + List.fold_left non_stack_locals_expr vars p + | _ -> vars + +and non_stack_locals_init vars init = + fold_over_init ~expr:non_stack_locals_expr vars init + +let add_vars env vars (id,ty) = + let volatile = List.mem AVolatile (attributes_of_type env ty) in + if not volatile then + IdentSet.add id vars + else + vars + +let non_stack_locals_stmt env vars s = + let decl vars loc (sto, id, ty, init) = + let vars = match init with + | Some init -> non_stack_locals_init vars init + | None -> vars in + add_vars env vars (id,ty) in + fold_over_stmt ~expr:non_stack_locals_expr ~decl:decl + vars s + +(* Check whether an expression is safe and can be always evaluated *) + +let safe_cast env tfrom tto = + match unroll env tfrom, unroll env tto with + | (TInt _ | TPtr _ | TArray _ | TFun _ | TEnum _), + (TInt _ | TPtr _ | TEnum _) -> true + | TFloat _, TFloat _ -> true + | _, _ -> equal_types env tfrom tto + +let safe_expr vars env e = + let rec expr e = + match e.edesc with + | EConst _ | ESizeof _ | EAlignof _ | ECompound _ -> true + | EVar id -> (IdentSet.mem id vars) || not (is_scalar_type env e.etyp) + | ECast (ty, e) -> + safe_cast env e.etyp ty && expr e + | EUnop (op, e) -> + unop op e + | EBinop (op, e1, e2, ty) -> + binop op e1 e2 + | EConditional _ -> false + | ECall _ -> false + and binop op e1 e2 = + let is_long_long_type ty = + match unroll env ty with + | TInt (ILongLong, _) + | TInt (IULongLong, _) -> true + | _ -> false in + match op with + | Oadd | Osub | Omul | Oand | Oor | Oxor | Oshl | Oshr -> + expr e1 && expr e2 + | Oeq | One | Olt | Ogt | Ole | Oge -> + let not_long_long = not (is_long_long_type e1.etyp) && not (is_long_long_type e2.etyp) in + not_long_long && expr e1 && expr e2 + | _ -> false + (* x.f if f has array or struct or union type *) + and unop op e = + match op with + | Ominus | Onot | Olognot | Oplus -> expr e + | Oaddrof -> + begin match e.edesc with + (* skip &*e *) + | EUnop (Oderef, e) -> expr e + (* skip &(e.f) *) + | EUnop (Odot f, e) -> expr e + | _ -> expr e + end + (* skip *&e *) + | Oderef -> + begin match e.edesc with + | EUnop (Oaddrof,e) -> expr e + | _ -> false + end + (* e.f is okay if f has array or composite type *) + | Odot m -> + let fld = field_of_dot_access env e.etyp m in + (is_array_type env fld.fld_typ || is_composite_type env fld.fld_typ) && expr e + | _ -> false in + expr e + +(* Check expressions if they contain conditionals that cannot be transformed in + linear code. The inner_cond parameter is used to mimic the translation of short + circuit logical or and logical and as well as conditional to if statements in + SimplExpr. *) + +let rec non_linear_cond_expr inner_cond vars env loc e = + match e.edesc with + | EConst _ | ESizeof _ | EAlignof _ | EVar _ -> () + | ECast (_ , e) | EUnop (_, e)-> non_linear_cond_expr false vars env loc e + | EBinop (op, e1, e2, ty) -> + let inner_cond = match op with + | Ocomma -> inner_cond + | Ologand | Ologor -> true + | _ -> false + in + non_linear_cond_expr false vars env loc e1; + non_linear_cond_expr inner_cond vars env loc e2 + | EConditional (c, e1, e2) -> + let can_cast = safe_cast env e1.etyp e.etyp && safe_cast env e2.etyp e.etyp in + if not can_cast || inner_cond || not (safe_expr vars env e1) || not (safe_expr vars env e2) then + warning loc Non_linear_cond_expr "conditional expression may not be linearized"; + non_linear_cond_expr true vars env loc e1; + non_linear_cond_expr true vars env loc e2; + | ECompound (ty, init) -> non_linear_cond_init vars env loc init + | ECall (e, params) -> + non_linear_cond_expr false vars env loc e; + List.iter (non_linear_cond_expr false vars env loc) params + +and non_linear_cond_init vars env loc init = + iter_over_init ~expr:(non_linear_cond_expr false vars env loc) init + +let non_linear_cond_stmt vars env s = + let decl loc (sto, id, ty, init) = + match init with + | None -> () + | Some init -> non_linear_cond_init vars env loc init in + iter_over_stmt_loc ~expr:(non_linear_cond_expr false vars env) ~decl:decl s + +let non_linear_conditional p = + if active_warning Non_linear_cond_expr && !Clflags.option_Obranchless then begin + let fundef env loc fd = + let vars = List.fold_left (add_vars env) IdentSet.empty fd.fd_params in + let vars = non_stack_locals_stmt env vars fd.fd_body in + non_linear_cond_stmt vars env fd.fd_body; + in + traverse_program + ~fundef:fundef + p + end diff --git a/cparser/Checks.mli b/cparser/Checks.mli index 4d61a5b8..cfd7b04d 100644 --- a/cparser/Checks.mli +++ b/cparser/Checks.mli @@ -16,3 +16,5 @@ val unknown_attrs_program: C.program -> unit val unused_variables: C.program -> unit + +val non_linear_conditional : C.program -> unit diff --git a/cparser/Cutil.ml b/cparser/Cutil.ml index cf67015a..3467c092 100644 --- a/cparser/Cutil.ml +++ b/cparser/Cutil.ml @@ -29,7 +29,7 @@ let no_loc = ("", -1) module Ident = struct type t = ident - let compare id1 id2 = Pervasives.compare id1.stamp id2.stamp + let compare id1 id2 = compare id1.stamp id2.stamp end module IdentSet = Set.Make(Ident) @@ -821,6 +821,11 @@ let is_composite_type env t = | TStruct _ | TUnion _ -> true | _ -> false +let is_array_type env t = + match unroll env t with + | TArray _ -> true + | _ -> false + let is_function_type env t = match unroll env t with | TFun _ -> true @@ -831,6 +836,12 @@ let is_anonymous_composite = function | TUnion (id,_) -> id.C.name = "" | _ -> false +let is_anonymous_type = function + | TEnum (id,_) + | TStruct (id,_) + | TUnion (id,_) -> id.C.name = "" + | _ -> false + let is_function_pointer_type env t = match unroll env t with | TPtr (ty, _) -> is_function_type env ty @@ -947,7 +958,7 @@ let binary_conversion env t1 t2 = end | _, _ -> assert false -(* Conversion on function arguments (with protoypes) *) +(* Conversion on function arguments (with prototypes) *) let argument_conversion env t = (* Arrays and functions degrade automatically to pointers *) diff --git a/cparser/Cutil.mli b/cparser/Cutil.mli index 5a1e9af3..2ddee78c 100644 --- a/cparser/Cutil.mli +++ b/cparser/Cutil.mli @@ -166,12 +166,16 @@ val is_scalar_type : Env.t -> typ -> bool (* Is type integer, float or pointer? *) val is_composite_type : Env.t -> typ -> bool (* Is type a struct or union? *) +val is_array_type : Env.t -> typ -> bool + (* Is type an array type? *) val is_function_type : Env.t -> typ -> bool (* Is type a function type? (not pointer to function) *) val is_function_pointer_type : Env.t -> typ -> bool (* Is type a pointer to function type? *) val is_anonymous_composite : typ -> bool (* Is type an anonymous composite? *) +val is_anonymous_type : typ -> bool + (* Is the type an anonymous composite or enum *) val is_qualified_array : typ -> bool (* Does the type contain a qualified array type (e.g. int[const 5])? *) val pointer_arithmetic_ok : Env.t -> typ -> bool diff --git a/cparser/Diagnostics.ml b/cparser/Diagnostics.ml index 172affab..7957375c 100644 --- a/cparser/Diagnostics.ml +++ b/cparser/Diagnostics.ml @@ -18,6 +18,10 @@ open Format open Commandline +(* Ensure that the error formatter is flushed at exit *) +let _ = + at_exit (pp_print_flush err_formatter) + (* Should errors be treated as fatal *) let error_fatal = ref false @@ -28,7 +32,7 @@ let max_error = ref 0 let diagnostics_show_option = ref true (* Test if color diagnostics are available by testing if stderr is a tty - and if the environment varibale TERM is set + and if the environment variable TERM is set *) let color_diagnostics = let term = try Sys.getenv "TERM" with Not_found -> "" in @@ -98,31 +102,48 @@ type warning_type = | Flexible_array_extensions | Tentative_incomplete_static | Reduced_alignment + | Non_linear_cond_expr + +(* List of all warnings with default status. + "true" means the warning is active by default. + "false" means the warning is off by default. *) +let all_warnings = + [ (Unnamed, true); + (Unknown_attribute, true); + (Zero_length_array, false); + (Celeven_extension, false); + (Gnu_empty_struct, true); + (Missing_declarations, true); + (Constant_conversion, true); + (Int_conversion, true); + (Varargs, true); + (Implicit_function_declaration, true); + (Pointer_type_mismatch, true); + (Compare_distinct_pointer_types, true); + (Implicit_int, true); + (Main_return_type, true); + (Invalid_noreturn, true); + (Return_type, true); + (Literal_range, true); + (Unknown_pragmas, false); + (CompCert_conformance, false); + (Inline_asm_sdump, true); + (Unused_variable, false); + (Unused_parameter, false); + (Wrong_ais_parameter, true); + (Unused_ais_parameter, true); + (Ignored_attributes, true); + (Extern_after_definition, true); + (Static_in_inline, true); + (Flexible_array_extensions, false); + (Tentative_incomplete_static, false); + (Reduced_alignment, false); + (Non_linear_cond_expr, false); + ] (* List of active warnings *) -let active_warnings: warning_type list ref = ref [ - Unnamed; - Unknown_attribute; - Gnu_empty_struct; - Missing_declarations; - Constant_conversion; - Int_conversion; - Varargs; - Implicit_function_declaration; - Pointer_type_mismatch; - Compare_distinct_pointer_types; - Implicit_int; - Main_return_type; - Invalid_noreturn; - Return_type; - Literal_range; - Inline_asm_sdump; - Wrong_ais_parameter; - Unused_ais_parameter; - Ignored_attributes; - Extern_after_definition; - Static_in_inline; -] +let active_warnings: warning_type list ref = + ref (List.map fst (List.filter snd all_warnings)) (* List of errors treated as warning *) let error_warnings: warning_type list ref = ref [] @@ -159,6 +180,7 @@ let string_of_warning = function | Flexible_array_extensions -> "flexible-array-extensions" | Tentative_incomplete_static -> "tentative-incomplete-static" | Reduced_alignment -> "reduced-alignment" + | Non_linear_cond_expr -> "non-linear-cond-expr" (* Activate the given warning *) let activate_warning w () = @@ -182,74 +204,14 @@ let warning_not_as_error w () = (* Activate all warnings *) let wall () = - active_warnings:=[ - Unnamed; - Unknown_attribute; - Zero_length_array; - Celeven_extension; - Gnu_empty_struct; - Missing_declarations; - Constant_conversion; - Int_conversion; - Varargs; - Implicit_function_declaration; - Pointer_type_mismatch; - Compare_distinct_pointer_types; - Implicit_int; - Main_return_type; - Invalid_noreturn; - Return_type; - Literal_range; - Unknown_pragmas; - CompCert_conformance; - Inline_asm_sdump; - Unused_variable; - Unused_parameter; - Wrong_ais_parameter; - Ignored_attributes; - Extern_after_definition; - Static_in_inline; - Flexible_array_extensions; - Tentative_incomplete_static; - Reduced_alignment; - ] + active_warnings:= List.map fst all_warnings let wnothing () = active_warnings :=[] (* Make all warnings an error *) let werror () = - error_warnings:=[ - Unnamed; - Unknown_attribute; - Zero_length_array; - Celeven_extension; - Gnu_empty_struct; - Missing_declarations; - Constant_conversion; - Int_conversion; - Varargs; - Implicit_function_declaration; - Pointer_type_mismatch; - Compare_distinct_pointer_types; - Implicit_int; - Main_return_type; - Invalid_noreturn; - Return_type; - Literal_range; - Unknown_pragmas; - CompCert_conformance; - Inline_asm_sdump; - Unused_variable; - Wrong_ais_parameter; - Unused_ais_parameter; - Ignored_attributes; - Extern_after_definition; - Static_in_inline; - Flexible_array_extensions; - Tentative_incomplete_static; - Reduced_alignment; - ] + error_warnings:= List.map fst all_warnings (* Generate the warning key for the message *) let key_of_warning w = @@ -403,36 +365,7 @@ let error_option w = Exact ("-Wno-error="^key), Unit ( warning_not_as_error w)] let warning_options = - error_option Unnamed @ - error_option Unknown_attribute @ - error_option Zero_length_array @ - error_option Celeven_extension @ - error_option Gnu_empty_struct @ - error_option Missing_declarations @ - error_option Constant_conversion @ - error_option Int_conversion @ - error_option Varargs @ - error_option Implicit_function_declaration @ - error_option Pointer_type_mismatch @ - error_option Compare_distinct_pointer_types @ - error_option Implicit_int @ - error_option Main_return_type @ - error_option Invalid_noreturn @ - error_option Return_type @ - error_option Literal_range @ - error_option Unknown_pragmas @ - error_option CompCert_conformance @ - error_option Inline_asm_sdump @ - error_option Unused_variable @ - error_option Unused_parameter @ - error_option Wrong_ais_parameter @ - error_option Unused_ais_parameter @ - error_option Ignored_attributes @ - error_option Extern_after_definition @ - error_option Static_in_inline @ - error_option Flexible_array_extensions @ - error_option Tentative_incomplete_static @ - error_option Reduced_alignment @ + List.concat (List.map (fun (w, active) -> error_option w) all_warnings) @ [Exact ("-Wfatal-errors"), Set error_fatal; Exact ("-fdiagnostics-color"), Ignore; (* Either output supports it or no color *) Exact ("-fno-diagnostics-color"), Unset color_diagnostics; @@ -469,7 +402,7 @@ let raise_on_errors () = let crash exn = if Version.buildnr <> "" && Version.tag <> "" then begin let backtrace = Printexc.get_backtrace () in - eprintf "%tThis is CompCert, %s, Build:%s, Tag:%s%t\n" + eprintf "%tThis is CompCert, Release %s, Build:%s, Tag:%s%t\n" bc Version.version Version.buildnr Version.tag rsc; eprintf "Backtrace (please include this in your support request):\n%s" backtrace; @@ -488,3 +421,6 @@ let crash exn = let no_loc = ("", -1) let file_loc file = (file,-10) + +let active_warning ty = + fst (classify_warning ty) <> SuppressedMsg diff --git a/cparser/Diagnostics.mli b/cparser/Diagnostics.mli index ded8019f..0f0a0ea5 100644 --- a/cparser/Diagnostics.mli +++ b/cparser/Diagnostics.mli @@ -22,22 +22,22 @@ exception Abort (** Exception raised upon fatal errors *) val check_errors : unit -> unit - (** Check whether errors occured and raise abort if an error occured *) + (** Check whether errors occurred and raise abort if an error occurred *) type warning_type = | Unnamed (** warnings which cannot be turned off *) | Unknown_attribute (** usage of unsupported/unknown attributes *) - | Zero_length_array (** gnu extension for zero lenght arrays *) + | Zero_length_array (** gnu extension for zero length arrays *) | Celeven_extension (** C11 features *) | Gnu_empty_struct (** gnu extension for empty struct *) - | Missing_declarations (** declation which do not declare anything *) + | Missing_declarations (** declaration which do not declare anything *) | Constant_conversion (** dangerous constant conversions *) | Int_conversion (** pointer <-> int conversions *) | Varargs (** promotable vararg argument *) | Implicit_function_declaration (** deprecated implicit function declaration *) | Pointer_type_mismatch (** pointer type mismatch in ?: operator *) | Compare_distinct_pointer_types (** comparison between different pointer types *) - | Implicit_int (** implict int parameter or return type *) + | Implicit_int (** implicit int parameter or return type *) | Main_return_type (** wrong return type for main *) | Invalid_noreturn (** noreturn function containing return *) | Return_type (** void return in non-void function *) @@ -55,6 +55,7 @@ type warning_type = | Flexible_array_extensions (** usange of structs with flexible arrays in structs and arrays *) | Tentative_incomplete_static (** static tentative definition with incomplete type *) | Reduced_alignment (** alignment reduction *) + | Non_linear_cond_expr (** condition that cannot be linearized *) val warning : (string * int) -> warning_type -> ('a, Format.formatter, unit, unit, unit, unit) format6 -> 'a (** [warning (f,c) w fmt arg1 ... argN] formats the arguments [arg1] to [argN] as warining according to @@ -95,3 +96,6 @@ val file_loc : string -> string * int val error_summary : unit -> unit (** Print a summary containing the numbers of errors encountered *) + +val active_warning : warning_type -> bool +(** Test whether a warning is active to avoid costly checks *) diff --git a/cparser/Elab.ml b/cparser/Elab.ml index a3915dc4..73a80c6f 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -21,7 +21,7 @@ open Machine open Cabs open C open Diagnostics -open !Cutil +open! Cutil (** * Utility functions *) @@ -39,7 +39,16 @@ let warning loc = let print_typ env fmt ty = match ty with | TNamed _ -> - Format.fprintf fmt "'%a' (aka '%a')" Cprint.typ_raw ty Cprint.typ_raw (unroll env ty) + Format.fprintf fmt "'%a'" Cprint.typ_raw ty; + let ty' = unroll env ty in + if not (is_anonymous_type ty') + then Format.fprintf fmt " (aka '%a')" Cprint.typ_raw ty' + | TStruct (id,_) when id.C.name = "" -> + Format.fprintf fmt "'struct <anonymous>'" + | TUnion (id,_) when id.C.name = "" -> + Format.fprintf fmt "'union <anonymous>'" + | TEnum (id,_) when id.C.name = "" -> + Format.fprintf fmt "'enum <anonymous>'" | _ -> Format.fprintf fmt "'%a'" Cprint.typ_raw ty let pp_field fmt id = @@ -172,7 +181,7 @@ let combine_toplevel_definitions loc env s old_sto old_ty sto ty = error loc "static declaration of '%s' follows non-static declaration" s; sto | Storage_static,_ -> Storage_static (* Static stays static *) - | Storage_extern,_ -> sto + | Storage_extern,_ -> if is_function_type env new_ty then Storage_extern else sto | Storage_default,Storage_extern -> if is_global_defined s && is_function_type env ty then warning loc Extern_after_definition "this extern declaration follows a non-extern definition and is ignored"; @@ -258,7 +267,7 @@ let enter_or_refine_function loc env id sto ty = (* Forward declarations *) -let elab_expr_f : (cabsloc -> Env.t -> Cabs.expression -> C.exp * Env.t) ref +let elab_expr_f : (Cabs.loc -> Env.t -> Cabs.expression -> C.exp * Env.t) ref = ref (fun _ _ _ -> assert false) let elab_funbody_f : (C.typ -> bool -> bool -> Env.t -> statement -> C.stmt) ref @@ -411,11 +420,12 @@ let elab_char_constant loc wide chars = warning loc Unnamed "character constant too long for its type"; (* C99 6.4.4.4 item 10: single character -> represent at type char or wchar_t *) - Ceval.normalize_int v + let k = (if List.length chars = 1 then if wide then wchar_ikind() else IChar else - IInt) + IInt) in + (Ceval.normalize_int v k, k) let elab_string_literal loc wide chars = let nbits = if wide then 8 * !config.sizeof_wchar else 8 in @@ -443,7 +453,7 @@ let elab_constant loc = function let (v, fk) = elab_float_constant f in CFloat(v, fk) | CONST_CHAR(wide, s) -> - CInt(elab_char_constant loc wide s, IInt, "") + CInt(fst (elab_char_constant loc wide s), IInt, "") | CONST_STRING(wide, s) -> elab_string_literal loc wide s @@ -836,7 +846,7 @@ and elab_type_declarator ?(fundef = false) loc env ty = function | Cabs.PROTO(d, (params, vararg)) -> elab_return_type loc env ty; let (ty, a) = get_nontype_attrs env ty in - let (params', env') = elab_parameters env params in + let (params', env') = elab_parameters loc env params in (* For a function declaration (fundef = false), the scope introduced to treat parameters ends here, so we discard the extended environment env' returned by elab_parameters. @@ -862,13 +872,15 @@ and elab_type_declarator ?(fundef = false) loc env ty = function (* Elaboration of parameters in a prototype *) -and elab_parameters env params = +and elab_parameters loc env params = (* Prototype introduces a new scope *) let (vars, env) = mmap elab_parameter (Env.new_scope env) params in (* Catch special case f(t) where t is void or a typedef to void *) match vars with | [ ( {C.name=""}, t) ] when is_void_type env t -> [],env - | _ -> vars,env + | _ -> if List.exists (fun (id, t) -> id.C.name = "" && is_void_type env t) vars then + error loc "'void' must be the only parameter"; + (vars, env) (* Elaboration of a function parameter *) @@ -941,31 +953,7 @@ and elab_name_group loc env (spec, namelist) = ((id, add_attributes_type a ty), env1) in (mmap elab_one_name env' namelist, sto) -(* Elaboration of an init-name group *) -and elab_init_name_group loc env (spec, namelist) = - let (sto, inl, noret, tydef, bty, env') = - elab_specifier ~only:(namelist=[]) loc env spec in - if noret && tydef then - error loc "'_Noreturn' can only appear on functions"; - let elab_one_name env (Init_name (Name (id, decl, attr, loc), init)) = - let ((ty, _), env1) = - elab_type_declarator loc env bty decl in - let a = elab_attributes env attr in - let has_fun_typ = is_function_type env ty in - if inl && not has_fun_typ then - error loc "'inline' can only appear on functions"; - let a' = - if noret then begin - warning loc Celeven_extension "_Noreturn functions are a C11 extension"; - if not has_fun_typ then - error loc "'_Noreturn' can only appear on functions"; - add_attributes [Attr("noreturn",[])] a - end else a in - if has_std_alignas env ty && has_fun_typ then - error loc "alignment specified for function '%s'" id; - ((id, add_attributes_type a' ty, init), env1) in - (mmap elab_one_name env' namelist, sto, tydef) (* Elaboration of a field group *) @@ -1078,7 +1066,7 @@ and elab_struct_or_union_info kind loc env members attrs = | fld :: rem -> if wrap incomplete_type loc env' fld.fld_typ then (* Must be fatal otherwise we get problems constructing the init *) - fatal_error loc "member '%a' has incomplete type" pp_field fld.fld_name; + fatal_error loc "member '%a' has incomplete type %a" pp_field fld.fld_name (print_typ env) fld.fld_typ; if wrap contains_flex_array_mem loc env' fld.fld_typ && kind = Struct then warning loc Flexible_array_extensions "%a may not be used as a struct member due to flexible array member" (print_typ env) fld.fld_typ; check_reduced_alignment loc env' fld.fld_typ; @@ -1633,7 +1621,7 @@ end; try elab_item (I.top env root ty_root) ie [] with No_default_init -> - error loc "variable has incomplete type %a" Cprint.typ ty_root; + error loc "variable has incomplete type %a" (print_typ env) ty_root; raise Exit (* Elaboration of a top-level initializer *) @@ -1708,7 +1696,7 @@ let elab_expr ctx loc env a = error "arithmetic on a pointer to an incomplete type %a in binary '%c'" (print_typ env) ty s in - let check_static_var id sto ty = + let check_static_var env id sto ty = if ctx.ctx_nonstatic_inline && sto = Storage_static && List.mem AConst (attributes_of_type env ty) @@ -1722,7 +1710,7 @@ let elab_expr ctx loc env a = | VARIABLE s -> begin match wrap Env.lookup_ident loc env s with | (id, Env.II_ident(sto, ty)) -> - check_static_var id sto ty; + check_static_var env id sto ty; { edesc = EVar id; etyp = ty },env | (id, Env.II_enum v) -> { edesc = EConst(CEnum(id, v)); etyp = TInt(enum_ikind, []) },env @@ -1824,13 +1812,54 @@ let elab_expr ctx loc env a = (print_typ env) ty (print_typ env) ty' (print_typ env) ty' (print_typ env) ty; { edesc = ECall(ident, [b2; b3]); etyp = ty },env + | CALL((VARIABLE "__builtin_sel" as a0), al) -> + begin match al with + | [a1; a2; a3] -> + let b0,env = elab env a0 in + let b1,env = elab env a1 in + let b2,env = elab env a2 in + let b3,env = elab env a3 in + if not (is_scalar_type env b1.etyp) then + error "first argument of '__builtin_sel' is not a scalar type (invalid %a)" + (print_typ env) b1.etyp; + let tyres = + match pointer_decay env b2.etyp, pointer_decay env b3.etyp with + | (TInt _ | TFloat _ | TEnum _), (TInt _ | TFloat _ | TEnum _) -> + binary_conversion env b2.etyp b3.etyp + | (TPtr(ty1, a1) as pty1), (TPtr(ty2, a2) as pty2) -> + if is_void_type env ty1 || is_void_type env ty2 then + TPtr(TVoid (add_attributes a1 a2), []) + else begin + match combine_types AttrIgnoreAll env pty1 pty2 with + | None -> + warning Pointer_type_mismatch "the second and third arguments of '__builtin_sel' have incompatible pointer types (%a and %a)" + (print_typ env) pty1 (print_typ env) pty2; + (* tolerance *) + TPtr(TVoid (add_attributes a1 a2), []) + | Some ty -> ty + end + | _, _ -> + fatal_error "wrong types (%a and %a) for the second and third arguments of '__builtin_sel'" + (print_typ env) b2.etyp (print_typ env) b3.etyp + + in + { edesc = ECall(b0, [b1; b2; b3]); etyp = tyres }, env + | _ -> + fatal_error "'__builtin_sel' expect 3 arguments" + end + | CALL(a1, al) -> let b1,env = (* Catch the old-style usage of calling a function without having declared it *) match a1 with | VARIABLE n when not (Env.ident_is_bound env n) -> - warning Implicit_function_declaration "implicit declaration of function '%s' is invalid in C99" n; + let is_builtin = String.length n > 10 + && String.sub n 0 10 = "__builtin_" in + if is_builtin then + error "use of unknown builtin '%s'" n + else + warning Implicit_function_declaration "implicit declaration of function '%s' is invalid in C99" n; let ty = TFun(TInt(IInt, []), None, false, []) in (* Check against other definitions and enter in env *) let (id, sto, env, ty, linkage) = @@ -1839,7 +1868,7 @@ let elab_expr ctx loc env a = emit_elab ~linkage env loc (Gdecl(sto, id, ty, None)); { edesc = EVar id; etyp = ty },env | _ -> elab env a1 in - let bl = mmap elab env al in + let (bl, env) = mmap elab env al in (* Extract type information *) let (res, args, vararg) = match unroll env b1.etyp with @@ -1854,14 +1883,19 @@ let elab_expr ctx loc env a = (* Type-check the arguments against the prototype *) let bl',env = match args with - | None -> bl - | Some proto -> elab_arguments 1 bl proto vararg in + | None -> + List.iter (fun arg -> + let arg_typ = argument_conversion env arg.etyp in + if incomplete_type env arg_typ then + error "argument type %a is incomplete" (print_typ env) arg.etyp; + ) bl; (bl,env) + | Some proto -> elab_arguments 1 (bl, env) proto vararg in { edesc = ECall(b1, bl'); etyp = res },env | UNARY(POSINCR, a1) -> - elab_pre_post_incr_decr Opostincr "increment" a1 + elab_pre_post_incr_decr env Opostincr "increment" a1 | UNARY(POSDECR, a1) -> - elab_pre_post_incr_decr Opostdecr "decrement" a1 + elab_pre_post_incr_decr env Opostdecr "decrement" a1 (* 6.5.4 Cast operators *) @@ -1890,6 +1924,8 @@ let elab_expr ctx loc env a = | CAST ((spec, dcl), ie) -> let (ty, env) = elab_type loc env spec dcl in + if not (is_array_type env ty) && incomplete_type env ty then + fatal_error "ill-formed compound literal with incomplete type %a" (print_typ env) ty; begin match elab_initializer loc env "<compound literal>" ty ie with | (ty', Some i) -> { edesc = ECompound(ty', i); etyp = ty' },env | (ty', None) -> fatal_error "ill-formed compound literal" @@ -2020,20 +2056,20 @@ let elab_expr ctx loc env a = end | UNARY(PREINCR, a1) -> - elab_pre_post_incr_decr Opreincr "increment" a1 + elab_pre_post_incr_decr env Opreincr "increment" a1 | UNARY(PREDECR, a1) -> - elab_pre_post_incr_decr Opredecr "decrement" a1 + elab_pre_post_incr_decr env Opredecr "decrement" a1 (* 6.5.5 to 6.5.12 Binary operator expressions *) | BINARY(MUL, a1, a2) -> - elab_binary_arithmetic "*" Omul a1 a2 + elab_binary_arithmetic env "*" Omul a1 a2 | BINARY(DIV, a1, a2) -> - elab_binary_arithmetic "/" Odiv a1 a2 + elab_binary_arithmetic env "/" Odiv a1 a2 | BINARY(MOD, a1, a2) -> - elab_binary_integer "%" Omod a1 a2 + elab_binary_integer env "%" Omod a1 a2 | BINARY(ADD, a1, a2) -> let b1,env = elab env a1 in @@ -2083,37 +2119,37 @@ let elab_expr ctx loc env a = { edesc = EBinop(Osub, b1, b2, tyop); etyp = tyres },env | BINARY(SHL, a1, a2) -> - elab_shift "<<" Oshl a1 a2 + elab_shift env "<<" Oshl a1 a2 | BINARY(SHR, a1, a2) -> - elab_shift ">>" Oshr a1 a2 + elab_shift env ">>" Oshr a1 a2 | BINARY(EQ, a1, a2) -> - elab_comparison Oeq a1 a2 + elab_comparison env Oeq a1 a2 | BINARY(NE, a1, a2) -> - elab_comparison One a1 a2 + elab_comparison env One a1 a2 | BINARY(LT, a1, a2) -> - elab_comparison Olt a1 a2 + elab_comparison env Olt a1 a2 | BINARY(GT, a1, a2) -> - elab_comparison Ogt a1 a2 + elab_comparison env Ogt a1 a2 | BINARY(LE, a1, a2) -> - elab_comparison Ole a1 a2 + elab_comparison env Ole a1 a2 | BINARY(GE, a1, a2) -> - elab_comparison Oge a1 a2 + elab_comparison env Oge a1 a2 | BINARY(BAND, a1, a2) -> - elab_binary_integer "&" Oand a1 a2 + elab_binary_integer env "&" Oand a1 a2 | BINARY(BOR, a1, a2) -> - elab_binary_integer "|" Oor a1 a2 + elab_binary_integer env "|" Oor a1 a2 | BINARY(XOR, a1, a2) -> - elab_binary_integer "^" Oxor a1 a2 + elab_binary_integer env "^" Oxor a1 a2 (* 6.5.13 and 6.5.14 Logical operator expressions *) | BINARY(AND, a1, a2) -> - elab_logical_operator "&&" Ologand a1 a2 + elab_logical_operator env "&&" Ologand a1 a2 | BINARY(OR, a1, a2) -> - elab_logical_operator "||" Ologor a1 a2 + elab_logical_operator env "||" Ologor a1 a2 (* 6.5.15 Conditional expressions *) | QUESTION(a1, a2, a3) -> @@ -2229,7 +2265,7 @@ let elab_expr ctx loc env a = { edesc = EBinop (Ocomma, b1, b2, ty2); etyp = ty2 },env (* Elaboration of pre- or post- increment/decrement *) - and elab_pre_post_incr_decr op msg a1 = + and elab_pre_post_incr_decr env op msg a1 = let b1,env = elab env a1 in if not (is_modifiable_lvalue env b1) then error "expression is not assignable"; @@ -2238,7 +2274,7 @@ let elab_expr ctx loc env a = { edesc = EUnop(op, b1); etyp = b1.etyp },env (* Elaboration of binary operators over integers *) - and elab_binary_integer msg op a1 a2 = + and elab_binary_integer env msg op a1 a2 = let b1,env = elab env a1 in let b2,env = elab env a2 in if not ((is_integer_type env b1.etyp) && (is_integer_type env b2.etyp)) then @@ -2248,7 +2284,7 @@ let elab_expr ctx loc env a = { edesc = EBinop(op, b1, b2, tyres); etyp = tyres },env (* Elaboration of binary operators over arithmetic types *) - and elab_binary_arithmetic msg op a1 a2 = + and elab_binary_arithmetic env msg op a1 a2 = let b1,env = elab env a1 in let b2,env = elab env a2 in if not ((is_arith_type env b1.etyp) && (is_arith_type env b2.etyp)) then @@ -2258,7 +2294,7 @@ let elab_expr ctx loc env a = { edesc = EBinop(op, b1, b2, tyres); etyp = tyres },env (* Elaboration of shift operators *) - and elab_shift msg op a1 a2 = + and elab_shift env msg op a1 a2 = let b1,env = elab env a1 in let b2,env = elab env a2 in if not ((is_integer_type env b1.etyp) && (is_integer_type env b2.etyp)) then @@ -2268,7 +2304,7 @@ let elab_expr ctx loc env a = { edesc = EBinop(op, b1, b2, tyres); etyp = tyres },env (* Elaboration of comparisons *) - and elab_comparison op a1 a2 = + and elab_comparison env op a1 a2 = let b1,env = elab env a1 in let b2,env = elab env a2 in let resdesc = @@ -2305,7 +2341,7 @@ let elab_expr ctx loc env a = { edesc = resdesc; etyp = TInt(IInt, []) },env (* Elaboration of && and || *) - and elab_logical_operator msg op a1 a2 = + and elab_logical_operator env msg op a1 a2 = let b1,env = elab env a1 in let b2,env = elab env a2 in if not ((is_scalar_type env b1.etyp) && (is_scalar_type env b2.etyp)) then @@ -2373,113 +2409,106 @@ let __func__type_and_init s = (* Elaboration of top-level and local definitions *) -let enter_typedefs loc env sto dl = - if sto <> Storage_default then - error loc "non-default storage class on 'typedef' definition"; - if dl = [] then - warning loc Missing_declarations "typedef requires a name"; - List.fold_left (fun env (s, ty, init) -> - if init <> NO_INIT then - error loc "initializer in typedef"; - if has_std_alignas env ty then - error loc "alignment specified for typedef '%s'" s; - List.iter - (fun a -> match class_of_attribute a with - | Attr_object | Attr_struct -> - error loc "attribute '%s' not allowed in 'typedef'" - (name_of_attribute a) - | _ -> ()) - (attributes_of_type_no_expand ty); - match previous_def Env.lookup_typedef env s with - | Some (s',ty') when Env.in_current_scope env s' -> - if equal_types env ty ty' then begin - warning loc Celeven_extension "redefinition of typedef '%s' is a C11 extension" s; - env - end else begin - error loc "typedef redefinition with different types (%a vs %a)" - (print_typ env) ty (print_typ env) ty'; - env - end - | _ -> - if redef Env.lookup_ident env s then - error loc "redefinition of '%s' as different kind of symbol" s; - let (id, env') = Env.enter_typedef env s ty in - check_reduced_alignment loc env' ty; - emit_elab env loc (Gtypedef(id, ty)); - env') env dl - -let enter_decdefs local nonstatic_inline loc env sto dl = - (* Sanity checks on storage class *) - if (sto = Storage_auto || sto = Storage_register) && not local then - fatal_error loc "illegal storage class %s on file-scoped variable" - (name_of_storage_class sto); - if sto <> Storage_default && dl = [] then - warning loc Missing_declarations "declaration does not declare anything"; - let enter_decdef (decls, env) (s, ty, init) = - let isfun = is_function_type env ty in - if sto = Storage_register && has_std_alignas env ty then - error loc "alignment specified for 'register' object '%s'" s; - if sto = Storage_extern && init <> NO_INIT then - error loc "'extern' declaration variable has an initializer"; - if local && isfun then begin - match sto with - | Storage_static -> - error loc "function declared in block scope cannot have 'static' storage class" - | Storage_auto | Storage_register -> - error loc "illegal storage class %s on function" - (name_of_storage_class sto) - | _ -> () - end; - if is_qualified_array ty then - error loc "type qualifier used in array declarator outside of function prototype"; - (* Local variable declarations with default storage are treated as 'auto'. - Local function declarations with default storage remain with - default storage. *) - let sto1 = - if local && sto = Storage_default && not isfun - then Storage_auto - else sto in - (* enter ident in environment with declared type, because - initializer can refer to the ident *) - let (id, sto', env1, ty, linkage) = - enter_or_refine_ident local loc env s sto1 ty in - if init <> NO_INIT && not local then - add_global_define loc s; - if not isfun && is_void_type env ty then - fatal_error loc "'%s' has incomplete type" s; - (* process the initializer *) - let (ty', init') = elab_initializer loc env1 s ty init in - (* update environment with refined type *) - let env2 = Env.add_ident env1 id sto' ty' in - (* check for incomplete type *) - if not isfun && wrap incomplete_type loc env ty' then - if not local && sto' = Storage_static then begin - warning loc Tentative_incomplete_static "tentative static definition with incomplete type"; - end else if local && sto' <> Storage_extern then - error loc "variable has incomplete type %a" (print_typ env) ty'; - (* check if alignment is reduced *) - check_reduced_alignment loc env ty'; - (* check for static variables in nonstatic inline functions *) - if local && nonstatic_inline - && sto' = Storage_static - && not (List.mem AConst (attributes_of_type env ty')) then - warning loc Static_in_inline "non-constant static local variable '%s' in inline function may be different in different files" s; - if local && not isfun && sto' <> Storage_extern && sto' <> Storage_static then - (* Local definition *) - ((sto', id, ty', init') :: decls, env2) +let enter_typedef loc env sto (s, ty, init) = + if init <> NO_INIT then + error loc "initializer in typedef"; + if has_std_alignas env ty then + error loc "alignment specified for typedef '%s'" s; + List.iter + (fun a -> match class_of_attribute a with + | Attr_object | Attr_struct -> + error loc "attribute '%s' not allowed in 'typedef'" + (name_of_attribute a) + | _ -> ()) + (attributes_of_type_no_expand ty); + match previous_def Env.lookup_typedef env s with + | Some (s',ty') when Env.in_current_scope env s' -> + if equal_types env ty ty' then begin + warning loc Celeven_extension "redefinition of typedef '%s' is a C11 extension" s; + env + end else begin - (* Global definition *) - emit_elab ~linkage env2 loc (Gdecl(sto', id, ty', init')); - (* Make sure the initializer is constant. *) - begin match init' with + error loc "redefinition of typedef '%s' with different type (%a vs %a)" + s (print_typ env) ty (print_typ env) ty'; + env + end + | _ -> + if redef Env.lookup_ident env s then + error loc "redefinition of '%s' as different kind of symbol" s; + let (id, env') = Env.enter_typedef env s ty in + check_reduced_alignment loc env' ty; + emit_elab env loc (Gtypedef(id, ty)); + env' + +let enter_decdef local nonstatic_inline loc sto (decls, env) (s, ty, init) = + let isfun = is_function_type env ty in + let has_init = init <> NO_INIT in + if sto = Storage_register && has_std_alignas env ty then + error loc "alignment specified for 'register' object '%s'" s; + if sto = Storage_extern && has_init then + error loc "'extern' declaration variable has an initializer"; + if local && isfun then begin + match sto with + | Storage_static -> + error loc "function declared in block scope cannot have 'static' storage class" + | Storage_auto | Storage_register -> + error loc "illegal storage class %s on function" + (name_of_storage_class sto) + | _ -> () + end; + if is_qualified_array ty then + error loc "type qualifier used in array declarator outside of function prototype"; + (* Local variable declarations with default storage are treated as 'auto'. + Local function declarations with default storage remain with + default storage. *) + let sto1 = + if local && sto = Storage_default && not isfun + then Storage_auto + else sto in + (* enter ident in environment with declared type, because + initializer can refer to the ident *) + let (id, sto', env1, ty, linkage) = + enter_or_refine_ident local loc env s sto1 ty in + if has_init && not local then + add_global_define loc s; + (* check if the type is void or incomplete and the declaration is initialized *) + if not isfun then begin + let incomplete_init = not (is_array_type env1 ty) && wrap incomplete_type loc env1 ty && has_init in + if is_void_type env1 ty || incomplete_init then + fatal_error loc "variable '%s' has incomplete type %a" s (print_typ env) ty; + end; + (* process the initializer *) + let (ty', init') = elab_initializer loc env1 s ty init in + (* update environment with refined type *) + let env2 = Env.add_ident env1 id sto' ty' in + (* check for incomplete type *) + if not isfun && wrap incomplete_type loc env ty' then + if not local && sto' = Storage_static then begin + warning loc Tentative_incomplete_static "tentative static definition with incomplete type"; + end + else if local && sto' <> Storage_extern then + error loc "variable '%s' has incomplete type %a" s (print_typ env) ty'; + (* check if alignment is reduced *) + check_reduced_alignment loc env ty'; + (* check for static variables in nonstatic inline functions *) + if local && nonstatic_inline + && sto' = Storage_static + && not (List.mem AConst (attributes_of_type env ty')) then + warning loc Static_in_inline "non-constant static local variable '%s' in inline function may be different in different files" s; + if local && not isfun && sto' <> Storage_extern && sto' <> Storage_static then + (* Local definition *) + ((sto', id, ty', init') :: decls, env2) + else begin + (* Global definition *) + emit_elab ~linkage env2 loc (Gdecl(sto', id, ty', init')); + (* Make sure the initializer is constant. *) + begin match init' with | Some i when not (Ceval.is_constant_init env2 i) -> - error loc "initializer is not a compile-time constant" + error loc "initializer is not a compile-time constant" | _ -> () - end; - (decls, env2) - end in - let (decls, env') = List.fold_left enter_decdef ([], env) dl in - (List.rev decls, env') + end; + (decls, env2) + end (* Processing of K&R-style function definitions. Synopsis: T f(X1, ..., Xn) @@ -2652,10 +2681,10 @@ let elab_fundef genv spec name defs body loc = and additionally they should have an identifier. In both cases a fatal error is raised in order to avoid problems at later places. *) let add_param env (id, ty) = - if wrap incomplete_type loc env ty then - fatal_error loc "parameter has incomplete type"; if id.C.name = "" then fatal_error loc "parameter name omitted"; + if wrap incomplete_type loc env ty then + fatal_error loc "parameter '%s' has incomplete type %a" id.C.name (print_typ env) ty; Env.add_ident env id Storage_default ty in (* Enter parameters and extra declarations in the local environment. @@ -2663,7 +2692,7 @@ let elab_fundef genv spec name defs body loc = For prototyped functions this has been done by [elab_fundef_name] already, but some parameter may have been shadowed by the function name, while it should be the other way around, e.g. - [int f(int f) { return f+1; }], with [f] refering to the + [int f(int f) { return f+1; }], with [f] referring to the parameter [f] and not to the function [f] within the body of the function. *) let lenv = @@ -2735,6 +2764,51 @@ let elab_fundef genv spec name defs body loc = genv (* Definitions *) +let elab_decdef (for_loop: bool) (local: bool) (nonstatic_inline: bool) + (env: Env.t) ((spec, namelist): Cabs.init_name_group) + (loc: Cabs.loc) : decl list * Env.t = + let (sto, inl, noret, tydef, bty, env') = + elab_specifier ~only:(namelist=[]) loc env spec in + (* Sanity checks on storage class *) + if tydef then begin + if sto <> Storage_default then + error loc "non-default storage class on 'typedef' definition"; + if namelist = [] then + warning loc Missing_declarations "typedef requires a name"; + end else begin + if (sto = Storage_auto || sto = Storage_register) && not local then + fatal_error loc "illegal storage class %s on file-scoped variable" + (name_of_storage_class sto); + if sto <> Storage_default && namelist = [] then + warning loc Missing_declarations "declaration does not declare anything"; + end; + let elab_one_name (decls, env) (Init_name (Name (id, decl, attr, loc), init)) = + let ((ty, _), env1) = + elab_type_declarator loc env bty decl in + let a = elab_attributes env attr in + let has_fun_typ = is_function_type env ty in + if for_loop && (has_fun_typ || sto = Storage_extern || sto = Storage_static || tydef) then + error loc "declaration of non-local variable in 'for' loop" ; + if has_fun_typ then begin + if noret then + warning loc Celeven_extension "_Noreturn functions are a C11 extension"; + end else begin + if inl then + error loc "'inline' can only appear on functions"; + if noret then + error loc "'_Noreturn' can only appear on functions"; + end; + let a' = if noret then add_attributes [Attr ("noreturn", [])] a else a in + if has_std_alignas env ty && has_fun_typ then + error loc "alignment specified for function '%s'" id; + let decl = (id, add_attributes_type a' ty, init) in + if tydef then + (decls, enter_typedef loc env1 sto decl) + else + enter_decdef local nonstatic_inline loc sto (decls, env1) decl + in + let (decls, env') = List.fold_left elab_one_name ([],env') namelist in + (List.rev decls, env') let elab_definition (for_loop: bool) (local: bool) (nonstatic_inline: bool) (env: Env.t) (def: Cabs.definition) @@ -2749,18 +2823,7 @@ let elab_definition (for_loop: bool) (local: bool) (nonstatic_inline: bool) (* "int x = 12, y[10], *z" *) | DECDEF(init_name_group, loc) -> - let ((dl, env1), sto, tydef) = - elab_init_name_group loc env init_name_group in - if for_loop then begin - let fun_declaration = List.exists (fun (_, ty, _) -> is_function_type env ty) dl in - if fun_declaration || sto = Storage_extern || sto = Storage_static || tydef then - error loc "declaration of non-local variable in 'for' loop" ; - end; - if tydef then - let env2 = enter_typedefs loc env1 sto dl - in ([], env2) - else - enter_decdefs local nonstatic_inline loc env1 sto dl + elab_decdef for_loop local nonstatic_inline env init_name_group loc (* pragma *) | PRAGMA(s, loc) -> @@ -2887,48 +2950,49 @@ let rec elab_stmt env ctx s = (* 6.8.4 Conditional statements *) | If(a, s1, s2, loc) -> - let a',env = elab_expr ctx loc env a in - if not (is_scalar_type env a'.etyp) then + let a',env' = elab_expr ctx loc (Env.new_scope env) a in + if not (is_scalar_type env' a'.etyp) then error loc "controlling expression of 'if' does not have scalar type (%a invalid)" - (print_typ env) a'.etyp; - let s1',env = elab_stmt env ctx s1 in - let s2',env = + (print_typ env') a'.etyp; + let s1' = elab_stmt_new_scope env' ctx s1 in + let s2' = match s2 with - | None -> sskip,env - | Some s2 -> elab_stmt env ctx s2 + | None -> sskip + | Some s2 -> elab_stmt_new_scope env' ctx s2 in { sdesc = Sif(a', s1', s2'); sloc = elab_loc loc },env (* 6.8.5 Iterative statements *) | WHILE(a, s1, loc) -> - let a',env = elab_expr ctx loc env a in - if not (is_scalar_type env a'.etyp) then + let a',env' = elab_expr ctx loc (Env.new_scope env) a in + if not (is_scalar_type env' a'.etyp) then error loc "controlling expression of 'while' does not have scalar type (%a invalid)" - (print_typ env) a'.etyp; - let s1',env = elab_stmt env (ctx_loop ctx) s1 in + (print_typ env') a'.etyp; + let s1' = elab_stmt_new_scope env' (ctx_loop ctx) s1 in { sdesc = Swhile(a', s1'); sloc = elab_loc loc },env | DOWHILE(a, s1, loc) -> - let s1',env = elab_stmt env (ctx_loop ctx) s1 in - let a',env = elab_expr ctx loc env a in - if not (is_scalar_type env a'.etyp) then + let s1' = elab_stmt_new_scope env (ctx_loop ctx) s1 in + let a',env' = elab_expr ctx loc (Env.new_scope env) a in + if not (is_scalar_type env' a'.etyp) then error loc "controlling expression of 'while' does not have scalar type (%a invalid)" - (print_typ env) a'.etyp; + (print_typ env') a'.etyp; { sdesc = Sdowhile(s1', a'); sloc = elab_loc loc },env | FOR(fc, a2, a3, s1, loc) -> + let env' = Env.new_scope env in let (a1', env_decls, decls') = match fc with | Some (FC_EXP a1) -> - let a1,env = elab_for_expr ctx loc env (Some a1) in + let a1,env = elab_for_expr ctx loc env' (Some a1) in (a1, env, None) | None -> - let a1,env = elab_for_expr ctx loc env None in + let a1,env = elab_for_expr ctx loc env' None in (a1, env, None) | Some (FC_DECL def) -> let (dcl, env') = elab_definition true true ctx.ctx_nonstatic_inline - (Env.new_scope env) def in + env' def in let loc = elab_loc (Cabshelper.get_definitionloc def) in (sskip, env', Some(List.map (fun d -> {sdesc = Sdecl d; sloc = loc}) dcl)) in @@ -2940,7 +3004,7 @@ let rec elab_stmt env ctx s = if not (is_scalar_type env_test a2'.etyp) then error loc "controlling expression of 'for' does not have scalar type (%a invalid)" (print_typ env) a2'.etyp; let a3',env_for = elab_for_expr ctx loc env_test a3 in - let s1',env_body = elab_stmt env_for (ctx_loop ctx) s1 in + let s1' = elab_stmt_new_scope env_for (ctx_loop ctx) s1 in let sfor = { sdesc = Sfor(a1', a2', a3', s1'); sloc = elab_loc loc } in begin match decls' with | None -> sfor,env @@ -2949,11 +3013,11 @@ let rec elab_stmt env ctx s = (* 6.8.4 Switch statement *) | SWITCH(a, s1, loc) -> - let a',env = elab_expr ctx loc env a in - if not (is_integer_type env a'.etyp) then + let a',env' = elab_expr ctx loc (Env.new_scope env) a in + if not (is_integer_type env' a'.etyp) then error loc "controlling expression of 'switch' does not have integer type (%a invalid)" - (print_typ env) a'.etyp; - let s1',env = elab_stmt env (ctx_switch ctx) s1 in + (print_typ env') a'.etyp; + let s1' = elab_stmt_new_scope env' (ctx_switch ctx) s1 in check_switch_cases s1'; { sdesc = Sswitch(a', s1'); sloc = elab_loc loc },env @@ -3027,6 +3091,10 @@ let rec elab_stmt env ctx s = | DEFINITION def -> error (Cabshelper.get_definitionloc def) "ill-placed definition"; sskip,env +(* Elaborate a statement as a block whose scope is a strict subset of the scope + of its enclosing block. *) +and elab_stmt_new_scope env ctx s = + fst (elab_stmt (Env.new_scope env) ctx s) and elab_block loc env ctx b = let b',_ = elab_block_body (Env.new_scope env) ctx b in @@ -3079,10 +3147,11 @@ let _ = elab_funbody_f := elab_funbody let elab_file prog = reset(); - let env = Builtins.environment () in + let env = Env.initial () in let elab_def env d = snd (elab_definition false false false env d) in ignore (List.fold_left elab_def env prog); let p = elaborated_program () in Checks.unused_variables p; Checks.unknown_attrs_program p; + Checks.non_linear_conditional p; p diff --git a/cparser/Elab.mli b/cparser/Elab.mli index f701e8c5..68e33d06 100644 --- a/cparser/Elab.mli +++ b/cparser/Elab.mli @@ -18,8 +18,8 @@ val elab_file : Cabs.definition list -> C.program definitions as produced by the parser into a program in C abstract syntax. *) -val elab_int_constant : Cabs.cabsloc -> string -> int64 * C.ikind +val elab_int_constant : Cabs.loc -> string -> int64 * C.ikind val elab_float_constant : Cabs.floatInfo -> C.float_cst * C.fkind -val elab_char_constant : Cabs.cabsloc -> bool -> int64 list -> int64 +val elab_char_constant : Cabs.loc -> bool -> int64 list -> int64 * C.ikind (* These auxiliary functions are exported so that they can be reused in other projects that deal with C-style source languages. *) diff --git a/cparser/Env.ml b/cparser/Env.ml index 5fa4571a..4723a725 100644 --- a/cparser/Env.ml +++ b/cparser/Env.ml @@ -276,6 +276,46 @@ let add_enum env id info = let add_types env_old env_new = { env_new with env_ident = env_old.env_ident;env_scope = env_old.env_scope;} +(* Initial environment describing the built-in types and functions *) + +module Init = struct + +let env = ref empty +let idents = ref [] +let decls = ref [] + +let no_loc = ("", -1) + +let add_typedef (s, ty) = + let (id, env') = enter_typedef !env s ty in + env := env'; + idents := id :: !idents; + decls := {gdesc = Gtypedef(id, ty); gloc = no_loc} :: !decls + +let add_function (s, (res, args, va)) = + let ty = + TFun(res, + Some (List.map (fun ty -> (fresh_ident "", ty)) args), + va, []) in + let (id, env') = enter_ident !env s Storage_extern ty in + env := env'; + idents := id :: !idents; + decls := + {gdesc = Gdecl(Storage_extern, id, ty, None); gloc = no_loc} :: !decls + +end + +let initial () = !Init.env +let initial_identifiers () = !Init.idents +let initial_declarations () = List.rev !Init.decls + +let set_builtins blt = + Init.env := empty; + Init.idents := []; + Init.decls := []; + List.iter Init.add_typedef blt.builtin_typedefs; + List.iter Init.add_function blt.builtin_functions + (* Error reporting *) open Printf diff --git a/cparser/Env.mli b/cparser/Env.mli index 7ea2c514..1baab68f 100644 --- a/cparser/Env.mli +++ b/cparser/Env.mli @@ -77,3 +77,10 @@ val add_typedef : t -> C.ident -> typedef_info -> t val add_enum : t -> C.ident -> enum_info -> t val add_types : t -> t -> t + +(* Initial environment describing the builtin types and functions *) + +val initial: unit -> t +val initial_identifiers: unit -> C.ident list +val initial_declarations: unit -> C.globdecl list +val set_builtins: C.builtins -> unit diff --git a/cparser/GCC.ml b/cparser/GCC.ml index 010d12f3..458e51d3 100644 --- a/cparser/GCC.ml +++ b/cparser/GCC.ml @@ -38,10 +38,10 @@ let intPtrType = TPtr(TInt(IInt, []), []) let sizeType() = TInt(size_t_ikind(), []) let builtins = { - Builtins.typedefs = [ + builtin_typedefs = [ "__builtin_va_list", voidPtrType ]; - Builtins.functions = [ + builtin_functions = [ "__builtin___fprintf_chk", (intType, [ voidPtrType; intType; charConstPtrType ], true) (* first argument is really FILE*, not void*, but we don't want to build in the definition for FILE *); "__builtin___memcpy_chk", (voidPtrType, [ voidPtrType; voidConstPtrType; sizeType(); sizeType() ], false); "__builtin___memmove_chk", (voidPtrType, [ voidPtrType; voidConstPtrType; sizeType(); sizeType() ], false); diff --git a/cparser/Lexer.mll b/cparser/Lexer.mll index b2a668f0..e44a330f 100644 --- a/cparser/Lexer.mll +++ b/cparser/Lexer.mll @@ -20,14 +20,21 @@ open Pre_parser_aux module SSet = Set.Make(String) -let lexicon : (string, Cabs.cabsloc -> token) Hashtbl.t = Hashtbl.create 17 +let lexicon : (string, Cabs.loc -> token) Hashtbl.t = Hashtbl.create 17 let ignored_keywords : SSet.t ref = ref SSet.empty +let reserved_keyword loc id = + Diagnostics.fatal_error (loc.Cabs.filename, loc.Cabs.lineno) + "illegal use of reserved keyword `%s'" id + let () = List.iter (fun (key, builder) -> Hashtbl.add lexicon key builder) - [ ("_Alignas", fun loc -> ALIGNAS loc); + [ + ("_Alignas", fun loc -> ALIGNAS loc); ("_Alignof", fun loc -> ALIGNOF loc); ("_Bool", fun loc -> UNDERSCORE_BOOL loc); + ("_Complex", fun loc -> reserved_keyword loc "_Complex"); + ("_Imaginary", fun loc -> reserved_keyword loc "_Imaginary"); ("__alignof", fun loc -> ALIGNOF loc); ("__alignof__", fun loc -> ALIGNOF loc); ("__asm", fun loc -> ASM loc); @@ -170,7 +177,7 @@ let identifier_nondigit = let identifier = identifier_nondigit (identifier_nondigit|digit)* (* Whitespaces *) -let whitespace_char_no_newline = [' ' '\t' '\012' '\r'] +let whitespace_char_no_newline = [' ' '\t' '\011' '\012' '\r'] (* Integer constants *) let nonzero_digit = ['1'-'9'] @@ -427,10 +434,7 @@ and singleline_comment = parse | _ { singleline_comment lexbuf } { - open Streams - open Specif - open Parser - open !Aut.GramDefs + open Parser.MenhirLibParser.Inter (* This is the main entry point to the lexer. *) @@ -456,8 +460,8 @@ and singleline_comment = parse curr_id := None; let loc = currentLoc lexbuf in let token = - if SSet.mem id !types_context then TYPEDEF_NAME (id, ref TypedefId, loc) - else VAR_NAME (id, ref VarId, loc) + if SSet.mem id !types_context then Pre_parser.TYPEDEF_NAME (id, ref TypedefId, loc) + else Pre_parser.VAR_NAME (id, ref VarId, loc) in Queue.push token tokens; token @@ -490,133 +494,129 @@ and singleline_comment = parse (* [tokens_stream filename text] runs the pre_parser and produces a stream of (appropriately classified) tokens. *) - let tokens_stream filename text : token coq_Stream = + let tokens_stream filename text : buffer = let tokens = Queue.create () in let buffer = ref ErrorReports.Zero in invoke_pre_parser filename text (lexer tokens buffer) buffer; - let rec compute_token_stream () = - let loop t v = - Cons (Coq_existT (t, Obj.magic v), Lazy.from_fun compute_token_stream) - in + let rec compute_buffer () = + let loop t = Buf_cons (t, Lazy.from_fun compute_buffer) in match Queue.pop tokens with - | ADD_ASSIGN loc -> loop ADD_ASSIGN't loc - | AND loc -> loop AND't loc - | ANDAND loc -> loop ANDAND't loc - | AND_ASSIGN loc -> loop AND_ASSIGN't loc - | AUTO loc -> loop AUTO't loc - | BANG loc -> loop BANG't loc - | BAR loc -> loop BAR't loc - | BARBAR loc -> loop BARBAR't loc - | UNDERSCORE_BOOL loc -> loop UNDERSCORE_BOOL't loc - | BREAK loc -> loop BREAK't loc - | BUILTIN_VA_ARG loc -> loop BUILTIN_VA_ARG't loc - | BUILTIN_OFFSETOF loc -> loop BUILTIN_OFFSETOF't loc - | CASE loc -> loop CASE't loc - | CHAR loc -> loop CHAR't loc - | COLON loc -> loop COLON't loc - | COMMA loc -> loop COMMA't loc - | CONST loc -> loop CONST't loc - | CONSTANT (cst, loc) -> loop CONSTANT't (cst, loc) - | CONTINUE loc -> loop CONTINUE't loc - | DEC loc -> loop DEC't loc - | DEFAULT loc -> loop DEFAULT't loc - | DIV_ASSIGN loc -> loop DIV_ASSIGN't loc - | DO loc -> loop DO't loc - | DOT loc -> loop DOT't loc - | DOUBLE loc -> loop DOUBLE't loc - | ELLIPSIS loc -> loop ELLIPSIS't loc - | ELSE loc -> loop ELSE't loc - | ENUM loc -> loop ENUM't loc - | EOF -> loop EOF't () - | EQ loc -> loop EQ't loc - | EQEQ loc -> loop EQEQ't loc - | EXTERN loc -> loop EXTERN't loc - | FLOAT loc -> loop FLOAT't loc - | FOR loc -> loop FOR't loc - | GEQ loc -> loop GEQ't loc - | GOTO loc -> loop GOTO't loc - | GT loc -> loop GT't loc - | HAT loc -> loop HAT't loc - | IF loc -> loop IF't loc - | INC loc -> loop INC't loc - | INLINE loc -> loop INLINE't loc - | INT loc -> loop INT't loc - | LBRACE loc -> loop LBRACE't loc - | LBRACK loc -> loop LBRACK't loc - | LEFT loc -> loop LEFT't loc - | LEFT_ASSIGN loc -> loop LEFT_ASSIGN't loc - | LEQ loc -> loop LEQ't loc - | LONG loc -> loop LONG't loc - | LPAREN loc -> loop LPAREN't loc - | LT loc -> loop LT't loc - | MINUS loc -> loop MINUS't loc - | MOD_ASSIGN loc -> loop MOD_ASSIGN't loc - | MUL_ASSIGN loc -> loop MUL_ASSIGN't loc - | NEQ loc -> loop NEQ't loc - | NORETURN loc -> loop NORETURN't loc - | OR_ASSIGN loc -> loop OR_ASSIGN't loc - | PACKED loc -> loop PACKED't loc - | PERCENT loc -> loop PERCENT't loc - | PLUS loc -> loop PLUS't loc - | PTR loc -> loop PTR't loc - | QUESTION loc -> loop QUESTION't loc - | RBRACE loc -> loop RBRACE't loc - | RBRACK loc -> loop RBRACK't loc - | REGISTER loc -> loop REGISTER't loc - | RESTRICT loc -> loop RESTRICT't loc - | RETURN loc -> loop RETURN't loc - | RIGHT loc -> loop RIGHT't loc - | RIGHT_ASSIGN loc -> loop RIGHT_ASSIGN't loc - | RPAREN loc -> loop RPAREN't loc - | SEMICOLON loc -> loop SEMICOLON't loc - | SHORT loc -> loop SHORT't loc - | SIGNED loc -> loop SIGNED't loc - | SIZEOF loc -> loop SIZEOF't loc - | SLASH loc -> loop SLASH't loc - | STAR loc -> loop STAR't loc - | STATIC loc -> loop STATIC't loc - | STRING_LITERAL (wide, str, loc) -> + | Pre_parser.ADD_ASSIGN loc -> loop (Parser.ADD_ASSIGN loc) + | Pre_parser.AND loc -> loop (Parser.AND loc) + | Pre_parser.ANDAND loc -> loop (Parser.ANDAND loc) + | Pre_parser.AND_ASSIGN loc -> loop (Parser.AND_ASSIGN loc) + | Pre_parser.AUTO loc -> loop (Parser.AUTO loc) + | Pre_parser.BANG loc -> loop (Parser.BANG loc) + | Pre_parser.BAR loc -> loop (Parser.BAR loc) + | Pre_parser.BARBAR loc -> loop (Parser.BARBAR loc) + | Pre_parser.UNDERSCORE_BOOL loc -> loop (Parser.UNDERSCORE_BOOL loc) + | Pre_parser.BREAK loc -> loop (Parser.BREAK loc) + | Pre_parser.BUILTIN_VA_ARG loc -> loop (Parser.BUILTIN_VA_ARG loc) + | Pre_parser.BUILTIN_OFFSETOF loc -> loop (Parser.BUILTIN_OFFSETOF loc) + | Pre_parser.CASE loc -> loop (Parser.CASE loc) + | Pre_parser.CHAR loc -> loop (Parser.CHAR loc) + | Pre_parser.COLON loc -> loop (Parser.COLON loc) + | Pre_parser.COMMA loc -> loop (Parser.COMMA loc) + | Pre_parser.CONST loc -> loop (Parser.CONST loc) + | Pre_parser.CONSTANT (cst, loc) -> loop (Parser.CONSTANT (cst, loc)) + | Pre_parser.CONTINUE loc -> loop (Parser.CONTINUE loc) + | Pre_parser.DEC loc -> loop (Parser.DEC loc) + | Pre_parser.DEFAULT loc -> loop (Parser.DEFAULT loc) + | Pre_parser.DIV_ASSIGN loc -> loop (Parser.DIV_ASSIGN loc) + | Pre_parser.DO loc -> loop (Parser.DO loc) + | Pre_parser.DOT loc -> loop (Parser.DOT loc) + | Pre_parser.DOUBLE loc -> loop (Parser.DOUBLE loc) + | Pre_parser.ELLIPSIS loc -> loop (Parser.ELLIPSIS loc) + | Pre_parser.ELSE loc -> loop (Parser.ELSE loc) + | Pre_parser.ENUM loc -> loop (Parser.ENUM loc) + | Pre_parser.EOF -> loop (Parser.EOF ()) + | Pre_parser.EQ loc -> loop (Parser.EQ loc) + | Pre_parser.EQEQ loc -> loop (Parser.EQEQ loc) + | Pre_parser.EXTERN loc -> loop (Parser.EXTERN loc) + | Pre_parser.FLOAT loc -> loop (Parser.FLOAT loc) + | Pre_parser.FOR loc -> loop (Parser.FOR loc) + | Pre_parser.GEQ loc -> loop (Parser.GEQ loc) + | Pre_parser.GOTO loc -> loop (Parser.GOTO loc) + | Pre_parser.GT loc -> loop (Parser.GT loc) + | Pre_parser.HAT loc -> loop (Parser.HAT loc) + | Pre_parser.IF loc -> loop (Parser.IF_ loc) + | Pre_parser.INC loc -> loop (Parser.INC loc) + | Pre_parser.INLINE loc -> loop (Parser.INLINE loc) + | Pre_parser.INT loc -> loop (Parser.INT loc) + | Pre_parser.LBRACE loc -> loop (Parser.LBRACE loc) + | Pre_parser.LBRACK loc -> loop (Parser.LBRACK loc) + | Pre_parser.LEFT loc -> loop (Parser.LEFT loc) + | Pre_parser.LEFT_ASSIGN loc -> loop (Parser.LEFT_ASSIGN loc) + | Pre_parser.LEQ loc -> loop (Parser.LEQ loc) + | Pre_parser.LONG loc -> loop (Parser.LONG loc) + | Pre_parser.LPAREN loc -> loop (Parser.LPAREN loc) + | Pre_parser.LT loc -> loop (Parser.LT loc) + | Pre_parser.MINUS loc -> loop (Parser.MINUS loc) + | Pre_parser.MOD_ASSIGN loc -> loop (Parser.MOD_ASSIGN loc) + | Pre_parser.MUL_ASSIGN loc -> loop (Parser.MUL_ASSIGN loc) + | Pre_parser.NEQ loc -> loop (Parser.NEQ loc) + | Pre_parser.NORETURN loc -> loop (Parser.NORETURN loc) + | Pre_parser.OR_ASSIGN loc -> loop (Parser.OR_ASSIGN loc) + | Pre_parser.PACKED loc -> loop (Parser.PACKED loc) + | Pre_parser.PERCENT loc -> loop (Parser.PERCENT loc) + | Pre_parser.PLUS loc -> loop (Parser.PLUS loc) + | Pre_parser.PTR loc -> loop (Parser.PTR loc) + | Pre_parser.QUESTION loc -> loop (Parser.QUESTION loc) + | Pre_parser.RBRACE loc -> loop (Parser.RBRACE loc) + | Pre_parser.RBRACK loc -> loop (Parser.RBRACK loc) + | Pre_parser.REGISTER loc -> loop (Parser.REGISTER loc) + | Pre_parser.RESTRICT loc -> loop (Parser.RESTRICT loc) + | Pre_parser.RETURN loc -> loop (Parser.RETURN loc) + | Pre_parser.RIGHT loc -> loop (Parser.RIGHT loc) + | Pre_parser.RIGHT_ASSIGN loc -> loop (Parser.RIGHT_ASSIGN loc) + | Pre_parser.RPAREN loc -> loop (Parser.RPAREN loc) + | Pre_parser.SEMICOLON loc -> loop (Parser.SEMICOLON loc) + | Pre_parser.SHORT loc -> loop (Parser.SHORT loc) + | Pre_parser.SIGNED loc -> loop (Parser.SIGNED loc) + | Pre_parser.SIZEOF loc -> loop (Parser.SIZEOF loc) + | Pre_parser.SLASH loc -> loop (Parser.SLASH loc) + | Pre_parser.STAR loc -> loop (Parser.STAR loc) + | Pre_parser.STATIC loc -> loop (Parser.STATIC loc) + | Pre_parser.STRING_LITERAL (wide, str, loc) -> (* Merge consecutive string literals *) let rec doConcat wide str = - try - match Queue.peek tokens with - | STRING_LITERAL (wide', str', loc) -> - ignore (Queue.pop tokens); - let (wide'', str'') = doConcat wide' str' in - if str'' <> [] - then (wide || wide'', str @ str'') - else (wide, str) - | _ -> - (wide, str) - with Queue.Empty -> (wide, str) in - let (wide', str') = doConcat wide str in - loop STRING_LITERAL't ((wide', str'), loc) - | STRUCT loc -> loop STRUCT't loc - | SUB_ASSIGN loc -> loop SUB_ASSIGN't loc - | SWITCH loc -> loop SWITCH't loc - | TILDE loc -> loop TILDE't loc - | TYPEDEF loc -> loop TYPEDEF't loc - | TYPEDEF_NAME (id, typ, loc) - | VAR_NAME (id, typ, loc) -> - let terminal = match !typ with - | VarId -> VAR_NAME't - | TypedefId -> TYPEDEF_NAME't - | OtherId -> OTHER_NAME't + match Queue.peek tokens with + | Pre_parser.STRING_LITERAL (wide', str', loc) -> + ignore (Queue.pop tokens); + let (wide'', str'') = doConcat wide' str' in + if str'' <> [] + then (wide || wide'', str @ str'') + else (wide, str) + | _ -> (wide, str) + | exception Queue.Empty -> (wide, str) in - loop terminal (id, loc) - | UNION loc -> loop UNION't loc - | UNSIGNED loc -> loop UNSIGNED't loc - | VOID loc -> loop VOID't loc - | VOLATILE loc -> loop VOLATILE't loc - | WHILE loc -> loop WHILE't loc - | XOR_ASSIGN loc -> loop XOR_ASSIGN't loc - | ALIGNAS loc -> loop ALIGNAS't loc - | ALIGNOF loc -> loop ALIGNOF't loc - | ATTRIBUTE loc -> loop ATTRIBUTE't loc - | ASM loc -> loop ASM't loc - | PRAGMA (s, loc) -> loop PRAGMA't (s, loc) - | PRE_NAME _ -> assert false + let (wide', str') = doConcat wide str in + loop (Parser.STRING_LITERAL ((wide', str'), loc)) + | Pre_parser.STRUCT loc -> loop (Parser.STRUCT loc) + | Pre_parser.SUB_ASSIGN loc -> loop (Parser.SUB_ASSIGN loc) + | Pre_parser.SWITCH loc -> loop (Parser.SWITCH loc) + | Pre_parser.TILDE loc -> loop (Parser.TILDE loc) + | Pre_parser.TYPEDEF loc -> loop (Parser.TYPEDEF loc) + | Pre_parser.TYPEDEF_NAME (id, typ, loc) + | Pre_parser.VAR_NAME (id, typ, loc) -> + begin match !typ with + | VarId -> loop (Parser.VAR_NAME (id, loc)) + | TypedefId -> loop (Parser.TYPEDEF_NAME (id, loc)) + | OtherId -> loop (Parser.OTHER_NAME (id, loc)) + end + | Pre_parser.UNION loc -> loop (Parser.UNION loc) + | Pre_parser.UNSIGNED loc -> loop (Parser.UNSIGNED loc) + | Pre_parser.VOID loc -> loop (Parser.VOID loc) + | Pre_parser.VOLATILE loc -> loop (Parser.VOLATILE loc) + | Pre_parser.WHILE loc -> loop (Parser.WHILE loc) + | Pre_parser.XOR_ASSIGN loc -> loop (Parser.XOR_ASSIGN loc) + | Pre_parser.ALIGNAS loc -> loop (Parser.ALIGNAS loc) + | Pre_parser.ALIGNOF loc -> loop (Parser.ALIGNOF loc) + | Pre_parser.ATTRIBUTE loc -> loop (Parser.ATTRIBUTE loc) + | Pre_parser.ASM loc -> loop (Parser.ASM loc) + | Pre_parser.PRAGMA (s, loc) -> loop (Parser.PRAGMA (s, loc)) + | Pre_parser.PRE_NAME _ -> assert false in - Lazy.from_fun compute_token_stream + Lazy.from_fun compute_buffer } diff --git a/cparser/Machine.ml b/cparser/Machine.ml index 089f2483..97bedb3b 100644 --- a/cparser/Machine.ml +++ b/cparser/Machine.ml @@ -237,6 +237,11 @@ let rv64 = struct_passing_style = SP_ref_callee; (* Wrong *) struct_return_style = SR_ref } (* to check *) +let aarch64 = + { i32lpll64 with name = "aarch64"; + struct_passing_style = SP_ref_callee; (* Wrong *) + struct_return_style = SR_ref } (* Wrong *) + (* Add GCC extensions re: sizeof and alignof *) let gcc_extensions c = diff --git a/cparser/Machine.mli b/cparser/Machine.mli index 8971e2a3..ca7de17b 100644 --- a/cparser/Machine.mli +++ b/cparser/Machine.mli @@ -86,6 +86,7 @@ val arm_littleendian : t val arm_bigendian : t val rv32 : t val rv64 : t +val aarch64 : t val gcc_extensions : t -> t val compcert_interpreter : t -> t diff --git a/cparser/MenhirLib/Alphabet.v b/cparser/MenhirLib/Alphabet.v deleted file mode 100644 index a13f69b0..00000000 --- a/cparser/MenhirLib/Alphabet.v +++ /dev/null @@ -1,320 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Import Int31. -Require Import Cyclic31. -Require Import Omega. -Require Import List. -Require Import Syntax. -Require Import Relations. -Require Import RelationClasses. - -Local Obligation Tactic := intros. - -(** A comparable type is equiped with a [compare] function, that define an order - relation. **) -Class Comparable (A:Type) := { - compare : A -> A -> comparison; - compare_antisym : forall x y, CompOpp (compare x y) = compare y x; - compare_trans : forall x y z c, - (compare x y) = c -> (compare y z) = c -> (compare x z) = c -}. - -Theorem compare_refl {A:Type} (C: Comparable A) : - forall x, compare x x = Eq. -Proof. -intros. -pose proof (compare_antisym x x). -destruct (compare x x); intuition; try discriminate. -Qed. - -(** The corresponding order is a strict order. **) -Definition comparableLt {A:Type} (C: Comparable A) : relation A := - fun x y => compare x y = Lt. - -Instance ComparableLtStrictOrder {A:Type} (C: Comparable A) : - StrictOrder (comparableLt C). -Proof. -apply Build_StrictOrder. -unfold Irreflexive, Reflexive, complement, comparableLt. -intros. -pose proof H. -rewrite <- compare_antisym in H. -rewrite H0 in H. -discriminate H. -unfold Transitive, comparableLt. -intros x y z. -apply compare_trans. -Qed. - -(** nat is comparable. **) -Program Instance natComparable : Comparable nat := - { compare := Nat.compare }. -Next Obligation. -symmetry. -destruct (Nat.compare x y) as [] eqn:?. -rewrite Nat.compare_eq_iff in Heqc. -destruct Heqc. -rewrite Nat.compare_eq_iff. -trivial. -rewrite <- nat_compare_lt in *. -rewrite <- nat_compare_gt in *. -trivial. -rewrite <- nat_compare_lt in *. -rewrite <- nat_compare_gt in *. -trivial. -Qed. -Next Obligation. -destruct c. -rewrite Nat.compare_eq_iff in *; destruct H; assumption. -rewrite <- nat_compare_lt in *. -apply (Nat.lt_trans _ _ _ H H0). -rewrite <- nat_compare_gt in *. -apply (gt_trans _ _ _ H H0). -Qed. - -(** A pair of comparable is comparable. **) -Program Instance PairComparable {A:Type} (CA:Comparable A) {B:Type} (CB:Comparable B) : - Comparable (A*B) := - { compare := fun x y => - let (xa, xb) := x in let (ya, yb) := y in - match compare xa ya return comparison with - | Eq => compare xb yb - | x => x - end }. -Next Obligation. -destruct x, y. -rewrite <- (compare_antisym a a0). -rewrite <- (compare_antisym b b0). -destruct (compare a a0); intuition. -Qed. -Next Obligation. -destruct x, y, z. -destruct (compare a a0) as [] eqn:?, (compare a0 a1) as [] eqn:?; -try (rewrite <- H0 in H; discriminate); -try (destruct (compare a a1) as [] eqn:?; - try (rewrite <- compare_antisym in Heqc0; - rewrite CompOpp_iff in Heqc0; - rewrite (compare_trans _ _ _ _ Heqc0 Heqc2) in Heqc1; - discriminate); - try (rewrite <- compare_antisym in Heqc1; - rewrite CompOpp_iff in Heqc1; - rewrite (compare_trans _ _ _ _ Heqc2 Heqc1) in Heqc0; - discriminate); - assumption); -rewrite (compare_trans _ _ _ _ Heqc0 Heqc1); -try assumption. -apply (compare_trans _ _ _ _ H H0). -Qed. - -(** Special case of comparable, where equality is usual equality. **) -Class ComparableUsualEq {A:Type} (C: Comparable A) := - compare_eq : forall x y, compare x y = Eq -> x = y. - -(** Boolean equality for a [Comparable]. **) -Definition compare_eqb {A:Type} {C:Comparable A} (x y:A) := - match compare x y with - | Eq => true - | _ => false - end. - -Theorem compare_eqb_iff {A:Type} {C:Comparable A} {U:ComparableUsualEq C} : - forall x y, compare_eqb x y = true <-> x = y. -Proof. -unfold compare_eqb. -intuition. -apply compare_eq. -destruct (compare x y); intuition; discriminate. -destruct H. -rewrite compare_refl; intuition. -Qed. - -(** [Comparable] provides a decidable equality. **) -Definition compare_eqdec {A:Type} {C:Comparable A} {U:ComparableUsualEq C} (x y:A): - {x = y} + {x <> y}. -Proof. -destruct (compare x y) as [] eqn:?; [left; apply compare_eq; intuition | ..]; - right; intro; destruct H; rewrite compare_refl in Heqc; discriminate. -Defined. - -Instance NComparableUsualEq : ComparableUsualEq natComparable := Nat.compare_eq. - -(** A pair of ComparableUsualEq is ComparableUsualEq **) -Instance PairComparableUsualEq - {A:Type} {CA:Comparable A} (UA:ComparableUsualEq CA) - {B:Type} {CB:Comparable B} (UB:ComparableUsualEq CB) : - ComparableUsualEq (PairComparable CA CB). -Proof. -intros x y; destruct x, y; simpl. -pose proof (compare_eq a a0); pose proof (compare_eq b b0). -destruct (compare a a0); try discriminate. -intuition. -destruct H2, H0. -reflexivity. -Qed. - -(** An [Finite] type is a type with the list of all elements. **) -Class Finite (A:Type) := { - all_list : list A; - all_list_forall : forall x:A, In x all_list -}. - -(** An alphabet is both [ComparableUsualEq] and [Finite]. **) -Class Alphabet (A:Type) := { - AlphabetComparable :> Comparable A; - AlphabetComparableUsualEq :> ComparableUsualEq AlphabetComparable; - AlphabetFinite :> Finite A -}. - -(** The [Numbered] class provides a conveniant way to build [Alphabet] instances, - with a good computationnal complexity. It is mainly a injection from it to - [Int31] **) -Class Numbered (A:Type) := { - inj : A -> int31; - surj : int31 -> A; - surj_inj_compat : forall x, surj (inj x) = x; - inj_bound : int31; - inj_bound_spec : forall x, (phi (inj x) < phi inj_bound)%Z -}. - -Program Instance NumberedAlphabet {A:Type} (N:Numbered A) : Alphabet A := - { AlphabetComparable := - {| compare := fun x y => compare31 (inj x) (inj y) |}; - AlphabetFinite := - {| all_list := fst (iter_int31 inj_bound _ - (fun p => (cons (surj (snd p)) (fst p), incr (snd p))) ([], 0%int31)) |} }. -Next Obligation. apply Zcompare_antisym. Qed. -Next Obligation. -destruct c. unfold compare31 in *. -rewrite Z.compare_eq_iff in *. congruence. -eapply Zcompare_Lt_trans. apply H. apply H0. -eapply Zcompare_Gt_trans. apply H. apply H0. -Qed. -Next Obligation. -intros x y H. unfold compare, compare31 in H. -rewrite Z.compare_eq_iff in *. -rewrite <- surj_inj_compat, <- phi_inv_phi with (inj y), <- H. -rewrite phi_inv_phi, surj_inj_compat; reflexivity. -Qed. -Next Obligation. -rewrite iter_int31_iter_nat. -pose proof (inj_bound_spec x). -match goal with |- In x (fst ?p) => destruct p as [] eqn:? end. -replace inj_bound with i in H. -revert l i Heqp x H. -induction (Z.abs_nat (phi inj_bound)); intros. -inversion Heqp; clear Heqp; subst. -rewrite spec_0 in H. pose proof (phi_bounded (inj x)). omega. -simpl in Heqp. -destruct nat_rect; specialize (IHn _ _ (eq_refl _) x); simpl in *. -inversion Heqp. subst. clear Heqp. -rewrite phi_incr in H. -pose proof (phi_bounded i0). -pose proof (phi_bounded (inj x)). -destruct (Z_lt_le_dec (Z.succ (phi i0)) (2 ^ Z.of_nat size)%Z). -rewrite Zmod_small in H by omega. -apply Zlt_succ_le, Zle_lt_or_eq in H. -destruct H; simpl; auto. left. -rewrite <- surj_inj_compat, <- phi_inv_phi with (inj x), H, phi_inv_phi; reflexivity. -replace (Z.succ (phi i0)) with (2 ^ Z.of_nat size)%Z in H by omega. -rewrite Z_mod_same_full in H. -exfalso; omega. -rewrite <- phi_inv_phi with i, <- phi_inv_phi with inj_bound; f_equal. -pose proof (phi_bounded inj_bound); pose proof (phi_bounded i). -rewrite <- Z.abs_eq with (phi i), <- Z.abs_eq with (phi inj_bound) by omega. -clear H H0 H1. -do 2 rewrite <- Zabs2Nat.id_abs. -f_equal. -revert l i Heqp. -assert (Z.abs_nat (phi inj_bound) < Z.abs_nat (2^31)). -apply Zabs_nat_lt, phi_bounded. -induction (Z.abs_nat (phi inj_bound)); intros. -inversion Heqp; reflexivity. -inversion Heqp; clear H1 H2 Heqp. -match goal with |- _ (_ (_ (snd ?p))) = _ => destruct p end. -pose proof (phi_bounded i0). -erewrite <- IHn, <- Zabs2Nat.inj_succ in H |- *; eauto; try omega. -rewrite phi_incr, Zmod_small; intuition; try omega. -apply inj_lt in H. -pose proof Z.le_le_succ_r. -do 2 rewrite Zabs2Nat.id_abs, Z.abs_eq in H; now eauto. -Qed. - -(** Previous class instances for [option A] **) -Program Instance OptionComparable {A:Type} (C:Comparable A) : Comparable (option A) := - { compare := fun x y => - match x, y return comparison with - | None, None => Eq - | None, Some _ => Lt - | Some _, None => Gt - | Some x, Some y => compare x y - end }. -Next Obligation. -destruct x, y; intuition. -apply compare_antisym. -Qed. -Next Obligation. -destruct x, y, z; try now intuition; -try (rewrite <- H in H0; discriminate). -apply (compare_trans _ _ _ _ H H0). -Qed. - -Instance OptionComparableUsualEq {A:Type} {C:Comparable A} (U:ComparableUsualEq C) : - ComparableUsualEq (OptionComparable C). -Proof. -intros x y. -destruct x, y; intuition; try discriminate. -rewrite (compare_eq a a0); intuition. -Qed. - -Program Instance OptionFinite {A:Type} (E:Finite A) : Finite (option A) := - { all_list := None :: map Some all_list }. -Next Obligation. -destruct x; intuition. -right. -apply in_map. -apply all_list_forall. -Defined. - -(** Definitions of [FSet]/[FMap] from [Comparable] **) -Require Import OrderedTypeAlt. -Require FSetAVL. -Require FMapAVL. -Import OrderedType. - -Module Type ComparableM. - Parameter t : Type. - Declare Instance tComparable : Comparable t. -End ComparableM. - -Module OrderedTypeAlt_from_ComparableM (C:ComparableM) <: OrderedTypeAlt. - Definition t := C.t. - Definition compare : t -> t -> comparison := compare. - - Infix "?=" := compare (at level 70, no associativity). - - Lemma compare_sym x y : (y?=x) = CompOpp (x?=y). - Proof. exact (Logic.eq_sym (compare_antisym x y)). Qed. - Lemma compare_trans c x y z : - (x?=y) = c -> (y?=z) = c -> (x?=z) = c. - Proof. - apply compare_trans. - Qed. -End OrderedTypeAlt_from_ComparableM. - -Module OrderedType_from_ComparableM (C:ComparableM) <: OrderedType. - Module Alt := OrderedTypeAlt_from_ComparableM C. - Include (OrderedType_from_Alt Alt). -End OrderedType_from_ComparableM. diff --git a/cparser/MenhirLib/Grammar.v b/cparser/MenhirLib/Grammar.v deleted file mode 100644 index 8e427cd9..00000000 --- a/cparser/MenhirLib/Grammar.v +++ /dev/null @@ -1,166 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Import List. -Require Import Syntax. -Require Import Alphabet. -Require Import Orders. -Require Tuples. - -(** The terminal non-terminal alphabets of the grammar. **) -Module Type Alphs. - Parameters terminal nonterminal : Type. - Declare Instance TerminalAlph: Alphabet terminal. - Declare Instance NonTerminalAlph: Alphabet nonterminal. -End Alphs. - -(** Definition of the alphabet of symbols, given the alphabet of terminals - and the alphabet of non terminals **) -Module Symbol(Import A:Alphs). - - Inductive symbol := - | T: terminal -> symbol - | NT: nonterminal -> symbol. - - Program Instance SymbolAlph : Alphabet symbol := - { AlphabetComparable := {| compare := fun x y => - match x, y return comparison with - | T _, NT _ => Gt - | NT _, T _ => Lt - | T x, T y => compare x y - | NT x, NT y => compare x y - end |}; - AlphabetFinite := {| all_list := - map T all_list++map NT all_list |} }. - Next Obligation. - destruct x; destruct y; intuition; apply compare_antisym. - Qed. - Next Obligation. - destruct x; destruct y; destruct z; intuition; try discriminate. - apply (compare_trans _ t0); intuition. - apply (compare_trans _ n0); intuition. - Qed. - Next Obligation. - intros x y. - destruct x; destruct y; try discriminate; intros. - rewrite (compare_eq t t0); intuition. - rewrite (compare_eq n n0); intuition. - Qed. - Next Obligation. - rewrite in_app_iff. - destruct x; [left | right]; apply in_map; apply all_list_forall. - Qed. - -End Symbol. - -Module Type T. - Export Tuples. - - Include Alphs <+ Symbol. - - (** [symbol_semantic_type] maps a symbols to the type of its semantic - values. **) - Parameter symbol_semantic_type: symbol -> Type. - - (** The type of productions identifiers **) - Parameter production : Type. - Declare Instance ProductionAlph : Alphabet production. - - (** Accessors for productions: left hand side, right hand side, - and semantic action. The semantic actions are given in the form - of curryfied functions, that take arguments in the reverse order. **) - Parameter prod_lhs: production -> nonterminal. - Parameter prod_rhs_rev: production -> list symbol. - Parameter prod_action: - forall p:production, - arrows_left - (map symbol_semantic_type (rev (prod_rhs_rev p))) - (symbol_semantic_type (NT (prod_lhs p))). - -End T. - -Module Defs(Import G:T). - - (** A token is a terminal and a semantic value for this terminal. **) - Definition token := {t:terminal & symbol_semantic_type (T t)}. - - (** A grammar creates a relation between word of tokens and semantic values. - This relation is parametrized by the head symbol. It defines the - "semantics" of the grammar. This relation is defined by a notion of - parse tree. **) - Inductive parse_tree: - forall (head_symbol:symbol) (word:list token) - (semantic_value:symbol_semantic_type head_symbol), Type := - - (** A single token has its semantic value as semantic value, for the - corresponding terminal as head symbol. **) - | Terminal_pt: - forall (t:terminal) (sem:symbol_semantic_type (T t)), - parse_tree (T t) - [existT (fun t => symbol_semantic_type (T t)) t sem] sem - - (** Given a production, if a word has a list of semantic values for the - right hand side as head symbols, then this word has the semantic value - given by the semantic action of the production for the left hand side - as head symbol.**) - | Non_terminal_pt: - forall {p:production} {word:list token} - {semantic_values:tuple (map symbol_semantic_type (rev (prod_rhs_rev p)))}, - parse_tree_list (rev (prod_rhs_rev p)) word semantic_values -> - parse_tree (NT (prod_lhs p)) word (uncurry (prod_action p) semantic_values) - - (** Basically the same relation as before, but for list of head symbols (ie. - We are building a forest of syntax trees. It is mutually recursive with the - previous relation **) - with parse_tree_list: - forall (head_symbols:list symbol) (word:list token) - (semantic_values:tuple (map symbol_semantic_type head_symbols)), - Type := - - (** The empty word has [()] as semantic for [[]] as head symbols list **) - | Nil_ptl: parse_tree_list [] [] () - - (** The cons of the semantic value for one head symbol and for a list of head - symbols **) - | Cons_ptl: - (** The semantic for the head **) - forall {head_symbolt:symbol} {wordt:list token} - {semantic_valuet:symbol_semantic_type head_symbolt}, - parse_tree head_symbolt wordt semantic_valuet -> - - (** and the semantic for the tail **) - forall {head_symbolsq:list symbol} {wordq:list token} - {semantic_valuesq:tuple (map symbol_semantic_type head_symbolsq)}, - parse_tree_list head_symbolsq wordq semantic_valuesq -> - - (** give the semantic of the cons **) - parse_tree_list - (head_symbolt::head_symbolsq) - (wordt++wordq) - (semantic_valuet, semantic_valuesq). - - - Fixpoint pt_size {head_symbol word sem} (tree:parse_tree head_symbol word sem) := - match tree with - | Terminal_pt _ _ => 1 - | Non_terminal_pt l => S (ptl_size l) - end - with ptl_size {head_symbols word sems} (tree:parse_tree_list head_symbols word sems) := - match tree with - | Nil_ptl => 0 - | Cons_ptl t q => - pt_size t + ptl_size q - end. -End Defs. diff --git a/cparser/MenhirLib/Interpreter.v b/cparser/MenhirLib/Interpreter.v deleted file mode 100644 index 4ac02693..00000000 --- a/cparser/MenhirLib/Interpreter.v +++ /dev/null @@ -1,228 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Import Streams. -Require Import List. -Require Import Syntax. -Require Automaton. -Require Import Alphabet. - -Module Make(Import A:Automaton.T). - -(** The error monad **) -Inductive result (A:Type) := - | Err: result A - | OK: A -> result A. - -Arguments Err [A]. -Arguments OK [A]. - -Definition bind {A B: Type} (f: result A) (g: A -> result B): result B := - match f with - | OK x => g x - | Err => Err - end. - -Definition bind2 {A B C: Type} (f: result (A * B)) (g: A -> B -> result C): - result C := - match f with - | OK (x, y) => g x y - | Err => Err - end. - -Notation "'do' X <- A ; B" := (bind A (fun X => B)) - (at level 200, X ident, A at level 100, B at level 200). - -Notation "'do' ( X , Y ) <- A ; B" := (bind2 A (fun X Y => B)) - (at level 200, X ident, Y ident, A at level 100, B at level 200). - -(** Some operations on streams **) - -(** Concatenation of a list and a stream **) -Fixpoint app_str {A:Type} (l:list A) (s:Stream A) := - match l with - | nil => s - | cons t q => Cons t (app_str q s) - end. - -Infix "++" := app_str (right associativity, at level 60). - -Lemma app_str_app_assoc {A:Type} (l1 l2:list A) (s:Stream A) : - l1 ++ (l2 ++ s) = (l1 ++ l2) ++ s. -Proof. -induction l1. -reflexivity. -simpl. -rewrite IHl1. -reflexivity. -Qed. - -(** The type of a non initial state: the type of semantic values associated - with the last symbol of this state. *) -Definition noninitstate_type state := - symbol_semantic_type (last_symb_of_non_init_state state). - -(** The stack of the automaton : it can be either nil or contains a non - initial state, a semantic value for the symbol associted with this state, - and a nested stack. **) -Definition stack := list (sigT noninitstate_type). (* eg. list {state & state_type state} *) - -Section Init. - -Variable init : initstate. - -(** The top state of a stack **) -Definition state_of_stack (stack:stack): state := - match stack with - | [] => init - | existT _ s _::_ => s - end. - -(** [pop] pops some symbols from the stack. It returns the popped semantic - values using [sem_popped] as an accumulator and discards the popped - states.**) -Fixpoint pop (symbols_to_pop:list symbol) (stack_cur:stack): - forall {A:Type} (action:arrows_right A (map symbol_semantic_type symbols_to_pop)), - result (stack * A) := - match symbols_to_pop return forall {A:Type} (action:arrows_right A (map _ symbols_to_pop)), result (stack * A) with - | [] => fun A action => OK (stack_cur, action) - | t::q => fun A action => - match stack_cur with - | existT _ state_cur sem::stack_rec => - match compare_eqdec (last_symb_of_non_init_state state_cur) t with - | left e => - let sem_conv := eq_rect _ symbol_semantic_type sem _ e in - pop q stack_rec (action sem_conv) - | right _ => Err - end - | [] => Err - end - end. - -(** [step_result] represents the result of one step of the automaton : it can - fail, accept or progress. [Fail_sr] means that the input is incorrect. - [Accept_sr] means that this is the last step of the automaton, and it - returns the semantic value of the input word. [Progress_sr] means that - some progress has been made, but new steps are needed in order to accept - a word. - - For [Accept_sr] and [Progress_sr], the result contains the new input buffer. - - [Fail_sr] means that the input word is rejected by the automaton. It is - different to [Err] (from the error monad), which mean that the automaton is - bogus and has perfomed a forbidden action. **) -Inductive step_result := - | Fail_sr: step_result - | Accept_sr: symbol_semantic_type (NT (start_nt init)) -> Stream token -> step_result - | Progress_sr: stack -> Stream token -> step_result. - -Program Definition prod_action': - forall p, - arrows_right (symbol_semantic_type (NT (prod_lhs p))) - (map symbol_semantic_type (prod_rhs_rev p)):= - fun p => - eq_rect _ (fun x => x) (prod_action p) _ _. -Next Obligation. -unfold arrows_left, arrows_right; simpl. -rewrite <- fold_left_rev_right, <- map_rev, rev_involutive. -reflexivity. -Qed. - -(** [reduce_step] does a reduce action : - - pops some elements from the stack - - execute the action of the production - - follows the goto for the produced non terminal symbol **) -Definition reduce_step stack_cur production buffer: result step_result := - do (stack_new, sem) <- - pop (prod_rhs_rev production) stack_cur (prod_action' production); - match goto_table (state_of_stack stack_new) (prod_lhs production) with - | Some (exist _ state_new e) => - let sem := eq_rect _ _ sem _ e in - OK (Progress_sr (existT noninitstate_type state_new sem::stack_new) buffer) - | None => - match stack_new with - | [] => - match compare_eqdec (prod_lhs production) (start_nt init) with - | left e => - let sem := eq_rect _ (fun nt => symbol_semantic_type (NT nt)) sem _ e in - OK (Accept_sr sem buffer) - | right _ => Err - end - | _::_ => Err - end - end. - -(** One step of parsing. **) -Definition step stack_cur buffer: result step_result := - match action_table (state_of_stack stack_cur) with - | Default_reduce_act production => - reduce_step stack_cur production buffer - | Lookahead_act awt => - match Streams.hd buffer with - | existT _ term sem => - match awt term with - | Shift_act state_new e => - let sem_conv := eq_rect _ symbol_semantic_type sem _ e in - OK (Progress_sr (existT noninitstate_type state_new sem_conv::stack_cur) - (Streams.tl buffer)) - | Reduce_act production => - reduce_step stack_cur production buffer - | Fail_action => - OK Fail_sr - end - end - end. - -(** The parsing use a [nat] parameter [n_steps], so that we do not have to prove - terminaison, which is difficult. So the result of a parsing is either - a failure (the automaton has rejected the input word), either a timeout - (the automaton has spent all the given [n_steps]), either a parsed semantic - value with a rest of the input buffer. -**) -Inductive parse_result := - | Fail_pr: parse_result - | Timeout_pr: parse_result - | Parsed_pr: symbol_semantic_type (NT (start_nt init)) -> Stream token -> parse_result. - -Fixpoint parse_fix stack_cur buffer n_steps: result parse_result:= - match n_steps with - | O => OK Timeout_pr - | S it => - do r <- step stack_cur buffer; - match r with - | Fail_sr => OK Fail_pr - | Accept_sr t buffer_new => OK (Parsed_pr t buffer_new) - | Progress_sr s buffer_new => parse_fix s buffer_new it - end - end. - -Definition parse buffer n_steps: result parse_result := - parse_fix [] buffer n_steps. - -End Init. - -Arguments Fail_sr [init]. -Arguments Accept_sr [init] _ _. -Arguments Progress_sr [init] _ _. - -Arguments Fail_pr [init]. -Arguments Timeout_pr [init]. -Arguments Parsed_pr [init] _ _. - -End Make. - -Module Type T(A:Automaton.T). - Include (Make A). -End T. diff --git a/cparser/MenhirLib/Interpreter_complete.v b/cparser/MenhirLib/Interpreter_complete.v deleted file mode 100644 index 2e64b8da..00000000 --- a/cparser/MenhirLib/Interpreter_complete.v +++ /dev/null @@ -1,686 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Import Streams. -Require Import ProofIrrelevance. -Require Import Equality. -Require Import List. -Require Import Syntax. -Require Import Alphabet. -Require Import Arith. -Require Grammar. -Require Automaton. -Require Interpreter. -Require Validator_complete. - -Module Make(Import A:Automaton.T) (Import Inter:Interpreter.T A). -Module Import Valid := Validator_complete.Make A. - -(** * Completeness Proof **) - -Section Completeness_Proof. - -Hypothesis complete: complete. - -Proposition nullable_stable: nullable_stable. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. -Proposition first_stable: first_stable. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. -Proposition start_future: start_future. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. -Proposition terminal_shift: terminal_shift. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. -Proposition end_reduce: end_reduce. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. -Proposition start_goto: start_goto. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. -Proposition non_terminal_goto: non_terminal_goto. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. -Proposition non_terminal_closed: non_terminal_closed. -Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed. - -(** If the nullable predicate has been validated, then it is correct. **) -Lemma nullable_correct: - forall head sem word, word = [] -> - parse_tree head word sem -> nullable_symb head = true -with nullable_correct_list: - forall heads sems word, word = [] -> - parse_tree_list heads word sems -> nullable_word heads = true. -Proof with eauto. -intros. -destruct X. -congruence. -apply nullable_stable... -intros. -destruct X; simpl... -apply andb_true_intro. -apply app_eq_nil in H; destruct H; split... -Qed. - -(** If the first predicate has been validated, then it is correct. **) -Lemma first_correct: - forall head sem word t q, word = t::q -> - parse_tree head word sem -> - TerminalSet.In (projT1 t) (first_symb_set head) -with first_correct_list: - forall heads sems word t q, word = t::q -> - parse_tree_list heads word sems -> - TerminalSet.In (projT1 t) (first_word_set heads). -Proof with eauto. -intros. -destruct X. -inversion H; subst. -apply TerminalSet.singleton_2, compare_refl... -apply first_stable... -intros. -destruct X. -congruence. -simpl. -case_eq wordt; intros. -erewrite nullable_correct... -apply TerminalSet.union_3. -subst... -rewrite H0 in *; inversion H; destruct H2. -destruct (nullable_symb head_symbolt)... -apply TerminalSet.union_2... -Qed. - -Variable init: initstate. -Variable full_word: list token. -Variable buffer_end: Stream token. -Variable full_sem: symbol_semantic_type (NT (start_nt init)). - -Inductive pt_zipper: - forall (hole_symb:symbol) (hole_word:list token) - (hole_sem:symbol_semantic_type hole_symb), Type := -| Top_ptz: - pt_zipper (NT (start_nt init)) (full_word) (full_sem) -| Cons_ptl_ptz: - forall {head_symbolt:symbol} - {wordt:list token} - {semantic_valuet:symbol_semantic_type head_symbolt}, - - forall {head_symbolsq:list symbol} - {wordq:list token} - {semantic_valuesq:tuple (map symbol_semantic_type head_symbolsq)}, - parse_tree_list head_symbolsq wordq semantic_valuesq -> - - ptl_zipper (head_symbolt::head_symbolsq) (wordt++wordq) - (semantic_valuet,semantic_valuesq) -> - - pt_zipper head_symbolt wordt semantic_valuet -with ptl_zipper: - forall (hole_symbs:list symbol) (hole_word:list token) - (hole_sems:tuple (map symbol_semantic_type hole_symbs)), Type := -| Non_terminal_pt_ptlz: - forall {p:production} {word:list token} - {semantic_values:tuple (map symbol_semantic_type (rev (prod_rhs_rev p)))}, - pt_zipper (NT (prod_lhs p)) word (uncurry (prod_action p) semantic_values) -> - ptl_zipper (rev (prod_rhs_rev p)) word semantic_values - -| Cons_ptl_ptlz: - forall {head_symbolt:symbol} - {wordt:list token} - {semantic_valuet:symbol_semantic_type head_symbolt}, - parse_tree head_symbolt wordt semantic_valuet -> - - forall {head_symbolsq:list symbol} - {wordq:list token} - {semantic_valuesq:tuple (map symbol_semantic_type head_symbolsq)}, - - ptl_zipper (head_symbolt::head_symbolsq) (wordt++wordq) - (semantic_valuet,semantic_valuesq) -> - - ptl_zipper head_symbolsq wordq semantic_valuesq. - -Fixpoint ptlz_cost {hole_symbs hole_word hole_sems} - (ptlz:ptl_zipper hole_symbs hole_word hole_sems) := - match ptlz with - | Non_terminal_pt_ptlz ptz => - ptz_cost ptz - | Cons_ptl_ptlz pt ptlz' => - ptlz_cost ptlz' - end -with ptz_cost {hole_symb hole_word hole_sem} - (ptz:pt_zipper hole_symb hole_word hole_sem) := - match ptz with - | Top_ptz => 0 - | Cons_ptl_ptz ptl ptlz' => - 1 + ptl_size ptl + ptlz_cost ptlz' - end. - -Inductive pt_dot: Type := -| Reduce_ptd: ptl_zipper [] [] () -> pt_dot -| Shift_ptd: - forall (term:terminal) (sem: symbol_semantic_type (T term)) - {symbolsq wordq semsq}, - parse_tree_list symbolsq wordq semsq -> - ptl_zipper (T term::symbolsq) (existT (fun t => symbol_semantic_type (T t)) term sem::wordq) (sem, semsq) -> - pt_dot. - -Definition ptd_cost (ptd:pt_dot) := - match ptd with - | Reduce_ptd ptlz => ptlz_cost ptlz - | Shift_ptd _ _ ptl ptlz => 1 + ptl_size ptl + ptlz_cost ptlz - end. - -Fixpoint ptlz_buffer {hole_symbs hole_word hole_sems} - (ptlz:ptl_zipper hole_symbs hole_word hole_sems): Stream token := - match ptlz with - | Non_terminal_pt_ptlz ptz => - ptz_buffer ptz - | Cons_ptl_ptlz _ ptlz' => - ptlz_buffer ptlz' - end -with ptz_buffer {hole_symb hole_word hole_sem} - (ptz:pt_zipper hole_symb hole_word hole_sem): Stream token := - match ptz with - | Top_ptz => buffer_end - | @Cons_ptl_ptz _ _ _ _ wordq _ ptl ptlz' => - wordq++ptlz_buffer ptlz' - end. - -Definition ptd_buffer (ptd:pt_dot) := - match ptd with - | Reduce_ptd ptlz => ptlz_buffer ptlz - | @Shift_ptd term sem _ wordq _ _ ptlz => - Cons (existT (fun t => symbol_semantic_type (T t)) term sem) - (wordq ++ ptlz_buffer ptlz) - end. - -Fixpoint ptlz_prod {hole_symbs hole_word hole_sems} - (ptlz:ptl_zipper hole_symbs hole_word hole_sems): production := - match ptlz with - | @Non_terminal_pt_ptlz prod _ _ _ => prod - | Cons_ptl_ptlz _ ptlz' => - ptlz_prod ptlz' - end. - -Fixpoint ptlz_past {hole_symbs hole_word hole_sems} - (ptlz:ptl_zipper hole_symbs hole_word hole_sems): list symbol := - match ptlz with - | Non_terminal_pt_ptlz _ => [] - | @Cons_ptl_ptlz s _ _ _ _ _ _ ptlz' => s::ptlz_past ptlz' - end. - -Lemma ptlz_past_ptlz_prod: - forall hole_symbs hole_word hole_sems - (ptlz:ptl_zipper hole_symbs hole_word hole_sems), - rev_append hole_symbs (ptlz_past ptlz) = prod_rhs_rev (ptlz_prod ptlz). -Proof. -fix ptlz_past_ptlz_prod 4. -destruct ptlz; simpl. -rewrite <- rev_alt, rev_involutive; reflexivity. -apply (ptlz_past_ptlz_prod _ _ _ ptlz). -Qed. - -Definition ptlz_state_compat {hole_symbs hole_word hole_sems} - (ptlz:ptl_zipper hole_symbs hole_word hole_sems) - (state:state): Prop := - state_has_future state (ptlz_prod ptlz) hole_symbs - (projT1 (Streams.hd (ptlz_buffer ptlz))). - -Fixpoint ptlz_stack_compat {hole_symbs hole_word hole_sems} - (ptlz:ptl_zipper hole_symbs hole_word hole_sems) - (stack:stack): Prop := - ptlz_state_compat ptlz (state_of_stack init stack) /\ - match ptlz with - | Non_terminal_pt_ptlz ptz => - ptz_stack_compat ptz stack - | @Cons_ptl_ptlz _ _ sem _ _ _ _ ptlz' => - match stack with - | [] => False - | existT _ _ sem'::stackq => - ptlz_stack_compat ptlz' stackq /\ - sem ~= sem' - end - end -with ptz_stack_compat {hole_symb hole_word hole_sem} - (ptz:pt_zipper hole_symb hole_word hole_sem) - (stack:stack): Prop := - match ptz with - | Top_ptz => stack = [] - | Cons_ptl_ptz _ ptlz' => - ptlz_stack_compat ptlz' stack - end. - -Lemma ptlz_stack_compat_ptlz_state_compat: - forall hole_symbs hole_word hole_sems - (ptlz:ptl_zipper hole_symbs hole_word hole_sems) - (stack:stack), - ptlz_stack_compat ptlz stack -> ptlz_state_compat ptlz (state_of_stack init stack). -Proof. -destruct ptlz; simpl; intuition. -Qed. - -Definition ptd_stack_compat (ptd:pt_dot) (stack:stack): Prop := - match ptd with - | Reduce_ptd ptlz => ptlz_stack_compat ptlz stack - | Shift_ptd _ _ _ ptlz => ptlz_stack_compat ptlz stack - end. - -Fixpoint build_pt_dot {hole_symbs hole_word hole_sems} - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems) - :pt_dot := - match ptl in parse_tree_list hole_symbs hole_word hole_sems - return ptl_zipper hole_symbs hole_word hole_sems -> _ - with - | Nil_ptl => fun ptlz => - Reduce_ptd ptlz - | Cons_ptl pt ptl' => - match pt in parse_tree hole_symb hole_word hole_sem - return ptl_zipper (hole_symb::_) (hole_word++_) (hole_sem,_) -> _ - with - | Terminal_pt term sem => fun ptlz => - Shift_ptd term sem ptl' ptlz - | Non_terminal_pt ptl'' => fun ptlz => - build_pt_dot ptl'' - (Non_terminal_pt_ptlz (Cons_ptl_ptz ptl' ptlz)) - end - end ptlz. - -Lemma build_pt_dot_cost: - forall hole_symbs hole_word hole_sems - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems), - ptd_cost (build_pt_dot ptl ptlz) = ptl_size ptl + ptlz_cost ptlz. -Proof. -fix build_pt_dot_cost 4. -destruct ptl; intros. -reflexivity. -destruct p. -reflexivity. -simpl; rewrite build_pt_dot_cost. -simpl; rewrite <- plus_n_Sm, Nat.add_assoc; reflexivity. -Qed. - -Lemma build_pt_dot_buffer: - forall hole_symbs hole_word hole_sems - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems), - ptd_buffer (build_pt_dot ptl ptlz) = hole_word ++ ptlz_buffer ptlz. -Proof. -fix build_pt_dot_buffer 4. -destruct ptl; intros. -reflexivity. -destruct p. -reflexivity. -simpl; rewrite build_pt_dot_buffer. -apply app_str_app_assoc. -Qed. - -Lemma ptd_stack_compat_build_pt_dot: - forall hole_symbs hole_word hole_sems - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems) - (stack:stack), - ptlz_stack_compat ptlz stack -> - ptd_stack_compat (build_pt_dot ptl ptlz) stack. -Proof. -fix ptd_stack_compat_build_pt_dot 4. -destruct ptl; intros. -eauto. -destruct p. -eauto. -simpl. -apply ptd_stack_compat_build_pt_dot. -split. -apply ptlz_stack_compat_ptlz_state_compat, non_terminal_closed in H. -apply H; clear H; eauto. -destruct wordq. -right; split. -eauto. -eapply nullable_correct_list; eauto. -left. -eapply first_correct_list; eauto. -eauto. -Qed. - -Program Fixpoint pop_ptlz {hole_symbs hole_word hole_sems} - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems): - { word:_ & { sem:_ & - (pt_zipper (NT (prod_lhs (ptlz_prod ptlz))) word sem * - parse_tree (NT (prod_lhs (ptlz_prod ptlz))) word sem)%type } } := - match ptlz in ptl_zipper hole_symbs hole_word hole_sems - return parse_tree_list hole_symbs hole_word hole_sems -> - { word:_ & { sem:_ & - (pt_zipper (NT (prod_lhs (ptlz_prod ptlz))) word sem * - parse_tree (NT (prod_lhs (ptlz_prod ptlz))) word sem)%type } } - with - | @Non_terminal_pt_ptlz prod word sem ptz => fun ptl => - let sem := uncurry (prod_action prod) sem in - existT _ word (existT _ sem (ptz, Non_terminal_pt ptl)) - | Cons_ptl_ptlz pt ptlz' => fun ptl => - pop_ptlz (Cons_ptl pt ptl) ptlz' - end ptl. - -Lemma pop_ptlz_cost: - forall hole_symbs hole_word hole_sems - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems), - let 'existT _ word (existT _ sem (ptz, pt)) := pop_ptlz ptl ptlz in - ptlz_cost ptlz = ptz_cost ptz. -Proof. -fix pop_ptlz_cost 5. -destruct ptlz. -reflexivity. -simpl; apply pop_ptlz_cost. -Qed. - -Lemma pop_ptlz_buffer: - forall hole_symbs hole_word hole_sems - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems), - let 'existT _ word (existT _ sem (ptz, pt)) := pop_ptlz ptl ptlz in - ptlz_buffer ptlz = ptz_buffer ptz. -Proof. -fix pop_ptlz_buffer 5. -destruct ptlz. -reflexivity. -simpl; apply pop_ptlz_buffer. -Qed. - -Lemma pop_ptlz_pop_stack_compat_converter: - forall A hole_symbs hole_word hole_sems (ptlz:ptl_zipper hole_symbs hole_word hole_sems), - arrows_left (map symbol_semantic_type (rev (prod_rhs_rev (ptlz_prod ptlz)))) A = - arrows_left (map symbol_semantic_type hole_symbs) - (arrows_right A (map symbol_semantic_type (ptlz_past ptlz))). -Proof. -intros. -rewrite <- ptlz_past_ptlz_prod. -unfold arrows_right, arrows_left. -rewrite rev_append_rev, map_rev, map_app, map_rev, <- fold_left_rev_right, rev_involutive, fold_right_app. -rewrite fold_left_rev_right. -reflexivity. -Qed. - -Lemma pop_ptlz_pop_stack_compat: - forall hole_symbs hole_word hole_sems - (ptl:parse_tree_list hole_symbs hole_word hole_sems) - (ptlz:ptl_zipper hole_symbs hole_word hole_sems) - (stack:stack), - - ptlz_stack_compat ptlz stack -> - - let action' := - eq_rect _ (fun x=>x) (prod_action (ptlz_prod ptlz)) _ - (pop_ptlz_pop_stack_compat_converter _ _ _ _ _) - in - let 'existT _ word (existT _ sem (ptz, pt)) := pop_ptlz ptl ptlz in - match pop (ptlz_past ptlz) stack (uncurry action' hole_sems) with - | OK (stack', sem') => - ptz_stack_compat ptz stack' /\ sem = sem' - | Err => True - end. -Proof. -Opaque AlphabetComparable AlphabetComparableUsualEq. -fix pop_ptlz_pop_stack_compat 5. -destruct ptlz. intros; simpl. -split. -apply H. -eapply (f_equal (fun X => uncurry X semantic_values)). -apply JMeq_eq, JMeq_sym, JMeq_eqrect with (P:=fun x => x). -simpl; intros; destruct stack0. -destruct (proj2 H). -simpl in H; destruct H. -destruct s as (state, sem'). -destruct H0. -specialize (pop_ptlz_pop_stack_compat _ _ _ (Cons_ptl p ptl) ptlz _ H0). -destruct (pop_ptlz (Cons_ptl p ptl) ptlz) as [word [sem []]]. -destruct (compare_eqdec (last_symb_of_non_init_state state) head_symbolt); intuition. -eapply JMeq_sym, JMeq_trans, JMeq_sym, JMeq_eq in H1; [|apply JMeq_eqrect with (e:=e)]. -rewrite <- H1. -simpl in pop_ptlz_pop_stack_compat. -erewrite proof_irrelevance with (p1:=pop_ptlz_pop_stack_compat_converter _ _ _ _ _). -apply pop_ptlz_pop_stack_compat. -Transparent AlphabetComparable AlphabetComparableUsualEq. -Qed. - -Definition next_ptd (ptd:pt_dot): option pt_dot := - match ptd with - | Shift_ptd term sem ptl ptlz => - Some (build_pt_dot ptl (Cons_ptl_ptlz (Terminal_pt term sem) ptlz)) - | Reduce_ptd ptlz => - let 'existT _ _ (existT _ _ (ptz, pt)) := pop_ptlz Nil_ptl ptlz in - match ptz in pt_zipper sym _ _ return parse_tree sym _ _ -> _ with - | Top_ptz => fun pt => None - | Cons_ptl_ptz ptl ptlz' => - fun pt => Some (build_pt_dot ptl (Cons_ptl_ptlz pt ptlz')) - end pt - end. - -Lemma next_ptd_cost: - forall ptd, - match next_ptd ptd with - | None => ptd_cost ptd = 0 - | Some ptd' => ptd_cost ptd = S (ptd_cost ptd') - end. -Proof. -destruct ptd. unfold next_ptd. -pose proof (pop_ptlz_cost _ _ _ Nil_ptl p). -destruct (pop_ptlz Nil_ptl p) as [word[sem[[]]]]. -assumption. -rewrite build_pt_dot_cost. -assumption. -simpl; rewrite build_pt_dot_cost; reflexivity. -Qed. - -Lemma reduce_step_next_ptd: - forall (ptlz:ptl_zipper [] [] ()) (stack:stack), - ptlz_stack_compat ptlz stack -> - match reduce_step init stack (ptlz_prod ptlz) (ptlz_buffer ptlz) with - | OK Fail_sr => - False - | OK (Accept_sr sem buffer) => - sem = full_sem /\ buffer = buffer_end /\ next_ptd (Reduce_ptd ptlz) = None - | OK (Progress_sr stack buffer) => - match next_ptd (Reduce_ptd ptlz) with - | None => False - | Some ptd => - ptd_stack_compat ptd stack /\ buffer = ptd_buffer ptd - end - | Err => - True - end. -Proof. -intros. -unfold reduce_step, next_ptd. -apply pop_ptlz_pop_stack_compat with (ptl:=Nil_ptl) in H. -pose proof (pop_ptlz_buffer _ _ _ Nil_ptl ptlz). -destruct (pop_ptlz Nil_ptl ptlz) as [word [sem [ptz pt]]]. -rewrite H0; clear H0. -revert H. -match goal with - |- match ?p1 with Err => _ | OK _ => _ end -> match bind2 ?p2 _ with Err => _ | OK _ => _ end => - replace p1 with p2; [destruct p2 as [|[]]; intros|] -end. -assumption. -simpl. -destruct H; subst. -generalize dependent s0. -generalize (prod_lhs (ptlz_prod ptlz)); clear ptlz stack0. -dependent destruction ptz; intros. -simpl in H; subst; simpl. -pose proof start_goto; unfold Valid.start_goto in H; rewrite H. -destruct (compare_eqdec (start_nt init) (start_nt init)); intuition. -apply JMeq_eq, JMeq_eqrect with (P:=fun nt => symbol_semantic_type (NT nt)). -pose proof (ptlz_stack_compat_ptlz_state_compat _ _ _ _ _ H). -apply non_terminal_goto in H0. -destruct (goto_table (state_of_stack init s) n) as [[]|]; intuition. -apply ptd_stack_compat_build_pt_dot; simpl; intuition. -symmetry; apply JMeq_eqrect with (P:=symbol_semantic_type). -symmetry; apply build_pt_dot_buffer. -destruct s; intuition. -simpl in H; apply ptlz_stack_compat_ptlz_state_compat in H. -destruct (H0 _ _ _ H eq_refl). -generalize (pop_ptlz_pop_stack_compat_converter (symbol_semantic_type (NT (prod_lhs (ptlz_prod ptlz)))) _ _ _ ptlz). -pose proof (ptlz_past_ptlz_prod _ _ _ ptlz); simpl in H. -rewrite H; clear H. -intro; f_equal; simpl. -apply JMeq_eq. -etransitivity. -apply JMeq_eqrect with (P:=fun x => x). -symmetry. -apply JMeq_eqrect with (P:=fun x => x). -Qed. - -Lemma step_next_ptd: - forall (ptd:pt_dot) (stack:stack), - ptd_stack_compat ptd stack -> - match step init stack (ptd_buffer ptd) with - | OK Fail_sr => - False - | OK (Accept_sr sem buffer) => - sem = full_sem /\ buffer = buffer_end /\ next_ptd ptd = None - | OK (Progress_sr stack buffer) => - match next_ptd ptd with - | None => False - | Some ptd => - ptd_stack_compat ptd stack /\ buffer = ptd_buffer ptd - end - | Err => - True - end. -Proof. -intros. -destruct ptd. -pose proof (ptlz_stack_compat_ptlz_state_compat _ _ _ _ _ H). -apply end_reduce in H0. -unfold step. -destruct (action_table (state_of_stack init stack0)). -rewrite H0 by reflexivity. -apply reduce_step_next_ptd; assumption. -simpl; destruct (Streams.hd (ptlz_buffer p)); simpl in H0. -destruct (l x); intuition; rewrite H1. -apply reduce_step_next_ptd; assumption. -pose proof (ptlz_stack_compat_ptlz_state_compat _ _ _ _ _ H). -apply terminal_shift in H0. -unfold step. -destruct (action_table (state_of_stack init stack0)); intuition. -simpl; destruct (Streams.hd (ptlz_buffer p0)) as [] eqn:?. -destruct (l term); intuition. -apply ptd_stack_compat_build_pt_dot; simpl; intuition. -unfold ptlz_state_compat; simpl; destruct Heqt; assumption. -symmetry; apply JMeq_eqrect with (P:=symbol_semantic_type). -rewrite build_pt_dot_buffer; reflexivity. -Qed. - -Lemma parse_fix_complete: - forall (ptd:pt_dot) (stack:stack) (n_steps:nat), - ptd_stack_compat ptd stack -> - match parse_fix init stack (ptd_buffer ptd) n_steps with - | OK (Parsed_pr sem_res buffer_end_res) => - sem_res = full_sem /\ buffer_end_res = buffer_end /\ - S (ptd_cost ptd) <= n_steps - | OK Fail_pr => False - | OK Timeout_pr => n_steps < S (ptd_cost ptd) - | Err => True - end. -Proof. -fix parse_fix_complete 3. -destruct n_steps; intros; simpl. -apply Nat.lt_0_succ. -apply step_next_ptd in H. -pose proof (next_ptd_cost ptd). -destruct (step init stack0 (ptd_buffer ptd)) as [|[]]; simpl; intuition. -rewrite H3 in H0; rewrite H0. -apply le_n_S, Nat.le_0_l. -destruct (next_ptd ptd); intuition; subst. -eapply parse_fix_complete with (n_steps:=n_steps) in H1. -rewrite H0. -destruct (parse_fix init s (ptd_buffer p) n_steps) as [|[]]; try assumption. -apply lt_n_S; assumption. -destruct H1 as [H1 []]; split; [|split]; try assumption. -apply le_n_S; assumption. -Qed. - -Variable full_pt: parse_tree (NT (start_nt init)) full_word full_sem. - -Definition init_ptd := - match full_pt in parse_tree head full_word full_sem return - pt_zipper head full_word full_sem -> - match head return Type with | T _ => unit | NT _ => pt_dot end - with - | Terminal_pt _ _ => fun _ => () - | Non_terminal_pt ptl => - fun top => build_pt_dot ptl (Non_terminal_pt_ptlz top) - end Top_ptz. - -Lemma init_ptd_compat: - ptd_stack_compat init_ptd []. -Proof. -unfold init_ptd. -assert (ptz_stack_compat Top_ptz []) by reflexivity. -pose proof (start_future init); revert H0. -generalize dependent Top_ptz. -generalize dependent full_word. -generalize full_sem. -generalize (start_nt init). -dependent destruction full_pt0. -intros. -apply ptd_stack_compat_build_pt_dot; simpl; intuition. -apply H0; reflexivity. -Qed. - -Lemma init_ptd_cost: - S (ptd_cost init_ptd) = pt_size full_pt. -Proof. -unfold init_ptd. -assert (ptz_cost Top_ptz = 0) by reflexivity. -generalize dependent Top_ptz. -generalize dependent full_word. -generalize full_sem. -generalize (start_nt init). -dependent destruction full_pt0. -intros. -rewrite build_pt_dot_cost; simpl. -rewrite H, Nat.add_0_r; reflexivity. -Qed. - -Lemma init_ptd_buffer: - ptd_buffer init_ptd = full_word ++ buffer_end. -Proof. -unfold init_ptd. -assert (ptz_buffer Top_ptz = buffer_end) by reflexivity. -generalize dependent Top_ptz. -generalize dependent full_word. -generalize full_sem. -generalize (start_nt init). -dependent destruction full_pt0. -intros. -rewrite build_pt_dot_buffer; simpl. -rewrite H; reflexivity. -Qed. - -Theorem parse_complete n_steps: - match parse init (full_word ++ buffer_end) n_steps with - | OK (Parsed_pr sem_res buffer_end_res) => - sem_res = full_sem /\ buffer_end_res = buffer_end /\ - pt_size full_pt <= n_steps - | OK Fail_pr => False - | OK Timeout_pr => n_steps < pt_size full_pt - | Err => True - end. -Proof. -pose proof (parse_fix_complete init_ptd [] n_steps init_ptd_compat). -rewrite init_ptd_buffer, init_ptd_cost in H. -apply H. -Qed. - -End Completeness_Proof. - -End Make. diff --git a/cparser/MenhirLib/Interpreter_correct.v b/cparser/MenhirLib/Interpreter_correct.v deleted file mode 100644 index 1263d4e3..00000000 --- a/cparser/MenhirLib/Interpreter_correct.v +++ /dev/null @@ -1,228 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Import Streams. -Require Import List. -Require Import Syntax. -Require Import Equality. -Require Import Alphabet. -Require Grammar. -Require Automaton. -Require Interpreter. - -Module Make(Import A:Automaton.T) (Import Inter:Interpreter.T A). - -(** * Correctness of the interpreter **) - -(** We prove that, in any case, if the interpreter accepts returning a - semantic value, then this is a semantic value of the input **) - -Section Init. - -Variable init:initstate. - -(** [word_has_stack_semantics] relates a word with a state, stating that the - word is a concatenation of words that have the semantic values stored in - the stack. **) -Inductive word_has_stack_semantics: - forall (word:list token) (stack:stack), Prop := - | Nil_stack_whss: word_has_stack_semantics [] [] - | Cons_stack_whss: - forall (wordq:list token) (stackq:stack), - word_has_stack_semantics wordq stackq -> - - forall (wordt:list token) (s:noninitstate) - (semantic_valuet:_), - inhabited (parse_tree (last_symb_of_non_init_state s) wordt semantic_valuet) -> - - word_has_stack_semantics - (wordq++wordt) (existT noninitstate_type s semantic_valuet::stackq). - -Lemma pop_invariant_converter: - forall A symbols_to_pop symbols_popped, - arrows_left (map symbol_semantic_type (rev_append symbols_to_pop symbols_popped)) A = - arrows_left (map symbol_semantic_type symbols_popped) - (arrows_right A (map symbol_semantic_type symbols_to_pop)). -Proof. -intros. -unfold arrows_right, arrows_left. -rewrite rev_append_rev, map_app, map_rev, fold_left_app. -apply f_equal. -rewrite <- fold_left_rev_right, rev_involutive. -reflexivity. -Qed. - -(** [pop] preserves the invariant **) -Lemma pop_invariant: - forall (symbols_to_pop symbols_popped:list symbol) - (stack_cur:stack) - (A:Type) - (action:arrows_left (map symbol_semantic_type (rev_append symbols_to_pop symbols_popped)) A), - forall word_stack word_popped, - forall sem_popped, - word_has_stack_semantics word_stack stack_cur -> - inhabited (parse_tree_list symbols_popped word_popped sem_popped) -> - let action' := eq_rect _ (fun x=>x) action _ (pop_invariant_converter _ _ _) in - match pop symbols_to_pop stack_cur (uncurry action' sem_popped) with - | OK (stack_new, sem) => - exists word1res word2res sem_full, - (word_stack = word1res ++ word2res)%list /\ - word_has_stack_semantics word1res stack_new /\ - sem = uncurry action sem_full /\ - inhabited ( - parse_tree_list (rev_append symbols_to_pop symbols_popped) (word2res++word_popped) sem_full) - | Err => True - end. -Proof. -induction symbols_to_pop; intros; unfold pop; fold pop. -exists word_stack, ([]:list token), sem_popped; intuition. -f_equal. -apply JMeq_eq, JMeq_eqrect with (P:=(fun x => x)). -destruct stack_cur as [|[]]; eauto. -destruct (compare_eqdec (last_symb_of_non_init_state x) a); eauto. -destruct e; simpl. -dependent destruction H. -destruct H0, H1. apply (Cons_ptl X), inhabits in X0. -specialize (IHsymbols_to_pop _ _ _ action0 _ _ _ H X0). -match goal with - IHsymbols_to_pop:match ?p1 with Err => _ | OK _ => _ end |- match ?p2 with Err => _ | OK _ => _ end => - replace p2 with p1; [destruct p1 as [|[]]|]; intuition -end. -destruct IHsymbols_to_pop as [word1res [word2res [sem_full []]]]; intuition; subst. -exists word1res. -eexists. -exists sem_full. -intuition. -rewrite <- app_assoc; assumption. -simpl; f_equal; f_equal. -apply JMeq_eq. -etransitivity. -apply JMeq_eqrect with (P:=(fun x => x)). -symmetry. -apply JMeq_eqrect with (P:=(fun x => x)). -Qed. - -(** [reduce_step] preserves the invariant **) -Lemma reduce_step_invariant (stack:stack) (prod:production): - forall word buffer, word_has_stack_semantics word stack -> - match reduce_step init stack prod buffer with - | OK (Accept_sr sem buffer_new) => - buffer = buffer_new /\ - inhabited (parse_tree (NT (start_nt init)) word sem) - | OK (Progress_sr stack_new buffer_new) => - buffer = buffer_new /\ - word_has_stack_semantics word stack_new - | Err | OK Fail_sr => True - end. -Proof with eauto. -intros. -unfold reduce_step. -pose proof (pop_invariant (prod_rhs_rev prod) [] stack (symbol_semantic_type (NT (prod_lhs prod)))). -revert H0. -generalize (pop_invariant_converter (symbol_semantic_type (NT (prod_lhs prod))) (prod_rhs_rev prod) []). -rewrite <- rev_alt. -intros. -specialize (H0 (prod_action prod) _ [] () H (inhabits Nil_ptl)). -match goal with H0:let action' := ?a in _ |- _ => replace a with (prod_action' prod) in H0 end. -simpl in H0. -destruct (pop (prod_rhs_rev prod) stack (prod_action' prod)) as [|[]]; intuition. -destruct H0 as [word1res [word2res [sem_full]]]; intuition. -destruct H4; apply Non_terminal_pt, inhabits in X. -match goal with X:inhabited (parse_tree _ _ ?u) |- _ => replace u with s0 in X end. -clear sem_full H2. -simpl; destruct (goto_table (state_of_stack init s) (prod_lhs prod)) as [[]|]; intuition; subst. -rewrite app_nil_r in X; revert s0 X; rewrite e0; intro; simpl. -constructor... -destruct s; intuition. -destruct (compare_eqdec (prod_lhs prod) (start_nt init)); intuition. -rewrite app_nil_r in X. -rewrite <- e0. -inversion H0. -destruct X; constructor... -apply JMeq_eq. -etransitivity. -apply JMeq_eqrect with (P:=(fun x => x)). -symmetry. -apply JMeq_eqrect with (P:=(fun x => x)). -Qed. - -(** [step] preserves the invariant **) -Lemma step_invariant (stack:stack) (buffer:Stream token): - forall buffer_tmp, - (exists word_old, - buffer = word_old ++ buffer_tmp /\ - word_has_stack_semantics word_old stack) -> - match step init stack buffer_tmp with - | OK (Accept_sr sem buffer_new) => - exists word_new, - buffer = word_new ++ buffer_new /\ - inhabited (parse_tree (NT (start_nt init)) word_new sem) - | OK (Progress_sr stack_new buffer_new) => - exists word_new, - buffer = word_new ++ buffer_new /\ - word_has_stack_semantics word_new stack_new - | Err | OK Fail_sr => True - end. -Proof with eauto. -intros. -destruct H, H. -unfold step. -destruct (action_table (state_of_stack init stack)). -pose proof (reduce_step_invariant stack p x buffer_tmp). -destruct (reduce_step init stack p buffer_tmp) as [|[]]; intuition; subst... -destruct buffer_tmp. -unfold Streams.hd. -destruct t. -destruct (l x0); intuition. -exists (x ++ [existT (fun t => symbol_semantic_type (T t)) x0 s])%list. -split. -now rewrite <- app_str_app_assoc; intuition. -apply Cons_stack_whss; intuition. -destruct e; simpl. -now exact (inhabits (Terminal_pt _ _)). -match goal with |- (match reduce_step init stack p ?buff with Err => _ | OK _ => _ end) => - pose proof (reduce_step_invariant stack p x buff); - destruct (reduce_step init stack p buff) as [|[]]; intuition; subst -end... -Qed. - -(** The interpreter is correct : if it returns a semantic value, then the input - word has this semantic value. -**) -Theorem parse_correct buffer n_steps: - match parse init buffer n_steps with - | OK (Parsed_pr sem buffer_new) => - exists word_new, - buffer = word_new ++ buffer_new /\ - inhabited (parse_tree (NT (start_nt init)) word_new sem) - | _ => True - end. -Proof. -unfold parse. -assert (exists w, buffer = w ++ buffer /\ word_has_stack_semantics w []). -exists ([]:list token); intuition. -now apply Nil_stack_whss. -revert H. -generalize ([]:stack), buffer at 2 3. -induction n_steps; simpl; intuition. -pose proof (step_invariant _ _ _ H). -destruct (step init s buffer0); simpl; intuition. -destruct s0; intuition. -apply IHn_steps; intuition. -Qed. - -End Init. - -End Make. diff --git a/cparser/MenhirLib/Interpreter_safe.v b/cparser/MenhirLib/Interpreter_safe.v deleted file mode 100644 index a1aa35b8..00000000 --- a/cparser/MenhirLib/Interpreter_safe.v +++ /dev/null @@ -1,275 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Import Streams. -Require Import Equality. -Require Import List. -Require Import Syntax. -Require Import Alphabet. -Require Grammar. -Require Automaton. -Require Validator_safe. -Require Interpreter. - -Module Make(Import A:Automaton.T) (Import Inter:Interpreter.T A). -Module Import Valid := Validator_safe.Make A. - -(** * A correct automaton does not crash **) - -Section Safety_proof. - -Hypothesis safe: safe. - -Proposition shift_head_symbs: shift_head_symbs. -Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed. -Proposition goto_head_symbs: goto_head_symbs. -Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed. -Proposition shift_past_state: shift_past_state. -Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed. -Proposition goto_past_state: goto_past_state. -Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed. -Proposition reduce_ok: reduce_ok. -Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed. - -(** We prove that a correct automaton won't crash : the interpreter will - not return [Err] **) - -Variable init : initstate. - -(** The stack of states of an automaton stack **) -Definition state_stack_of_stack (stack:stack) := - (List.map - (fun cell:sigT noninitstate_type => singleton_state_pred (projT1 cell)) - stack ++ [singleton_state_pred init])%list. - -(** The stack of symbols of an automaton stack **) -Definition symb_stack_of_stack (stack:stack) := - List.map - (fun cell:sigT noninitstate_type => last_symb_of_non_init_state (projT1 cell)) - stack. - -(** The stack invariant : it basically states that the assumptions on the - states are true. **) -Inductive stack_invariant: stack -> Prop := - | stack_invariant_constr: - forall stack, - prefix (head_symbs_of_state (state_of_stack init stack)) - (symb_stack_of_stack stack) -> - prefix_pred (head_states_of_state (state_of_stack init stack)) - (state_stack_of_stack stack) -> - stack_invariant_next stack -> - stack_invariant stack -with stack_invariant_next: stack -> Prop := - | stack_invariant_next_nil: - stack_invariant_next [] - | stack_invariant_next_cons: - forall state_cur st stack_rec, - stack_invariant stack_rec -> - stack_invariant_next (existT _ state_cur st::stack_rec). - -(** [pop] conserves the stack invariant and does not crash - under the assumption that we can pop at this place. - Moreover, after a pop, the top state of the stack is allowed. **) -Lemma pop_stack_invariant_conserved - (symbols_to_pop:list symbol) (stack_cur:stack) A action: - stack_invariant stack_cur -> - prefix symbols_to_pop (head_symbs_of_state (state_of_stack init stack_cur)) -> - match pop symbols_to_pop stack_cur (A:=A) action with - | OK (stack_new, _) => - stack_invariant stack_new /\ - state_valid_after_pop - (state_of_stack init stack_new) symbols_to_pop - (head_states_of_state (state_of_stack init stack_cur)) - | Err => False - end. -Proof with eauto. - intros. - pose proof H. - destruct H. - revert H H0 H1 H2 H3. - generalize (head_symbs_of_state (state_of_stack init stack0)). - generalize (head_states_of_state (state_of_stack init stack0)). - revert stack0 A action. - induction symbols_to_pop; intros. - - split... - destruct l; constructor. - inversion H2; subst. - specialize (H7 (state_of_stack init stack0)). - destruct (f2 (state_of_stack init stack0)) as [] eqn:? ... - destruct stack0 as [|[]]; simpl in *. - + inversion H6; subst. - unfold singleton_state_pred in Heqb0. - now rewrite compare_refl in Heqb0; discriminate. - + inversion H6; subst. - unfold singleton_state_pred in Heqb0. - now rewrite compare_refl in Heqb0; discriminate. - - destruct stack0 as [|[]]; unfold pop. - + inversion H0; subst. - now inversion H. - + fold pop. - destruct (compare_eqdec (last_symb_of_non_init_state x) a). - * inversion H0; subst. clear H0. - inversion H; subst. clear H. - dependent destruction H3; simpl. - assert (prefix_pred (List.tl l) (state_stack_of_stack stack0)). - unfold tl; destruct l; [constructor | inversion H2]... - pose proof H. destruct H3. - specialize (IHsymbols_to_pop stack0 A (action0 n) _ _ H4 H7 H H0 H6). - revert IHsymbols_to_pop. - fold (noninitstate_type x); generalize (pop symbols_to_pop stack0 (action0 n)). - destruct r as [|[]]; intuition... - destruct l; constructor... - * apply n0. - inversion H0; subst. - inversion H; subst... -Qed. - -(** [prefix] is associative **) -Lemma prefix_ass: - forall (l1 l2 l3:list symbol), prefix l1 l2 -> prefix l2 l3 -> prefix l1 l3. -Proof. -induction l1; intros. -constructor. -inversion H; subst. -inversion H0; subst. -constructor; eauto. -Qed. - -(** [prefix_pred] is associative **) -Lemma prefix_pred_ass: - forall (l1 l2 l3:list (state->bool)), - prefix_pred l1 l2 -> prefix_pred l2 l3 -> prefix_pred l1 l3. -Proof. -induction l1; intros. -constructor. -inversion H; subst. -inversion H0; subst. -constructor; eauto. -intro. -specialize (H3 x). -specialize (H4 x). -destruct (f0 x); simpl in *; intuition. -rewrite H4 in H3; intuition. -Qed. - -(** If we have the right to reduce at this state, then the stack invariant - is conserved by [reduce_step] and [reduce_step] does not crash. **) -Lemma reduce_step_stack_invariant_conserved stack prod buff: - stack_invariant stack -> - valid_for_reduce (state_of_stack init stack) prod -> - match reduce_step init stack prod buff with - | OK (Fail_sr | Accept_sr _ _) => True - | OK (Progress_sr stack_new _) => stack_invariant stack_new - | Err => False - end. -Proof with eauto. -unfold valid_for_reduce. -intuition. -unfold reduce_step. -pose proof (pop_stack_invariant_conserved (prod_rhs_rev prod) stack _ (prod_action' prod)). -destruct (pop (prod_rhs_rev prod) stack (prod_action' prod)) as [|[]]. -apply H0... -destruct H0... -pose proof (goto_head_symbs (state_of_stack init s) (prod_lhs prod)). -pose proof (goto_past_state (state_of_stack init s) (prod_lhs prod)). -unfold bind2. -destruct H0. -specialize (H2 _ H3)... -destruct (goto_table (state_of_stack init stack0) (prod_lhs prod)) as [[]|]. -constructor. -simpl. -constructor. -eapply prefix_ass... -unfold state_stack_of_stack; simpl; constructor. -intro; destruct (singleton_state_pred x x0); reflexivity. -eapply prefix_pred_ass... -constructor... -constructor... -destruct stack0 as [|[]]... -destruct (compare_eqdec (prod_lhs prod) (start_nt init))... -apply n, H2, eq_refl. -apply H2, eq_refl. -Qed. - -(** If the automaton is safe, then the stack invariant is conserved by - [step] and [step] does not crash. **) -Lemma step_stack_invariant_conserved (stack:stack) buffer: - stack_invariant stack -> - match step init stack buffer with - | OK (Fail_sr | Accept_sr _ _) => True - | OK (Progress_sr stack_new _) => stack_invariant stack_new - | Err => False - end. -Proof with eauto. -intro. -unfold step. -pose proof (shift_head_symbs (state_of_stack init stack)). -pose proof (shift_past_state (state_of_stack init stack)). -pose proof (reduce_ok (state_of_stack init stack)). -destruct (action_table (state_of_stack init stack)). -apply reduce_step_stack_invariant_conserved... -destruct buffer as [[]]; simpl. -specialize (H0 x); specialize (H1 x); specialize (H2 x). -destruct (l x)... -destruct H. -constructor. -unfold state_of_stack. -constructor. -eapply prefix_ass... -unfold state_stack_of_stack; simpl; constructor. -intro; destruct (singleton_state_pred s0 x0)... -eapply prefix_pred_ass... -constructor... -constructor... -apply reduce_step_stack_invariant_conserved... -Qed. - -(** If the automaton is safe, then it does not crash **) -Theorem parse_no_err buffer n_steps: - parse init buffer n_steps <> Err. -Proof with eauto. -unfold parse. -assert (stack_invariant []). -constructor. -constructor. -unfold state_stack_of_stack; simpl; constructor. -intro; destruct (singleton_state_pred init x)... -constructor. -constructor. -revert H. -generalize buffer ([]:stack). -induction n_steps; simpl. -now discriminate. -intros. -pose proof (step_stack_invariant_conserved s buffer0 H). -destruct (step init s buffer0) as [|[]]; simpl... -discriminate. -discriminate. -Qed. - -(** A version of [parse] that uses safeness in order to return a - [parse_result], and not a [result parse_result] : we have proven that - parsing does not return [Err]. **) -Definition parse_with_safe (buffer:Stream token) (n_steps:nat): - parse_result init. -Proof with eauto. -pose proof (parse_no_err buffer n_steps). -destruct (parse init buffer n_steps)... -congruence. -Defined. - -End Safety_proof. - -End Make. diff --git a/cparser/MenhirLib/Main.v b/cparser/MenhirLib/Main.v deleted file mode 100644 index 1a17e988..00000000 --- a/cparser/MenhirLib/Main.v +++ /dev/null @@ -1,92 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Grammar. -Require Automaton. -Require Interpreter_safe. -Require Interpreter_correct. -Require Interpreter_complete. -Require Import Syntax. - -Module Make(Export Aut:Automaton.T). -Export Aut.Gram. -Export Aut.GramDefs. - -Module Import Inter := Interpreter.Make Aut. -Module Safe := Interpreter_safe.Make Aut Inter. -Module Correct := Interpreter_correct.Make Aut Inter. -Module Complete := Interpreter_complete.Make Aut Inter. - -Definition complete_validator:unit->bool := Complete.Valid.is_complete. -Definition safe_validator:unit->bool := Safe.Valid.is_safe. -Definition parse (safe:safe_validator ()=true) init n_steps buffer : parse_result init:= - Safe.parse_with_safe (Safe.Valid.is_safe_correct safe) init buffer n_steps. - -(** Correction theorem. **) -Theorem parse_correct - (safe:safe_validator ()= true) init n_steps buffer: - match parse safe init n_steps buffer with - | Parsed_pr sem buffer_new => - exists word, - buffer = word ++ buffer_new /\ inhabited (parse_tree (NT (start_nt init)) word sem) - | _ => True - end. -Proof. -unfold parse, Safe.parse_with_safe. -pose proof (Correct.parse_correct init buffer n_steps). -generalize (Safe.parse_no_err (Safe.Valid.is_safe_correct safe) init buffer n_steps). -destruct (Inter.parse init buffer n_steps); intros. -now destruct (n (eq_refl _)). -now destruct p; trivial. -Qed. - -(** Completeness theorem. **) -Theorem parse_complete - (safe:safe_validator () = true) init n_steps word buffer_end sem: - complete_validator () = true -> - forall tree:parse_tree (NT (start_nt init)) word sem, - match parse safe init n_steps (word ++ buffer_end) with - | Fail_pr => False - | Parsed_pr sem_res buffer_end_res => - sem_res = sem /\ buffer_end_res = buffer_end /\ pt_size tree <= n_steps - | Timeout_pr => n_steps < pt_size tree - end. -Proof. -intros. -unfold parse, Safe.parse_with_safe. -pose proof (Complete.parse_complete (Complete.Valid.is_complete_correct H) init _ buffer_end _ tree n_steps). -generalize (Safe.parse_no_err (Safe.Valid.is_safe_correct safe) init (word ++ buffer_end) n_steps). -destruct (Inter.parse init (word ++ buffer_end) n_steps); intros. -now destruct (n eq_refl). -now exact H0. -Qed. - -(** Unambiguity theorem. **) -Theorem unambiguity: - safe_validator () = true -> complete_validator () = true -> inhabited token -> - forall init word, - forall sem1 (tree1:parse_tree (NT (start_nt init)) word sem1), - forall sem2 (tree2:parse_tree (NT (start_nt init)) word sem2), - sem1 = sem2. -Proof. -intros. -destruct H1. -pose proof (parse_complete H init (pt_size tree1) word (Streams.const X) sem1) H0 tree1. -pose proof (parse_complete H init (pt_size tree1) word (Streams.const X) sem2) H0 tree2. -destruct (parse H init (pt_size tree1) (word ++ Streams.const X)); intuition. -rewrite <- H3, H1; reflexivity. -Qed. - -End Make. diff --git a/cparser/MenhirLib/Tuples.v b/cparser/MenhirLib/Tuples.v deleted file mode 100644 index 3fd2ec03..00000000 --- a/cparser/MenhirLib/Tuples.v +++ /dev/null @@ -1,49 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Import List. -Require Import Coq.Program.Syntax. -Require Import Equality. - -(** A curryfied function with multiple parameters **) -Definition arrows_left: list Type -> Type -> Type := - fold_left (fun A B => B -> A). - -(** A curryfied function with multiple parameters **) -Definition arrows_right: Type -> list Type -> Type := - fold_right (fun A B => A -> B). - -(** A tuple is a heterogeneous list. For convenience, we use pairs. **) -Fixpoint tuple (types : list Type) : Type := - match types with - | nil => unit - | t::q => prod t (tuple q) - end. - -Fixpoint uncurry {args:list Type} {res:Type}: - arrows_left args res -> tuple args -> res := - match args return forall res, arrows_left args res -> tuple args -> res with - | [] => fun _ f _ => f - | t::q => fun res f p => let (d, t) := p in - (@uncurry q _ f t) d - end res. - -Lemma JMeq_eqrect: - forall (U:Type) (a b:U) (P:U -> Type) (x:P a) (e:a=b), - eq_rect a P x b e ~= x. -Proof. -destruct e. -reflexivity. -Qed. diff --git a/cparser/MenhirLib/Validator_complete.v b/cparser/MenhirLib/Validator_complete.v deleted file mode 100644 index a9823278..00000000 --- a/cparser/MenhirLib/Validator_complete.v +++ /dev/null @@ -1,542 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Automaton. -Require Import Alphabet. -Require Import List. -Require Import Syntax. - -Module Make(Import A:Automaton.T). - -(** We instantiate some sets/map. **) -Module TerminalComparableM <: ComparableM. - Definition t := terminal. - Instance tComparable : Comparable t := _. -End TerminalComparableM. -Module TerminalOrderedType := OrderedType_from_ComparableM TerminalComparableM. -Module StateProdPosComparableM <: ComparableM. - Definition t := (state*production*nat)%type. - Instance tComparable : Comparable t := _. -End StateProdPosComparableM. -Module StateProdPosOrderedType := - OrderedType_from_ComparableM StateProdPosComparableM. - -Module TerminalSet := FSetAVL.Make TerminalOrderedType. -Module StateProdPosMap := FMapAVL.Make StateProdPosOrderedType. - -(** Nullable predicate for symbols and list of symbols. **) -Definition nullable_symb (symbol:symbol) := - match symbol with - | NT nt => nullable_nterm nt - | _ => false - end. - -Definition nullable_word (word:list symbol) := - forallb nullable_symb word. - -(** First predicate for non terminal, symbols and list of symbols, given as FSets. **) -Definition first_nterm_set (nterm:nonterminal) := - fold_left (fun acc t => TerminalSet.add t acc) - (first_nterm nterm) TerminalSet.empty. - -Definition first_symb_set (symbol:symbol) := - match symbol with - | NT nt => first_nterm_set nt - | T t => TerminalSet.singleton t - end. - -Fixpoint first_word_set (word:list symbol) := - match word with - | [] => TerminalSet.empty - | t::q => - if nullable_symb t then - TerminalSet.union (first_symb_set t) (first_word_set q) - else - first_symb_set t - end. - -(** Small helper for finding the part of an item that is after the dot. **) -Definition future_of_prod prod dot_pos : list symbol := - (fix loop n lst := - match n with - | O => lst - | S x => match loop x lst with [] => [] | _::q => q end - end) - dot_pos (rev' (prod_rhs_rev prod)). - -(** We build a fast map to store all the items of all the states. **) -Definition items_map (_:unit): StateProdPosMap.t TerminalSet.t := - fold_left (fun acc state => - fold_left (fun acc item => - let key := (state, prod_item item, dot_pos_item item) in - let data := fold_left (fun acc t => TerminalSet.add t acc) - (lookaheads_item item) TerminalSet.empty - in - let old := - match StateProdPosMap.find key acc with - | Some x => x | None => TerminalSet.empty - end - in - StateProdPosMap.add key (TerminalSet.union data old) acc - ) (items_of_state state) acc - ) all_list (StateProdPosMap.empty TerminalSet.t). - -(** Accessor. **) -Definition find_items_map items_map state prod dot_pos : TerminalSet.t := - match StateProdPosMap.find (state, prod, dot_pos) items_map with - | None => TerminalSet.empty - | Some x => x - end. - -Definition state_has_future state prod (fut:list symbol) (lookahead:terminal) := - exists dot_pos:nat, - fut = future_of_prod prod dot_pos /\ - TerminalSet.In lookahead (find_items_map (items_map ()) state prod dot_pos). - -(** Iterator over items. **) -Definition forallb_items items_map (P:state -> production -> nat -> TerminalSet.t -> bool): bool:= - StateProdPosMap.fold (fun key set acc => - match key with (st, p, pos) => (acc && P st p pos set)%bool end - ) items_map true. - -Lemma forallb_items_spec : - forall p, forallb_items (items_map ()) p = true -> - forall st prod fut lookahead, state_has_future st prod fut lookahead -> - forall P:state -> production -> list symbol -> terminal -> Prop, - (forall st prod pos set lookahead, - TerminalSet.In lookahead set -> p st prod pos set = true -> - P st prod (future_of_prod prod pos) lookahead) -> - P st prod fut lookahead. -Proof. -intros. -unfold forallb_items in H. -rewrite StateProdPosMap.fold_1 in H. -destruct H0; destruct H0. -specialize (H1 st prod x _ _ H2). -destruct H0. -apply H1. -unfold find_items_map in *. -pose proof (@StateProdPosMap.find_2 _ (items_map ()) (st, prod, x)). -destruct (StateProdPosMap.find (st, prod, x) (items_map ())); [ |destruct (TerminalSet.empty_1 H2)]. -specialize (H0 _ (eq_refl _)). -pose proof (StateProdPosMap.elements_1 H0). -revert H. -generalize true at 1. -induction H3. -destruct H. -destruct y. -simpl in H3; destruct H3. -pose proof (compare_eq (st, prod, x) k H). -destruct H3. -simpl. -generalize (p st prod x t). -induction l; simpl; intros. -rewrite Bool.andb_true_iff in H3. -intuition. -destruct a; destruct k; destruct p0. -simpl in H3. -replace (b0 && b && p s p0 n t0)%bool with (b0 && p s p0 n t0 && b)%bool in H3. -apply (IHl _ _ H3). -destruct b, b0, (p s p0 n t0); reflexivity. -intro. -apply IHInA. -Qed. - -(** * Validation for completeness **) - -(** The nullable predicate is a fixpoint : it is correct. **) -Definition nullable_stable:= - forall p:production, - nullable_word (rev (prod_rhs_rev p)) = true -> - nullable_nterm (prod_lhs p) = true. - -Definition is_nullable_stable (_:unit) := - forallb (fun p:production => - implb (nullable_word (rev' (prod_rhs_rev p))) (nullable_nterm (prod_lhs p))) - all_list. - -Property is_nullable_stable_correct : - is_nullable_stable () = true -> nullable_stable. -Proof. -unfold is_nullable_stable, nullable_stable. -intros. -rewrite forallb_forall in H. -specialize (H p (all_list_forall p)). -unfold rev' in H; rewrite <- rev_alt in H. -rewrite H0 in H; intuition. -Qed. - -(** The first predicate is a fixpoint : it is correct. **) -Definition first_stable:= - forall (p:production), - TerminalSet.Subset (first_word_set (rev (prod_rhs_rev p))) - (first_nterm_set (prod_lhs p)). - -Definition is_first_stable (_:unit) := - forallb (fun p:production => - TerminalSet.subset (first_word_set (rev' (prod_rhs_rev p))) - (first_nterm_set (prod_lhs p))) - all_list. - -Property is_first_stable_correct : - is_first_stable () = true -> first_stable. -Proof. -unfold is_first_stable, first_stable. -intros. -rewrite forallb_forall in H. -specialize (H p (all_list_forall p)). -unfold rev' in H; rewrite <- rev_alt in H. -apply TerminalSet.subset_2; intuition. -Qed. - -(** The initial state has all the S=>.u items, where S is the start non-terminal **) -Definition start_future := - forall (init:initstate) (t:terminal) (p:production), - prod_lhs p = start_nt init -> - state_has_future init p (rev (prod_rhs_rev p)) t. - -Definition is_start_future items_map := - forallb (fun init => - forallb (fun prod => - if compare_eqb (prod_lhs prod) (start_nt init) then - let lookaheads := find_items_map items_map init prod 0 in - forallb (fun t => TerminalSet.mem t lookaheads) all_list - else - true) all_list) all_list. - -Property is_start_future_correct : - is_start_future (items_map ()) = true -> start_future. -Proof. -unfold is_start_future, start_future. -intros. -rewrite forallb_forall in H. -specialize (H init (all_list_forall _)). -rewrite forallb_forall in H. -specialize (H p (all_list_forall _)). -rewrite <- compare_eqb_iff in H0. -rewrite H0 in H. -rewrite forallb_forall in H. -specialize (H t (all_list_forall _)). -exists 0. -split. -apply rev_alt. -apply TerminalSet.mem_2; eauto. -Qed. - -(** If a state contains an item of the form A->_.av[[b]], where a is a - terminal, then reading an a does a [Shift_act], to a state containing - an item of the form A->_.v[[b]]. **) -Definition terminal_shift := - forall (s1:state) prod fut lookahead, - state_has_future s1 prod fut lookahead -> - match fut with - | T t::q => - match action_table s1 with - | Lookahead_act awp => - match awp t with - | Shift_act s2 _ => - state_has_future s2 prod q lookahead - | _ => False - end - | _ => False - end - | _ => True - end. - -Definition is_terminal_shift items_map := - forallb_items items_map (fun s1 prod pos lset => - match future_of_prod prod pos with - | T t::_ => - match action_table s1 with - | Lookahead_act awp => - match awp t with - | Shift_act s2 _ => - TerminalSet.subset lset (find_items_map items_map s2 prod (S pos)) - | _ => false - end - | _ => false - end - | _ => true - end). - -Property is_terminal_shift_correct : - is_terminal_shift (items_map ()) = true -> terminal_shift. -Proof. -unfold is_terminal_shift, terminal_shift. -intros. -apply (forallb_items_spec _ H _ _ _ _ H0 (fun _ _ fut look => _)). -intros. -destruct (future_of_prod prod0 pos) as [|[]] eqn:?; intuition. -destruct (action_table st); intuition. -destruct (l0 t); intuition. -exists (S pos). -split. -unfold future_of_prod in *. -rewrite Heql; reflexivity. -apply (TerminalSet.subset_2 H2); intuition. -Qed. - -(** If a state contains an item of the form A->_.[[a]], then either we do a - [Default_reduce_act] of the corresponding production, either a is a - terminal (ie. there is a lookahead terminal), and reading a does a - [Reduce_act] of the corresponding production. **) -Definition end_reduce := - forall (s:state) prod fut lookahead, - state_has_future s prod fut lookahead -> - fut = [] -> - match action_table s with - | Default_reduce_act p => p = prod - | Lookahead_act awt => - match awt lookahead with - | Reduce_act p => p = prod - | _ => False - end - end. - -Definition is_end_reduce items_map := - forallb_items items_map (fun s prod pos lset => - match future_of_prod prod pos with - | [] => - match action_table s with - | Default_reduce_act p => compare_eqb p prod - | Lookahead_act awt => - TerminalSet.fold (fun lookahead acc => - match awt lookahead with - | Reduce_act p => (acc && compare_eqb p prod)%bool - | _ => false - end) lset true - end - | _ => true - end). - -Property is_end_reduce_correct : - is_end_reduce (items_map ()) = true -> end_reduce. -Proof. -unfold is_end_reduce, end_reduce. -intros. -revert H1. -apply (forallb_items_spec _ H _ _ _ _ H0 (fun st prod fut look => _ -> - match action_table st with - | Default_reduce_act p => p = prod - | _ => _ - end)). -intros. -rewrite H3 in H2. -destruct (action_table st); intuition. -apply compare_eqb_iff; intuition. -rewrite TerminalSet.fold_1 in H2. -revert H2. -generalize true at 1. -pose proof (TerminalSet.elements_1 H1). -induction H2. -pose proof (compare_eq _ _ H2). -destruct H4. -simpl. -assert (fold_left - (fun (a : bool) (e : TerminalSet.elt) => - match l e with - | Shift_act _ _ => false - | Reduce_act p => (a && compare_eqb p prod0)%bool - | Fail_act => false - end) l0 false = true -> False). -induction l0; intuition. -apply IHl0. -simpl in H4. -destruct (l a); intuition. -destruct (l lookahead0); intuition. -apply compare_eqb_iff. -destruct (compare_eqb p prod0); intuition. -destruct b; intuition. -simpl; intros. -eapply IHInA; eauto. -Qed. - -(** If a state contains an item of the form A->_.Bv[[b]], where B is a - non terminal, then the goto table says we have to go to a state containing - an item of the form A->_.v[[b]]. **) -Definition non_terminal_goto := - forall (s1:state) prod fut lookahead, - state_has_future s1 prod fut lookahead -> - match fut with - | NT nt::q => - match goto_table s1 nt with - | Some (exist _ s2 _) => - state_has_future s2 prod q lookahead - | None => - forall prod fut lookahead, - state_has_future s1 prod fut lookahead -> - match fut with - | NT nt'::_ => nt <> nt' - | _ => True - end - end - | _ => True - end. - -Definition is_non_terminal_goto items_map := - forallb_items items_map (fun s1 prod pos lset => - match future_of_prod prod pos with - | NT nt::_ => - match goto_table s1 nt with - | Some (exist _ s2 _) => - TerminalSet.subset lset (find_items_map items_map s2 prod (S pos)) - | None => forallb_items items_map (fun s1' prod' pos' _ => - (implb (compare_eqb s1 s1') - match future_of_prod prod' pos' with - | NT nt' :: _ => negb (compare_eqb nt nt') - | _ => true - end)%bool) - end - | _ => true - end). - -Property is_non_terminal_goto_correct : - is_non_terminal_goto (items_map ()) = true -> non_terminal_goto. -Proof. -unfold is_non_terminal_goto, non_terminal_goto. -intros. -apply (forallb_items_spec _ H _ _ _ _ H0 (fun st prod fut look => - match fut with - | NT nt :: q => - match goto_table st nt with - | Some _ => _ - | None => - forall p f l, state_has_future st p f l -> (_:Prop) - end - | _ => _ - end)). -intros. -destruct (future_of_prod prod0 pos) as [|[]] eqn:?; intuition. -destruct (goto_table st n) as [[]|]. -exists (S pos). -split. -unfold future_of_prod in *. -rewrite Heql; reflexivity. -apply (TerminalSet.subset_2 H2); intuition. -intros. -remember st in H2; revert Heqs. -apply (forallb_items_spec _ H2 _ _ _ _ H3 (fun st' prod fut look => s = st' -> match fut return Prop with [] => _ | _ => _ end)); intros. -rewrite <- compare_eqb_iff in H6; rewrite H6 in H5. -destruct (future_of_prod prod1 pos0) as [|[]]; intuition. -rewrite <- compare_eqb_iff in H7; rewrite H7 in H5. -discriminate. -Qed. - -Definition start_goto := - forall (init:initstate), goto_table init (start_nt init) = None. - -Definition is_start_goto (_:unit) := - forallb (fun (init:initstate) => - match goto_table init (start_nt init) with - | Some _ => false - | None => true - end) all_list. - -Definition is_start_goto_correct: - is_start_goto () = true -> start_goto. -Proof. -unfold is_start_goto, start_goto. -rewrite forallb_forall. -intros. -specialize (H init (all_list_forall _)). -destruct (goto_table init (start_nt init)); congruence. -Qed. - -(** Closure property of item sets : if a state contains an item of the form - A->_.Bv[[b]], then for each production B->u and each terminal a of - first(vb), the state contains an item of the form B->_.u[[a]] **) -Definition non_terminal_closed := - forall (s1:state) prod fut lookahead, - state_has_future s1 prod fut lookahead -> - match fut with - | NT nt::q => - forall (p:production) (lookahead2:terminal), - prod_lhs p = nt -> - TerminalSet.In lookahead2 (first_word_set q) \/ - lookahead2 = lookahead /\ nullable_word q = true -> - state_has_future s1 p (rev (prod_rhs_rev p)) lookahead2 - | _ => True - end. - -Definition is_non_terminal_closed items_map := - forallb_items items_map (fun s1 prod pos lset => - match future_of_prod prod pos with - | NT nt::q => - forallb (fun p => - if compare_eqb (prod_lhs p) nt then - let lookaheads := find_items_map items_map s1 p 0 in - (implb (nullable_word q) (TerminalSet.subset lset lookaheads)) && - TerminalSet.subset (first_word_set q) lookaheads - else true - )%bool all_list - | _ => true - end). - -Property is_non_terminal_closed_correct: - is_non_terminal_closed (items_map ()) = true -> non_terminal_closed. -Proof. -unfold is_non_terminal_closed, non_terminal_closed. -intros. -apply (forallb_items_spec _ H _ _ _ _ H0 (fun st prod fut look => - match fut with - | NT nt :: q => forall p l, _ -> _ -> state_has_future st _ _ _ - | _ => _ - end)). -intros. -destruct (future_of_prod prod0 pos); intuition. -destruct s; eauto; intros. -rewrite forallb_forall in H2. -specialize (H2 p (all_list_forall p)). -rewrite <- compare_eqb_iff in H3. -rewrite H3 in H2. -rewrite Bool.andb_true_iff in H2. -destruct H2. -exists 0. -split. -apply rev_alt. -destruct H4 as [|[]]; subst. -apply (TerminalSet.subset_2 H5); intuition. -rewrite H6 in H2. -apply (TerminalSet.subset_2 H2); intuition. -Qed. - -(** The automaton is complete **) -Definition complete := - nullable_stable /\ first_stable /\ start_future /\ terminal_shift - /\ end_reduce /\ non_terminal_goto /\ start_goto /\ non_terminal_closed. - -Definition is_complete (_:unit) := - let items_map := items_map () in - (is_nullable_stable () && is_first_stable () && is_start_future items_map && - is_terminal_shift items_map && is_end_reduce items_map && is_start_goto () && - is_non_terminal_goto items_map && is_non_terminal_closed items_map)%bool. - -Property is_complete_correct: - is_complete () = true -> complete. -Proof. -unfold is_complete, complete. -repeat rewrite Bool.andb_true_iff. -intuition. -apply is_nullable_stable_correct; assumption. -apply is_first_stable_correct; assumption. -apply is_start_future_correct; assumption. -apply is_terminal_shift_correct; assumption. -apply is_end_reduce_correct; assumption. -apply is_non_terminal_goto_correct; assumption. -apply is_start_goto_correct; assumption. -apply is_non_terminal_closed_correct; assumption. -Qed. - -End Make. diff --git a/cparser/MenhirLib/Validator_safe.v b/cparser/MenhirLib/Validator_safe.v deleted file mode 100644 index 183d661b..00000000 --- a/cparser/MenhirLib/Validator_safe.v +++ /dev/null @@ -1,414 +0,0 @@ -(* *********************************************************************) -(* *) -(* The Compcert verified compiler *) -(* *) -(* Jacques-Henri Jourdan, INRIA Paris-Rocquencourt *) -(* *) -(* Copyright Institut National de Recherche en Informatique et en *) -(* Automatique. All rights reserved. This file is distributed *) -(* under the terms of the GNU General Public License as published by *) -(* the Free Software Foundation, either version 2 of the License, or *) -(* (at your option) any later version. This file is also distributed *) -(* under the terms of the INRIA Non-Commercial License Agreement. *) -(* *) -(* *********************************************************************) - -Require Automaton. -Require Import Alphabet. -Require Import List. -Require Import Syntax. - -Module Make(Import A:Automaton.T). - -(** The singleton predicate for states **) -Definition singleton_state_pred (state:state) := - (fun state' => match compare state state' with Eq => true |_ => false end). - -(** [past_state_of_non_init_state], extended for all states. **) -Definition past_state_of_state (state:state) := - match state with - | Init _ => [] - | Ninit nis => past_state_of_non_init_state nis - end. - -(** Concatenations of last and past **) -Definition head_symbs_of_state (state:state) := - match state with - | Init _ => [] - | Ninit s => - last_symb_of_non_init_state s::past_symb_of_non_init_state s - end. -Definition head_states_of_state (state:state) := - singleton_state_pred state::past_state_of_state state. - -(** * Validation for correctness **) - -(** Prefix predicate between two lists of symbols. **) -Inductive prefix: list symbol -> list symbol -> Prop := - | prefix_nil: forall l, prefix [] l - | prefix_cons: forall l1 l2 x, prefix l1 l2 -> prefix (x::l1) (x::l2). - -Fixpoint is_prefix (l1 l2:list symbol):= - match l1, l2 with - | [], _ => true - | t1::q1, t2::q2 => (compare_eqb t1 t2 && is_prefix q1 q2)%bool - | _::_, [] => false - end. - -Property is_prefix_correct (l1 l2:list symbol): - is_prefix l1 l2 = true -> prefix l1 l2. -Proof. -revert l2. -induction l1; intros. -apply prefix_nil. -unfold is_prefix in H. -destruct l2; intuition; try discriminate. -rewrite Bool.andb_true_iff in H. -destruct H. -rewrite compare_eqb_iff in H. -destruct H. -apply prefix_cons. -apply IHl1; intuition. -Qed. - -(** If we shift, then the known top symbols of the destination state is - a prefix of the known top symbols of the source state, with the new - symbol added. **) -Definition shift_head_symbs := - forall s, - match action_table s with - | Lookahead_act awp => - forall t, match awp t with - | Shift_act s2 _ => - prefix (past_symb_of_non_init_state s2) (head_symbs_of_state s) - | _ => True - end - | _ => True - end. - -Definition is_shift_head_symbs (_:unit) := - forallb (fun s:state => - match action_table s with - | Lookahead_act awp => - forallb (fun t => - match awp t with - | Shift_act s2 _ => - is_prefix (past_symb_of_non_init_state s2) (head_symbs_of_state s) - | _ => true - end) - all_list - | _ => true - end) - all_list. - -Property is_shift_head_symbs_correct: - is_shift_head_symbs () = true -> shift_head_symbs. -Proof. -unfold is_shift_head_symbs, shift_head_symbs. -intros. -rewrite forallb_forall in H. -specialize (H s (all_list_forall s)). -destruct (action_table s); intuition. -rewrite forallb_forall in H. -specialize (H t (all_list_forall t)). -destruct (l t); intuition. -apply is_prefix_correct; intuition. -Qed. - -(** When a goto happens, then the known top symbols of the destination state - is a prefix of the known top symbols of the source state, with the new - symbol added. **) -Definition goto_head_symbs := - forall s nt, - match goto_table s nt with - | Some (exist _ s2 _) => - prefix (past_symb_of_non_init_state s2) (head_symbs_of_state s) - | None => True - end. - -Definition is_goto_head_symbs (_:unit) := - forallb (fun s:state => - forallb (fun nt => - match goto_table s nt with - | Some (exist _ s2 _) => - is_prefix (past_symb_of_non_init_state s2) (head_symbs_of_state s) - | None => true - end) - all_list) - all_list. - -Property is_goto_head_symbs_correct: - is_goto_head_symbs () = true -> goto_head_symbs. -Proof. -unfold is_goto_head_symbs, goto_head_symbs. -intros. -rewrite forallb_forall in H. -specialize (H s (all_list_forall s)). -rewrite forallb_forall in H. -specialize (H nt (all_list_forall nt)). -destruct (goto_table s nt); intuition. -destruct s0. -apply is_prefix_correct; intuition. -Qed. - -(** We have to say the same kind of checks for the assumptions about the - states stack. However, theses assumptions are predicates. So we define - a notion of "prefix" over predicates lists, that means, basically, that - an assumption entails another **) -Inductive prefix_pred: list (state->bool) -> list (state->bool) -> Prop := - | prefix_pred_nil: forall l, prefix_pred [] l - | prefix_pred_cons: forall l1 l2 f1 f2, - (forall x, implb (f2 x) (f1 x) = true) -> - prefix_pred l1 l2 -> prefix_pred (f1::l1) (f2::l2). - -Fixpoint is_prefix_pred (l1 l2:list (state->bool)) := - match l1, l2 with - | [], _ => true - | f1::q1, f2::q2 => - (forallb (fun x => implb (f2 x) (f1 x)) all_list - && is_prefix_pred q1 q2)%bool - | _::_, [] => false - end. - -Property is_prefix_pred_correct (l1 l2:list (state->bool)) : - is_prefix_pred l1 l2 = true -> prefix_pred l1 l2. -Proof. -revert l2. -induction l1. -intros. -apply prefix_pred_nil. -intros. -destruct l2; intuition; try discriminate. -unfold is_prefix_pred in H. -rewrite Bool.andb_true_iff in H. -rewrite forallb_forall in H. -apply prefix_pred_cons; intuition. -apply H0. -apply all_list_forall. -Qed. - -(** The assumptions about state stack is conserved when we shift **) -Definition shift_past_state := - forall s, - match action_table s with - | Lookahead_act awp => - forall t, match awp t with - | Shift_act s2 _ => - prefix_pred (past_state_of_non_init_state s2) - (head_states_of_state s) - | _ => True - end - | _ => True - end. - -Definition is_shift_past_state (_:unit) := - forallb (fun s:state => - match action_table s with - | Lookahead_act awp => - forallb (fun t => - match awp t with - | Shift_act s2 _ => - is_prefix_pred - (past_state_of_non_init_state s2) (head_states_of_state s) - | _ => true - end) - all_list - | _ => true - end) - all_list. - -Property is_shift_past_state_correct: - is_shift_past_state () = true -> shift_past_state. -Proof. -unfold is_shift_past_state, shift_past_state. -intros. -rewrite forallb_forall in H. -specialize (H s (all_list_forall s)). -destruct (action_table s); intuition. -rewrite forallb_forall in H. -specialize (H t (all_list_forall t)). -destruct (l t); intuition. -apply is_prefix_pred_correct; intuition. -Qed. - -(** The assumptions about state stack is conserved when we do a goto **) -Definition goto_past_state := - forall s nt, - match goto_table s nt with - | Some (exist _ s2 _) => - prefix_pred (past_state_of_non_init_state s2) - (head_states_of_state s) - | None => True - end. - -Definition is_goto_past_state (_:unit) := - forallb (fun s:state => - forallb (fun nt => - match goto_table s nt with - | Some (exist _ s2 _) => - is_prefix_pred - (past_state_of_non_init_state s2) (head_states_of_state s) - | None => true - end) - all_list) - all_list. - -Property is_goto_past_state_correct : - is_goto_past_state () = true -> goto_past_state. -Proof. -unfold is_goto_past_state, goto_past_state. -intros. -rewrite forallb_forall in H. -specialize (H s (all_list_forall s)). -rewrite forallb_forall in H. -specialize (H nt (all_list_forall nt)). -destruct (goto_table s nt); intuition. -destruct s0. -apply is_prefix_pred_correct; intuition. -Qed. - -(** What states are possible after having popped these symbols from the - stack, given the annotation of the current state ? **) -Inductive state_valid_after_pop (s:state): - list symbol -> list (state -> bool) -> Prop := - | state_valid_after_pop_nil1: - forall p pl, p s = true -> state_valid_after_pop s [] (p::pl) - | state_valid_after_pop_nil2: - forall sl, state_valid_after_pop s sl [] - | state_valid_after_pop_cons: - forall st sq p pl, state_valid_after_pop s sq pl -> - state_valid_after_pop s (st::sq) (p::pl). - -Fixpoint is_state_valid_after_pop - (state:state) (to_pop:list symbol) annot := - match annot, to_pop with - | [], _ => true - | p::_, [] => p state - | p::pl, s::sl => is_state_valid_after_pop state sl pl - end. - -Property is_state_valid_after_pop_complete state sl pl : - state_valid_after_pop state sl pl -> is_state_valid_after_pop state sl pl = true. -Proof. -intro. -induction H; intuition. -destruct sl; intuition. -Qed. - -(** A state is valid for reducing a production when : - - The assumptions on the state are such that we will find the right hand - side of the production on the stack. - - We will be able to do a goto after having popped the right hand side. -**) -Definition valid_for_reduce (state:state) prod := - prefix (prod_rhs_rev prod) (head_symbs_of_state state) /\ - forall state_new, - state_valid_after_pop state_new - (prod_rhs_rev prod) (head_states_of_state state) -> - goto_table state_new (prod_lhs prod) = None -> - match state_new with - | Init i => prod_lhs prod = start_nt i - | Ninit _ => False - end. - -Definition is_valid_for_reduce (state:state) prod:= - (is_prefix (prod_rhs_rev prod) (head_symbs_of_state state) && - forallb (fun state_new => - if is_state_valid_after_pop state_new (prod_rhs_rev prod) - (head_states_of_state state) then - match goto_table state_new (prod_lhs prod) with - | Some _ => true - | None => - match state_new with - | Init i => compare_eqb (prod_lhs prod) (start_nt i) - | Ninit _ => false - end - end - else - true) - all_list)%bool. - -Property is_valid_for_reduce_correct (state:state) prod: - is_valid_for_reduce state prod = true -> valid_for_reduce state prod. -Proof. -unfold is_valid_for_reduce, valid_for_reduce. -intros. -rewrite Bool.andb_true_iff in H. -split. -apply is_prefix_correct. -intuition. -intros. -rewrite forallb_forall in H. -destruct H. -specialize (H2 state_new (all_list_forall state_new)). -rewrite is_state_valid_after_pop_complete, H1 in H2. -destruct state_new; intuition. -rewrite compare_eqb_iff in H2; intuition. -intuition. -Qed. - -(** All the states that does a reduce are valid for reduction **) -Definition reduce_ok := - forall s, - match action_table s with - | Lookahead_act awp => - forall t, match awp t with - | Reduce_act p => valid_for_reduce s p - | _ => True - end - | Default_reduce_act p => valid_for_reduce s p - end. - -Definition is_reduce_ok (_:unit) := - forallb (fun s => - match action_table s with - | Lookahead_act awp => - forallb (fun t => - match awp t with - | Reduce_act p => is_valid_for_reduce s p - | _ => true - end) - all_list - | Default_reduce_act p => is_valid_for_reduce s p - end) - all_list. - -Property is_reduce_ok_correct : - is_reduce_ok () = true -> reduce_ok. -Proof. -unfold is_reduce_ok, reduce_ok. -intros. -rewrite forallb_forall in H. -specialize (H s (all_list_forall s)). -destruct (action_table s). -apply is_valid_for_reduce_correct; intuition. -intro. -rewrite forallb_forall in H. -specialize (H t (all_list_forall t)). -destruct (l t); intuition. -apply is_valid_for_reduce_correct; intuition. -Qed. - -(** The automaton is safe **) -Definition safe := - shift_head_symbs /\ goto_head_symbs /\ shift_past_state /\ - goto_past_state /\ reduce_ok. - -Definition is_safe (_:unit) := - (is_shift_head_symbs () && is_goto_head_symbs () && is_shift_past_state () && - is_goto_past_state () && is_reduce_ok ())%bool. - -Property is_safe_correct: - is_safe () = true -> safe. -Proof. -unfold safe, is_safe. -repeat rewrite Bool.andb_true_iff. -intuition. -apply is_shift_head_symbs_correct; assumption. -apply is_goto_head_symbs_correct; assumption. -apply is_shift_past_state_correct; assumption. -apply is_goto_past_state_correct; assumption. -apply is_reduce_ok_correct; assumption. -Qed. - -End Make. diff --git a/cparser/PackedStructs.ml b/cparser/PackedStructs.ml index 3c27f3a9..4c70c7ae 100644 --- a/cparser/PackedStructs.ml +++ b/cparser/PackedStructs.ml @@ -418,4 +418,4 @@ let program p = | _ -> false end; Hashtbl.clear byteswapped_fields; - transf_globdecls (Builtins.environment()) [] p + transf_globdecls (Env.initial()) [] p diff --git a/cparser/Parse.ml b/cparser/Parse.ml index 154e3dcf..29245083 100644 --- a/cparser/Parse.ml +++ b/cparser/Parse.ml @@ -56,22 +56,21 @@ let preprocessed_file transfs name sourcefile = let text = read_file sourcefile in let p = let t = parse_transformations transfs in - let rec inf = Datatypes.S inf in + let log_fuel = Camlcoq.Nat.of_int 50 in let ast : Cabs.definition list = - Obj.magic (match Timing.time "Parsing" (* The call to Lexer.tokens_stream results in the pre parsing of the entire file. This is non-negligeabe, so we cannot use Timing.time2 *) (fun () -> - Parser.translation_unit_file inf (Lexer.tokens_stream name text)) () + Parser.translation_unit_file log_fuel (Lexer.tokens_stream name text)) () with - | Parser.Parser.Inter.Fail_pr -> + | Parser.MenhirLibParser.Inter.Fail_pr -> (* Theoretically impossible : implies inconsistencies between grammars. *) - Diagnostics.fatal_error Diagnostics.no_loc "internal error while parsing" - | Parser.Parser.Inter.Timeout_pr -> assert false - | Parser.Parser.Inter.Parsed_pr (ast, _ ) -> ast) in + Diagnostics.fatal_error Diagnostics.no_loc "internal error while parsing" + | Parser.MenhirLibParser.Inter.Timeout_pr -> assert false + | Parser.MenhirLibParser.Inter.Parsed_pr (ast, _ ) -> ast) in let p1 = Timing.time "Elaboration" Elab.elab_file ast in Diagnostics.check_errors (); Timing.time2 "Emulations" transform_program t p1 name in diff --git a/cparser/Parser.vy b/cparser/Parser.vy index 79e3793d..03bfa590 100644 --- a/cparser/Parser.vy +++ b/cparser/Parser.vy @@ -15,96 +15,99 @@ %{ -Require Import Cabs. Require Import List. +Require Cabs. %} -%token<string * cabsloc> VAR_NAME TYPEDEF_NAME OTHER_NAME -%token<string * cabsloc> PRAGMA -%token<bool * list char_code * cabsloc> STRING_LITERAL -%token<constant * cabsloc> CONSTANT -%token<cabsloc> SIZEOF PTR INC DEC LEFT RIGHT LEQ GEQ EQEQ EQ NEQ LT GT +%token<Cabs.string * Cabs.loc> VAR_NAME TYPEDEF_NAME OTHER_NAME +%token<Cabs.string * Cabs.loc> PRAGMA +%token<bool * list Cabs.char_code * Cabs.loc> STRING_LITERAL +%token<Cabs.constant * Cabs.loc> CONSTANT +%token<Cabs.loc> SIZEOF PTR INC DEC LEFT RIGHT LEQ GEQ EQEQ EQ NEQ LT GT ANDAND BARBAR PLUS MINUS STAR TILDE BANG SLASH PERCENT HAT BAR QUESTION COLON AND ALIGNOF -%token<cabsloc> MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN +%token<Cabs.loc> MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN -%token<cabsloc> LPAREN RPAREN LBRACK RBRACK LBRACE RBRACE DOT COMMA - SEMICOLON ELLIPSIS TYPEDEF EXTERN STATIC RESTRICT AUTO REGISTER INLINE NORETURN - CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE CONST VOLATILE VOID +%token<Cabs.loc> LPAREN RPAREN LBRACK RBRACK LBRACE RBRACE DOT COMMA + SEMICOLON ELLIPSIS TYPEDEF EXTERN STATIC RESTRICT AUTO REGISTER INLINE + NORETURN CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE CONST VOLATILE VOID STRUCT UNION ENUM UNDERSCORE_BOOL PACKED ALIGNAS ATTRIBUTE ASM -%token<cabsloc> CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK +%token<Cabs.loc> CASE DEFAULT IF_ ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN BUILTIN_VA_ARG BUILTIN_OFFSETOF %token EOF -%type<expression * cabsloc> primary_expression postfix_expression +%type<Cabs.expression * Cabs.loc> primary_expression postfix_expression unary_expression cast_expression multiplicative_expression additive_expression shift_expression relational_expression equality_expression AND_expression exclusive_OR_expression inclusive_OR_expression logical_AND_expression logical_OR_expression conditional_expression assignment_expression constant_expression expression -%type<unary_operator * cabsloc> unary_operator -%type<binary_operator> assignment_operator -%type<list expression (* Reverse order *)> argument_expression_list -%type<definition> declaration -%type<list spec_elem * cabsloc> declaration_specifiers -%type<list spec_elem> declaration_specifiers_typespec_opt -%type<list init_name (* Reverse order *)> init_declarator_list -%type<init_name> init_declarator -%type<storage * cabsloc> storage_class_specifier -%type<typeSpecifier * cabsloc> type_specifier struct_or_union_specifier enum_specifier -%type<structOrUnion * cabsloc> struct_or_union -%type<list field_group (* Reverse order *)> struct_declaration_list -%type<field_group> struct_declaration -%type<list spec_elem * cabsloc> specifier_qualifier_list -%type<list (option name * option expression) (* Reverse order *)> struct_declarator_list -%type<option name * option expression> struct_declarator -%type<list (string * option expression * cabsloc) (* Reverse order *)> enumerator_list -%type<string * option expression * cabsloc> enumerator -%type<string * cabsloc> enumeration_constant -%type<cvspec * cabsloc> type_qualifier type_qualifier_noattr -%type<funspec * cabsloc> function_specifier -%type<name> declarator declarator_noattrend direct_declarator -%type<(decl_type -> decl_type) * cabsloc> pointer -%type<list cvspec (* Reverse order *)> type_qualifier_list -%type<list parameter * bool> parameter_type_list -%type<list parameter (* Reverse order *)> parameter_list -%type<parameter> parameter_declaration -%type<list spec_elem * decl_type> type_name -%type<decl_type> abstract_declarator direct_abstract_declarator -%type<init_expression> c_initializer -%type<list (list initwhat * init_expression) (* Reverse order *)> initializer_list -%type<list initwhat> designation -%type<list initwhat (* Reverse order *)> designator_list -%type<initwhat> designator -%type<statement> statement_dangerous statement_safe +%type<Cabs.unary_operator * Cabs.loc> unary_operator +%type<Cabs.binary_operator> assignment_operator +%type<list Cabs.expression (* Reverse order *)> argument_expression_list +%type<Cabs.definition> declaration +%type<list Cabs.spec_elem * Cabs.loc> declaration_specifiers +%type<list Cabs.spec_elem> declaration_specifiers_typespec_opt +%type<list Cabs.init_name (* Reverse order *)> init_declarator_list +%type<Cabs.init_name> init_declarator +%type<Cabs.storage * Cabs.loc> storage_class_specifier +%type<Cabs.typeSpecifier * Cabs.loc> type_specifier struct_or_union_specifier enum_specifier +%type<Cabs.structOrUnion * Cabs.loc> struct_or_union +%type<list Cabs.field_group (* Reverse order *)> struct_declaration_list +%type<Cabs.field_group> struct_declaration +%type<list Cabs.spec_elem * Cabs.loc> specifier_qualifier_list +%type<list (option Cabs.name * option Cabs.expression) (* Reverse order *)> + struct_declarator_list +%type<option Cabs.name * option Cabs.expression> struct_declarator +%type<list (Cabs.string * option Cabs.expression * Cabs.loc) (* Reverse order *)> + enumerator_list +%type<Cabs.string * option Cabs.expression * Cabs.loc> enumerator +%type<Cabs.string * Cabs.loc> enumeration_constant +%type<Cabs.cvspec * Cabs.loc> type_qualifier type_qualifier_noattr +%type<Cabs.funspec * Cabs.loc> function_specifier +%type<Cabs.name> declarator declarator_noattrend direct_declarator +%type<(Cabs.decl_type -> Cabs.decl_type) * Cabs.loc> pointer +%type<list Cabs.cvspec (* Reverse order *)> type_qualifier_list +%type<list Cabs.parameter * bool> parameter_type_list +%type<list Cabs.parameter (* Reverse order *)> parameter_list +%type<Cabs.parameter> parameter_declaration +%type<list Cabs.spec_elem * Cabs.decl_type> type_name +%type<Cabs.decl_type> abstract_declarator direct_abstract_declarator +%type<Cabs.init_expression> c_initializer +%type<list (list Cabs.initwhat * Cabs.init_expression) (* Reverse order *)> + initializer_list +%type<list Cabs.initwhat> designation +%type<list Cabs.initwhat (* Reverse order *)> designator_list +%type<Cabs.initwhat> designator +%type<Cabs.statement> statement_dangerous statement_safe labeled_statement(statement_safe) labeled_statement(statement_dangerous) iteration_statement(statement_safe) iteration_statement(statement_dangerous) compound_statement -%type<list statement (* Reverse order *)> block_item_list -%type<statement> block_item expression_statement selection_statement_dangerous +%type<list Cabs.statement (* Reverse order *)> block_item_list +%type<Cabs.statement> block_item expression_statement selection_statement_dangerous selection_statement_safe jump_statement asm_statement -%type<list definition (* Reverse order *)> translation_unit -%type<definition> external_declaration function_definition -%type<list definition> declaration_list -%type<attribute * cabsloc> attribute_specifier -%type<list attribute> attribute_specifier_list -%type<gcc_attribute> gcc_attribute -%type<list gcc_attribute> gcc_attribute_list -%type<gcc_attribute_word> gcc_attribute_word -%type<list string (* Reverse order *)> identifier_list -%type<list asm_flag> asm_flags -%type<option string> asm_op_name -%type<asm_operand> asm_operand -%type<list asm_operand> asm_operands asm_operands_ne -%type<list asm_operand * list asm_operand * list asm_flag> asm_arguments -%type<list cvspec> asm_attributes - -%start<list definition> translation_unit_file +%type<list Cabs.definition (* Reverse order *)> translation_unit +%type<Cabs.definition> external_declaration function_definition +%type<list Cabs.definition> declaration_list +%type<Cabs.attribute * Cabs.loc> attribute_specifier +%type<list Cabs.attribute> attribute_specifier_list +%type<Cabs.gcc_attribute> gcc_attribute +%type<list Cabs.gcc_attribute> gcc_attribute_list +%type<Cabs.gcc_attribute_word> gcc_attribute_word +%type<list Cabs.string (* Reverse order *)> identifier_list +%type<list Cabs.asm_flag> asm_flags +%type<option Cabs.string> asm_op_name +%type<Cabs.asm_operand> asm_operand +%type<list Cabs.asm_operand> asm_operands asm_operands_ne +%type<list Cabs.asm_operand * list Cabs.asm_operand * list Cabs.asm_flag> asm_arguments +%type<list Cabs.cvspec> asm_attributes + +%start<list Cabs.definition> translation_unit_file %% (* Actual grammar *) @@ -112,12 +115,12 @@ Require Import List. (* 6.5.1 *) primary_expression: | var = VAR_NAME - { (VARIABLE (fst var), snd var) } + { (Cabs.VARIABLE (fst var), snd var) } | cst = CONSTANT - { (CONSTANT (fst cst), snd cst) } + { (Cabs.CONSTANT (fst cst), snd cst) } | str = STRING_LITERAL { let '((wide, chars), loc) := str in - (CONSTANT (CONST_STRING wide chars), loc) } + (Cabs.CONSTANT (Cabs.CONST_STRING wide chars), loc) } | loc = LPAREN expr = expression RPAREN { (fst expr, loc)} @@ -126,29 +129,30 @@ postfix_expression: | expr = primary_expression { expr } | expr = postfix_expression LBRACK index = expression RBRACK - { (INDEX (fst expr) (fst index), snd expr) } + { (Cabs.INDEX (fst expr) (fst index), snd expr) } | expr = postfix_expression LPAREN args = argument_expression_list RPAREN - { (CALL (fst expr) (rev' args), snd expr) } + { (Cabs.CALL (fst expr) (rev' args), snd expr) } | expr = postfix_expression LPAREN RPAREN - { (CALL (fst expr) [], snd expr) } + { (Cabs.CALL (fst expr) [], snd expr) } | loc = BUILTIN_VA_ARG LPAREN expr = assignment_expression COMMA ty = type_name RPAREN - { (BUILTIN_VA_ARG (fst expr) ty, loc) } + { (Cabs.BUILTIN_VA_ARG (fst expr) ty, loc) } | expr = postfix_expression DOT mem = OTHER_NAME - { (MEMBEROF (fst expr) (fst mem), snd expr) } + { (Cabs.MEMBEROF (fst expr) (fst mem), snd expr) } | expr = postfix_expression PTR mem = OTHER_NAME - { (MEMBEROFPTR (fst expr) (fst mem), snd expr) } + { (Cabs.MEMBEROFPTR (fst expr) (fst mem), snd expr) } | expr = postfix_expression INC - { (UNARY POSINCR (fst expr), snd expr) } + { (Cabs.UNARY Cabs.POSINCR (fst expr), snd expr) } | expr = postfix_expression DEC - { (UNARY POSDECR (fst expr), snd expr) } + { (Cabs.UNARY Cabs.POSDECR (fst expr), snd expr) } | loc = LPAREN typ = type_name RPAREN LBRACE init = initializer_list RBRACE - { (CAST typ (COMPOUND_INIT (rev' init)), loc) } + { (Cabs.CAST typ (Cabs.COMPOUND_INIT (rev' init)), loc) } | loc = LPAREN typ = type_name RPAREN LBRACE init = initializer_list COMMA RBRACE - { (CAST typ (COMPOUND_INIT (rev' init)), loc) } -| loc = BUILTIN_OFFSETOF LPAREN typ = type_name COMMA id = OTHER_NAME mems = designator_list RPAREN - { (BUILTIN_OFFSETOF typ ((INFIELD_INIT (fst id))::(rev mems)), loc) } + { (Cabs.CAST typ (Cabs.COMPOUND_INIT (rev' init)), loc) } +| loc = BUILTIN_OFFSETOF LPAREN typ = type_name COMMA id = OTHER_NAME + mems = designator_list RPAREN + { (Cabs.BUILTIN_OFFSETOF typ ((Cabs.INFIELD_INIT (fst id))::(rev mems)), loc) } | loc = BUILTIN_OFFSETOF LPAREN typ = type_name COMMA mem = OTHER_NAME RPAREN - { (BUILTIN_OFFSETOF typ [INFIELD_INIT (fst mem)], loc) } + { (Cabs.BUILTIN_OFFSETOF typ [Cabs.INFIELD_INIT (fst mem)], loc) } (* Semantic value is in reverse order. *) argument_expression_list: @@ -162,170 +166,171 @@ unary_expression: | expr = postfix_expression { expr } | loc = INC expr = unary_expression - { (UNARY PREINCR (fst expr), loc) } + { (Cabs.UNARY Cabs.PREINCR (fst expr), loc) } | loc = DEC expr = unary_expression - { (UNARY PREDECR (fst expr), loc) } + { (Cabs.UNARY Cabs.PREDECR (fst expr), loc) } | op = unary_operator expr = cast_expression - { (UNARY (fst op) (fst expr), snd op) } + { (Cabs.UNARY (fst op) (fst expr), snd op) } | loc = SIZEOF expr = unary_expression - { (EXPR_SIZEOF (fst expr), loc) } + { (Cabs.EXPR_SIZEOF (fst expr), loc) } | loc = SIZEOF LPAREN typ = type_name RPAREN - { (TYPE_SIZEOF typ, loc) } + { (Cabs.TYPE_SIZEOF typ, loc) } (* Non-standard *) | loc = ALIGNOF LPAREN typ = type_name RPAREN - { (ALIGNOF typ, loc) } + { (Cabs.ALIGNOF typ, loc) } unary_operator: | loc = AND - { (ADDROF, loc) } + { (Cabs.ADDROF, loc) } | loc = STAR - { (MEMOF, loc) } + { (Cabs.MEMOF, loc) } | loc = PLUS - { (PLUS, loc) } + { (Cabs.PLUS, loc) } | loc = MINUS - { (MINUS, loc) } + { (Cabs.MINUS, loc) } | loc = TILDE - { (BNOT, loc) } + { (Cabs.BNOT, loc) } | loc = BANG - { (NOT, loc) } + { (Cabs.NOT, loc) } (* 6.5.4 *) cast_expression: | expr = unary_expression { expr } | loc = LPAREN typ = type_name RPAREN expr = cast_expression - { (CAST typ (SINGLE_INIT (fst expr)), loc) } + { (Cabs.CAST typ (Cabs.SINGLE_INIT (fst expr)), loc) } (* 6.5.5 *) multiplicative_expression: | expr = cast_expression { expr } | expr1 = multiplicative_expression STAR expr2 = cast_expression - { (BINARY MUL (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.MUL (fst expr1) (fst expr2), snd expr1) } | expr1 = multiplicative_expression SLASH expr2 = cast_expression - { (BINARY DIV (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.DIV (fst expr1) (fst expr2), snd expr1) } | expr1 = multiplicative_expression PERCENT expr2 = cast_expression - { (BINARY MOD (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.MOD (fst expr1) (fst expr2), snd expr1) } (* 6.5.6 *) additive_expression: | expr = multiplicative_expression { expr } | expr1 = additive_expression PLUS expr2 = multiplicative_expression - { (BINARY ADD (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.ADD (fst expr1) (fst expr2), snd expr1) } | expr1 = additive_expression MINUS expr2 = multiplicative_expression - { (BINARY SUB (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.SUB (fst expr1) (fst expr2), snd expr1) } (* 6.5.7 *) shift_expression: | expr = additive_expression { expr } | expr1 = shift_expression LEFT expr2 = additive_expression - { (BINARY SHL (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.SHL (fst expr1) (fst expr2), snd expr1) } | expr1 = shift_expression RIGHT expr2 = additive_expression - { (BINARY SHR (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.SHR (fst expr1) (fst expr2), snd expr1) } (* 6.5.8 *) relational_expression: | expr = shift_expression { expr } | expr1 = relational_expression LT expr2 = shift_expression - { (BINARY LT (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.LT (fst expr1) (fst expr2), snd expr1) } | expr1 = relational_expression GT expr2 = shift_expression - { (BINARY GT (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.GT (fst expr1) (fst expr2), snd expr1) } | expr1 = relational_expression LEQ expr2 = shift_expression - { (BINARY LE (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.LE (fst expr1) (fst expr2), snd expr1) } | expr1 = relational_expression GEQ expr2 = shift_expression - { (BINARY GE (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.GE (fst expr1) (fst expr2), snd expr1) } (* 6.5.9 *) equality_expression: | expr = relational_expression { expr } | expr1 = equality_expression EQEQ expr2 = relational_expression - { (BINARY EQ (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.EQ (fst expr1) (fst expr2), snd expr1) } | expr1 = equality_expression NEQ expr2 = relational_expression - { (BINARY NE (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.NE (fst expr1) (fst expr2), snd expr1) } (* 6.5.10 *) AND_expression: | expr = equality_expression { expr } | expr1 = AND_expression AND expr2 = equality_expression - { (BINARY BAND (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.BAND (fst expr1) (fst expr2), snd expr1) } (* 6.5.11 *) exclusive_OR_expression: | expr = AND_expression { expr } | expr1 = exclusive_OR_expression HAT expr2 = AND_expression - { (BINARY XOR (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.XOR (fst expr1) (fst expr2), snd expr1) } (* 6.5.12 *) inclusive_OR_expression: | expr = exclusive_OR_expression { expr } | expr1 = inclusive_OR_expression BAR expr2 = exclusive_OR_expression - { (BINARY BOR (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.BOR (fst expr1) (fst expr2), snd expr1) } (* 6.5.13 *) logical_AND_expression: | expr = inclusive_OR_expression { expr } | expr1 = logical_AND_expression ANDAND expr2 = inclusive_OR_expression - { (BINARY AND (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.AND (fst expr1) (fst expr2), snd expr1) } (* 6.5.14 *) logical_OR_expression: | expr = logical_AND_expression { expr } | expr1 = logical_OR_expression BARBAR expr2 = logical_AND_expression - { (BINARY OR (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.OR (fst expr1) (fst expr2), snd expr1) } (* 6.5.15 *) conditional_expression: | expr = logical_OR_expression { expr } -| expr1 = logical_OR_expression QUESTION expr2 = expression COLON expr3 = conditional_expression - { (QUESTION (fst expr1) (fst expr2) (fst expr3), snd expr1) } +| expr1 = logical_OR_expression QUESTION expr2 = expression COLON + expr3 = conditional_expression + { (Cabs.QUESTION (fst expr1) (fst expr2) (fst expr3), snd expr1) } (* 6.5.16 *) assignment_expression: | expr = conditional_expression { expr } | expr1 = unary_expression op = assignment_operator expr2 = assignment_expression - { (BINARY op (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY op (fst expr1) (fst expr2), snd expr1) } assignment_operator: | EQ - { ASSIGN } + { Cabs.ASSIGN } | MUL_ASSIGN - { MUL_ASSIGN } + { Cabs.MUL_ASSIGN } | DIV_ASSIGN - { DIV_ASSIGN } + { Cabs.DIV_ASSIGN } | MOD_ASSIGN - { MOD_ASSIGN } + { Cabs.MOD_ASSIGN } | ADD_ASSIGN - { ADD_ASSIGN } + { Cabs.ADD_ASSIGN } | SUB_ASSIGN - { SUB_ASSIGN } + { Cabs.SUB_ASSIGN } | LEFT_ASSIGN - { SHL_ASSIGN } + { Cabs.SHL_ASSIGN } | RIGHT_ASSIGN - { SHR_ASSIGN } + { Cabs.SHR_ASSIGN } | XOR_ASSIGN - { XOR_ASSIGN } + { Cabs.XOR_ASSIGN } | OR_ASSIGN - { BOR_ASSIGN } + { Cabs.BOR_ASSIGN } | AND_ASSIGN - { BAND_ASSIGN } + { Cabs.BAND_ASSIGN } (* 6.5.17 *) expression: | expr = assignment_expression { expr } | expr1 = expression COMMA expr2 = assignment_expression - { (BINARY COMMA (fst expr1) (fst expr2), snd expr1) } + { (Cabs.BINARY Cabs.COMMA (fst expr1) (fst expr2), snd expr1) } (* 6.6 *) constant_expression: @@ -335,19 +340,19 @@ constant_expression: (* 6.7 *) declaration: | decspec = declaration_specifiers decls = init_declarator_list SEMICOLON - { DECDEF (fst decspec, rev' decls) (snd decspec) } + { Cabs.DECDEF (fst decspec, rev' decls) (snd decspec) } | decspec = declaration_specifiers SEMICOLON - { DECDEF (fst decspec, []) (snd decspec) } + { Cabs.DECDEF (fst decspec, []) (snd decspec) } declaration_specifiers_typespec_opt: | storage = storage_class_specifier rest = declaration_specifiers_typespec_opt - { SpecStorage (fst storage)::rest } + { Cabs.SpecStorage (fst storage)::rest } | typ = type_specifier rest = declaration_specifiers_typespec_opt - { SpecType (fst typ)::rest } + { Cabs.SpecType (fst typ)::rest } | qual = type_qualifier rest = declaration_specifiers_typespec_opt - { SpecCV (fst qual)::rest } + { Cabs.SpecCV (fst qual)::rest } | func = function_specifier rest = declaration_specifiers_typespec_opt - { SpecFunction (fst func)::rest } + { Cabs.SpecFunction (fst func)::rest } | /* empty */ { [] } @@ -357,16 +362,16 @@ declaration_specifiers_typespec_opt: specifier. *) declaration_specifiers: | storage = storage_class_specifier rest = declaration_specifiers - { (SpecStorage (fst storage)::fst rest, snd storage) } + { (Cabs.SpecStorage (fst storage)::fst rest, snd storage) } | typ = type_specifier rest = declaration_specifiers_typespec_opt - { (SpecType (fst typ)::rest, snd typ) } + { (Cabs.SpecType (fst typ)::rest, snd typ) } (* We have to inline type_qualifier in order to avoid a conflict. *) | qual = type_qualifier_noattr rest = declaration_specifiers - { (SpecCV (fst qual)::fst rest, snd qual) } + { (Cabs.SpecCV (fst qual)::fst rest, snd qual) } | attr = attribute_specifier rest = declaration_specifiers - { (SpecCV (CV_ATTR (fst attr))::fst rest, snd attr) } + { (Cabs.SpecCV (Cabs.CV_ATTR (fst attr))::fst rest, snd attr) } | func = function_specifier rest = declaration_specifiers - { (SpecFunction (fst func)::fst rest, snd func) } + { (Cabs.SpecFunction (fst func)::fst rest, snd func) } init_declarator_list: | init = init_declarator @@ -376,71 +381,71 @@ init_declarator_list: init_declarator: | name = declarator - { Init_name name NO_INIT } + { Cabs.Init_name name Cabs.NO_INIT } | name = declarator EQ init = c_initializer - { Init_name name init } + { Cabs.Init_name name init } (* 6.7.1 *) storage_class_specifier: | loc = TYPEDEF - { (TYPEDEF, loc) } + { (Cabs.TYPEDEF, loc) } | loc = EXTERN - { (EXTERN, loc) } + { (Cabs.EXTERN, loc) } | loc = STATIC - { (STATIC, loc) } + { (Cabs.STATIC, loc) } | loc = AUTO - { (AUTO, loc) } + { (Cabs.AUTO, loc) } | loc = REGISTER - { (REGISTER, loc) } + { (Cabs.REGISTER, loc) } (* 6.7.2 *) type_specifier: | loc = VOID - { (Tvoid, loc) } + { (Cabs.Tvoid, loc) } | loc = CHAR - { (Tchar, loc) } + { (Cabs.Tchar, loc) } | loc = SHORT - { (Tshort, loc) } + { (Cabs.Tshort, loc) } | loc = INT - { (Tint, loc) } + { (Cabs.Tint, loc) } | loc = LONG - { (Tlong, loc) } + { (Cabs.Tlong, loc) } | loc = FLOAT - { (Tfloat, loc) } + { (Cabs.Tfloat, loc) } | loc = DOUBLE - { (Tdouble, loc) } + { (Cabs.Tdouble, loc) } | loc = SIGNED - { (Tsigned, loc) } + { (Cabs.Tsigned, loc) } | loc = UNSIGNED - { (Tunsigned, loc) } + { (Cabs.Tunsigned, loc) } | loc = UNDERSCORE_BOOL - { (T_Bool, loc) } + { (Cabs.T_Bool, loc) } | spec = struct_or_union_specifier { spec } | spec = enum_specifier { spec } | id = TYPEDEF_NAME - { (Tnamed (fst id), snd id) } + { (Cabs.Tnamed (fst id), snd id) } (* 6.7.2.1 *) struct_or_union_specifier: | str_uni = struct_or_union attrs = attribute_specifier_list id = OTHER_NAME LBRACE decls = struct_declaration_list RBRACE - { (Tstruct_union (fst str_uni) (Some (fst id)) (Some (rev' decls)) attrs, + { (Cabs.Tstruct_union (fst str_uni) (Some (fst id)) (Some (rev' decls)) attrs, snd str_uni) } | str_uni = struct_or_union attrs = attribute_specifier_list LBRACE decls = struct_declaration_list RBRACE - { (Tstruct_union (fst str_uni) None (Some (rev' decls)) attrs, + { (Cabs.Tstruct_union (fst str_uni) None (Some (rev' decls)) attrs, snd str_uni) } | str_uni = struct_or_union attrs = attribute_specifier_list id = OTHER_NAME - { (Tstruct_union (fst str_uni) (Some (fst id)) None attrs, + { (Cabs.Tstruct_union (fst str_uni) (Some (fst id)) None attrs, snd str_uni) } struct_or_union: | loc = STRUCT - { (STRUCT, loc) } + { (Cabs.STRUCT, loc) } | loc = UNION - { (UNION, loc) } + { (Cabs.UNION, loc) } struct_declaration_list: | (* empty *) @@ -450,20 +455,20 @@ struct_declaration_list: struct_declaration: | decspec = specifier_qualifier_list decls = struct_declarator_list SEMICOLON - { Field_group (fst decspec) (rev' decls) (snd decspec) } + { Cabs.Field_group (fst decspec) (rev' decls) (snd decspec) } (* Extension to C99 grammar needed to parse some GNU header files. *) | decspec = specifier_qualifier_list SEMICOLON - { Field_group (fst decspec) [(None,None)] (snd decspec) } + { Cabs.Field_group (fst decspec) [(None,None)] (snd decspec) } specifier_qualifier_list: | typ = type_specifier rest = specifier_qualifier_list - { (SpecType (fst typ)::fst rest, snd typ) } + { (Cabs.SpecType (fst typ)::fst rest, snd typ) } | typ = type_specifier - { ([SpecType (fst typ)], snd typ) } + { ([Cabs.SpecType (fst typ)], snd typ) } | qual = type_qualifier rest = specifier_qualifier_list - { (SpecCV (fst qual)::fst rest, snd qual) } + { (Cabs.SpecCV (fst qual)::fst rest, snd qual) } | qual = type_qualifier - { ([SpecCV (fst qual)], snd qual) } + { ([Cabs.SpecCV (fst qual)], snd qual) } struct_declarator_list: | decl = struct_declarator @@ -483,18 +488,18 @@ struct_declarator: enum_specifier: | loc = ENUM attrs = attribute_specifier_list name = OTHER_NAME LBRACE enum_list = enumerator_list RBRACE - { (Tenum (Some (fst name)) (Some (rev' enum_list)) attrs, loc) } + { (Cabs.Tenum (Some (fst name)) (Some (rev' enum_list)) attrs, loc) } | loc = ENUM attrs = attribute_specifier_list LBRACE enum_list = enumerator_list RBRACE - { (Tenum None (Some (rev' enum_list)) attrs, loc) } + { (Cabs.Tenum None (Some (rev' enum_list)) attrs, loc) } | loc = ENUM attrs = attribute_specifier_list name = OTHER_NAME LBRACE enum_list = enumerator_list COMMA RBRACE - { (Tenum (Some (fst name)) (Some (rev' enum_list)) attrs, loc) } + { (Cabs.Tenum (Some (fst name)) (Some (rev' enum_list)) attrs, loc) } | loc = ENUM attrs = attribute_specifier_list LBRACE enum_list = enumerator_list COMMA RBRACE - { (Tenum None (Some (rev' enum_list)) attrs, loc) } + { (Cabs.Tenum None (Some (rev' enum_list)) attrs, loc) } | loc = ENUM attrs = attribute_specifier_list name = OTHER_NAME - { (Tenum (Some (fst name)) None attrs, loc) } + { (Cabs.Tenum (Some (fst name)) None attrs, loc) } enumerator_list: | enum = enumerator @@ -515,18 +520,18 @@ enumeration_constant: (* 6.7.3 *) type_qualifier_noattr: | loc = CONST - { (CV_CONST, loc) } + { (Cabs.CV_CONST, loc) } | loc = RESTRICT - { (CV_RESTRICT, loc) } + { (Cabs.CV_RESTRICT, loc) } | loc = VOLATILE - { (CV_VOLATILE, loc) } + { (Cabs.CV_VOLATILE, loc) } type_qualifier: | qual = type_qualifier_noattr { qual } (* Non-standard *) | attr = attribute_specifier - { (CV_ATTR (fst attr), snd attr) } + { (Cabs.CV_ATTR (fst attr), snd attr) } (* Non-standard *) @@ -538,13 +543,13 @@ attribute_specifier_list: attribute_specifier: | loc = ATTRIBUTE LPAREN LPAREN attr = gcc_attribute_list RPAREN RPAREN - { (GCC_ATTR (rev' attr) loc, loc) } + { (Cabs.GCC_ATTR (rev' attr) loc, loc) } | loc = PACKED LPAREN args = argument_expression_list RPAREN - { (PACKED_ATTR (rev' args) loc, loc) } + { (Cabs.PACKED_ATTR (rev' args) loc, loc) } | loc = ALIGNAS LPAREN args = argument_expression_list RPAREN - { (ALIGNAS_ATTR (rev' args) loc, loc) } + { (Cabs.ALIGNAS_ATTR (rev' args) loc, loc) } | loc = ALIGNAS LPAREN typ = type_name RPAREN - { (ALIGNAS_ATTR [ALIGNOF typ] loc, loc) } + { (Cabs.ALIGNAS_ATTR [Cabs.ALIGNOF typ] loc, loc) } gcc_attribute_list: | a = gcc_attribute @@ -554,80 +559,81 @@ gcc_attribute_list: gcc_attribute: | /* empty */ - { GCC_ATTR_EMPTY } + { Cabs.GCC_ATTR_EMPTY } | w = gcc_attribute_word - { GCC_ATTR_NOARGS w } + { Cabs.GCC_ATTR_NOARGS w } | w = gcc_attribute_word LPAREN RPAREN - { GCC_ATTR_ARGS w [] } + { Cabs.GCC_ATTR_ARGS w [] } | w = gcc_attribute_word LPAREN args = argument_expression_list RPAREN - { GCC_ATTR_ARGS w (rev' args) } + { Cabs.GCC_ATTR_ARGS w (rev' args) } gcc_attribute_word: | i = OTHER_NAME - { GCC_ATTR_IDENT (fst i) } + { Cabs.GCC_ATTR_IDENT (fst i) } | CONST - { GCC_ATTR_CONST } + { Cabs.GCC_ATTR_CONST } | PACKED - { GCC_ATTR_PACKED } + { Cabs.GCC_ATTR_PACKED } (* 6.7.4 *) function_specifier: | loc = INLINE - { (INLINE, loc) } + { (Cabs.INLINE, loc) } | loc = NORETURN - { (NORETURN, loc)} + { (Cabs.NORETURN, loc)} (* 6.7.5 *) declarator: | decl = declarator_noattrend attrs = attribute_specifier_list - { match decl with Name name typ attr loc => - Name name typ (List.app attr attrs) loc end } + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name typ (List.app attr attrs) loc } declarator_noattrend: | decl = direct_declarator { decl } | pt = pointer decl = direct_declarator - { match decl with Name name typ attr _ => - Name name ((fst pt) typ) attr (snd pt) end } + { let 'Cabs.Name name typ attr _ := decl in + Cabs.Name name ((fst pt) typ) attr (snd pt) } direct_declarator: | id = VAR_NAME - { Name (fst id) JUSTBASE [] (snd id) } + { Cabs.Name (fst id) Cabs.JUSTBASE [] (snd id) } | LPAREN decl = declarator RPAREN { decl } -| decl = direct_declarator LBRACK quallst = type_qualifier_list expr = assignment_expression RBRACK - { match decl with Name name typ attr loc => - Name name (ARRAY typ (rev' quallst) (Some (fst expr))) attr loc end } +| decl = direct_declarator LBRACK quallst = type_qualifier_list + expr = assignment_expression RBRACK + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name (Cabs.ARRAY typ (rev' quallst) (Some (fst expr))) attr loc } | decl = direct_declarator LBRACK expr = assignment_expression RBRACK - { match decl with Name name typ attr loc => - Name name (ARRAY typ [] (Some (fst expr))) attr loc end } + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name (Cabs.ARRAY typ [] (Some (fst expr))) attr loc } | decl = direct_declarator LBRACK quallst = type_qualifier_list RBRACK - { match decl with Name name typ attr loc => - Name name (ARRAY typ (rev' quallst) None) attr loc end } + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name (Cabs.ARRAY typ (rev' quallst) None) attr loc } | decl = direct_declarator LBRACK RBRACK - { match decl with Name name typ attr loc => - Name name (ARRAY typ [] None) attr loc end } + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name (Cabs.ARRAY typ [] None) attr loc } (*| direct_declarator LBRACK ... STATIC ... RBRACK | direct_declarator LBRACK STAR RBRACK*) | decl = direct_declarator LPAREN params = parameter_type_list RPAREN - { match decl with Name name typ attr loc => - Name name (PROTO typ params) attr loc end } + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name (Cabs.PROTO typ params) attr loc } | decl = direct_declarator LPAREN RPAREN - { match decl with Name name typ attr loc => - Name name (PROTO_OLD typ []) attr loc end } + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name (Cabs.PROTO_OLD typ []) attr loc } | decl = direct_declarator LPAREN params = identifier_list RPAREN - { match decl with Name name typ attr loc => - Name name (PROTO_OLD typ (rev' params)) attr loc end } + { let 'Cabs.Name name typ attr loc := decl in + Cabs.Name name (Cabs.PROTO_OLD typ (rev' params)) attr loc } pointer: | loc = STAR - { (fun typ => PTR [] typ, loc) } + { (fun typ => Cabs.PTR [] typ, loc) } | loc = STAR quallst = type_qualifier_list - { (fun typ => PTR (rev' quallst) typ, loc) } + { (fun typ => Cabs.PTR (rev' quallst) typ, loc) } | loc = STAR pt = pointer - { (fun typ => PTR [] ((fst pt) typ), loc) } + { (fun typ => Cabs.PTR [] ((fst pt) typ), loc) } | loc = STAR quallst = type_qualifier_list pt = pointer - { (fun typ => PTR (rev' quallst) ((fst pt) typ), loc) } + { (fun typ => Cabs.PTR (rev' quallst) ((fst pt) typ), loc) } type_qualifier_list: | qual = type_qualifier @@ -649,12 +655,12 @@ parameter_list: parameter_declaration: | specs = declaration_specifiers decl = declarator - { match decl with Name name typ attr _ => - PARAM (fst specs) (Some name) typ attr (snd specs) end } + { match decl with Cabs.Name name typ attr _ => + Cabs.PARAM (fst specs) (Some name) typ attr (snd specs) end } | specs = declaration_specifiers decl = abstract_declarator - { PARAM (fst specs) None decl [] (snd specs) } + { Cabs.PARAM (fst specs) None decl [] (snd specs) } | specs = declaration_specifiers - { PARAM (fst specs) None JUSTBASE [] (snd specs) } + { Cabs.PARAM (fst specs) None Cabs.JUSTBASE [] (snd specs) } identifier_list: | id = VAR_NAME @@ -665,13 +671,13 @@ identifier_list: (* 6.7.6 *) type_name: | specqual = specifier_qualifier_list - { (fst specqual, JUSTBASE) } + { (fst specqual, Cabs.JUSTBASE) } | specqual = specifier_qualifier_list typ = abstract_declarator { (fst specqual, typ) } abstract_declarator: | pt = pointer - { (fst pt) JUSTBASE } + { (fst pt) Cabs.JUSTBASE } | pt = pointer typ = direct_abstract_declarator { (fst pt) typ } | typ = direct_abstract_declarator @@ -680,41 +686,42 @@ abstract_declarator: direct_abstract_declarator: | LPAREN typ = abstract_declarator RPAREN { typ } -| typ = direct_abstract_declarator LBRACK cvspec = type_qualifier_list expr = assignment_expression RBRACK - { ARRAY typ cvspec (Some (fst expr)) } +| typ = direct_abstract_declarator LBRACK cvspec = type_qualifier_list + expr = assignment_expression RBRACK + { Cabs.ARRAY typ cvspec (Some (fst expr)) } | LBRACK cvspec = type_qualifier_list expr = assignment_expression RBRACK - { ARRAY JUSTBASE cvspec (Some (fst expr)) } + { Cabs.ARRAY Cabs.JUSTBASE cvspec (Some (fst expr)) } | typ = direct_abstract_declarator LBRACK expr = assignment_expression RBRACK - { ARRAY typ [] (Some (fst expr)) } + { Cabs.ARRAY typ [] (Some (fst expr)) } | LBRACK expr = assignment_expression RBRACK - { ARRAY JUSTBASE [] (Some (fst expr)) } + { Cabs.ARRAY Cabs.JUSTBASE [] (Some (fst expr)) } | typ = direct_abstract_declarator LBRACK cvspec = type_qualifier_list RBRACK - { ARRAY typ cvspec None } + { Cabs.ARRAY typ cvspec None } | LBRACK cvspec = type_qualifier_list RBRACK - { ARRAY JUSTBASE cvspec None } + { Cabs.ARRAY Cabs.JUSTBASE cvspec None } | typ = direct_abstract_declarator LBRACK RBRACK - { ARRAY typ [] None } + { Cabs.ARRAY typ [] None } | LBRACK RBRACK - { ARRAY JUSTBASE [] None } + { Cabs.ARRAY Cabs.JUSTBASE [] None } (*| direct_abstract_declarator? LBRACK STAR RBRACK*) (*| direct_abstract_declarator? LBRACK ... STATIC ... RBRACK*) | typ = direct_abstract_declarator LPAREN params = parameter_type_list RPAREN - { PROTO typ params } + { Cabs.PROTO typ params } | LPAREN params = parameter_type_list RPAREN - { PROTO JUSTBASE params } + { Cabs.PROTO Cabs.JUSTBASE params } | typ = direct_abstract_declarator LPAREN RPAREN - { PROTO typ ([], false) } + { Cabs.PROTO typ ([], false) } | LPAREN RPAREN - { PROTO JUSTBASE ([], false) } + { Cabs.PROTO Cabs.JUSTBASE ([], false) } (* 6.7.8 *) c_initializer: | expr = assignment_expression - { SINGLE_INIT (fst expr) } + { Cabs.SINGLE_INIT (fst expr) } | LBRACE init = initializer_list RBRACE - { COMPOUND_INIT (rev' init) } + { Cabs.COMPOUND_INIT (rev' init) } | LBRACE init = initializer_list COMMA RBRACE - { COMPOUND_INIT (rev' init) } + { Cabs.COMPOUND_INIT (rev' init) } initializer_list: | design = designation init = c_initializer @@ -738,9 +745,9 @@ designator_list: designator: | LBRACK expr = constant_expression RBRACK - { ATINDEX_INIT (fst expr) } + { Cabs.ATINDEX_INIT (fst expr) } | DOT id = OTHER_NAME - { INFIELD_INIT (fst id) } + { Cabs.INFIELD_INIT (fst id) } (* 6.8 *) statement_dangerous: @@ -768,18 +775,18 @@ statement_safe: (* 6.8.1 *) labeled_statement(last_statement): | lbl = OTHER_NAME COLON stmt = last_statement - { LABEL (fst lbl) stmt (snd lbl) } + { Cabs.LABEL (fst lbl) stmt (snd lbl) } | loc = CASE expr = constant_expression COLON stmt = last_statement - { CASE (fst expr) stmt loc } + { Cabs.CASE (fst expr) stmt loc } | loc = DEFAULT COLON stmt = last_statement - { DEFAULT stmt loc } + { Cabs.DEFAULT stmt loc } (* 6.8.2 *) compound_statement: | loc = LBRACE lst = block_item_list RBRACE - { BLOCK (rev' lst) loc } + { Cabs.BLOCK (rev' lst) loc } | loc = LBRACE RBRACE - { BLOCK [] loc } + { Cabs.BLOCK [] loc } block_item_list: | stmt = block_item @@ -789,93 +796,103 @@ block_item_list: block_item: | decl = declaration - { DEFINITION decl } + { Cabs.DEFINITION decl } | stmt = statement_dangerous { stmt } (* Non-standard *) | p = PRAGMA - { DEFINITION (PRAGMA (fst p) (snd p)) } + { Cabs.DEFINITION (Cabs.PRAGMA (fst p) (snd p)) } (* 6.8.3 *) expression_statement: | expr = expression SEMICOLON - { COMPUTATION (fst expr) (snd expr) } + { Cabs.COMPUTATION (fst expr) (snd expr) } | loc = SEMICOLON - { NOP loc } + { Cabs.NOP loc } (* 6.8.4 *) selection_statement_dangerous: -| loc = IF LPAREN expr = expression RPAREN stmt = statement_dangerous - { If (fst expr) stmt None loc } -| loc = IF LPAREN expr = expression RPAREN stmt1 = statement_safe ELSE stmt2 = statement_dangerous - { If (fst expr) stmt1 (Some stmt2) loc } +| loc = IF_ LPAREN expr = expression RPAREN stmt = statement_dangerous + { Cabs.If (fst expr) stmt None loc } +| loc = IF_ LPAREN expr = expression RPAREN stmt1 = statement_safe ELSE + stmt2 = statement_dangerous + { Cabs.If (fst expr) stmt1 (Some stmt2) loc } | loc = SWITCH LPAREN expr = expression RPAREN stmt = statement_dangerous - { SWITCH (fst expr) stmt loc } + { Cabs.SWITCH (fst expr) stmt loc } selection_statement_safe: -| loc = IF LPAREN expr = expression RPAREN stmt1 = statement_safe ELSE stmt2 = statement_safe - { If (fst expr) stmt1 (Some stmt2) loc } +| loc = IF_ LPAREN expr = expression RPAREN stmt1 = statement_safe ELSE + stmt2 = statement_safe + { Cabs.If (fst expr) stmt1 (Some stmt2) loc } | loc = SWITCH LPAREN expr = expression RPAREN stmt = statement_safe - { SWITCH (fst expr) stmt loc } + { Cabs.SWITCH (fst expr) stmt loc } (* 6.8.5 *) iteration_statement(last_statement): | loc = WHILE LPAREN expr = expression RPAREN stmt = last_statement - { WHILE (fst expr) stmt loc } + { Cabs.WHILE (fst expr) stmt loc } | loc = DO stmt = statement_dangerous WHILE LPAREN expr = expression RPAREN SEMICOLON - { DOWHILE (fst expr) stmt loc } -| loc = FOR LPAREN expr1 = expression SEMICOLON expr2 = expression SEMICOLON expr3 = expression RPAREN stmt = last_statement - { FOR (Some (FC_EXP (fst expr1))) (Some (fst expr2)) (Some (fst expr3)) stmt loc } -| loc = FOR LPAREN decl1 = declaration expr2 = expression SEMICOLON expr3 = expression RPAREN stmt = last_statement - { FOR (Some (FC_DECL decl1)) (Some (fst expr2)) (Some (fst expr3)) stmt loc } -| loc = FOR LPAREN SEMICOLON expr2 = expression SEMICOLON expr3 = expression RPAREN stmt = last_statement - { FOR None (Some (fst expr2)) (Some (fst expr3)) stmt loc } -| loc = FOR LPAREN expr1 = expression SEMICOLON SEMICOLON expr3 = expression RPAREN stmt = last_statement - { FOR (Some (FC_EXP (fst expr1))) None (Some (fst expr3)) stmt loc } -| loc = FOR LPAREN decl1 = declaration SEMICOLON expr3 = expression RPAREN stmt = last_statement - { FOR (Some (FC_DECL decl1)) None (Some (fst expr3)) stmt loc } + { Cabs.DOWHILE (fst expr) stmt loc } +| loc = FOR LPAREN expr1 = expression SEMICOLON expr2 = expression SEMICOLON + expr3 = expression RPAREN stmt = last_statement + { Cabs.FOR (Some (Cabs.FC_EXP (fst expr1))) (Some (fst expr2)) (Some (fst expr3)) stmt loc } +| loc = FOR LPAREN decl1 = declaration expr2 = expression SEMICOLON + expr3 = expression RPAREN stmt = last_statement + { Cabs.FOR (Some (Cabs.FC_DECL decl1)) (Some (fst expr2)) (Some (fst expr3)) stmt loc } +| loc = FOR LPAREN SEMICOLON expr2 = expression SEMICOLON expr3 = expression RPAREN + stmt = last_statement + { Cabs.FOR None (Some (fst expr2)) (Some (fst expr3)) stmt loc } +| loc = FOR LPAREN expr1 = expression SEMICOLON SEMICOLON expr3 = expression RPAREN + stmt = last_statement + { Cabs.FOR (Some (Cabs.FC_EXP (fst expr1))) None (Some (fst expr3)) stmt loc } +| loc = FOR LPAREN decl1 = declaration SEMICOLON expr3 = expression RPAREN + stmt = last_statement + { Cabs.FOR (Some (Cabs.FC_DECL decl1)) None (Some (fst expr3)) stmt loc } | loc = FOR LPAREN SEMICOLON SEMICOLON expr3 = expression RPAREN stmt = last_statement - { FOR None None (Some (fst expr3)) stmt loc } -| loc = FOR LPAREN expr1 = expression SEMICOLON expr2 = expression SEMICOLON RPAREN stmt = last_statement - { FOR (Some (FC_EXP (fst expr1))) (Some (fst expr2)) None stmt loc } -| loc = FOR LPAREN decl1 = declaration expr2 = expression SEMICOLON RPAREN stmt = last_statement - { FOR (Some (FC_DECL decl1)) (Some (fst expr2)) None stmt loc } + { Cabs.FOR None None (Some (fst expr3)) stmt loc } +| loc = FOR LPAREN expr1 = expression SEMICOLON expr2 = expression SEMICOLON RPAREN + stmt = last_statement + { Cabs.FOR (Some (Cabs.FC_EXP (fst expr1))) (Some (fst expr2)) None stmt loc } +| loc = FOR LPAREN decl1 = declaration expr2 = expression SEMICOLON RPAREN + stmt = last_statement + { Cabs.FOR (Some (Cabs.FC_DECL decl1)) (Some (fst expr2)) None stmt loc } | loc = FOR LPAREN SEMICOLON expr2 = expression SEMICOLON RPAREN stmt = last_statement - { FOR None (Some (fst expr2)) None stmt loc } + { Cabs.FOR None (Some (fst expr2)) None stmt loc } | loc = FOR LPAREN expr1 = expression SEMICOLON SEMICOLON RPAREN stmt = last_statement - { FOR (Some (FC_EXP (fst expr1))) None None stmt loc } + { Cabs.FOR (Some (Cabs.FC_EXP (fst expr1))) None None stmt loc } | loc = FOR LPAREN decl1 = declaration SEMICOLON RPAREN stmt = last_statement - { FOR (Some (FC_DECL decl1)) None None stmt loc } + { Cabs.FOR (Some (Cabs.FC_DECL decl1)) None None stmt loc } | loc = FOR LPAREN SEMICOLON SEMICOLON RPAREN stmt = last_statement - { FOR None None None stmt loc } + { Cabs.FOR None None None stmt loc } (* 6.8.6 *) jump_statement: | loc = GOTO id = OTHER_NAME SEMICOLON - { GOTO (fst id) loc } + { Cabs.GOTO (fst id) loc } | loc = CONTINUE SEMICOLON - { CONTINUE loc } + { Cabs.CONTINUE loc } | loc = BREAK SEMICOLON - { BREAK loc } + { Cabs.BREAK loc } | loc = RETURN expr = expression SEMICOLON - { RETURN (Some (fst expr)) loc } + { Cabs.RETURN (Some (fst expr)) loc } | loc = RETURN SEMICOLON - { RETURN None loc } + { Cabs.RETURN None loc } (* Non-standard *) asm_statement: -| loc = ASM attr = asm_attributes LPAREN template = STRING_LITERAL args = asm_arguments RPAREN SEMICOLON +| loc = ASM attr = asm_attributes LPAREN template = STRING_LITERAL args = asm_arguments + RPAREN SEMICOLON { let '(wide, chars, _) := template in let '(outputs, inputs, flags) := args in - ASM attr wide chars outputs inputs flags loc } + Cabs.ASM attr wide chars outputs inputs flags loc } asm_attributes: | /* empty */ { [] } | CONST attr = asm_attributes - { CV_CONST :: attr } + { Cabs.CV_CONST :: attr } | VOLATILE attr = asm_attributes - { CV_VOLATILE :: attr } + { Cabs.CV_VOLATILE :: attr } asm_arguments: | /* empty */ @@ -897,7 +914,7 @@ asm_operands_ne: asm_operand: | n = asm_op_name cstr = STRING_LITERAL LPAREN e = expression RPAREN - { let '(wide, s, loc) := cstr in ASMOPERAND n wide s (fst e) } + { let '(wide, s, loc) := cstr in Cabs.ASMOPERAND n wide s (fst e) } asm_op_name: | /* empty */ { None } @@ -934,7 +951,7 @@ external_declaration: { def } (* Non-standard *) | p = PRAGMA - { PRAGMA (fst p) (snd p) } + { Cabs.PRAGMA (fst p) (snd p) } (* 6.9.1 *) @@ -943,11 +960,11 @@ function_definition: decl = declarator_noattrend dlist = declaration_list stmt = compound_statement - { FUNDEF (fst specs) decl (List.rev' dlist) stmt (snd specs) } + { Cabs.FUNDEF (fst specs) decl (List.rev' dlist) stmt (snd specs) } | specs = declaration_specifiers decl = declarator stmt = compound_statement - { FUNDEF (fst specs) decl [] stmt (snd specs) } + { Cabs.FUNDEF (fst specs) decl [] stmt (snd specs) } declaration_list: | d = declaration diff --git a/cparser/Rename.ml b/cparser/Rename.ml index eb31eaf0..64412194 100644 --- a/cparser/Rename.ml +++ b/cparser/Rename.ml @@ -246,7 +246,7 @@ let rec globdecls env accu = function (* Reserve names of builtins *) let reserve_builtins () = - List.fold_left enter_public empty_env (Builtins.identifiers()) + List.fold_left enter_public empty_env (Env.initial_identifiers()) (* Reserve global declarations with public visibility *) diff --git a/cparser/Transform.ml b/cparser/Transform.ml index 0a2ce3bb..349a3155 100644 --- a/cparser/Transform.ml +++ b/cparser/Transform.ml @@ -227,4 +227,4 @@ let program in transf_globdecls env' ({g with gdesc = desc'} :: accu) gl - in transf_globdecls (Builtins.environment()) [] p + in transf_globdecls (Env.initial()) [] p diff --git a/cparser/Unblock.ml b/cparser/Unblock.ml index da8049a5..d25f70c6 100644 --- a/cparser/Unblock.ml +++ b/cparser/Unblock.ml @@ -340,7 +340,6 @@ and unblock_block env ctx ploc = function let unblock_fundef env f = local_variables := []; - next_scope_id := 0; curr_fun_id:= f.fd_name.stamp; (* TODO: register the parameters as being declared in function scope *) let body = unblock_stmt env [] no_loc f.fd_body in @@ -398,5 +397,6 @@ let rec unblock_glob env accu = function (* Entry point *) let program p = + next_scope_id := 0; {gloc = no_loc; gdesc = Gdecl(Storage_extern, debug_id, debug_ty, None)} :: - unblock_glob (Builtins.environment()) [] p + unblock_glob (Env.initial()) [] p diff --git a/cparser/handcrafted.messages b/cparser/handcrafted.messages index 95077739..6d972439 100644 --- a/cparser/handcrafted.messages +++ b/cparser/handcrafted.messages @@ -4477,7 +4477,7 @@ translation_unit_file: VOID PRE_NAME TYPEDEF_NAME PACKED LPAREN CONSTANT RPAREN ## # We have just parsed a list of attribute specifiers, but we cannot -# print it because it is not available. We do not know wether it is +# print it because it is not available. We do not know whether it is # part of the declaration or whether it is part of the first K&R parameter # declaration. @@ -4599,7 +4599,7 @@ translation_unit_file: PACKED LPAREN BUILTIN_OFFSETOF XOR_ASSIGN ## Ill-formed __builtin_offsetof. -At this point, an opening paranthesis '(' is expected. +At this point, an opening parenthesis '(' is expected. #------------------------------------------------------------------------------ diff --git a/cparser/pre_parser.mly b/cparser/pre_parser.mly index 71eaf419..669ecf5e 100644 --- a/cparser/pre_parser.mly +++ b/cparser/pre_parser.mly @@ -43,13 +43,13 @@ %} %token<string> PRE_NAME -%token<string * Pre_parser_aux.identifier_type ref * Cabs.cabsloc> +%token<string * Pre_parser_aux.identifier_type ref * Cabs.loc> VAR_NAME TYPEDEF_NAME -%token<Cabs.constant * Cabs.cabsloc> CONSTANT -%token<bool * int64 list * Cabs.cabsloc> STRING_LITERAL -%token<string * Cabs.cabsloc> PRAGMA +%token<Cabs.constant * Cabs.loc> CONSTANT +%token<bool * int64 list * Cabs.loc> STRING_LITERAL +%token<string * Cabs.loc> PRAGMA -%token<Cabs.cabsloc> SIZEOF PTR INC DEC LEFT RIGHT LEQ GEQ EQEQ EQ NEQ LT GT +%token<Cabs.loc> SIZEOF PTR INC DEC LEFT RIGHT LEQ GEQ EQEQ EQ NEQ LT GT ANDAND BARBAR PLUS MINUS STAR TILDE BANG SLASH PERCENT HAT BAR QUESTION COLON AND MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN LPAREN RPAREN LBRACK RBRACK diff --git a/debug/Debug.ml b/debug/Debug.ml index 168df5a0..812f57cc 100644 --- a/debug/Debug.ml +++ b/debug/Debug.ml @@ -47,7 +47,7 @@ type implem = exists_section: section_name -> bool; remove_unused: ident -> unit; remove_unused_function: ident -> unit; - variable_printed: string -> unit; + symbol_printed: string -> unit; add_diab_info: section_name -> int -> int -> int -> unit; } @@ -79,7 +79,7 @@ let default_implem = exists_section = (fun _ -> true); remove_unused = (fun _ -> ()); remove_unused_function = (fun _ -> ()); - variable_printed = (fun _ -> ()); + symbol_printed = (fun _ -> ()); add_diab_info = (fun _ _ _ _ -> ()); } @@ -111,5 +111,5 @@ let compute_diab_file_enum end_l entry_l line_e = !implem.compute_diab_file_enum let compute_gnu_file_enum f = !implem.compute_gnu_file_enum f let remove_unused ident = !implem.remove_unused ident let remove_unused_function ident = !implem.remove_unused_function ident -let variable_printed ident = !implem.variable_printed ident +let symbol_printed ident = !implem.symbol_printed ident let add_diab_info sec line_start debug_info low_pc = !implem.add_diab_info sec line_start debug_info low_pc diff --git a/debug/Debug.mli b/debug/Debug.mli index 3869a056..60e2f9bc 100644 --- a/debug/Debug.mli +++ b/debug/Debug.mli @@ -46,7 +46,7 @@ type implem = exists_section: section_name -> bool; remove_unused: ident -> unit; remove_unused_function: ident -> unit; - variable_printed: string -> unit; + symbol_printed: string -> unit; add_diab_info: section_name -> int -> int -> int -> unit; } @@ -80,5 +80,5 @@ val compute_gnu_file_enum: (string -> unit) -> unit val exists_section: section_name -> bool val remove_unused: ident -> unit val remove_unused_function: ident -> unit -val variable_printed: string -> unit +val symbol_printed: string -> unit val add_diab_info: section_name -> int -> int -> int -> unit diff --git a/debug/DebugInformation.ml b/debug/DebugInformation.ml index e3f5d98e..f9684355 100644 --- a/debug/DebugInformation.ml +++ b/debug/DebugInformation.ml @@ -123,7 +123,7 @@ let insert_type ty = | TNamed (id,_) -> let typ = try let _,t = - List.find (fun a -> fst a = id.name) CBuiltins.builtins.Builtins.typedefs in + List.find (fun a -> fst a = id.name) CBuiltins.builtins.builtin_typedefs in Some (attr_aux t) with Not_found -> None in let t = { @@ -223,7 +223,7 @@ let name_to_definition: (string,int) Hashtbl.t = Hashtbl.create 7 (* Mapping from atom to debug id *) let atom_to_definition: (atom, int) Hashtbl.t = Hashtbl.create 7 -(* Various lookup functions for defintions *) +(* Various lookup functions for definitions *) let find_gvar_stamp id = let id = (Hashtbl.find stamp_to_definition id) in let var = Hashtbl.find definitions id in @@ -342,7 +342,7 @@ let insert_global_declaration env dec = replace_var id ({var with gvar_declaration = false;}) end end else begin - (* Implict declarations need special handling *) + (* Implicit declarations need special handling *) let id' = try Hashtbl.find name_to_definition id.name with Not_found -> let id' = next_id () in Hashtbl.add name_to_definition id.name id';id' in @@ -553,7 +553,10 @@ let close_scope atom s_id lbl = | a::rest -> a,rest | _ -> assert false (* We must have an opening scope *) end in - let new_r = ({last_r with end_addr = Some lbl;})::rest in + let new_r = if last_r.start_addr = Some lbl then + rest + else + ({last_r with end_addr = Some lbl;})::rest in Hashtbl.replace scope_ranges s_id new_r with Not_found -> () @@ -632,12 +635,12 @@ let compute_gnu_file_enum f = let all_files_iter f = StringSet.iter f !all_files -let printed_vars: StringSet.t ref = ref StringSet.empty +let printed_symbols: StringSet.t ref = ref StringSet.empty -let is_variable_printed id = StringSet.mem id !printed_vars +let is_symbol_printed id = StringSet.mem id !printed_symbols -let variable_printed id = - printed_vars := StringSet.add id !printed_vars +let symbol_printed id = + printed_symbols := StringSet.add id !printed_symbols let init name = id := 0; @@ -660,7 +663,7 @@ let init name = Hashtbl.reset scope_ranges; Hashtbl.reset label_translation; all_files := StringSet.singleton name; - printed_vars := StringSet.empty + printed_symbols := StringSet.empty let default_debug = { @@ -690,6 +693,6 @@ let default_debug = exists_section = exists_section; remove_unused = remove_unused; remove_unused_function = remove_unused_function; - variable_printed = variable_printed; + symbol_printed = symbol_printed; add_diab_info = (fun _ _ _ _ -> ()); } diff --git a/debug/DebugInformation.mli b/debug/DebugInformation.mli index 8905d8bf..0cf34756 100644 --- a/debug/DebugInformation.mli +++ b/debug/DebugInformation.mli @@ -23,7 +23,7 @@ val get_type: int -> debug_types val fold_types: (int -> debug_types -> 'a -> 'a) -> 'a -> 'a -val is_variable_printed: string -> bool +val is_symbol_printed: string -> bool val variable_location: atom -> atom -> var_location diff --git a/debug/DwarfPrinter.ml b/debug/DwarfPrinter.ml index a45fff0c..2cb8c7d9 100644 --- a/debug/DwarfPrinter.ml +++ b/debug/DwarfPrinter.ml @@ -241,9 +241,12 @@ module DwarfPrinter(Target: DWARF_TARGET): let abbrev = !curr_abbrev in incr curr_abbrev;abbrev - (* Mapping from abbreviation string to abbreviaton id *) + (* Mapping from abbreviation string to abbreviation id *) let abbrev_mapping: (string,int) Hashtbl.t = Hashtbl.create 7 + (* Mapping from abbreviation range id to label *) + let range_labels : (int, int) Hashtbl.t = Hashtbl.create 7 + (* Look up the id of the abbreviation and add it if it is missing *) let get_abbrev entity has_sibling = let abbrev_string = abbrev_string_of_entity entity has_sibling in @@ -265,7 +268,7 @@ module DwarfPrinter(Target: DWARF_TARGET): (* Print the debug_abbrev section using the previous computed abbreviations*) let print_abbrev oc = let abbrevs = Hashtbl.fold (fun s i acc -> (s,i)::acc) abbrev_mapping [] in - let abbrevs = List.sort (fun (_,a) (_,b) -> Pervasives.compare a b) abbrevs in + let abbrevs = List.sort (fun (_,a) (_,b) -> compare a b) abbrevs in section oc Section_debug_abbrev; print_label oc !abbrev_start_addr; List.iter (fun (s,id) -> @@ -439,8 +442,11 @@ module DwarfPrinter(Target: DWARF_TARGET): | Pc_pair (l,h) -> print_addr oc "DW_AT_low_pc" l; print_addr oc "DW_AT_high_pc" h - | Offset i -> fprintf oc " .4byte %a+0x%d%a\n" - label !debug_ranges_addr i print_comment "DW_AT_ranges" + | Offset i -> + let lbl = new_label () in + Hashtbl.add range_labels i lbl; + fprintf oc " .4byte %a%a\n" + label lbl print_comment "DW_AT_ranges" | _ -> () let print_compilation_unit oc tag = @@ -596,8 +602,13 @@ module DwarfPrinter(Target: DWARF_TARGET): print_sleb128 oc "" 0; print_label oc debug_end (* End of the debug section *) - let print_location_entry oc c_low l = + let print_location_entry oc needs_base c_low l = print_label oc (loc_to_label l.loc_id); + (* If we have multiple ranges per compilation unit we need to specify a base address for the location *) + if needs_base then begin + fprintf oc " %s -1\n" address; + fprintf oc " %s %a\n" address label c_low; + end; List.iter (fun (b,e,loc) -> fprintf oc " %s %a-%a\n" address label b label c_low; fprintf oc " %s %a-%a\n" address label e label c_low; @@ -615,11 +626,11 @@ module DwarfPrinter(Target: DWARF_TARGET): fprintf oc " %s 0\n" address - let print_location_list oc (c_low,l) = - let f = match c_low with - | Some s -> print_location_entry oc s - | None -> print_location_entry_abs oc in - List.iter f l + let print_location_list oc needs_base l = + let f l = match l.loc_sec_begin with + | Some s -> print_location_entry oc needs_base s l + | None -> print_location_entry_abs oc l in + List.iter f l let list_opt l f = match l with @@ -629,29 +640,38 @@ module DwarfPrinter(Target: DWARF_TARGET): let print_diab_entries oc entries = let abbrev_start = new_label () in abbrev_start_addr := abbrev_start; - List.iter (fun e -> compute_abbrev e.entry) entries; + List.iter (fun e -> compute_abbrev e.diab_entry) entries; print_abbrev oc; List.iter (fun e -> let name = if e.section_name <> ".text" then Some e.section_name else None in section oc (Section_debug_info name); - print_debug_info oc e.start_label e.line_label e.entry) entries; - if List.exists (fun e -> match e.dlocs with _,[] -> false | _,_ -> true) entries then begin + print_debug_info oc e.start_label e.line_label e.diab_entry) entries; + if List.exists (fun e -> match e.diab_locs with [] -> false | _ -> true) entries then begin section oc Section_debug_loc; - List.iter (fun e -> print_location_list oc e.dlocs) entries + List.iter (fun e -> print_location_list oc false e.diab_locs) entries end let print_ranges oc r = + let print_range_entry = function + | AddressRange l -> + List.iter (fun (b,e) -> + fprintf oc " %s %a\n" address label b; + fprintf oc " %s %a\n" address label e) l; + | OffsetRange (start, l) -> + List.iter (fun (b,e) -> + fprintf oc " %s %a-%a\n" address label b label start; + fprintf oc " %s %a-%a\n" address label e label start) l + in section oc Section_debug_ranges; print_label oc !debug_ranges_addr; - List.iter (fun l -> - List.iter (fun (b,e) -> - fprintf oc " %s %a\n" address label b; - fprintf oc " %s %a\n" address label e) l; - fprintf oc " %s 0\n" address; - fprintf oc " %s 0\n" address) r - - let print_gnu_entries oc cp (lpc,loc) s r = - compute_abbrev cp; + List.iter (fun (lbl,l) -> + print_label oc (Hashtbl.find range_labels lbl); + print_range_entry l; + fprintf oc " %s 0\n" address; + fprintf oc " %s 0\n" address) r + + let print_gnu_entries oc entries = + compute_abbrev entries.gnu_entry; let line_start = new_label () and start = new_label () and abbrev_start = new_label () @@ -659,18 +679,18 @@ module DwarfPrinter(Target: DWARF_TARGET): debug_ranges_addr := range_label; abbrev_start_addr := abbrev_start; section oc (Section_debug_info None); - print_debug_info oc start line_start cp; + print_debug_info oc start line_start entries.gnu_entry; print_abbrev oc; - list_opt loc (fun () -> + list_opt entries.gnu_locs (fun () -> section oc Section_debug_loc; - print_location_list oc (lpc,loc)); - list_opt r (fun () -> - print_ranges oc r); + print_location_list oc entries.several_secs entries.gnu_locs); + list_opt entries.range_table (fun () -> + print_ranges oc entries.range_table); section oc (Section_debug_line None); print_label oc line_start; - list_opt s (fun () -> + list_opt entries.string_table (fun () -> section oc Section_debug_str; - let s = List.sort (fun (a,_) (b,_) -> Pervasives.compare a b) s in + let s = List.sort (fun (a,_) (b,_) -> compare a b) entries.string_table in List.iter (fun (id,s) -> print_label oc (loc_to_label id); fprintf oc " .asciz %S\n" s) s) @@ -679,9 +699,10 @@ module DwarfPrinter(Target: DWARF_TARGET): (* Print the debug info and abbrev section *) let print_debug oc debug = Hashtbl.clear abbrev_mapping; + Hashtbl.clear range_labels; Hashtbl.clear loc_labels; match debug with | Diab entries -> print_diab_entries oc entries - | Gnu (cp,loc,s,r) -> print_gnu_entries oc cp loc s r + | Gnu entries -> print_gnu_entries oc entries end diff --git a/debug/DwarfPrinter.mli b/debug/DwarfPrinter.mli index e1e10601..78dc05fb 100644 --- a/debug/DwarfPrinter.mli +++ b/debug/DwarfPrinter.mli @@ -12,7 +12,7 @@ open DwarfTypes -module DwarfPrinter: functor (Target: DWARF_TARGET) -> +module DwarfPrinter: DWARF_TARGET -> sig val print_debug: out_channel -> debug_entries -> unit end diff --git a/debug/DwarfTypes.mli b/debug/DwarfTypes.mli index 23aba448..567c65cd 100644 --- a/debug/DwarfTypes.mli +++ b/debug/DwarfTypes.mli @@ -266,15 +266,19 @@ type dw_entry = (* The type for the location list. *) type location_entry = - { - loc: (address * address * location_value) list; - loc_id: reference; - } -type dw_locations = constant option * location_entry list + { + loc: (address * address * location_value) list; + loc_id: reference; + loc_sec_begin : address option; + } + +type dw_locations = location_entry list -type range_entry = (address * address) list +type range_entry = + | AddressRange of (address * address) list + | OffsetRange of reference * (address * address) list -type dw_ranges = range_entry list +type dw_ranges = (int * range_entry) list type dw_string = (int * string) list @@ -283,13 +287,20 @@ type diab_entry = section_name: string; start_label: int; line_label: int; - entry: dw_entry; - dlocs: dw_locations; + diab_entry: dw_entry; + diab_locs: dw_locations; } type diab_entries = diab_entry list -type gnu_entries = dw_entry * dw_locations * dw_string * dw_ranges +type gnu_entries = + { + string_table: dw_string; + range_table: dw_ranges; + gnu_locs: dw_locations; + gnu_entry: dw_entry; + several_secs: bool; + } type debug_entries = | Diab of diab_entries diff --git a/debug/Dwarfgen.ml b/debug/Dwarfgen.ml index ee568042..6c1d0846 100644 --- a/debug/Dwarfgen.ml +++ b/debug/Dwarfgen.ml @@ -72,7 +72,9 @@ let up_locs acc loc = {acc with locs = loc@acc.locs;} let up_ranges acc r = - {acc with ranges = r;} + let off, old_r = acc.ranges in + let new_r = (off +1 ), (off, r):: old_r in + (Offset off), {acc with ranges = new_r;} let empty_accu = { @@ -90,6 +92,8 @@ module Dwarfgenaux (Target: TARGET) = let subrange_type : int option ref = ref None + let current_section_start : int option ref = ref None + let encoding_of_ikind = function | IBool -> DW_ATE_boolean | IChar -> @@ -340,7 +344,7 @@ module Dwarfgenaux (Target: TARGET) = let global_variable_to_entry acc id v = let loc = match v.gvar_atom with - | Some a when is_variable_printed (extern_atom a) -> + | Some a when is_symbol_printed (extern_atom a) -> Some (LocSymbol a) | _ -> None in let var = { @@ -404,7 +408,7 @@ module Dwarfgenaux (Target: TARGET) = and lo = translate_label f_id lo in hi,lo,range_entry_loc i.var_loc) l in let id = next_id () in - Some (LocRef id),[{loc = l;loc_id = id;}] + Some (LocRef id),[{loc_sec_begin = !current_section_start; loc = l;loc_id = id;}] end with Not_found -> None,[] else @@ -424,7 +428,7 @@ module Dwarfgenaux (Target: TARGET) = let acc = up_locs (up_typs acc p.formal_parameter_type) loc_list in new_entry (next_id ()) (DW_TAG_formal_parameter p),acc - let scope_range f_id id (o,dwr) = + let scope_range f_id id acc = try let r = get_scope_ranges id in let lbl l h = match l,h with @@ -435,19 +439,22 @@ module Dwarfgenaux (Target: TARGET) = | _ -> raise Not_found in begin match r with - | [] -> Empty,(o,dwr) + | [] -> Empty,acc | [a] -> let l,h = lbl a.start_addr a.end_addr in - Pc_pair (l,h),(o,dwr) + Pc_pair (l,h), acc | a::rest -> if !Clflags.option_gdwarf > 2 then let r = List.map (fun e -> lbl e.start_addr e.end_addr) r in - (Offset o), (o + 2 + 4 * (List.length r),r::dwr) - else + let r = match !current_section_start with + | None -> AddressRange r + | Some s -> OffsetRange (s, r) in + up_ranges acc r + else let l,h = lbl (List.hd (List.rev rest)).start_addr a.end_addr in - Pc_pair (l,h),(o,dwr) + Pc_pair (l,h), acc end - with Not_found -> Empty,(o,dwr) + with Not_found -> Empty, acc let rec local_variable_to_entry f_id acc v id = match v.lvar_atom with @@ -466,11 +473,10 @@ module Dwarfgenaux (Target: TARGET) = Some (new_entry id (DW_TAG_variable var)),acc and scope_to_entry f_id acc sc id = - let r,dwr = scope_range f_id id acc.ranges in + let r, acc = scope_range f_id id acc in let scope = { lexical_block_range = r; } in - let acc = up_ranges acc dwr in let vars,acc = mmap_opt (local_to_entry f_id) acc sc.scope_variables in let entry = new_entry id (DW_TAG_lexical_block scope) in add_children entry vars,acc @@ -490,7 +496,7 @@ module Dwarfgenaux (Target: TARGET) = | Scope sc -> mmap_opt (local_to_entry f_id) acc sc.scope_variables | _ -> assert false) - let function_to_entry acc id f = + let function_to_entry sec_name acc id f = let r = match f.fun_low_pc, f.fun_high_pc with | Some l,Some h -> Pc_pair (l,h) | _ -> Empty in @@ -503,8 +509,13 @@ module Dwarfgenaux (Target: TARGET) = subprogram_range = r; } in let f_id = get_opt_val f.fun_atom in + let start_sec = + try + Some (section_start (sec_name f_id)) + with Not_found -> None in + current_section_start := start_sec; let acc = match f.fun_return_type with Some s -> up_typs acc s | None -> acc in - let f_entry = new_entry id (DW_TAG_subprogram f_tag) in + let f_entry = new_entry id (DW_TAG_subprogram f_tag) in let children,acc = if !Clflags.option_gdepth > 1 then let params,acc = mmap (function_parameter_to_entry f_id) acc f.fun_parameter in @@ -514,10 +525,14 @@ module Dwarfgenaux (Target: TARGET) = [],acc in add_children f_entry (children),acc - let definition_to_entry acc id t = + let definition_to_entry sec_name acc id t = match t with - | GlobalVariable g -> global_variable_to_entry acc id g - | Function f -> function_to_entry acc id f + | GlobalVariable g -> Some (global_variable_to_entry acc id g) + | Function f -> + if is_symbol_printed f.fun_name then + Some (function_to_entry sec_name acc id f) + else + None end @@ -529,20 +544,21 @@ let diab_file_loc sec (f,l) = let prod_name = let version_string = if Version.buildnr <> "" && Version.tag <> "" then - Printf.sprintf "%s, Build: %s, Tag: %s" Version.version Version.buildnr Version.tag + Printf.sprintf "Release: %s, Build: %s, Tag: %s" Version.version Version.buildnr Version.tag else Version.version in Printf.sprintf "AbsInt Angewandte Informatik GmbH:CompCert Version %s:(%s,%s,%s,%s)" version_string Configuration.arch Configuration.system Configuration.abi Configuration.model -let diab_gen_compilation_section s defs acc = +let diab_gen_compilation_section sec_name s defs acc = let module Gen = Dwarfgenaux(struct let file_loc = diab_file_loc s let string_entry s = Simple_string s end) in let defs,accu = List.fold_left (fun (acc,bcc) (id,t) -> - let t,bcc = Gen.definition_to_entry bcc id t in - t::acc,bcc) ([],empty_accu) defs in + match Gen.definition_to_entry sec_name bcc id t with + | Some (t,bcc) -> t::acc,bcc + | None -> acc,bcc) ([],empty_accu) defs in let low_pc = section_start s and line_start,debug_start = diab_additional_section s and high_pc = section_end s in @@ -558,8 +574,8 @@ let diab_gen_compilation_section s defs acc = section_name = s; start_label = debug_start; line_label = line_start; - entry = cp; - dlocs = Some low_pc,accu.locs; + diab_entry = cp; + diab_locs = accu.locs; }::acc let gen_diab_debug_info sec_name var_section : debug_entries = @@ -569,7 +585,7 @@ let gen_diab_debug_info sec_name var_section : debug_entries = | Function f -> sec_name (get_opt_val f.fun_atom) in let old = try StringMap.find s acc with Not_found -> [] in StringMap.add s ((id,t)::old) acc) StringMap.empty in - let entries = StringMap.fold diab_gen_compilation_section defs [] in + let entries = StringMap.fold (diab_gen_compilation_section sec_name) defs [] in Diab entries let gnu_file_loc (f,l) = @@ -579,7 +595,7 @@ let string_table: (string,int) Hashtbl.t = Hashtbl.create 7 let gnu_string_entry s = if (String.length s < 4 && Configuration.system <> "macosx") (* macosx needs debug_str *) - || Configuration.system = "cygwin" then (*Cygwin does not use the debug_str seciton*) + || Configuration.system = "cygwin" then (*Cygwin does not use the debug_str section*) Simple_string s else try @@ -592,30 +608,32 @@ let gnu_string_entry s = let gen_gnu_debug_info sec_name var_section : debug_entries = Hashtbl.clear string_table; - let r,dwr,low_pc = - try if !Clflags.option_gdwarf > 3 then + let r,accu,low_pc = + try if !Clflags.option_gdwarf > 2 then let pcs = fold_section_start (fun s low acc -> (low,section_end s)::acc) [] in match pcs with - | [] -> Empty,(0,[]),None - | [(l,h)] -> Pc_pair (l,h),(0,[]),Some l - | _ -> Offset 0,(2 + 4 * (List.length pcs),[pcs]),None + | [] -> Empty, empty_accu, None + | [(l,h)] -> Pc_pair (l,h), empty_accu, Some l + | _ -> + let off, acc = up_ranges empty_accu (AddressRange pcs) in + off, acc, None else let l = section_start ".text" and h = section_end ".text" in - Pc_pair(l,h),(0,[]),Some l - with Not_found -> Empty,(0,[]),None in - let accu = up_ranges empty_accu dwr in + Pc_pair(l,h), empty_accu,Some l + with Not_found -> Empty ,empty_accu, None in let module Gen = Dwarfgenaux (struct let file_loc = gnu_file_loc let string_entry = gnu_string_entry end) in - let defs,accu,sec = fold_definitions (fun id t (acc,bcc,sec) -> + let defs,accu,sec = fold_definitions (fun id t (acc,bcc,sec) -> let s = match t with | GlobalVariable _ -> var_section | Function f -> sec_name (get_opt_val f.fun_atom) in - let t,bcc = Gen.definition_to_entry bcc id t in - t::acc,bcc,StringSet.add s sec) ([],accu,StringSet.empty) in + match Gen.definition_to_entry sec_name bcc id t with + | Some (t,bcc) -> t::acc,bcc,StringSet.add s sec + | None -> acc, bcc, sec) ([],accu,StringSet.empty) in let types = Gen.gen_types accu.typs in let cp = { compile_unit_name = gnu_string_entry !file_name; @@ -625,6 +643,12 @@ let gen_gnu_debug_info sec_name var_section : debug_entries = } in let cp = new_entry (next_id ()) (DW_TAG_compile_unit cp) in let cp = add_children cp (types@defs) in - let loc_pc = if StringSet.cardinal sec > 1 then None else low_pc in let string_table = Hashtbl.fold (fun s i acc -> (i,s)::acc) string_table [] in - Gnu (cp,(loc_pc,accu.locs),string_table,snd accu.ranges) + let cp = { + string_table = string_table; + range_table = snd accu.ranges; + gnu_locs = accu.locs; + gnu_entry = cp; + several_secs = StringSet.cardinal sec > 1} + in + Gnu cp diff --git a/doc/ccomp.1 b/doc/ccomp.1 index 374bd2e7..89e8c823 100644 --- a/doc/ccomp.1 +++ b/doc/ccomp.1 @@ -125,7 +125,8 @@ Enabled by default. .TP .B \-O0 Turn off most optimizations. -Synonymous to \fB\-fno\-const\-prop\fP \fB\-fno\-cse\fP \fB\-fno\-redundancy\fP \fB\-fno\-tailcalls\fP. +Synonymous to \fB\-fno\-const\-prop\fP \fB\-fno\-cse\fP \fB\-fno\-if\-conversion\fP +\fB\-fno\-inline\fP \fB\-fno\-redundancy\fP \fB\-fno\-tailcalls\fP. . .TP .BR \-O1 ", " \-O2 ", " \-O3 @@ -136,6 +137,13 @@ Synonymous for \fB\-O\fP. Optimize for code size in preference to code speed. . .TP +.B \-Obranchless +Optimize to generate fewer conditional branches and use branch-free +instruction sequences instead. When \fB-fif\-conversion\fP is +enabled, the conversion is peformed aggressively even if the resulting +code is less performant. +. +.TP .BR \-fconst\-prop ", " \-fno\-const\-prop Turn on/off global constant propagation. Enabled by default. @@ -146,6 +154,11 @@ Turn on/off common subexpression elimination. Enabled by default. . .TP +.BR \-fif\-conversion ", " \-fno\-if\-conversion +Turn on/off generation of conditional moves. +Enabled by default. +. +.TP .BR \-finline ", " \-fno\-inline Turn on/off inlining of functions. Enabled by default. @@ -180,6 +193,12 @@ Set alignment of function entry points to <n> bytes. The default alignment is 16 bytes for x86 targets and 4 bytes for ARM and PowerPC. . .TP +.BR \-fcommon ", " \-fno\-common +Turn on/off placement of global variables defined without an initializer (tentative definitions) in the common section. +Disabling the use of the common section inhibits merging of tentative definitions by the linker and may lead to multiple-definition errors. +Enabled by default. +. +.TP .BR \-ffpu ", " \-fno\-fpu Turn on/off use of FP registers for some integer operations. Enabled by default. @@ -191,12 +210,12 @@ Code Generation Options (PowerPC) .TP .B \-falign\-branch\-targets <n> Set alignment of branch targets to <n> bytes. -The default alignment is 0 bytes, which deactivates alignment of branch targets. +By default alignment of branch targets is deactivated. . .TP .B \-falign\-cond\-branches <n> -Set alignment of conditional branches to <n> bytes. -The default alignment is 0 bytes, which deactivates alignment of conditional branch targets. +Set alignment of conditional branch instructions to <n> bytes. +By default alignment of conditional branches is deactivated. . .SS Code Generation Options (PowerPC with Diab Backend) @@ -424,9 +443,14 @@ Wrong return type for main. Enabled by default. .sp \fImissing\-declarations\fP: -Declations which do not declare anything. +Declarations which do not declare anything. Enabled by default. .sp +\fInon\-linear\-cond\-expr\fP: +Conditional expression that may not be optimized to branchless code. +Only issued in \fB-Obranchless\fP mode. +Disabled by default. +.sp \fIpointer\-type\-mismatch\fP: Use of incompatible pointer types in conditional expressions. Enabled by default. @@ -461,7 +485,7 @@ Disabled by default. .sp \fIunused\-variable\fP: Unused local variables. -Enabled by default. +Disabled by default. .sp \fIvarargs\fP: Promotable vararg arguments. diff --git a/doc/index.html b/doc/index.html index edb3accd..5f4ac5e1 100644 --- a/doc/index.html +++ b/doc/index.html @@ -24,7 +24,7 @@ a:active {color : Red; text-decoration : underline; } <H1 align="center">The CompCert verified compiler</H1> <H2 align="center">Commented Coq development</H2> -<H3 align="center">Version 3.5, 2019-02-27</H3> +<H3 align="center">Version 3.7, 2020-03-31</H3> <H2>Introduction</H2> @@ -101,6 +101,8 @@ See also: <A HREF="html/compcert.common.Memdata.html">Memdata</A> (in-memory rep <LI> <A HREF="html/compcert.common.Determinism.html">Determinism</A>: determinism properties of small-step semantics. <LI> <A HREF="html/compcert.powerpc.Op.html"><I>Op</I></A>: operators, addressing modes and their semantics. +<LI> <A HREF="html/compcert.common.Builtins.html">Builtins</A>: semantics of built-in functions. <BR> +See also: <A HREF="html/compcert.common.Builtins0.html">Builtins0</A> (target-independent part), <A HREF="html/compcert.powerpc.Builtins1.html"><I>Builtins1</I></A> (target-dependent part). <LI> <A HREF="html/compcert.common.Unityping.html">Unityping</A>: a solver for atomic unification constraints. </UL> @@ -180,7 +182,8 @@ code. </TR> <TR valign="top"> - <TD>Recognition of operators<br>and addressing modes</TD> + <TD>Recognition of operators<br>and addressing modes;<br> + if-conversion</TD> <TD>Cminor to CminorSel</TD> <TD><A HREF="html/compcert.backend.Selection.html">Selection</A><br> <A HREF="html/compcert.powerpc.SelectOp.html"><I>SelectOp</I></A><br> @@ -338,7 +341,8 @@ See also: <A HREF="html/compcert.powerpc.NeedOp.html"><I>NeedOp</I></A>: process The <A HREF="html/compcert.cfrontend.Ctyping.html">type system of CompCert C</A> is fully formalized. For some intermediate languages of the back-end, simpler type systems are used to statically capture well-formedness conditions. <UL> -<LI> <A HREF="html/compcert.cfrontend.Ctyping.html">Ctyping</A>: typing for CompCert C + type-checking functions. +<LI> <A HREF="html/compcert.cfrontend.Ctyping.html">Ctyping</A>: typing for + CompCert C + type-checking functions. <LI> <A HREF="html/compcert.backend.RTLtyping.html">RTLtyping</A>: typing for RTL + type reconstruction. <LI> <A HREF="html/compcert.backend.Lineartyping.html">Lineartyping</A>: typing for Linear. diff --git a/driver/Clflags.ml b/driver/Clflags.ml index a886ee9b..2db9399f 100644 --- a/driver/Clflags.ml +++ b/driver/Clflags.ml @@ -27,10 +27,13 @@ let option_ftailcalls = ref true let option_fconstprop = ref true let option_fcse = ref true let option_fredundancy = ref true +let option_fifconversion = ref true +let option_Obranchless = ref false let option_falignfunctions = ref (None: int option) let option_falignbranchtargets = ref 0 let option_faligncondbranchs = ref 0 let option_finline_asm = ref false +let option_fcommon = ref true let option_mthumb = ref (Configuration.model = "armv7m") let option_Osize = ref false let option_finline = ref true diff --git a/driver/Commandline.ml b/driver/Commandline.ml index 75ca1683..672ed834 100644 --- a/driver/Commandline.ml +++ b/driver/Commandline.ml @@ -16,7 +16,6 @@ (* Parsing of command-line flags and arguments *) open Printf -open Responsefile type pattern = | Exact of string @@ -114,14 +113,15 @@ let parse_array spec argv first last = end in parse first -let argv : string array ref = ref [||] +let argv = + try + Responsefile.expandargv Sys.argv + with Responsefile.Error msg | Sys_error msg -> + eprintf "Error while processing the command line: %s\n" msg; + exit 2 let parse_cmdline spec = - try - argv := expandargv Sys.argv; - parse_array spec !argv 1 (Array.length !argv - 1) - with Responsefile.Error s -> - raise (CmdError s) + parse_array spec argv 1 (Array.length argv - 1) let long_int_action key s = let ls = String.length s diff --git a/driver/Commandline.mli b/driver/Commandline.mli index e1b917f2..8bb6f18f 100644 --- a/driver/Commandline.mli +++ b/driver/Commandline.mli @@ -39,11 +39,11 @@ type action = patterns are tried in the order in which they appear in the list. *) exception CmdError of string -(** Raise by [parse_cmdline] when an error occured *) +(** Raise by [parse_cmdline] when an error occurred *) val parse_cmdline: (pattern * action) list -> unit -(** [parse_cmdline actions] parses the commandline and performs all [actions]. - Raises [CmdError] if an error occurred. +(** [parse_cmdline actions] parses the command line (after @-file expansion) + and performs all [actions]. Raises [CmdError] if an error occurred. *) val longopt_int: string -> (int -> unit) -> pattern * action @@ -51,5 +51,5 @@ val longopt_int: string -> (int -> unit) -> pattern * action options of the form [key=<n>] and calls [fn] with the integer argument *) -val argv: string array ref +val argv: string array (** [argv] contains the complete command line after @-file expandsion *) diff --git a/driver/CommonOptions.ml b/driver/CommonOptions.ml index 58dd4007..c151ecf2 100644 --- a/driver/CommonOptions.ml +++ b/driver/CommonOptions.ml @@ -14,9 +14,9 @@ open Clflags open Commandline (* The version string for [tool_name] *) -let version_string tool_name= +let version_string tool_name = if Version.buildnr <> "" && Version.tag <> "" then - Printf.sprintf "The CompCert %s, %s, Build: %s, Tag: %s\n" tool_name Version.version Version.buildnr Version.tag + Printf.sprintf "The CompCert %s, Release: %s, Build: %s, Tag: %s\n" tool_name Version.version Version.buildnr Version.tag else Printf.sprintf "The CompCert %s, version %s\n" tool_name Version.version diff --git a/driver/Configuration.ml b/driver/Configuration.ml index 972fd295..2188acf0 100644 --- a/driver/Configuration.ml +++ b/driver/Configuration.ml @@ -13,11 +13,11 @@ open Printf let search_argv key = - let len = Array.length Sys.argv in + let len = Array.length Commandline.argv in let res: string option ref = ref None in for i = 1 to len - 2 do - if Sys.argv.(i) = key then - res := Some Sys.argv.(i + 1); + if Commandline.argv.(i) = key then + res := Some Commandline.argv.(i + 1); done; !res @@ -123,7 +123,7 @@ let get_bool_config key = let arch = match get_config_string "arch" with - | "powerpc"|"arm"|"x86"|"riscV" as a -> a + | "powerpc"|"arm"|"x86"|"riscV"|"aarch64" as a -> a | v -> bad_config "arch" [v] let model = get_config_string "model" let abi = get_config_string "abi" diff --git a/driver/Driver.ml b/driver/Driver.ml index 8ab8557c..be1252f9 100644 --- a/driver/Driver.ml +++ b/driver/Driver.ml @@ -187,6 +187,8 @@ Processing options: -O0 Do not optimize the compiled code -O1 -O2 -O3 Synonymous for -O -Os Optimize for code size in preference to code speed + -Obranchless Optimize to generate fewer conditional branches; try to produce + branch-free instruction sequences as much as possible -ftailcalls Optimize function calls in tail position [on] -fconst-prop Perform global constant propagation [on] -ffloat-const-prop <n> Control constant propagation of floats @@ -196,6 +198,7 @@ Processing options: -finline Perform inlining of functions [on] -finline-functions-called-once Integrate functions only required by their single caller [on] + -fif-conversion Perform if-conversion (generation of conditional moves) [on] Code generation options: (use -fno-<opt> to turn off -f<opt>) -ffpu Use FP registers for some integer operations [on] -fsmall-data <n> Set maximal size <n> for allocation in small data area @@ -203,6 +206,7 @@ Code generation options: (use -fno-<opt> to turn off -f<opt>) -falign-functions <n> Set alignment (in bytes) of function entry points -falign-branch-targets <n> Set alignment (in bytes) of branch targets -falign-cond-branches <n> Set alignment (in bytes) of conditional branches + -fcommon Put uninitialized globals in the common section [on]. |} ^ target_help ^ toolchain_help ^ @@ -249,7 +253,8 @@ let dump_mnemonics destfile = exit 0 let optimization_options = [ - option_ftailcalls; option_fconstprop; option_fcse; option_fredundancy; option_finline_functions_called_once; + option_ftailcalls; option_fifconversion; option_fconstprop; option_fcse; + option_fredundancy; option_finline; option_finline_functions_called_once; ] let set_all opts () = List.iter (fun r -> r := true) opts @@ -262,6 +267,10 @@ let num_input_files = ref 0 let cmdline_actions = let f_opt name ref = [Exact("-f" ^ name), Set ref; Exact("-fno-" ^ name), Unset ref] in + let check_align n = + if n <= 0 || ((n land (n - 1)) <> 0) then + error no_loc "requested alignment %d is not a power of 2" n + in [ (* Getting help *) Exact "-help", Unit print_usage_and_exit; @@ -294,12 +303,14 @@ let cmdline_actions = Exact "-O", Unit (set_all optimization_options); _Regexp "-O[123]$", Unit (set_all optimization_options); Exact "-Os", Set option_Osize; + Exact "-Obranchless", Set option_Obranchless; Exact "-fsmall-data", Integer(fun n -> option_small_data := n); Exact "-fsmall-const", Integer(fun n -> option_small_const := n); - Exact "-ffloat-const-prop", Integer(fun n -> option_ffloatconstprop := n); - Exact "-falign-functions", Integer(fun n -> option_falignfunctions := Some n); - Exact "-falign-branch-targets", Integer(fun n -> option_falignbranchtargets := n); - Exact "-falign-cond-branches", Integer(fun n -> option_faligncondbranchs := n);] @ + Exact "-ffloat-const-prop", Integer(fun n -> option_ffloatconstprop := n); + Exact "-falign-functions", Integer(fun n -> check_align n; option_falignfunctions := Some n); + Exact "-falign-branch-targets", Integer(fun n -> check_align n; option_falignbranchtargets := n); + Exact "-falign-cond-branches", Integer(fun n -> check_align n; option_faligncondbranchs := n);] @ + f_opt "common" option_fcommon @ (* Target processor options *) (if Configuration.arch = "arm" then if Configuration.model = "armv6" then @@ -358,6 +369,7 @@ let cmdline_actions = (* Optimization options *) (* -f options: come in -f and -fno- variants *) @ f_opt "tailcalls" option_ftailcalls + @ f_opt "if-conversion" option_fifconversion @ f_opt "const-prop" option_fconstprop @ f_opt "cse" option_fcse @ f_opt "redundancy" option_fredundancy diff --git a/driver/Frontend.ml b/driver/Frontend.ml index 929d9fd7..74791247 100644 --- a/driver/Frontend.ml +++ b/driver/Frontend.ml @@ -116,9 +116,10 @@ let init () = | "riscV" -> if Configuration.model = "64" then Machine.rv64 else Machine.rv32 + | "aarch64" -> Machine.aarch64 | _ -> assert false end; - Builtins.set C2C.builtins; + Env.set_builtins C2C.builtins; Cutil.declare_attributes C2C.attributes; CPragmas.initialize() @@ -131,7 +132,7 @@ let gnu_prepro_opt_key key s = let gnu_prepro_opt s = prepro_options := s::!prepro_options -(* Add gnu preprocessor option s and the implict -E *) +(* Add gnu preprocessor option s and the implicit -E *) let gnu_prepro_opt_e s = prepro_options := s :: !prepro_options; option_E := true @@ -171,7 +172,7 @@ let prepro_actions = [ @ (if Configuration.gnu_toolchain then gnu_prepro_actions else []) let gnu_prepro_help = -{| -M Ouput a rule suitable for make describing the +{| -M Output a rule suitable for make describing the dependencies of the main source file -MM Like -M but do not mention system header files -MF <file> Specifies file <file> as output file for -M or -MM diff --git a/driver/Interp.ml b/driver/Interp.ml index 6760e76c..d4286779 100644 --- a/driver/Interp.ml +++ b/driver/Interp.ml @@ -15,12 +15,12 @@ open Format open Camlcoq open AST -open Integers +open! Integers open Values open Memory open Globalenvs open Events -open Ctypes +open! Ctypes open Csyntax open Csem @@ -145,7 +145,7 @@ let print_state p (prog, ge, s) = let compare_mem m1 m2 = (* assumes nextblocks were already compared equal *) (* should permissions be taken into account? *) - Pervasives.compare m1.Mem.mem_contents m2.Mem.mem_contents + compare m1.Mem.mem_contents m2.Mem.mem_contents (* Comparing continuations *) diff --git a/exportclight/Clightgen.ml b/exportclight/Clightgen.ml index 1eb4fe03..f7279a5e 100644 --- a/exportclight/Clightgen.ml +++ b/exportclight/Clightgen.ml @@ -45,12 +45,7 @@ let compile_c_ast sourcename csyntax ofile = | Errors.Error msg -> fatal_error loc "%a" print_error msg in (* Dump Clight in C syntax if requested *) - if !option_dclight then begin - let ofile = Filename.chop_suffix sourcename ".c" ^ ".light.c" in - let oc = open_out ofile in - PrintClight.print_program (Format.formatter_of_out_channel oc) clight; - close_out oc - end; + PrintClight.print_if_2 clight; (* Print Clight in Coq syntax *) let oc = open_out ofile in ExportClight.print_program (Format.formatter_of_out_channel oc) @@ -60,6 +55,12 @@ let compile_c_ast sourcename csyntax ofile = (* From C source to Clight *) let compile_c_file sourcename ifile ofile = + let set_dest dst opt ext = + dst := if !opt then Some (output_filename sourcename ".c" ext) + else None in + set_dest Cprint.destination option_dparse ".parsed.c"; + set_dest PrintCsyntax.destination option_dcmedium ".compcert.c"; + set_dest PrintClight.destination option_dclight ".light.c"; compile_c_ast sourcename (parse_c_file sourcename ifile) ofile let output_filename sourcename suff = @@ -74,7 +75,10 @@ let process_c_file sourcename = if !option_E then begin preprocess sourcename "-" end else begin - let preproname = Driveraux.tmp_file ".i" in + let preproname = if !option_dprepro then + Driveraux.output_filename sourcename ".c" ".i" + else + Driveraux.tmp_file ".i" in preprocess sourcename preproname; compile_c_file sourcename preproname ofile end @@ -100,9 +104,11 @@ Processing options: prepro_help ^ language_support_help ^ {|Tracing options: + -dprepro Save C file after preprocessing in <file>.i -dparse Save C file after parsing and elaboration in <file>.parsed.c -dc Save generated Compcert C in <file>.compcert.c -dclight Save generated Clight in <file>.light.c + -dall Save all generated intermediate files in <file>.<ext> |} ^ general_help ^ warning_help @@ -142,9 +148,16 @@ let cmdline_actions = (* Preprocessing options *) @ prepro_actions @ (* Tracing options *) - [ Exact "-dparse", Set option_dparse; - Exact "-dc", Set option_dcmedium; - Exact "-dclight", Set option_dclight;] + [ Exact "-dprepro", Set option_dprepro; + Exact "-dparse", Set option_dparse; + Exact "-dc", Set option_dcmedium; + Exact "-dclight", Set option_dclight; + Exact "-dall", Self (fun _ -> + option_dprepro := true; + option_dparse := true; + option_dcmedium := true; + option_dclight := true;); + ] @ general_options (* Diagnostic options *) @ warning_options diff --git a/exportclight/ExportClight.ml b/exportclight/ExportClight.ml index b124586a..c9d6fced 100644 --- a/exportclight/ExportClight.ml +++ b/exportclight/ExportClight.ml @@ -18,7 +18,7 @@ open Format open Camlcoq open AST -open Ctypes +open! Ctypes open Cop open Clight @@ -221,6 +221,14 @@ let asttype p t = | AST.Tany32 -> "AST.Tany32" | AST.Tany64 -> "AST.Tany64") +let astrettype p = function + | AST.Tret t -> asttype p t + | AST.Tvoid -> fprintf p "AST.Tvoid" + | AST.Tint8signed -> fprintf p "AST.Tint8signed" + | AST.Tint8unsigned -> fprintf p "AST.Tint8unsigned" + | AST.Tint16signed -> fprintf p "AST.Tint16signed" + | AST.Tint16unsigned -> fprintf p "AST.Tint16unsigned" + let name_of_chunk = function | Mint8signed -> "Mint8signed" | Mint8unsigned -> "Mint8unsigned" @@ -236,7 +244,7 @@ let name_of_chunk = function let signatur p sg = fprintf p "@[<hov 2>(mksignature@ %a@ %a@ %a)@]" (print_list asttype) sg.sig_args - (print_option asttype) sg.sig_res + astrettype sg.sig_res callconv sg.sig_cc let assertions = ref ([]: (string * typ list) list) @@ -381,7 +389,7 @@ and lblstmts p = function (print_option coqZ) lbl stmt s lblstmts ls let print_function p (id, f) = - fprintf p "Definition f_%s := {|@ " (extern_atom id); + fprintf p "Definition f_%s := {|@ " (sanitize (extern_atom id)); fprintf p " fn_return := %a;@ " typ f.fn_return; fprintf p " fn_callconv := %a;@ " callconv f.fn_callconv; fprintf p " fn_params := %a;@ " (print_list (print_pair ident typ)) f.fn_params; @@ -402,7 +410,7 @@ let init_data p = function | Init_addrof(id,ofs) -> fprintf p "Init_addrof %a %a" ident id coqptrofs ofs let print_variable p (id, v) = - fprintf p "Definition v_%s := {|@ " (extern_atom id); + fprintf p "Definition v_%s := {|@ " (sanitize (extern_atom id)); fprintf p " gvar_info := %a;@ " typ v.gvar_info; fprintf p " gvar_init := %a;@ " (print_list init_data) v.gvar_init; fprintf p " gvar_readonly := %B;@ " v.gvar_readonly; @@ -417,12 +425,12 @@ let print_globdef p (id, gd) = let print_ident_globdef p = function | (id, Gfun(Ctypes.Internal f)) -> - fprintf p "(%a, Gfun(Internal f_%s))" ident id (extern_atom id) + fprintf p "(%a, Gfun(Internal f_%s))" ident id (sanitize (extern_atom id)) | (id, Gfun(Ctypes.External(ef, targs, tres, cc))) -> fprintf p "@[<hov 2>(%a,@ @[<hov 2>Gfun(External %a@ %a@ %a@ %a))@]@]" ident id external_function ef typlist targs typ tres callconv cc | (id, Gvar v) -> - fprintf p "(%a, Gvar v_%s)" ident id (extern_atom id) + fprintf p "(%a, Gvar v_%s)" ident id (sanitize (extern_atom id)) (* Composite definitions *) diff --git a/extraction/extraction.v b/extraction/extraction.v index 15a64d89..521c0cdd 100644 --- a/extraction/extraction.v +++ b/extraction/extraction.v @@ -34,7 +34,6 @@ Require Clight. Require Compiler. Require Parser. Require Initializers. -Require Int31. (* Standard lib *) Require Import ExtrOcamlBasic. @@ -72,6 +71,7 @@ Extract Constant Iteration.GenIter.iterate => (* Selection *) Extract Constant Selection.compile_switch => "Switchaux.compile_switch". +Extract Constant Selection.if_conversion_heuristic => "Selectionaux.if_conversion_heuristic". (* RTLgen *) Extract Constant RTLgen.more_likely => "RTLgenaux.more_likely". @@ -127,7 +127,7 @@ Extract Constant Compiler.time => "Timing.time_coq". (*Extraction Inline Compiler.apply_total Compiler.apply_partial.*) (* Cabs *) -Extract Constant Cabs.cabsloc => +Extract Constant Cabs.loc => "{ lineno : int; filename: string; byteno: int; @@ -136,15 +136,6 @@ Extract Constant Cabs.cabsloc => Extract Inlined Constant Cabs.string => "String.t". Extract Constant Cabs.char_code => "int64". -(* Int31 *) -Extract Inductive Int31.digits => "bool" [ "false" "true" ]. -Extract Inductive Int31.int31 => "int" [ "Camlcoq.Int31.constr" ] "Camlcoq.Int31.destr". -Extract Constant Int31.twice => "Camlcoq.Int31.twice". -Extract Constant Int31.twice_plus_one => "Camlcoq.Int31.twice_plus_one". -Extract Constant Int31.compare31 => "Camlcoq.Int31.compare". -Extract Constant Int31.On => "0". -Extract Constant Int31.In => "1". - (* Processor-specific extraction directives *) Load extractionMachdep. @@ -171,9 +162,10 @@ Separate Extraction Cexec.do_initial_state Cexec.do_step Cexec.at_final_state Ctypes.merge_attributes Ctypes.remove_attributes Ctypes.build_composite_env Initializers.transl_init Initializers.constval - Csyntax.Eindex Csyntax.Epreincr + Csyntax.Eindex Csyntax.Epreincr Csyntax.Eselection Ctyping.typecheck_program Ctyping.epostincr Ctyping.epostdecr Ctyping.epreincr Ctyping.epredecr + Ctyping.eselection Ctypes.make_program Clight.type_of_function Conventions1.callee_save_type Conventions1.is_float_reg diff --git a/flocq/IEEE754/Binary.v b/flocq/IEEE754/Binary.v index 0ec3a297..ac38c761 100644 --- a/flocq/IEEE754/Binary.v +++ b/flocq/IEEE754/Binary.v @@ -1839,6 +1839,127 @@ now rewrite <- cond_Zopp_negb. now destruct y as [ | | | ]. Qed. +(** Fused Multiply-Add *) + +Definition Bfma_szero m (x y z: binary_float) : bool := + let s_xy := xorb (Bsign x) (Bsign y) in (* sign of product x*y *) + if Bool.eqb s_xy (Bsign z) then s_xy + else match m with mode_DN => true | _ => false end. + +Definition Bfma fma_nan m (x y z: binary_float) := + match x, y with + | B754_nan _ _ _, _ | _, B754_nan _ _ _ + | B754_infinity _, B754_zero _ + | B754_zero _, B754_infinity _ => + (* Multiplication produces NaN *) + build_nan (fma_nan x y z) + | B754_infinity sx, B754_infinity sy + | B754_infinity sx, B754_finite sy _ _ _ + | B754_finite sx _ _ _, B754_infinity sy => + let s := xorb sx sy in + (* Multiplication produces infinity with sign [s] *) + match z with + | B754_nan _ _ _ => build_nan (fma_nan x y z) + | B754_infinity sz => + if Bool.eqb s sz then z else build_nan (fma_nan x y z) + | _ => B754_infinity s + end + | B754_finite sx _ _ _, B754_zero sy + | B754_zero sx, B754_finite sy _ _ _ + | B754_zero sx, B754_zero sy => + (* Multiplication produces zero *) + match z with + | B754_nan _ _ _ => build_nan (fma_nan x y z) + | B754_zero _ => B754_zero (Bfma_szero m x y z) + | _ => z + end + | B754_finite sx mx ex _, B754_finite sy my ey _ => + (* Multiplication produces a finite, non-zero result *) + match z with + | B754_nan _ _ _ => build_nan (fma_nan x y z) + | B754_infinity sz => z + | B754_zero _ => + let X := Float radix2 (cond_Zopp sx (Zpos mx)) ex in + let Y := Float radix2 (cond_Zopp sy (Zpos my)) ey in + let '(Float _ mr er) := Fmult X Y in + binary_normalize m mr er (Bfma_szero m x y z) + | B754_finite sz mz ez _ => + let X := Float radix2 (cond_Zopp sx (Zpos mx)) ex in + let Y := Float radix2 (cond_Zopp sy (Zpos my)) ey in + let Z := Float radix2 (cond_Zopp sz (Zpos mz)) ez in + let '(Float _ mr er) := Fplus (Fmult X Y) Z in + binary_normalize m mr er (Bfma_szero m x y z) + end + end. + +Theorem Bfma_correct: + forall fma_nan m x y z, + let res := (B2R x * B2R y + B2R z)%R in + is_finite x = true -> + is_finite y = true -> + is_finite z = true -> + if Rlt_bool (Rabs (round radix2 fexp (round_mode m) res)) (bpow radix2 emax) then + B2R (Bfma fma_nan m x y z) = round radix2 fexp (round_mode m) res /\ + is_finite (Bfma fma_nan m x y z) = true /\ + Bsign (Bfma fma_nan m x y z) = + match Rcompare res 0 with + | Eq => Bfma_szero m x y z + | Lt => true + | Gt => false + end + else + B2FF (Bfma fma_nan m x y z) = binary_overflow m (Rlt_bool res 0). +Proof. + intros. pattern (Bfma fma_nan m x y z). + match goal with |- ?p ?x => set (PROP := p) end. + set (szero := Bfma_szero m x y z). + assert (BINORM: forall mr er, F2R (Float radix2 mr er) = res -> + PROP (binary_normalize m mr er szero)). + { intros mr er E. + specialize (binary_normalize_correct m mr er szero). + change (FLT_exp (3 - emax - prec) prec) with fexp. rewrite E. tauto. + } + set (add_zero := + match z with + | B754_nan _ _ _ => build_nan (fma_nan x y z) + | B754_zero sz => B754_zero szero + | _ => z + end). + assert (ADDZERO: B2R x = 0%R \/ B2R y = 0%R -> PROP add_zero). + { + intros Z. + assert (RES: res = B2R z). + { unfold res. destruct Z as [E|E]; rewrite E, ?Rmult_0_l, ?Rmult_0_r, Rplus_0_l; auto. } + unfold PROP, add_zero; destruct z as [ sz | sz | sz plz | sz mz ez Bz]; try discriminate. + - simpl in RES; rewrite RES; rewrite round_0 by apply valid_rnd_round_mode. + rewrite Rlt_bool_true. split. reflexivity. split. reflexivity. + rewrite Rcompare_Eq by auto. reflexivity. + rewrite Rabs_R0; apply bpow_gt_0. + - rewrite RES, round_generic, Rlt_bool_true. + split. reflexivity. split. reflexivity. + unfold B2R. destruct sz. + rewrite Rcompare_Lt. auto. apply F2R_lt_0. reflexivity. + rewrite Rcompare_Gt. auto. apply F2R_gt_0. reflexivity. + apply abs_B2R_lt_emax. apply valid_rnd_round_mode. apply generic_format_B2R. + } + destruct x as [ sx | sx | sx plx | sx mx ex Bx]; + destruct y as [ sy | sy | sy ply | sy my ey By]; + try discriminate. +- apply ADDZERO; auto. +- apply ADDZERO; auto. +- apply ADDZERO; auto. +- destruct z as [ sz | sz | sz plz | sz mz ez Bz]; try discriminate; unfold Bfma. ++ set (X := Float radix2 (cond_Zopp sx (Zpos mx)) ex). + set (Y := Float radix2 (cond_Zopp sy (Zpos my)) ey). + destruct (Fmult X Y) as [mr er] eqn:FRES. + apply BINORM. unfold res. rewrite <- FRES, F2R_mult, Rplus_0_r. auto. ++ set (X := Float radix2 (cond_Zopp sx (Zpos mx)) ex). + set (Y := Float radix2 (cond_Zopp sy (Zpos my)) ey). + set (Z := Float radix2 (cond_Zopp sz (Zpos mz)) ez). + destruct (Fplus (Fmult X Y) Z) as [mr er] eqn:FRES. + apply BINORM. unfold res. rewrite <- FRES, F2R_plus, F2R_mult. auto. +Qed. + (** Division *) Definition Fdiv_core_binary m1 e1 m2 e2 := diff --git a/flocq/Prop/Div_sqrt_error.v b/flocq/Prop/Div_sqrt_error.v index 76c7af95..79220438 100644 --- a/flocq/Prop/Div_sqrt_error.v +++ b/flocq/Prop/Div_sqrt_error.v @@ -366,7 +366,7 @@ unfold Rdiv; apply (Rplus_le_reg_r (/ sqrt (1 + 2 * u_ro))); ring_simplify. apply (Rmult_le_reg_r (sqrt (1 + 4 * u_ro) * sqrt (1 + 2 * u_ro))). { apply Rmult_lt_0_compat; apply sqrt_lt_R0; lra. } field_simplify; [|split; apply Rgt_not_eq, Rlt_gt, sqrt_lt_R0; lra]. -unfold Rdiv; rewrite Rinv_1, !Rmult_1_r. +try unfold Rdiv; rewrite ?Rinv_1, ?Rmult_1_r. apply Rsqr_incr_0_var; [|now apply Rmult_le_pos; apply sqrt_pos]. rewrite <-sqrt_mult; [|lra|lra]. rewrite Rsqr_sqrt; [|apply Rmult_le_pos; lra]. @@ -409,7 +409,7 @@ assert (Pu_ro := u_ro_pos beta prec). apply (Rmult_le_reg_r (sqrt (1 + 2 * u_ro) * (1 + u_ro))). { apply Rmult_lt_0_compat; [apply sqrt_lt_R0|]; lra. } field_simplify; [|lra|intro H; apply sqrt_eq_0 in H; lra]. -unfold Rdiv, Rminus; rewrite Rinv_1, !Rmult_1_r, !Rplus_assoc. +try unfold Rdiv; unfold Rminus; rewrite ?Rinv_1, ?Rmult_1_r, !Rplus_assoc. rewrite <-(Rplus_0_r (sqrt _ * _)) at 2; apply Rplus_le_compat_l. apply (Rplus_le_reg_r (1 + u_ro)); ring_simplify. rewrite <-(sqrt_square (_ + 1)); [|lra]; apply sqrt_le_1_alt. diff --git a/flocq/Prop/Relative.v b/flocq/Prop/Relative.v index b936f2f7..5f87bd84 100644 --- a/flocq/Prop/Relative.v +++ b/flocq/Prop/Relative.v @@ -566,7 +566,7 @@ assert (H : (Rabs ((rx - x) / x) <= u_ro / (1 + u_ro))%R). now rewrite (Rabs_Rinv _ Nzx); apply Rinv_le. } apply (Rmult_le_reg_r ((ufpx + Rabs (rx - x)) * (1 + u_ro))). { apply Rmult_lt_0_compat; lra. } - field_simplify; [unfold Rdiv; rewrite Rinv_1, !Rmult_1_r| |]; lra. } + field_simplify; [try unfold Rdiv; rewrite ?Rinv_1, ?Rmult_1_r| |]; lra. } revert H; unfold Rdiv; rewrite Rabs_mult, (Rabs_Rinv _ Nzx); intro H. apply (Rmult_le_reg_r (/ Rabs x)); [now apply Rinv_0_lt_compat, Rabs_pos_lt|]. now apply (Rle_trans _ _ _ H); right; field; split; [apply Rabs_no_R0|lra]. diff --git a/lib/BoolEqual.v b/lib/BoolEqual.v index c9e7bad5..e8c1d831 100644 --- a/lib/BoolEqual.v +++ b/lib/BoolEqual.v @@ -106,8 +106,8 @@ Ltac bool_eq_refl_case := end. Ltac bool_eq_refl := - let H := fresh "Hrec" in let x := fresh "x" in - fix H 1; intros x; destruct x; simpl; bool_eq_refl_case. + let Hrec := fresh "Hrec" in let x := fresh "x" in + fix Hrec 1; intros x; destruct x; simpl; bool_eq_refl_case. Lemma false_not_true: forall (P: Prop), false = true -> P. @@ -124,7 +124,6 @@ Qed. Ltac bool_eq_sound_case := match goal with - | [ H: false = true |- _ ] => exact (false_not_true _ H) | [ H: _ && _ = true |- _ ] => apply andb_prop in H; destruct H; bool_eq_sound_case | [ H: proj_sumbool ?a = true |- _ ] => apply proj_sumbool_true in H; bool_eq_sound_case | [ |- ?C ?x1 ?x2 ?x3 ?x4 = ?C ?y1 ?y2 ?y3 ?y4 ] => apply f_equal4; auto @@ -137,7 +136,9 @@ Ltac bool_eq_sound_case := Ltac bool_eq_sound := let Hrec := fresh "Hrec" in let x := fresh "x" in let y := fresh "y" in - fix Hrec 1; intros x y; destruct x, y; simpl; intro; bool_eq_sound_case. + let H := fresh "EQ" in + fix Hrec 1; intros x y; destruct x, y; intro H; + try (apply (false_not_true _ H)); simpl in H; bool_eq_sound_case. Lemma dec_eq_from_bool_eq: forall (A: Type) (f: A -> A -> bool) diff --git a/lib/Camlcoq.ml b/lib/Camlcoq.ml index d94e3582..66322efb 100644 --- a/lib/Camlcoq.ml +++ b/lib/Camlcoq.ml @@ -335,54 +335,3 @@ let coqfloat32_of_camlfloat f = Float32.of_bits(coqint_of_camlint(Int32.bits_of_float f)) let camlfloat_of_coqfloat32 f = Int32.float_of_bits(camlint_of_coqint(Float32.to_bits f)) - -(* Int31 *) - -module Int31 = struct - -(* -let constr (b30,b29,b28,b27,b26,b25,b24, - b23,b22,b21,b20,b19,b18,b17,b16, - b15,b14,b13,b12,b11,b10,b9,b8, - b7,b6,b5,b4,b3,b2,b1,b0) = - let f i b accu = if b then accu + (1 lsl i) else accu in - f 30 b30 (f 29 b29 (f 28 b28 (f 27 b27 (f 26 b26 (f 25 b25 (f 24 b24 - (f 23 b23 (f 22 b22 (f 21 b21 (f 20 b20 (f 19 b19 (f 18 b18 (f 17 b17 (f 16 b16 - (f 15 b15 (f 14 b14 (f 13 b13 (f 12 b12 (f 11 b11 (f 10 b10 (f 9 b9 (f 8 b8 - (f 7 b7 (f 6 b6 (f 5 b5 (f 4 b4 (f 3 b3 (f 2 b2 (f 1 b1 (f 0 b0 0)))))))))))))))))))))))))))))) -*) - -let constr (b30,b29,b28,b27,b26,b25,b24, - b23,b22,b21,b20,b19,b18,b17,b16, - b15,b14,b13,b12,b11,b10,b9,b8, - b7,b6,b5,b4,b3,b2,b1,b0) = - let f i b = if b then 1 lsl i else 0 in - f 30 b30 + f 29 b29 + f 28 b28 + f 27 b27 + f 26 b26 + f 25 b25 + f 24 b24 + - f 23 b23 + f 22 b22 + f 21 b21 + f 20 b20 + f 19 b19 + f 18 b18 + f 17 b17 + f 16 b16 + - f 15 b15 + f 14 b14 + f 13 b13 + f 12 b12 + f 11 b11 + f 10 b10 + f 9 b9 + f 8 b8 + - f 7 b7 + f 6 b6 + f 5 b5 + f 4 b4 + f 3 b3 + f 2 b2 + f 1 b1 + f 0 b0 - -let destr f n = - let b i = n land (1 lsl i) <> 0 in - f (b 30) (b 29) (b 28) (b 27) (b 26) (b 25) (b 24) - (b 23) (b 22) (b 21) (b 20) (b 19) (b 18) (b 17) (b 16) - (b 15) (b 14) (b 13) (b 12) (b 11) (b 10) (b 9) (b 8) - (b 7) (b 6) (b 5) (b 4) (b 3) (b 2) (b 1) (b 0) - -let twice n = - (n lsl 1) land 0x7FFFFFFF - -let twice_plus_one n = - ((n lsl 1) land 0x7FFFFFFF) lor 1 - -let compare (x:int) (y:int) = - if x = y then Datatypes.Eq - else begin - let sx = x < 0 and sy = y < 0 in - if sx = sy then - (if x < y then Datatypes.Lt else Datatypes.Gt) - else - (if sx then Datatypes.Gt else Datatypes.Lt) - end - -end diff --git a/lib/Coqlib.v b/lib/Coqlib.v index 3b8e5b3b..02c5d07f 100644 --- a/lib/Coqlib.v +++ b/lib/Coqlib.v @@ -411,42 +411,12 @@ Qed. (** Properties of Euclidean division and modulus. *) -Lemma Zdiv_small: - forall x y, 0 <= x < y -> x / y = 0. -Proof. - intros. assert (y > 0). omega. - assert (forall a b, - 0 <= a < y -> - 0 <= y * b + a < y -> - b = 0). - intros. - assert (b = 0 \/ b > 0 \/ (-b) > 0). omega. - elim H3; intro. - auto. - elim H4; intro. - assert (y * b >= y * 1). apply Zmult_ge_compat_l. omega. omega. - omegaContradiction. - assert (y * (-b) >= y * 1). apply Zmult_ge_compat_l. omega. omega. - rewrite <- Zopp_mult_distr_r in H6. omegaContradiction. - apply H1 with (x mod y). - apply Z_mod_lt. auto. - rewrite <- Z_div_mod_eq. auto. auto. -Qed. - -Lemma Zmod_small: - forall x y, 0 <= x < y -> x mod y = x. -Proof. - intros. assert (y > 0). omega. - generalize (Z_div_mod_eq x y H0). - rewrite (Zdiv_small x y H). omega. -Qed. - Lemma Zmod_unique: forall x y a b, x = a * y + b -> 0 <= b < y -> x mod y = b. Proof. intros. subst x. rewrite Z.add_comm. - rewrite Z_mod_plus. apply Zmod_small. auto. omega. + rewrite Z_mod_plus. apply Z.mod_small. auto. omega. Qed. Lemma Zdiv_unique: @@ -461,30 +431,7 @@ Lemma Zdiv_Zdiv: forall a b c, b > 0 -> c > 0 -> (a / b) / c = a / (b * c). Proof. - intros. - generalize (Z_div_mod_eq a b H). generalize (Z_mod_lt a b H). intros. - generalize (Z_div_mod_eq (a/b) c H0). generalize (Z_mod_lt (a/b) c H0). intros. - set (q1 := a / b) in *. set (r1 := a mod b) in *. - set (q2 := q1 / c) in *. set (r2 := q1 mod c) in *. - symmetry. apply Zdiv_unique with (r2 * b + r1). - rewrite H2. rewrite H4. ring. - split. - assert (0 <= r2 * b). apply Z.mul_nonneg_nonneg. omega. omega. omega. - assert ((r2 + 1) * b <= c * b). - apply Zmult_le_compat_r. omega. omega. - replace ((r2 + 1) * b) with (r2 * b + b) in H5 by ring. - replace (c * b) with (b * c) in H5 by ring. - omega. -Qed. - -Lemma Zmult_le_compat_l_neg : - forall n m p:Z, n >= m -> p <= 0 -> p * n <= p * m. -Proof. - intros. - assert ((-p) * n >= (-p) * m). apply Zmult_ge_compat_l. auto. omega. - replace (p * n) with (- ((-p) * n)) by ring. - replace (p * m) with (- ((-p) * m)) by ring. - omega. + intros. apply Z.div_div; omega. Qed. Lemma Zdiv_interval_1: @@ -516,9 +463,9 @@ Proof. intros. assert (lo <= a / b < hi+1). apply Zdiv_interval_1. omega. omega. auto. - assert (lo * b <= lo * 1). apply Zmult_le_compat_l_neg. omega. omega. + assert (lo * b <= lo * 1) by (apply Z.mul_le_mono_nonpos_l; omega). replace (lo * 1) with lo in H3 by ring. - assert ((hi + 1) * 1 <= (hi + 1) * b). apply Zmult_le_compat_l. omega. omega. + assert ((hi + 1) * 1 <= (hi + 1) * b) by (apply Z.mul_le_mono_nonneg_l; omega). replace ((hi + 1) * 1) with (hi + 1) in H4 by ring. omega. omega. @@ -529,42 +476,11 @@ Lemma Zmod_recombine: a > 0 -> b > 0 -> x mod (a * b) = ((x/b) mod a) * b + (x mod b). Proof. - intros. - set (xb := x/b). - apply Zmod_unique with (xb/a). - generalize (Z_div_mod_eq x b H0); fold xb; intro EQ1. - generalize (Z_div_mod_eq xb a H); intro EQ2. - rewrite EQ2 in EQ1. - eapply eq_trans. eexact EQ1. ring. - generalize (Z_mod_lt x b H0). intro. - generalize (Z_mod_lt xb a H). intro. - assert (0 <= xb mod a * b <= a * b - b). - split. apply Z.mul_nonneg_nonneg; omega. - replace (a * b - b) with ((a - 1) * b) by ring. - apply Zmult_le_compat; omega. - omega. + intros. rewrite (Z.mul_comm a b). rewrite Z.rem_mul_r by omega. ring. Qed. (** Properties of divisibility. *) -Lemma Zdivides_trans: - forall x y z, (x | y) -> (y | z) -> (x | z). -Proof. - intros x y z [a A] [b B]; subst. exists (a*b); ring. -Qed. - -Definition Zdivide_dec: - forall (p q: Z), p > 0 -> { (p|q) } + { ~(p|q) }. -Proof. - intros. destruct (zeq (Z.modulo q p) 0). - left. exists (q / p). - transitivity (p * (q / p) + (q mod p)). apply Z_div_mod_eq; auto. - transitivity (p * (q / p)). omega. ring. - right; red; intros. elim n. apply Z_div_exact_1; auto. - inv H0. rewrite Z_div_mult; auto. ring. -Defined. -Global Opaque Zdivide_dec. - Lemma Zdivide_interval: forall a b c, 0 < c -> 0 <= a < b -> (c | a) -> (c | b) -> 0 <= a <= b - c. @@ -577,43 +493,20 @@ Qed. (** Conversion from [Z] to [nat]. *) -Definition nat_of_Z: Z -> nat := Z.to_nat. - -Lemma nat_of_Z_of_nat: - forall n, nat_of_Z (Z.of_nat n) = n. -Proof. - exact Nat2Z.id. -Qed. - -Lemma nat_of_Z_max: - forall z, Z.of_nat (nat_of_Z z) = Z.max z 0. -Proof. - intros. unfold Z.max. destruct z; simpl; auto. - change (Z.of_nat (Z.to_nat (Zpos p)) = Zpos p). - apply Z2Nat.id. compute; intuition congruence. -Qed. - -Lemma nat_of_Z_eq: - forall z, z >= 0 -> Z.of_nat (nat_of_Z z) = z. -Proof. - unfold nat_of_Z; intros. apply Z2Nat.id. omega. -Qed. - -Lemma nat_of_Z_neg: - forall n, n <= 0 -> nat_of_Z n = O. +Lemma Z_to_nat_neg: + forall n, n <= 0 -> Z.to_nat n = O. Proof. destruct n; unfold Z.le; simpl; auto. congruence. Qed. -Lemma nat_of_Z_plus: - forall p q, - p >= 0 -> q >= 0 -> - nat_of_Z (p + q) = (nat_of_Z p + nat_of_Z q)%nat. +Lemma Z_to_nat_max: + forall z, Z.of_nat (Z.to_nat z) = Z.max z 0. Proof. - unfold nat_of_Z; intros. apply Z2Nat.inj_add; omega. + intros. destruct (zle 0 z). +- rewrite Z2Nat.id by auto. xomega. +- rewrite Z_to_nat_neg by omega. xomega. Qed. - (** Alignment: [align n amount] returns the smallest multiple of [amount] greater than or equal to [n]. *) diff --git a/lib/Floats.v b/lib/Floats.v index 3ce8f4b4..13350dd0 100644 --- a/lib/Floats.v +++ b/lib/Floats.v @@ -16,15 +16,15 @@ (** Formalization of floating-point numbers, using the Flocq library. *) -Require Import Coqlib. -Require Import Integers. +Require Import Coqlib Zbits Integers. (*From Flocq*) Require Import Binary Bits Core. -Require Import Fappli_IEEE_extra. +Require Import IEEE754_extra. Require Import Program. Require Archi. Close Scope R_scope. +Open Scope Z_scope. Definition float := binary64. (**r the type of IEE754 double-precision FP numbers *) Definition float32 := binary32. (**r the type of IEE754 single-precision FP numbers *) @@ -94,21 +94,53 @@ Proof. destruct x as [[]|]; simpl; intros; discriminate. Qed. -(** Relation between number of bits and base-2 logarithm *) +(** Normalization of NaN payloads *) -Lemma digits2_log2: - forall p, Z.pos (Digits.digits2_pos p) = Z.succ (Z.log2 (Z.pos p)). +Lemma normalized_nan: forall prec n p, + Z.of_nat n = prec - 1 -> 1 < prec -> + nan_pl prec (Z.to_pos (P_mod_two_p p n)) = true. Proof. - assert (E: forall p, Digits.digits2_pos p = Pos.size p). - { induction p; simpl; rewrite ?IHp; auto. } - intros p. rewrite E. - destruct p; simpl; rewrite ?Pos.add_1_r; reflexivity. + intros. unfold nan_pl. apply Z.ltb_lt. rewrite Digits.Zpos_digits2_pos. + set (p' := P_mod_two_p p n). + assert (A: 0 <= p' < 2 ^ Z.of_nat n). + { rewrite <- two_power_nat_equiv; apply P_mod_two_p_range. } + assert (B: Digits.Zdigits radix2 p' <= prec - 1). + { apply Digits.Zdigits_le_Zpower. rewrite <- H. rewrite Z.abs_eq; tauto. } + destruct (zeq p' 0). +- rewrite e. simpl; auto. +- rewrite Z2Pos.id by omega. omega. Qed. +(** Transform a Nan payload to a quiet Nan payload. *) + +Definition quiet_nan_64_payload (p: positive) := + Z.to_pos (P_mod_two_p (Pos.lor p ((iter_nat xO 51 1%positive))) 52%nat). + +Lemma quiet_nan_64_proof: forall p, nan_pl 53 (quiet_nan_64_payload p) = true. +Proof. intros; apply normalized_nan; auto; omega. Qed. + +Definition quiet_nan_64 (sp: bool * positive) : {x :float | is_nan _ _ x = true} := + let (s, p) := sp in + exist _ (B754_nan 53 1024 s (quiet_nan_64_payload p) (quiet_nan_64_proof p)) (eq_refl true). + +Definition default_nan_64 := quiet_nan_64 Archi.default_nan_64. + +Definition quiet_nan_32_payload (p: positive) := + Z.to_pos (P_mod_two_p (Pos.lor p ((iter_nat xO 22 1%positive))) 23%nat). + +Lemma quiet_nan_32_proof: forall p, nan_pl 24 (quiet_nan_32_payload p) = true. +Proof. intros; apply normalized_nan; auto; omega. Qed. + +Definition quiet_nan_32 (sp: bool * positive) : {x :float32 | is_nan _ _ x = true} := + let (s, p) := sp in + exist _ (B754_nan 24 128 s (quiet_nan_32_payload p) (quiet_nan_32_proof p)) (eq_refl true). + +Definition default_nan_32 := quiet_nan_32 Archi.default_nan_32. + Local Notation __ := (eq_refl Datatypes.Lt). -Local Hint Extern 1 (Prec_gt_0 _) => exact (eq_refl Datatypes.Lt). -Local Hint Extern 1 (_ < _) => exact (eq_refl Datatypes.Lt). +Local Hint Extern 1 (Prec_gt_0 _) => exact (eq_refl Datatypes.Lt) : core. +Local Hint Extern 1 (_ < _) => exact (eq_refl Datatypes.Lt) : core. (** * Double-precision FP numbers *) @@ -119,68 +151,44 @@ Module Float. (** The following definitions are not part of the IEEE754 standard but apply to all architectures supported by CompCert. *) -(** Transform a Nan payload to a quiet Nan payload. *) - -Lemma transform_quiet_nan_proof (p : positive) : - nan_pl 53 p = true -> - nan_pl 53 (Pos.lor p (iter_nat xO 51 1%positive)) = true. -Proof. - unfold nan_pl. intros K. - simpl. rewrite Z.ltb_lt, digits2_log2 in *. - change (Z.pos (Pos.lor p 2251799813685248)) with (Z.lor (Z.pos p) 2251799813685248%Z). - rewrite Z.log2_lor by xomega. - now apply Z.max_case. -Qed. - -Definition transform_quiet_nan s p H : {x :float | is_nan _ _ x = true} := - exist _ (B754_nan 53 1024 s _ (transform_quiet_nan_proof p H)) (eq_refl true). - (** Nan payload operations for single <-> double conversions. *) +Definition expand_nan_payload (p: positive) := Pos.shiftl_nat p 29. + Lemma expand_nan_proof (p : positive) : nan_pl 24 p = true -> - nan_pl 53 (Pos.shiftl_nat p 29) = true. + nan_pl 53 (expand_nan_payload p) = true. Proof. - unfold nan_pl. intros K. + unfold nan_pl, expand_nan_payload. intros K. rewrite Z.ltb_lt in *. unfold Pos.shiftl_nat, nat_rect, Digits.digits2_pos. fold (Digits.digits2_pos p). zify; omega. Qed. -Definition expand_nan s p H : {x | is_nan 53 1024 x = true} := - exist _ (B754_nan 53 1024 s _ (expand_nan_proof p H)) (eq_refl true). +Definition expand_nan s p H : {x | is_nan _ _ x = true} := + exist _ (B754_nan 53 1024 s (expand_nan_payload p) (expand_nan_proof p H)) (eq_refl true). Definition of_single_nan (f : float32) : { x : float | is_nan _ _ x = true } := match f with | B754_nan s p H => if Archi.float_of_single_preserves_sNaN then expand_nan s p H - else transform_quiet_nan s _ (expand_nan_proof p H) - | _ => Archi.default_nan_64 + else quiet_nan_64 (s, expand_nan_payload p) + | _ => default_nan_64 end. -Lemma reduce_nan_proof (p : positive) : - nan_pl 53 p = true -> - nan_pl 24 (Pos.shiftr_nat p 29) = true. -Proof. - unfold nan_pl. intros K. - rewrite Z.ltb_lt in *. - unfold Pos.shiftr_nat, nat_rect. - assert (H : forall x, Digits.digits2_pos (Pos.div2 x) = (Digits.digits2_pos x - 1)%positive) - by (destruct x; simpl; auto; rewrite Pplus_one_succ_r, Pos.add_sub; auto). - rewrite !H, !Pos2Z.inj_sub_max. - repeat (apply Z.max_lub_lt; [reflexivity |apply Z.lt_sub_lt_add_l]). - exact K. -Qed. - -Definition reduce_nan s p H : {x : float32 | is_nan _ _ x = true} := - exist _ (B754_nan 24 128 s _ (reduce_nan_proof p H)) (eq_refl true). +Definition reduce_nan_payload (p: positive) := + (* The [quiet_nan_64_payload p] before the right shift is redundant with + the [quiet_nan_32_payload p] performed after, in [to_single_nan]. + However the former ensures that the result of the right shift is + not 0 and therefore representable as a positive. *) + Pos.shiftr_nat (quiet_nan_64_payload p) 29. Definition to_single_nan (f : float) : { x : float32 | is_nan _ _ x = true } := match f with - | B754_nan s p H => reduce_nan s _ (transform_quiet_nan_proof p H) - | _ => Archi.default_nan_32 + | B754_nan s p H => quiet_nan_32 (s, reduce_nan_payload p) + | _ => default_nan_32 end. (** NaN payload operations for opposite and absolute value. *) @@ -188,33 +196,62 @@ Definition to_single_nan (f : float) : { x : float32 | is_nan _ _ x = true } := Definition neg_nan (f : float) : { x : float | is_nan _ _ x = true } := match f with | B754_nan s p H => exist _ (B754_nan 53 1024 (negb s) p H) (eq_refl true) - | _ => Archi.default_nan_64 + | _ => default_nan_64 end. Definition abs_nan (f : float) : { x : float | is_nan _ _ x = true } := match f with | B754_nan s p H => exist _ (B754_nan 53 1024 false p H) (eq_refl true) - | _ => Archi.default_nan_64 + | _ => default_nan_64 end. -(** The NaN payload operations for two-argument arithmetic operations - are not part of the IEEE754 standard, but all architectures of - Compcert share a similar NaN behavior, parameterized by: -- a "default" payload which occurs when an operation generates a NaN from - non-NaN arguments; -- a choice function determining which of the payload arguments to choose, - when an operation is given two NaN arguments. *) +(** When an arithmetic operation returns a NaN, the sign and payload + of this NaN are not fully specified by the IEEE standard, and vary + among the architectures supported by CompCert. However, the following + behavior applies to all the supported architectures: the payload is either +- a default payload, independent of the arguments, or +- the payload of one of the NaN arguments, if any. + +For each supported architecture, the functions [Archi.choose_nan_64] +and [Archi.choose_nan_32] determine the payload of the result as a +function of the payloads of the NaN arguments. + +Additionally, signaling NaNs are converted to quiet NaNs, as required by the standard. +*) + +Definition cons_pl (x: float) (l: list (bool * positive)) := + match x with B754_nan s p _ => (s, p) :: l | _ => l end. -Definition binop_nan (x y : float) : {x : float | is_nan 53 1024 x = true} := - if Archi.fpu_returns_default_qNaN then Archi.default_nan_64 else +Definition unop_nan (x: float) : {x : float | is_nan _ _ x = true} := + quiet_nan_64 (Archi.choose_nan_64 (cons_pl x [])). + +Definition binop_nan (x y: float) : {x : float | is_nan _ _ x = true} := + quiet_nan_64 (Archi.choose_nan_64 (cons_pl x (cons_pl y []))). + +(** For fused multiply-add, the order in which arguments are examined + to select a NaN payload varies across platforms. E.g. in [fma x y z], + x86 considers [x] first, then [y], then [z], while ARM considers [z] first, + then [x], then [y]. The corresponding permutation is defined + for each target, as function [Archi.fma_order]. *) + +Definition fma_nan_1 (x y z: float) : {x : float | is_nan _ _ x = true} := + let '(a, b, c) := Archi.fma_order x y z in + quiet_nan_64 (Archi.choose_nan_64 (cons_pl a (cons_pl b (cons_pl c [])))). + +(** One last wrinkle for fused multiply-add: [fma zero infinity nan] + can return either the quiesced [nan], or the default NaN arising out + of the invalid operation [zero * infinity]. Of our target platforms, + only ARM honors the latter case. The choice between the default NaN + and [nan] is done as in the case of two-argument arithmetic operations. *) + +Definition fma_nan (x y z: float) : {x : float | is_nan _ _ x = true} := match x, y with - | B754_nan s1 pl1 H1, B754_nan s2 pl2 H2 => - if Archi.choose_binop_pl_64 pl1 pl2 - then transform_quiet_nan s2 pl2 H2 - else transform_quiet_nan s1 pl1 H1 - | B754_nan s1 pl1 H1, _ => transform_quiet_nan s1 pl1 H1 - | _, B754_nan s2 pl2 H2 => transform_quiet_nan s2 pl2 H2 - | _, _ => Archi.default_nan_64 + | B754_infinity _, B754_zero _ | B754_zero _, B754_infinity _ => + if Archi.fma_invalid_mul_is_nan + then quiet_nan_64 (Archi.choose_nan_64 (Archi.default_nan_64 :: cons_pl z [])) + else fma_nan_1 x y z + | _, _ => + fma_nan_1 x y z end. (** ** Operations over double-precision floats *) @@ -227,6 +264,8 @@ Definition eq_dec: forall (f1 f2: float), {f1 = f2} + {f1 <> f2} := Beq_dec _ _. Definition neg: float -> float := Bopp _ _ neg_nan. (**r opposite (change sign) *) Definition abs: float -> float := Babs _ _ abs_nan. (**r absolute value (set sign to [+]) *) +Definition sqrt: float -> float := + Bsqrt 53 1024 __ __ unop_nan mode_NE. (**r square root *) Definition add: float -> float -> float := Bplus 53 1024 __ __ binop_nan mode_NE. (**r addition *) Definition sub: float -> float -> float := @@ -235,6 +274,8 @@ Definition mul: float -> float -> float := Bmult 53 1024 __ __ binop_nan mode_NE. (**r multiplication *) Definition div: float -> float -> float := Bdiv 53 1024 __ __ binop_nan mode_NE. (**r division *) +Definition fma: float -> float -> float -> float := + Bfma 53 1024 __ __ fma_nan mode_NE. (**r fused multiply-add [x * y + z] *) Definition compare (f1 f2: float) : option Datatypes.comparison := (**r general comparison *) Bcompare 53 1024 f1 f2. Definition cmp (c:comparison) (f1 f2: float) : bool := (**r Boolean comparison *) @@ -302,19 +343,15 @@ Ltac smart_omega := Theorem add_commut: forall x y, is_nan _ _ x = false \/ is_nan _ _ y = false -> add x y = add y x. Proof. - intros. apply Bplus_commut. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x, y; try reflexivity. - now destruct H. + intros. apply Bplus_commut. + destruct x, y; try reflexivity; now destruct H. Qed. Theorem mul_commut: forall x y, is_nan _ _ x = false \/ is_nan _ _ y = false -> mul x y = mul y x. Proof. - intros. apply Bmult_commut. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x, y; try reflexivity. - now destruct H. + intros. apply Bmult_commut. + destruct x, y; try reflexivity; now destruct H. Qed. (** Multiplication by 2 is diagonal addition. *) @@ -324,9 +361,8 @@ Theorem mul2_add: Proof. intros. apply Bmult2_Bplus. intros x y Hx Hy. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x as [| |sx px Nx|]; try discriminate. - now destruct y, Archi.choose_binop_pl_64. + destruct x; try discriminate. simpl. rewrite Archi.choose_nan_64_idem. + destruct y; reflexivity || discriminate. Qed. (** Divisions that can be turned into multiplication by an inverse. *) @@ -338,9 +374,8 @@ Theorem div_mul_inverse: Proof. intros. apply Bdiv_mult_inverse. 2: easy. intros x0 y0 z0 Hx Hy Hz. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x0 as [| |sx px Nx|]; try discriminate. - now destruct y0, z0. + destruct x0; try discriminate. + destruct y0, z0; reflexivity || discriminate. Qed. (** Properties of comparisons. *) @@ -414,6 +449,7 @@ Qed. to emulate the former.) *) Definition ox8000_0000 := Int.repr Int.half_modulus. (**r [0x8000_0000] *) +Definition ox7FFF_FFFF := Int.repr Int.max_signed. (**r [0x7FFF_FFFF] *) Theorem of_intu_of_int_1: forall x, @@ -444,6 +480,46 @@ Proof. compute_this (Int.unsigned ox8000_0000); smart_omega. Qed. +Theorem of_intu_of_int_3: + forall x, + of_intu x = sub (of_int (Int.and x ox7FFF_FFFF)) (of_int (Int.and x ox8000_0000)). +Proof. + intros. + set (hi := Int.and x ox8000_0000). + set (lo := Int.and x ox7FFF_FFFF). + assert (R: forall n, integer_representable 53 1024 (Int.signed n)). + { intros. pose proof (Int.signed_range n). + apply integer_representable_n; auto; smart_omega. } + unfold sub, of_int. rewrite BofZ_minus by auto. unfold of_intu. f_equal. + assert (E: Int.add hi lo = x). + { unfold hi, lo. rewrite Int.add_is_or. + - rewrite <- Int.and_or_distrib. apply Int.and_mone. + - rewrite Int.and_assoc. rewrite (Int.and_commut ox8000_0000). rewrite Int.and_assoc. + change (Int.and ox7FFF_FFFF ox8000_0000) with Int.zero. rewrite ! Int.and_zero; auto. + } + assert (RNG: 0 <= Int.unsigned lo < two_p 31). + { unfold lo. change ox7FFF_FFFF with (Int.repr (two_p 31 - 1)). rewrite <- Int.zero_ext_and by omega. + apply Int.zero_ext_range. compute_this Int.zwordsize. omega. } + assert (B: forall i, 0 <= i < Int.zwordsize -> Int.testbit ox8000_0000 i = if zeq i 31 then true else false). + { intros; unfold Int.testbit. change (Int.unsigned ox8000_0000) with (2^31). + destruct (zeq i 31). subst i; auto. apply Z.pow2_bits_false; auto. } + assert (EITHER: hi = Int.zero \/ hi = ox8000_0000). + { unfold hi; destruct (Int.testbit x 31) eqn:B31; [right|left]; + Int.bit_solve; rewrite B by auto. + - destruct (zeq i 31). subst i; rewrite B31; auto. apply andb_false_r. + - destruct (zeq i 31). subst i; rewrite B31; auto. apply andb_false_r. + } + assert (SU: - Int.signed hi = Int.unsigned hi). + { destruct EITHER as [EQ|EQ]; rewrite EQ; reflexivity. } + unfold Z.sub; rewrite SU, <- E. + unfold Int.add; rewrite Int.unsigned_repr, Int.signed_eq_unsigned. omega. + - assert (Int.max_signed = two_p 31 - 1) by reflexivity. omega. + - assert (Int.unsigned hi = 0 \/ Int.unsigned hi = two_p 31) + by (destruct EITHER as [EQ|EQ]; rewrite EQ; [left|right]; reflexivity). + assert (Int.max_unsigned = two_p 31 + two_p 31 - 1) by reflexivity. + omega. +Qed. + Theorem to_intu_to_int_1: forall x n, cmp Clt x (of_intu ox8000_0000) = true -> @@ -919,44 +995,39 @@ End Float. Module Float32. -(** ** NaN payload manipulations *) - -Lemma transform_quiet_nan_proof (p : positive) : - nan_pl 24 p = true -> - nan_pl 24 (Pos.lor p (iter_nat xO 22 1%positive)) = true. -Proof. - unfold nan_pl. intros K. - simpl. rewrite Z.ltb_lt, digits2_log2 in *. - change (Z.pos (Pos.lor p 4194304)) with (Z.lor (Z.pos p) 4194304%Z). - rewrite Z.log2_lor by xomega. - now apply Z.max_case. -Qed. - -Definition transform_quiet_nan s p H : {x : float32 | is_nan _ _ x = true} := - exist _ (B754_nan 24 128 s _ (transform_quiet_nan_proof p H)) (eq_refl true). - Definition neg_nan (f : float32) : { x : float32 | is_nan _ _ x = true } := match f with | B754_nan s p H => exist _ (B754_nan 24 128 (negb s) p H) (eq_refl true) - | _ => Archi.default_nan_32 + | _ => default_nan_32 end. Definition abs_nan (f : float32) : { x : float32 | is_nan _ _ x = true } := match f with | B754_nan s p H => exist _ (B754_nan 24 128 false p H) (eq_refl true) - | _ => Archi.default_nan_32 + | _ => default_nan_32 end. -Definition binop_nan (x y : float32) : {x : float32 | is_nan _ _ x = true} := - if Archi.fpu_returns_default_qNaN then Archi.default_nan_32 else +Definition cons_pl (x: float32) (l: list (bool * positive)) := + match x with B754_nan s p _ => (s, p) :: l | _ => l end. + +Definition unop_nan (x: float32) : {x : float32 | is_nan _ _ x = true} := + quiet_nan_32 (Archi.choose_nan_32 (cons_pl x [])). + +Definition binop_nan (x y: float32) : {x : float32 | is_nan _ _ x = true} := + quiet_nan_32 (Archi.choose_nan_32 (cons_pl x (cons_pl y []))). + +Definition fma_nan_1 (x y z: float32) : {x : float32 | is_nan _ _ x = true} := + let '(a, b, c) := Archi.fma_order x y z in + quiet_nan_32 (Archi.choose_nan_32 (cons_pl a (cons_pl b (cons_pl c [])))). + +Definition fma_nan (x y z: float32) : {x : float32 | is_nan _ _ x = true} := match x, y with - | B754_nan s1 pl1 H1, B754_nan s2 pl2 H2 => - if Archi.choose_binop_pl_32 pl1 pl2 - then transform_quiet_nan s2 pl2 H2 - else transform_quiet_nan s1 pl1 H1 - | B754_nan s1 pl1 H1, _ => transform_quiet_nan s1 pl1 H1 - | _, B754_nan s2 pl2 H2 => transform_quiet_nan s2 pl2 H2 - | _, _ => Archi.default_nan_32 + | B754_infinity _, B754_zero _ | B754_zero _, B754_infinity _ => + if Archi.fma_invalid_mul_is_nan + then quiet_nan_32 (Archi.choose_nan_32 (Archi.default_nan_32 :: cons_pl z [])) + else fma_nan_1 x y z + | _, _ => + fma_nan_1 x y z end. (** ** Operations over single-precision floats *) @@ -969,6 +1040,8 @@ Definition eq_dec: forall (f1 f2: float32), {f1 = f2} + {f1 <> f2} := Beq_dec _ Definition neg: float32 -> float32 := Bopp _ _ neg_nan. (**r opposite (change sign) *) Definition abs: float32 -> float32 := Babs _ _ abs_nan. (**r absolute value (set sign to [+]) *) +Definition sqrt: float32 -> float32 := + Bsqrt 24 128 __ __ unop_nan mode_NE. (**r square root *) Definition add: float32 -> float32 -> float32 := Bplus 24 128 __ __ binop_nan mode_NE. (**r addition *) Definition sub: float32 -> float32 -> float32 := @@ -977,6 +1050,8 @@ Definition mul: float32 -> float32 -> float32 := Bmult 24 128 __ __ binop_nan mode_NE. (**r multiplication *) Definition div: float32 -> float32 -> float32 := Bdiv 24 128 __ __ binop_nan mode_NE. (**r division *) +Definition fma: float32 -> float32 -> float32 -> float32 := + Bfma 24 128 __ __ fma_nan mode_NE. (**r fused multiply-add [x * y + z] *) Definition compare (f1 f2: float32) : option Datatypes.comparison := (**r general comparison *) Bcompare 24 128 f1 f2. Definition cmp (c:comparison) (f1 f2: float32) : bool := (**r comparison *) @@ -1024,19 +1099,15 @@ Definition of_bits (b: int): float32 := b32_of_bits (Int.unsigned b). Theorem add_commut: forall x y, is_nan _ _ x = false \/ is_nan _ _ y = false -> add x y = add y x. Proof. - intros. apply Bplus_commut. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x, y; try reflexivity. - now destruct H. + intros. apply Bplus_commut. + destruct x, y; try reflexivity; now destruct H. Qed. Theorem mul_commut: forall x y, is_nan _ _ x = false \/ is_nan _ _ y = false -> mul x y = mul y x. Proof. - intros. apply Bmult_commut. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x, y; try reflexivity. - now destruct H. + intros. apply Bmult_commut. + destruct x, y; try reflexivity; now destruct H. Qed. (** Multiplication by 2 is diagonal addition. *) @@ -1046,9 +1117,8 @@ Theorem mul2_add: Proof. intros. apply Bmult2_Bplus. intros x y Hx Hy. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x as [| |sx px Nx|]; try discriminate. - now destruct y, Archi.choose_binop_pl_32. + destruct x; try discriminate. simpl. rewrite Archi.choose_nan_32_idem. + destruct y; reflexivity || discriminate. Qed. (** Divisions that can be turned into multiplication by an inverse. *) @@ -1060,9 +1130,8 @@ Theorem div_mul_inverse: Proof. intros. apply Bdiv_mult_inverse. 2: easy. intros x0 y0 z0 Hx Hy Hz. unfold binop_nan. - destruct Archi.fpu_returns_default_qNaN. easy. - destruct x0 as [| |sx px Nx|]; try discriminate. - now destruct y0, z0. + destruct x0; try discriminate. + destruct y0, z0; reflexivity || discriminate. Qed. (** Properties of comparisons. *) @@ -1218,15 +1287,15 @@ Proof. set (m := n mod 2^p + (2^p-1)) in *. assert (C: m / 2^p = if zeq (n mod 2^p) 0 then 0 else 1). { unfold m. destruct (zeq (n mod 2^p) 0). - rewrite e. apply Zdiv_small. omega. - eapply Zdiv_unique with (n mod 2^p - 1). ring. omega. } + rewrite e. apply Z.div_small. omega. + eapply Coqlib.Zdiv_unique with (n mod 2^p - 1). ring. omega. } assert (D: Z.testbit m p = if zeq (n mod 2^p) 0 then false else true). { destruct (zeq (n mod 2^p) 0). apply Z.testbit_false; auto. rewrite C; auto. apply Z.testbit_true; auto. rewrite C; auto. } assert (E: forall i, p < i -> Z.testbit m i = false). { intros. apply Z.testbit_false. omega. - replace (m / 2^i) with 0. auto. symmetry. apply Zdiv_small. + replace (m / 2^i) with 0. auto. symmetry. apply Z.div_small. unfold m. split. omega. apply Z.lt_le_trans with (2 * 2^p). omega. change 2 with (2^1) at 1. rewrite <- (Zpower_plus radix2) by omega. apply Zpower_le. omega. } @@ -1356,9 +1425,9 @@ Proof. rewrite Int64.testbit_repr by auto. f_equal. f_equal. unfold Int64.and. change (Int64.unsigned (Int64.repr 2047)) with 2047. change 2047 with (Z.ones 11). rewrite ! Z.land_ones by omega. - rewrite Int64.unsigned_repr. apply Int64.eqmod_mod_eq. + rewrite Int64.unsigned_repr. apply eqmod_mod_eq. apply Z.lt_gt. apply (Zpower_gt_0 radix2); omega. - apply Int64.eqmod_divides with (2^64). apply Int64.eqm_signed_unsigned. + apply eqmod_divides with (2^64). apply Int64.eqm_signed_unsigned. exists (2^(64-11)); auto. exploit (Z_mod_lt (Int64.unsigned n) (2^11)). compute; auto. assert (2^11 < Int64.max_unsigned) by (compute; auto). omega. diff --git a/lib/Heaps.v b/lib/Heaps.v index 9fa07a1d..85343998 100644 --- a/lib/Heaps.v +++ b/lib/Heaps.v @@ -256,14 +256,14 @@ Proof. eapply gt_heap_trans with y; eauto. red; auto. - intuition. eapply lt_heap_trans; eauto. red; auto. - eapply gt_heap_trans; eauto. red; auto. + eapply gt_heap_trans; eauto. red; auto with ordered_type. - intuition. eapply gt_heap_trans; eauto. red; auto. - rewrite e3 in *; simpl in *. intuition. eapply lt_heap_trans with y; eauto. red; auto. eapply gt_heap_trans; eauto. red; auto. - intuition. eapply lt_heap_trans with y; eauto. red; auto. - eapply gt_heap_trans; eauto. red; auto. + eapply gt_heap_trans; eauto. red; auto with ordered_type. eapply gt_heap_trans with x; eauto. red; auto. - rewrite e3 in *; simpl in *; intuition. eapply gt_heap_trans; eauto. red; auto. @@ -308,7 +308,7 @@ Proof. intros. unfold insert. case_eq (partition x h). intros a b EQ; simpl. assert (E.eq y x \/ ~E.eq y x). - destruct (E.compare y x); auto. + destruct (E.compare y x); auto with ordered_type. right; red; intros. elim (E.lt_not_eq l). apply E.eq_sym; auto. destruct H0. tauto. diff --git a/lib/Fappli_IEEE_extra.v b/lib/IEEE754_extra.v index c23149be..c23149be 100644 --- a/lib/Fappli_IEEE_extra.v +++ b/lib/IEEE754_extra.v diff --git a/lib/Integers.v b/lib/Integers.v index 4b75e71e..8990c78d 100644 --- a/lib/Integers.v +++ b/lib/Integers.v @@ -16,7 +16,7 @@ (** Formalizations of machine integers modulo $2^N$ #2<sup>N</sup>#. *) Require Import Eqdep_dec Zquot Zwf. -Require Import Coqlib. +Require Import Coqlib Zbits. Require Archi. (** * Comparisons *) @@ -91,6 +91,8 @@ Proof. generalize modulus_gt_one; omega. Qed. +Hint Resolve modulus_pos: ints. + (** * Representation of machine integers *) (** A machine integer (type [int]) is represented as a Coq arbitrary-precision @@ -101,17 +103,6 @@ Record int: Type := mkint { intval: Z; intrange: -1 < intval < modulus }. (** Fast normalization modulo [2^wordsize] *) -Fixpoint P_mod_two_p (p: positive) (n: nat) {struct n} : Z := - match n with - | O => 0 - | S m => - match p with - | xH => 1 - | xO q => Z.double (P_mod_two_p q m) - | xI q => Z.succ_double (P_mod_two_p q m) - end - end. - Definition Z_mod_modulus (x: Z) : Z := match x with | Z0 => 0 @@ -119,51 +110,9 @@ Definition Z_mod_modulus (x: Z) : Z := | Zneg p => let r := P_mod_two_p p wordsize in if zeq r 0 then 0 else modulus - r end. -Lemma P_mod_two_p_range: - forall n p, 0 <= P_mod_two_p p n < two_power_nat n. -Proof. - induction n; simpl; intros. - - rewrite two_power_nat_O. omega. - - rewrite two_power_nat_S. destruct p. - + generalize (IHn p). rewrite Z.succ_double_spec. omega. - + generalize (IHn p). rewrite Z.double_spec. omega. - + generalize (two_power_nat_pos n). omega. -Qed. - -Lemma P_mod_two_p_eq: - forall n p, P_mod_two_p p n = (Zpos p) mod (two_power_nat n). -Proof. - assert (forall n p, exists y, Zpos p = y * two_power_nat n + P_mod_two_p p n). - { - induction n; simpl; intros. - - rewrite two_power_nat_O. exists (Zpos p). ring. - - rewrite two_power_nat_S. destruct p. - + destruct (IHn p) as [y EQ]. exists y. - change (Zpos p~1) with (2 * Zpos p + 1). rewrite EQ. - rewrite Z.succ_double_spec. ring. - + destruct (IHn p) as [y EQ]. exists y. - change (Zpos p~0) with (2 * Zpos p). rewrite EQ. - rewrite (Z.double_spec (P_mod_two_p p n)). ring. - + exists 0; omega. - } - intros. - destruct (H n p) as [y EQ]. - symmetry. apply Zmod_unique with y. auto. apply P_mod_two_p_range. -Qed. - Lemma Z_mod_modulus_range: forall x, 0 <= Z_mod_modulus x < modulus. -Proof. - intros; unfold Z_mod_modulus. - destruct x. - - generalize modulus_pos; intuition. - - apply P_mod_two_p_range. - - set (r := P_mod_two_p p wordsize). - assert (0 <= r < modulus) by apply P_mod_two_p_range. - destruct (zeq r 0). - + generalize modulus_pos; intuition. - + Psatz.lia. -Qed. +Proof (Z_mod_two_p_range wordsize). Lemma Z_mod_modulus_range': forall x, -1 < Z_mod_modulus x < modulus. @@ -173,22 +122,7 @@ Qed. Lemma Z_mod_modulus_eq: forall x, Z_mod_modulus x = x mod modulus. -Proof. - intros. unfold Z_mod_modulus. destruct x. - - rewrite Zmod_0_l. auto. - - apply P_mod_two_p_eq. - - generalize (P_mod_two_p_range wordsize p) (P_mod_two_p_eq wordsize p). - fold modulus. intros A B. - exploit (Z_div_mod_eq (Zpos p) modulus). apply modulus_pos. intros C. - set (q := Zpos p / modulus) in *. - set (r := P_mod_two_p p wordsize) in *. - rewrite <- B in C. - change (Z.neg p) with (- (Z.pos p)). destruct (zeq r 0). - + symmetry. apply Zmod_unique with (-q). rewrite C; rewrite e. Psatz.lia. - intuition. - + symmetry. apply Zmod_unique with (-q - 1). rewrite C. Psatz.lia. - intuition. -Qed. +Proof (Z_mod_two_p_eq wordsize). (** The [unsigned] and [signed] functions return the Coq integer corresponding to the given machine integer, interpreted as unsigned or signed @@ -323,63 +257,20 @@ Definition shr_carry (x y: int) : int := (** Zero and sign extensions *) -Definition Zshiftin (b: bool) (x: Z) : Z := - if b then Z.succ_double x else Z.double x. - -(** In pseudo-code: -<< - Fixpoint Zzero_ext (n: Z) (x: Z) : Z := - if zle n 0 then - 0 - else - Zshiftin (Z.odd x) (Zzero_ext (Z.pred n) (Z.div2 x)). - Fixpoint Zsign_ext (n: Z) (x: Z) : Z := - if zle n 1 then - if Z.odd x then -1 else 0 - else - Zshiftin (Z.odd x) (Zzero_ext (Z.pred n) (Z.div2 x)). ->> - We encode this [nat]-like recursion using the [Z.iter] iteration - function, in order to make the [Zzero_ext] and [Zsign_ext] - functions efficiently executable within Coq. -*) - -Definition Zzero_ext (n: Z) (x: Z) : Z := - Z.iter n - (fun rec x => Zshiftin (Z.odd x) (rec (Z.div2 x))) - (fun x => 0) - x. - -Definition Zsign_ext (n: Z) (x: Z) : Z := - Z.iter (Z.pred n) - (fun rec x => Zshiftin (Z.odd x) (rec (Z.div2 x))) - (fun x => if Z.odd x then -1 else 0) - x. - Definition zero_ext (n: Z) (x: int) : int := repr (Zzero_ext n (unsigned x)). - Definition sign_ext (n: Z) (x: int) : int := repr (Zsign_ext n (unsigned x)). (** Decomposition of a number as a sum of powers of two. *) -Fixpoint Z_one_bits (n: nat) (x: Z) (i: Z) {struct n}: list Z := - match n with - | O => nil - | S m => - if Z.odd x - then i :: Z_one_bits m (Z.div2 x) (i+1) - else Z_one_bits m (Z.div2 x) (i+1) - end. - Definition one_bits (x: int) : list int := List.map repr (Z_one_bits wordsize (unsigned x) 0). (** Recognition of powers of two. *) Definition is_power2 (x: int) : option int := - match Z_one_bits wordsize (unsigned x) 0 with - | i :: nil => Some (repr i) - | _ => None + match Z_is_power2 (unsigned x) with + | Some i => Some (repr i) + | None => None end. (** Comparisons. *) @@ -503,101 +394,7 @@ Qed. (** ** Modulo arithmetic *) -(** We define and state properties of equality and arithmetic modulo a - positive integer. *) - -Section EQ_MODULO. - -Variable modul: Z. -Hypothesis modul_pos: modul > 0. - -Definition eqmod (x y: Z) : Prop := exists k, x = k * modul + y. - -Lemma eqmod_refl: forall x, eqmod x x. -Proof. - intros; red. exists 0. omega. -Qed. - -Lemma eqmod_refl2: forall x y, x = y -> eqmod x y. -Proof. - intros. subst y. apply eqmod_refl. -Qed. - -Lemma eqmod_sym: forall x y, eqmod x y -> eqmod y x. -Proof. - intros x y [k EQ]; red. exists (-k). subst x. ring. -Qed. - -Lemma eqmod_trans: forall x y z, eqmod x y -> eqmod y z -> eqmod x z. -Proof. - intros x y z [k1 EQ1] [k2 EQ2]; red. - exists (k1 + k2). subst x; subst y. ring. -Qed. - -Lemma eqmod_small_eq: - forall x y, eqmod x y -> 0 <= x < modul -> 0 <= y < modul -> x = y. -Proof. - intros x y [k EQ] I1 I2. - generalize (Zdiv_unique _ _ _ _ EQ I2). intro. - rewrite (Zdiv_small x modul I1) in H. subst k. omega. -Qed. - -Lemma eqmod_mod_eq: - forall x y, eqmod x y -> x mod modul = y mod modul. -Proof. - intros x y [k EQ]. subst x. - rewrite Z.add_comm. apply Z_mod_plus. auto. -Qed. - -Lemma eqmod_mod: - forall x, eqmod x (x mod modul). -Proof. - intros; red. exists (x / modul). - rewrite Z.mul_comm. apply Z_div_mod_eq. auto. -Qed. - -Lemma eqmod_add: - forall a b c d, eqmod a b -> eqmod c d -> eqmod (a + c) (b + d). -Proof. - intros a b c d [k1 EQ1] [k2 EQ2]; red. - subst a; subst c. exists (k1 + k2). ring. -Qed. - -Lemma eqmod_neg: - forall x y, eqmod x y -> eqmod (-x) (-y). -Proof. - intros x y [k EQ]; red. exists (-k). rewrite EQ. ring. -Qed. - -Lemma eqmod_sub: - forall a b c d, eqmod a b -> eqmod c d -> eqmod (a - c) (b - d). -Proof. - intros a b c d [k1 EQ1] [k2 EQ2]; red. - subst a; subst c. exists (k1 - k2). ring. -Qed. - -Lemma eqmod_mult: - forall a b c d, eqmod a c -> eqmod b d -> eqmod (a * b) (c * d). -Proof. - intros a b c d [k1 EQ1] [k2 EQ2]; red. - subst a; subst b. - exists (k1 * k2 * modul + c * k2 + k1 * d). - ring. -Qed. - -End EQ_MODULO. - -Lemma eqmod_divides: - forall n m x y, eqmod n x y -> Z.divide m n -> eqmod m x y. -Proof. - intros. destruct H as [k1 EQ1]. destruct H0 as [k2 EQ2]. - exists (k1*k2). rewrite <- Z.mul_assoc. rewrite <- EQ2. auto. -Qed. - -(** We then specialize these definitions to equality modulo - $2^{wordsize}$ #2<sup>wordsize</sup>#. *) - -Hint Resolve modulus_pos: ints. +(** [eqm] is equality modulo $2^{wordsize}$ #2<sup>wordsize</sup>#. *) Definition eqm := eqmod modulus. @@ -643,6 +440,19 @@ Lemma eqm_mult: Proof (eqmod_mult modulus). Hint Resolve eqm_mult: ints. +Lemma eqm_same_bits: + forall x y, + (forall i, 0 <= i < zwordsize -> Z.testbit x i = Z.testbit y i) -> + eqm x y. +Proof (eqmod_same_bits wordsize). + +Lemma same_bits_eqm: + forall x y i, + eqm x y -> + 0 <= i < zwordsize -> + Z.testbit x i = Z.testbit y i. +Proof (same_bits_eqmod wordsize). + (** ** Properties of the coercions between [Z] and [int] *) Lemma eqm_samerepr: forall x y, eqm x y -> repr x = repr y. @@ -712,7 +522,7 @@ Theorem repr_unsigned: forall i, repr (unsigned i) = i. Proof. destruct i; simpl. unfold repr. apply mkint_eq. - rewrite Z_mod_modulus_eq. apply Zmod_small; omega. + rewrite Z_mod_modulus_eq. apply Z.mod_small; omega. Qed. Hint Resolve repr_unsigned: ints. @@ -735,7 +545,7 @@ Theorem unsigned_repr: forall z, 0 <= z <= max_unsigned -> unsigned (repr z) = z. Proof. intros. rewrite unsigned_repr_eq. - apply Zmod_small. unfold max_unsigned in H. omega. + apply Z.mod_small. unfold max_unsigned in H. omega. Qed. Hint Resolve unsigned_repr: ints. @@ -782,7 +592,7 @@ Qed. Theorem unsigned_one: unsigned one = 1. Proof. - unfold one; rewrite unsigned_repr_eq. apply Zmod_small. split. omega. + unfold one; rewrite unsigned_repr_eq. apply Z.mod_small. split. omega. unfold modulus. replace wordsize with (S(Init.Nat.pred wordsize)). rewrite two_power_nat_S. generalize (two_power_nat_pos (Init.Nat.pred wordsize)). omega. @@ -793,7 +603,7 @@ Theorem unsigned_mone: unsigned mone = modulus - 1. Proof. unfold mone; rewrite unsigned_repr_eq. replace (-1) with ((modulus - 1) + (-1) * modulus). - rewrite Z_mod_plus_full. apply Zmod_small. + rewrite Z_mod_plus_full. apply Z.mod_small. generalize modulus_pos. omega. omega. Qed. @@ -825,7 +635,7 @@ Qed. Theorem unsigned_repr_wordsize: unsigned iwordsize = zwordsize. Proof. - unfold iwordsize; rewrite unsigned_repr_eq. apply Zmod_small. + unfold iwordsize; rewrite unsigned_repr_eq. apply Z.mod_small. generalize wordsize_pos wordsize_max_unsigned; unfold max_unsigned; omega. Qed. @@ -858,6 +668,11 @@ Proof. intros. generalize (eq_spec x y); case (eq x y); intros; congruence. Qed. +Theorem same_if_eq: forall x y, eq x y = true -> x = y. +Proof. + intros. generalize (eq_spec x y); rewrite H; auto. +Qed. + Theorem eq_signed: forall x y, eq x y = if zeq (signed x) (signed y) then true else false. Proof. @@ -1304,298 +1119,6 @@ Qed. (** ** Bit-level properties *) -(** ** Properties of bit-level operations over [Z] *) - -Remark Ztestbit_0: forall n, Z.testbit 0 n = false. -Proof Z.testbit_0_l. - -Remark Ztestbit_1: forall n, Z.testbit 1 n = zeq n 0. -Proof. - intros. destruct n; simpl; auto. -Qed. - -Remark Ztestbit_m1: forall n, 0 <= n -> Z.testbit (-1) n = true. -Proof. - intros. destruct n; simpl; auto. -Qed. - -Remark Zshiftin_spec: - forall b x, Zshiftin b x = 2 * x + (if b then 1 else 0). -Proof. - unfold Zshiftin; intros. destruct b. - - rewrite Z.succ_double_spec. omega. - - rewrite Z.double_spec. omega. -Qed. - -Remark Zshiftin_inj: - forall b1 x1 b2 x2, - Zshiftin b1 x1 = Zshiftin b2 x2 -> b1 = b2 /\ x1 = x2. -Proof. - intros. rewrite !Zshiftin_spec in H. - destruct b1; destruct b2. - split; [auto|omega]. - omegaContradiction. - omegaContradiction. - split; [auto|omega]. -Qed. - -Remark Zdecomp: - forall x, x = Zshiftin (Z.odd x) (Z.div2 x). -Proof. - intros. destruct x; simpl. - - auto. - - destruct p; auto. - - destruct p; auto. simpl. rewrite Pos.pred_double_succ. auto. -Qed. - -Remark Ztestbit_shiftin: - forall b x n, - 0 <= n -> - Z.testbit (Zshiftin b x) n = if zeq n 0 then b else Z.testbit x (Z.pred n). -Proof. - intros. rewrite Zshiftin_spec. destruct (zeq n 0). - - subst n. destruct b. - + apply Z.testbit_odd_0. - + rewrite Z.add_0_r. apply Z.testbit_even_0. - - assert (0 <= Z.pred n) by omega. - set (n' := Z.pred n) in *. - replace n with (Z.succ n') by (unfold n'; omega). - destruct b. - + apply Z.testbit_odd_succ; auto. - + rewrite Z.add_0_r. apply Z.testbit_even_succ; auto. -Qed. - -Remark Ztestbit_shiftin_base: - forall b x, Z.testbit (Zshiftin b x) 0 = b. -Proof. - intros. rewrite Ztestbit_shiftin. apply zeq_true. omega. -Qed. - -Remark Ztestbit_shiftin_succ: - forall b x n, 0 <= n -> Z.testbit (Zshiftin b x) (Z.succ n) = Z.testbit x n. -Proof. - intros. rewrite Ztestbit_shiftin. rewrite zeq_false. rewrite Z.pred_succ. auto. - omega. omega. -Qed. - -Remark Ztestbit_eq: - forall n x, 0 <= n -> - Z.testbit x n = if zeq n 0 then Z.odd x else Z.testbit (Z.div2 x) (Z.pred n). -Proof. - intros. rewrite (Zdecomp x) at 1. apply Ztestbit_shiftin; auto. -Qed. - -Remark Ztestbit_base: - forall x, Z.testbit x 0 = Z.odd x. -Proof. - intros. rewrite Ztestbit_eq. apply zeq_true. omega. -Qed. - -Remark Ztestbit_succ: - forall n x, 0 <= n -> Z.testbit x (Z.succ n) = Z.testbit (Z.div2 x) n. -Proof. - intros. rewrite Ztestbit_eq. rewrite zeq_false. rewrite Z.pred_succ. auto. - omega. omega. -Qed. - -Lemma eqmod_same_bits: - forall n x y, - (forall i, 0 <= i < Z.of_nat n -> Z.testbit x i = Z.testbit y i) -> - eqmod (two_power_nat n) x y. -Proof. - induction n; intros. - - change (two_power_nat 0) with 1. exists (x-y); ring. - - rewrite two_power_nat_S. - assert (eqmod (two_power_nat n) (Z.div2 x) (Z.div2 y)). - apply IHn. intros. rewrite <- !Ztestbit_succ. apply H. rewrite Nat2Z.inj_succ; omega. - omega. omega. - destruct H0 as [k EQ]. - exists k. rewrite (Zdecomp x). rewrite (Zdecomp y). - replace (Z.odd y) with (Z.odd x). - rewrite EQ. rewrite !Zshiftin_spec. ring. - exploit (H 0). rewrite Nat2Z.inj_succ; omega. - rewrite !Ztestbit_base. auto. -Qed. - -Lemma eqm_same_bits: - forall x y, - (forall i, 0 <= i < zwordsize -> Z.testbit x i = Z.testbit y i) -> - eqm x y. -Proof (eqmod_same_bits wordsize). - -Lemma same_bits_eqmod: - forall n x y i, - eqmod (two_power_nat n) x y -> 0 <= i < Z.of_nat n -> - Z.testbit x i = Z.testbit y i. -Proof. - induction n; intros. - - simpl in H0. omegaContradiction. - - rewrite Nat2Z.inj_succ in H0. rewrite two_power_nat_S in H. - rewrite !(Ztestbit_eq i); intuition. - destruct H as [k EQ]. - assert (EQ': Zshiftin (Z.odd x) (Z.div2 x) = - Zshiftin (Z.odd y) (k * two_power_nat n + Z.div2 y)). - { - rewrite (Zdecomp x) in EQ. rewrite (Zdecomp y) in EQ. - rewrite EQ. rewrite !Zshiftin_spec. ring. - } - exploit Zshiftin_inj; eauto. intros [A B]. - destruct (zeq i 0). - + auto. - + apply IHn. exists k; auto. omega. -Qed. - -Lemma same_bits_eqm: - forall x y i, - eqm x y -> - 0 <= i < zwordsize -> - Z.testbit x i = Z.testbit y i. -Proof (same_bits_eqmod wordsize). - -Remark two_power_nat_infinity: - forall x, 0 <= x -> exists n, x < two_power_nat n. -Proof. - intros x0 POS0; pattern x0; apply natlike_ind; auto. - exists O. compute; auto. - intros. destruct H0 as [n LT]. exists (S n). rewrite two_power_nat_S. - generalize (two_power_nat_pos n). omega. -Qed. - -Lemma equal_same_bits: - forall x y, - (forall i, 0 <= i -> Z.testbit x i = Z.testbit y i) -> - x = y. -Proof. - intros. - set (z := if zlt x y then y - x else x - y). - assert (0 <= z). - unfold z; destruct (zlt x y); omega. - exploit (two_power_nat_infinity z); auto. intros [n LT]. - assert (eqmod (two_power_nat n) x y). - apply eqmod_same_bits. intros. apply H. tauto. - assert (eqmod (two_power_nat n) z 0). - unfold z. destruct (zlt x y). - replace 0 with (y - y) by omega. apply eqmod_sub. apply eqmod_refl. auto. - replace 0 with (x - x) by omega. apply eqmod_sub. apply eqmod_refl. apply eqmod_sym; auto. - assert (z = 0). - apply eqmod_small_eq with (two_power_nat n). auto. omega. generalize (two_power_nat_pos n); omega. - unfold z in H3. destruct (zlt x y); omega. -Qed. - -Lemma Z_one_complement: - forall i, 0 <= i -> - forall x, Z.testbit (-x-1) i = negb (Z.testbit x i). -Proof. - intros i0 POS0. pattern i0. apply Zlt_0_ind; auto. - intros i IND POS x. - rewrite (Zdecomp x). set (y := Z.div2 x). - replace (- Zshiftin (Z.odd x) y - 1) - with (Zshiftin (negb (Z.odd x)) (- y - 1)). - rewrite !Ztestbit_shiftin; auto. - destruct (zeq i 0). auto. apply IND. omega. - rewrite !Zshiftin_spec. destruct (Z.odd x); simpl negb; ring. -Qed. - -Lemma Ztestbit_above: - forall n x i, - 0 <= x < two_power_nat n -> - i >= Z.of_nat n -> - Z.testbit x i = false. -Proof. - induction n; intros. - - change (two_power_nat 0) with 1 in H. - replace x with 0 by omega. - apply Z.testbit_0_l. - - rewrite Nat2Z.inj_succ in H0. rewrite Ztestbit_eq. rewrite zeq_false. - apply IHn. rewrite two_power_nat_S in H. rewrite (Zdecomp x) in H. - rewrite Zshiftin_spec in H. destruct (Z.odd x); omega. - omega. omega. omega. -Qed. - -Lemma Ztestbit_above_neg: - forall n x i, - -two_power_nat n <= x < 0 -> - i >= Z.of_nat n -> - Z.testbit x i = true. -Proof. - intros. set (y := -x-1). - assert (Z.testbit y i = false). - apply Ztestbit_above with n. - unfold y; omega. auto. - unfold y in H1. rewrite Z_one_complement in H1. - change true with (negb false). rewrite <- H1. rewrite negb_involutive; auto. - omega. -Qed. - -Lemma Zsign_bit: - forall n x, - 0 <= x < two_power_nat (S n) -> - Z.testbit x (Z.of_nat n) = if zlt x (two_power_nat n) then false else true. -Proof. - induction n; intros. - - change (two_power_nat 1) with 2 in H. - assert (x = 0 \/ x = 1) by omega. - destruct H0; subst x; reflexivity. - - rewrite Nat2Z.inj_succ. rewrite Ztestbit_eq. rewrite zeq_false. rewrite Z.pred_succ. - rewrite IHn. rewrite two_power_nat_S. - destruct (zlt (Z.div2 x) (two_power_nat n)); rewrite (Zdecomp x); rewrite Zshiftin_spec. - rewrite zlt_true. auto. destruct (Z.odd x); omega. - rewrite zlt_false. auto. destruct (Z.odd x); omega. - rewrite (Zdecomp x) in H; rewrite Zshiftin_spec in H. - rewrite two_power_nat_S in H. destruct (Z.odd x); omega. - omega. omega. -Qed. - -Lemma Zshiftin_ind: - forall (P: Z -> Prop), - P 0 -> - (forall b x, 0 <= x -> P x -> P (Zshiftin b x)) -> - forall x, 0 <= x -> P x. -Proof. - intros. destruct x. - - auto. - - induction p. - + change (P (Zshiftin true (Z.pos p))). auto. - + change (P (Zshiftin false (Z.pos p))). auto. - + change (P (Zshiftin true 0)). apply H0. omega. auto. - - compute in H1. intuition congruence. -Qed. - -Lemma Zshiftin_pos_ind: - forall (P: Z -> Prop), - P 1 -> - (forall b x, 0 < x -> P x -> P (Zshiftin b x)) -> - forall x, 0 < x -> P x. -Proof. - intros. destruct x; simpl in H1; try discriminate. - induction p. - + change (P (Zshiftin true (Z.pos p))). auto. - + change (P (Zshiftin false (Z.pos p))). auto. - + auto. -Qed. - -Lemma Ztestbit_le: - forall x y, - 0 <= y -> - (forall i, 0 <= i -> Z.testbit x i = true -> Z.testbit y i = true) -> - x <= y. -Proof. - intros x y0 POS0; revert x; pattern y0; apply Zshiftin_ind; auto; intros. - - replace x with 0. omega. apply equal_same_bits; intros. - rewrite Ztestbit_0. destruct (Z.testbit x i) as [] eqn:E; auto. - exploit H; eauto. rewrite Ztestbit_0. auto. - - assert (Z.div2 x0 <= x). - { apply H0. intros. exploit (H1 (Z.succ i)). - omega. rewrite Ztestbit_succ; auto. rewrite Ztestbit_shiftin_succ; auto. - } - rewrite (Zdecomp x0). rewrite !Zshiftin_spec. - destruct (Z.odd x0) as [] eqn:E1; destruct b as [] eqn:E2; try omega. - exploit (H1 0). omega. rewrite Ztestbit_base; auto. - rewrite Ztestbit_shiftin_base. congruence. -Qed. - -(** ** Bit-level reasoning over type [int] *) - Definition testbit (x: int) (i: Z) : bool := Z.testbit (unsigned x) i. Lemma testbit_repr: @@ -1621,6 +1144,12 @@ Proof. intros. apply Ztestbit_above with wordsize; auto. apply unsigned_range. Qed. +Lemma bits_below: + forall x i, i < 0 -> testbit x i = false. +Proof. + intros. apply Z.testbit_neg_r; auto. +Qed. + Lemma bits_zero: forall i, testbit zero i = false. Proof. @@ -1894,7 +1423,7 @@ Proof. rewrite bits_or; auto. rewrite H0; auto. Qed. -(** Properties of bitwise complement.*) +(** ** Properties of bitwise complement.*) Theorem not_involutive: forall (x: int), not (not x) = x. @@ -2013,7 +1542,7 @@ Proof. rewrite xor_idem. rewrite unsigned_one, unsigned_zero; auto. Qed. -(** Connections between [add] and bitwise logical operations. *) +(** ** Connections between [add] and bitwise logical operations. *) Lemma Z_add_is_or: forall i, 0 <= i -> @@ -2464,7 +1993,7 @@ Proof. - rewrite andb_false_r; auto. - generalize (unsigned_range n); intros. rewrite bits_mone. rewrite andb_true_r. f_equal. - symmetry. apply Zmod_small. omega. + symmetry. apply Z.mod_small. omega. omega. Qed. @@ -2491,7 +2020,7 @@ Theorem rol_zero: rol x zero = x. Proof. bit_solve. f_equal. rewrite unsigned_zero. rewrite Z.sub_0_r. - apply Zmod_small; auto. + apply Z.mod_small; auto. Qed. Lemma bitwise_binop_rol: @@ -2616,65 +2145,31 @@ Proof. rewrite !testbit_repr; auto. rewrite !Z.lor_spec. rewrite orb_comm. f_equal; apply same_bits_eqm; auto. - apply eqm_unsigned_repr_r. apply eqm_refl2. f_equal. - rewrite Zmod_small; auto. + rewrite Z.mod_small; auto. assert (unsigned (add y z) = zwordsize). rewrite H1. apply unsigned_repr_wordsize. unfold add in H5. rewrite unsigned_repr in H5. omega. generalize two_wordsize_max_unsigned; omega. - apply eqm_unsigned_repr_r. apply eqm_refl2. f_equal. - apply Zmod_small; auto. + apply Z.mod_small; auto. Qed. -(** ** Properties of [Z_one_bits] and [is_power2]. *) +(** ** Properties of [is_power2]. *) -Fixpoint powerserie (l: list Z): Z := - match l with - | nil => 0 - | x :: xs => two_p x + powerserie xs - end. - -Lemma Z_one_bits_powerserie: - forall x, 0 <= x < modulus -> x = powerserie (Z_one_bits wordsize x 0). -Proof. - assert (forall n x i, - 0 <= i -> - 0 <= x < two_power_nat n -> - x * two_p i = powerserie (Z_one_bits n x i)). - { - induction n; intros. - simpl. rewrite two_power_nat_O in H0. - assert (x = 0) by omega. subst x. omega. - rewrite two_power_nat_S in H0. simpl Z_one_bits. - rewrite (Zdecomp x) in H0. rewrite Zshiftin_spec in H0. - assert (EQ: Z.div2 x * two_p (i + 1) = powerserie (Z_one_bits n (Z.div2 x) (i + 1))). - apply IHn. omega. - destruct (Z.odd x); omega. - rewrite two_p_is_exp in EQ. change (two_p 1) with 2 in EQ. - rewrite (Zdecomp x) at 1. rewrite Zshiftin_spec. - destruct (Z.odd x); simpl powerserie; rewrite <- EQ; ring. - omega. omega. - } - intros. rewrite <- H. change (two_p 0) with 1. omega. - omega. exact H0. -Qed. - -Lemma Z_one_bits_range: - forall x i, In i (Z_one_bits wordsize x 0) -> 0 <= i < zwordsize. +Remark is_power2_inv: + forall n logn, + is_power2 n = Some logn -> + Z_is_power2 (unsigned n) = Some (unsigned logn) /\ 0 <= unsigned logn < zwordsize. Proof. - assert (forall n x i j, - In j (Z_one_bits n x i) -> i <= j < i + Z.of_nat n). - { - induction n; simpl In. - tauto. - intros x i j. rewrite Nat2Z.inj_succ. - assert (In j (Z_one_bits n (Z.div2 x) (i + 1)) -> i <= j < i + Z.succ (Z.of_nat n)). - intros. exploit IHn; eauto. omega. - destruct (Z.odd x); simpl. - intros [A|B]. subst j. omega. auto. - auto. - } - intros. generalize (H wordsize x 0 i H0). fold zwordsize; omega. + unfold is_power2; intros. + destruct (Z_is_power2 (unsigned n)) as [i|] eqn:E; inv H. + assert (0 <= i < zwordsize). + { apply Z_is_power2_range with (unsigned n). + generalize wordsize_pos; omega. + rewrite <- modulus_power. apply unsigned_range. + auto. } + rewrite unsigned_repr; auto. generalize wordsize_max_unsigned; omega. Qed. Lemma is_power2_rng: @@ -2682,16 +2177,7 @@ Lemma is_power2_rng: is_power2 n = Some logn -> 0 <= unsigned logn < zwordsize. Proof. - intros n logn. unfold is_power2. - generalize (Z_one_bits_range (unsigned n)). - destruct (Z_one_bits wordsize (unsigned n) 0). - intros; discriminate. - destruct l. - intros. injection H0; intro; subst logn; clear H0. - assert (0 <= z < zwordsize). - apply H. auto with coqlib. - rewrite unsigned_repr. auto. generalize wordsize_max_unsigned; omega. - intros; discriminate. + intros. apply (is_power2_inv n logn); auto. Qed. Theorem is_power2_range: @@ -2707,18 +2193,8 @@ Lemma is_power2_correct: is_power2 n = Some logn -> unsigned n = two_p (unsigned logn). Proof. - intros n logn. unfold is_power2. - generalize (Z_one_bits_powerserie (unsigned n) (unsigned_range n)). - generalize (Z_one_bits_range (unsigned n)). - destruct (Z_one_bits wordsize (unsigned n) 0). - intros; discriminate. - destruct l. - intros. simpl in H0. injection H1; intros; subst logn; clear H1. - rewrite unsigned_repr. replace (two_p z) with (two_p z + 0). - auto. omega. elim (H z); intros. - generalize wordsize_max_unsigned; omega. - auto with coqlib. - intros; discriminate. + intros. apply is_power2_inv in H. destruct H as [P Q]. + apply Z_is_power2_sound in P. tauto. Qed. Remark two_p_range: @@ -2733,34 +2209,12 @@ Proof. unfold max_unsigned, modulus. omega. Qed. -Remark Z_one_bits_zero: - forall n i, Z_one_bits n 0 i = nil. -Proof. - induction n; intros; simpl; auto. -Qed. - -Remark Z_one_bits_two_p: - forall n x i, - 0 <= x < Z.of_nat n -> - Z_one_bits n (two_p x) i = (i + x) :: nil. -Proof. - induction n; intros; simpl. simpl in H. omegaContradiction. - rewrite Nat2Z.inj_succ in H. - assert (x = 0 \/ 0 < x) by omega. destruct H0. - subst x; simpl. decEq. omega. apply Z_one_bits_zero. - assert (Z.odd (two_p x) = false /\ Z.div2 (two_p x) = two_p (x-1)). - apply Zshiftin_inj. rewrite <- Zdecomp. rewrite !Zshiftin_spec. - rewrite <- two_p_S. rewrite Z.add_0_r. f_equal; omega. omega. - destruct H1 as [A B]; rewrite A; rewrite B. - rewrite IHn. f_equal; omega. omega. -Qed. - Lemma is_power2_two_p: forall n, 0 <= n < zwordsize -> is_power2 (repr (two_p n)) = Some (repr n). Proof. intros. unfold is_power2. rewrite unsigned_repr. - rewrite Z_one_bits_two_p. auto. auto. + rewrite Z_is_power2_complete by omega; auto. apply two_p_range. auto. Qed. @@ -2768,19 +2222,6 @@ Qed. (** Left shifts and multiplications by powers of 2. *) -Lemma Zshiftl_mul_two_p: - forall x n, 0 <= n -> Z.shiftl x n = x * two_p n. -Proof. - intros. destruct n; simpl. - - omega. - - pattern p. apply Pos.peano_ind. - + change (two_power_pos 1) with 2. simpl. ring. - + intros. rewrite Pos.iter_succ. rewrite H0. - rewrite Pplus_one_succ_l. rewrite two_power_pos_is_exp. - change (two_power_pos 1) with 2. ring. - - compute in H. congruence. -Qed. - Lemma shl_mul_two_p: forall x y, shl x y = mul x (repr (two_p (unsigned y))). @@ -2840,21 +2281,6 @@ Qed. (** Unsigned right shifts and unsigned divisions by powers of 2. *) -Lemma Zshiftr_div_two_p: - forall x n, 0 <= n -> Z.shiftr x n = x / two_p n. -Proof. - intros. destruct n; unfold Z.shiftr; simpl. - - rewrite Zdiv_1_r. auto. - - pattern p. apply Pos.peano_ind. - + change (two_power_pos 1) with 2. simpl. apply Zdiv2_div. - + intros. rewrite Pos.iter_succ. rewrite H0. - rewrite Pplus_one_succ_l. rewrite two_power_pos_is_exp. - change (two_power_pos 1) with 2. - rewrite Zdiv2_div. rewrite Z.mul_comm. apply Zdiv_Zdiv. - rewrite two_power_pos_nat. apply two_power_nat_pos. omega. - - compute in H. congruence. -Qed. - Lemma shru_div_two_p: forall x y, shru x y = repr (unsigned x / two_p (unsigned y)). @@ -2897,43 +2323,6 @@ Qed. (** Unsigned modulus over [2^n] is masking with [2^n-1]. *) -Lemma Ztestbit_mod_two_p: - forall n x i, - 0 <= n -> 0 <= i -> - Z.testbit (x mod (two_p n)) i = if zlt i n then Z.testbit x i else false. -Proof. - intros n0 x i N0POS. revert x i; pattern n0; apply natlike_ind; auto. - - intros. change (two_p 0) with 1. rewrite Zmod_1_r. rewrite Z.testbit_0_l. - rewrite zlt_false; auto. omega. - - intros. rewrite two_p_S; auto. - replace (x0 mod (2 * two_p x)) - with (Zshiftin (Z.odd x0) (Z.div2 x0 mod two_p x)). - rewrite Ztestbit_shiftin; auto. rewrite (Ztestbit_eq i x0); auto. destruct (zeq i 0). - + rewrite zlt_true; auto. omega. - + rewrite H0. destruct (zlt (Z.pred i) x). - * rewrite zlt_true; auto. omega. - * rewrite zlt_false; auto. omega. - * omega. - + rewrite (Zdecomp x0) at 3. set (x1 := Z.div2 x0). symmetry. - apply Zmod_unique with (x1 / two_p x). - rewrite !Zshiftin_spec. rewrite Z.add_assoc. f_equal. - transitivity (2 * (two_p x * (x1 / two_p x) + x1 mod two_p x)). - f_equal. apply Z_div_mod_eq. apply two_p_gt_ZERO; auto. - ring. - rewrite Zshiftin_spec. exploit (Z_mod_lt x1 (two_p x)). apply two_p_gt_ZERO; auto. - destruct (Z.odd x0); omega. -Qed. - -Corollary Ztestbit_two_p_m1: - forall n i, 0 <= n -> 0 <= i -> - Z.testbit (two_p n - 1) i = if zlt i n then true else false. -Proof. - intros. replace (two_p n - 1) with ((-1) mod (two_p n)). - rewrite Ztestbit_mod_two_p; auto. destruct (zlt i n); auto. apply Ztestbit_m1; auto. - apply Zmod_unique with (-1). ring. - exploit (two_p_gt_ZERO n). auto. omega. -Qed. - Theorem modu_and: forall x n logn, is_power2 n = Some logn -> @@ -2955,21 +2344,6 @@ Qed. (** ** Properties of [shrx] (signed division by a power of 2) *) -Lemma Zquot_Zdiv: - forall x y, - y > 0 -> - Z.quot x y = if zlt x 0 then (x + y - 1) / y else x / y. -Proof. - intros. destruct (zlt x 0). - - symmetry. apply Zquot_unique_full with ((x + y - 1) mod y - (y - 1)). - + red. right; split. omega. - exploit (Z_mod_lt (x + y - 1) y); auto. - rewrite Z.abs_eq. omega. omega. - + transitivity ((y * ((x + y - 1) / y) + (x + y - 1) mod y) - (y-1)). - rewrite <- Z_div_mod_eq. ring. auto. ring. - - apply Zquot_Zdiv_pos; omega. -Qed. - Theorem shrx_zero: forall x, zwordsize > 1 -> shrx x zero = x. Proof. @@ -3048,17 +2422,6 @@ Proof. bit_solve. destruct (zlt (i + unsigned (sub iwordsize y)) zwordsize); auto. Qed. -Lemma Zdiv_shift: - forall x y, y > 0 -> - (x + (y - 1)) / y = x / y + if zeq (Z.modulo x y) 0 then 0 else 1. -Proof. - intros. generalize (Z_div_mod_eq x y H). generalize (Z_mod_lt x y H). - set (q := x / y). set (r := x mod y). intros. - destruct (zeq r 0). - apply Zdiv_unique with (y - 1). rewrite H1. rewrite e. ring. omega. - apply Zdiv_unique with (r - 1). rewrite H1. ring. omega. -Qed. - Theorem shrx_carry: forall x y, ltu y (repr (zwordsize - 1)) = true -> @@ -3149,51 +2512,6 @@ Qed. (** ** Properties of integer zero extension and sign extension. *) -Lemma Ziter_base: - forall (A: Type) n (f: A -> A) x, n <= 0 -> Z.iter n f x = x. -Proof. - intros. unfold Z.iter. destruct n; auto. compute in H. elim H; auto. -Qed. - -Lemma Ziter_succ: - forall (A: Type) n (f: A -> A) x, - 0 <= n -> Z.iter (Z.succ n) f x = f (Z.iter n f x). -Proof. - intros. destruct n; simpl. - - auto. - - rewrite Pos.add_1_r. apply Pos.iter_succ. - - compute in H. elim H; auto. -Qed. - -Lemma Znatlike_ind: - forall (P: Z -> Prop), - (forall n, n <= 0 -> P n) -> - (forall n, 0 <= n -> P n -> P (Z.succ n)) -> - forall n, P n. -Proof. - intros. destruct (zle 0 n). - apply natlike_ind; auto. apply H; omega. - apply H. omega. -Qed. - -Lemma Zzero_ext_spec: - forall n x i, 0 <= i -> - Z.testbit (Zzero_ext n x) i = if zlt i n then Z.testbit x i else false. -Proof. - unfold Zzero_ext. induction n using Znatlike_ind. - - intros. rewrite Ziter_base; auto. - rewrite zlt_false. rewrite Ztestbit_0; auto. omega. - - intros. rewrite Ziter_succ; auto. - rewrite Ztestbit_shiftin; auto. - rewrite (Ztestbit_eq i x); auto. - destruct (zeq i 0). - + subst i. rewrite zlt_true; auto. omega. - + rewrite IHn. destruct (zlt (Z.pred i) n). - rewrite zlt_true; auto. omega. - rewrite zlt_false; auto. omega. - omega. -Qed. - Lemma bits_zero_ext: forall n x i, 0 <= i -> testbit (zero_ext n x) i = if zlt i n then testbit x i else false. @@ -3203,42 +2521,12 @@ Proof. rewrite !bits_above; auto. destruct (zlt i n); auto. Qed. -Lemma Zsign_ext_spec: - forall n x i, 0 <= i -> 0 < n -> - Z.testbit (Zsign_ext n x) i = Z.testbit x (if zlt i n then i else n - 1). -Proof. - intros n0 x i I0 N0. - revert x i I0. pattern n0. apply Zlt_lower_bound_ind with (z := 1). - - unfold Zsign_ext. intros. - destruct (zeq x 1). - + subst x; simpl. - replace (if zlt i 1 then i else 0) with 0. - rewrite Ztestbit_base. - destruct (Z.odd x0). - apply Ztestbit_m1; auto. - apply Ztestbit_0. - destruct (zlt i 1); omega. - + set (x1 := Z.pred x). replace x1 with (Z.succ (Z.pred x1)). - rewrite Ziter_succ. rewrite Ztestbit_shiftin. - destruct (zeq i 0). - * subst i. rewrite zlt_true. rewrite Ztestbit_base; auto. omega. - * rewrite H. unfold x1. destruct (zlt (Z.pred i) (Z.pred x)). - rewrite zlt_true. rewrite (Ztestbit_eq i x0); auto. rewrite zeq_false; auto. omega. - rewrite zlt_false. rewrite (Ztestbit_eq (x - 1) x0). rewrite zeq_false; auto. - omega. omega. omega. unfold x1; omega. omega. - * omega. - * unfold x1; omega. - * omega. - - omega. -Qed. - Lemma bits_sign_ext: - forall n x i, 0 <= i < zwordsize -> 0 < n -> + forall n x i, 0 <= i < zwordsize -> testbit (sign_ext n x) i = testbit x (if zlt i n then i else n - 1). Proof. intros. unfold sign_ext. - rewrite testbit_repr; auto. rewrite Zsign_ext_spec. destruct (zlt i n); auto. - omega. auto. + rewrite testbit_repr; auto. apply Zsign_ext_spec. omega. Qed. Hint Rewrite bits_zero_ext bits_sign_ext: ints. @@ -3250,12 +2538,24 @@ Proof. rewrite bits_zero_ext. apply zlt_true. omega. omega. Qed. +Theorem zero_ext_below: + forall n x, n <= 0 -> zero_ext n x = zero. +Proof. + intros. bit_solve. destruct (zlt i n); auto. apply bits_below; omega. omega. +Qed. + Theorem sign_ext_above: forall n x, n >= zwordsize -> sign_ext n x = x. Proof. intros. apply same_bits_eq; intros. unfold sign_ext; rewrite testbit_repr; auto. - rewrite Zsign_ext_spec. rewrite zlt_true. auto. omega. omega. omega. + rewrite Zsign_ext_spec. rewrite zlt_true. auto. omega. omega. +Qed. + +Theorem sign_ext_below: + forall n x, n <= 0 -> sign_ext n x = zero. +Proof. + intros. bit_solve. apply bits_below. destruct (zlt i n); omega. Qed. Theorem zero_ext_and: @@ -3292,7 +2592,7 @@ Proof. Qed. Theorem sign_ext_widen: - forall x n n', 0 < n <= n' -> + forall x n n', 0 < n <= n' -> sign_ext n' (sign_ext n x) = sign_ext n x. Proof. intros. destruct (zlt n' zwordsize). @@ -3300,9 +2600,8 @@ Proof. auto. rewrite (zlt_false _ i n). destruct (zlt (n' - 1) n); f_equal; omega. - omega. omega. + omega. destruct (zlt i n'); omega. - omega. omega. apply sign_ext_above; auto. Qed. @@ -3316,7 +2615,6 @@ Proof. auto. rewrite !zlt_false. auto. omega. omega. omega. destruct (zlt i n'); omega. - omega. apply sign_ext_above; auto. Qed. @@ -3336,9 +2634,7 @@ Theorem sign_ext_narrow: Proof. intros. destruct (zlt n zwordsize). bit_solve. destruct (zlt i n); f_equal; apply zlt_true; omega. - omega. destruct (zlt i n); omega. - omega. omega. rewrite (sign_ext_above n'). auto. omega. Qed. @@ -3350,7 +2646,7 @@ Proof. bit_solve. destruct (zlt i n); auto. rewrite zlt_true; auto. omega. - omega. omega. omega. + omega. omega. rewrite sign_ext_above; auto. Qed. @@ -3365,7 +2661,7 @@ Theorem sign_ext_idem: Proof. intros. apply sign_ext_widen. omega. Qed. - + Theorem sign_ext_zero_ext: forall n x, 0 < n -> sign_ext n (zero_ext n x) = sign_ext n x. Proof. @@ -3393,42 +2689,93 @@ Proof. rewrite <- (sign_ext_zero_ext n y H). congruence. Qed. -Theorem zero_ext_shru_shl: +Theorem shru_shl: + forall x y z, ltu y iwordsize = true -> ltu z iwordsize = true -> + shru (shl x y) z = + if ltu z y then shl (zero_ext (zwordsize - unsigned y) x) (sub y z) + else zero_ext (zwordsize - unsigned z) (shru x (sub z y)). +Proof. + intros. apply ltu_iwordsize_inv in H; apply ltu_iwordsize_inv in H0. + unfold ltu. set (Y := unsigned y) in *; set (Z := unsigned z) in *. + apply same_bits_eq; intros. rewrite bits_shru by auto. fold Z. + destruct (zlt Z Y). +- assert (A: unsigned (sub y z) = Y - Z). + { apply unsigned_repr. generalize wordsize_max_unsigned; omega. } + symmetry; rewrite bits_shl, A by omega. + destruct (zlt (i + Z) zwordsize). ++ rewrite bits_shl by omega. fold Y. + destruct (zlt i (Y - Z)); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. + rewrite bits_zero_ext by omega. rewrite zlt_true by omega. f_equal; omega. ++ rewrite bits_zero_ext by omega. rewrite ! zlt_false by omega. auto. +- assert (A: unsigned (sub z y) = Z - Y). + { apply unsigned_repr. generalize wordsize_max_unsigned; omega. } + rewrite bits_zero_ext, bits_shru, A by omega. + destruct (zlt (i + Z) zwordsize); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. + rewrite bits_shl by omega. fold Y. + destruct (zlt (i + Z) Y). ++ rewrite zlt_false by omega. auto. ++ rewrite zlt_true by omega. f_equal; omega. +Qed. + +Corollary zero_ext_shru_shl: forall n x, 0 < n < zwordsize -> let y := repr (zwordsize - n) in zero_ext n x = shru (shl x y) y. Proof. intros. - assert (unsigned y = zwordsize - n). - unfold y. apply unsigned_repr. generalize wordsize_max_unsigned. omega. - apply same_bits_eq; intros. - rewrite bits_zero_ext. - rewrite bits_shru; auto. - destruct (zlt i n). - rewrite zlt_true. rewrite bits_shl. rewrite zlt_false. f_equal. omega. - omega. omega. omega. - rewrite zlt_false. auto. omega. - omega. -Qed. - -Theorem sign_ext_shr_shl: + assert (A: unsigned y = zwordsize - n). + { unfold y. apply unsigned_repr. generalize wordsize_max_unsigned. omega. } + assert (B: ltu y iwordsize = true). + { unfold ltu; rewrite A, unsigned_repr_wordsize. apply zlt_true; omega. } + rewrite shru_shl by auto. unfold ltu; rewrite zlt_false by omega. + rewrite sub_idem, shru_zero. f_equal. rewrite A; omega. +Qed. + +Theorem shr_shl: + forall x y z, ltu y iwordsize = true -> ltu z iwordsize = true -> + shr (shl x y) z = + if ltu z y then shl (sign_ext (zwordsize - unsigned y) x) (sub y z) + else sign_ext (zwordsize - unsigned z) (shr x (sub z y)). +Proof. + intros. apply ltu_iwordsize_inv in H; apply ltu_iwordsize_inv in H0. + unfold ltu. set (Y := unsigned y) in *; set (Z := unsigned z) in *. + apply same_bits_eq; intros. rewrite bits_shr by auto. fold Z. + rewrite bits_shl by (destruct (zlt (i + Z) zwordsize); omega). fold Y. + destruct (zlt Z Y). +- assert (A: unsigned (sub y z) = Y - Z). + { apply unsigned_repr. generalize wordsize_max_unsigned; omega. } + rewrite bits_shl, A by omega. + destruct (zlt i (Y - Z)). ++ apply zlt_true. destruct (zlt (i + Z) zwordsize); omega. ++ rewrite zlt_false by (destruct (zlt (i + Z) zwordsize); omega). + rewrite bits_sign_ext by omega. f_equal. + destruct (zlt (i + Z) zwordsize). + rewrite zlt_true by omega. omega. + rewrite zlt_false by omega. omega. +- assert (A: unsigned (sub z y) = Z - Y). + { apply unsigned_repr. generalize wordsize_max_unsigned; omega. } + rewrite bits_sign_ext by omega. + rewrite bits_shr by (destruct (zlt i (zwordsize - Z)); omega). + rewrite A. rewrite zlt_false by (destruct (zlt (i + Z) zwordsize); omega). + f_equal. destruct (zlt i (zwordsize - Z)). ++ rewrite ! zlt_true by omega. omega. ++ rewrite ! zlt_false by omega. rewrite zlt_true by omega. omega. +Qed. + +Corollary sign_ext_shr_shl: forall n x, 0 < n < zwordsize -> let y := repr (zwordsize - n) in sign_ext n x = shr (shl x y) y. Proof. intros. - assert (unsigned y = zwordsize - n). - unfold y. apply unsigned_repr. generalize wordsize_max_unsigned. omega. - apply same_bits_eq; intros. - rewrite bits_sign_ext. - rewrite bits_shr; auto. - destruct (zlt i n). - rewrite zlt_true. rewrite bits_shl. rewrite zlt_false. f_equal. omega. - omega. omega. omega. - rewrite zlt_false. rewrite bits_shl. rewrite zlt_false. f_equal. omega. - omega. omega. omega. omega. omega. + assert (A: unsigned y = zwordsize - n). + { unfold y. apply unsigned_repr. generalize wordsize_max_unsigned. omega. } + assert (B: ltu y iwordsize = true). + { unfold ltu; rewrite A, unsigned_repr_wordsize. apply zlt_true; omega. } + rewrite shr_shl by auto. unfold ltu; rewrite zlt_false by omega. + rewrite sub_idem, shr_zero. f_equal. rewrite A; omega. Qed. (** [zero_ext n x] is the unique integer congruent to [x] modulo [2^n] @@ -3488,7 +2835,7 @@ Proof. apply eqmod_same_bits; intros. rewrite H0 in H1. rewrite H0. fold (testbit (sign_ext n x) i). rewrite bits_sign_ext. - rewrite zlt_true. auto. omega. omega. omega. + rewrite zlt_true. auto. omega. omega. Qed. Lemma eqmod_sign_ext: @@ -3503,6 +2850,132 @@ Proof. apply eqmod_sign_ext'; auto. Qed. +(** Combinations of shifts and zero/sign extensions *) + +Lemma shl_zero_ext: + forall n m x, 0 <= n -> + shl (zero_ext n x) m = zero_ext (n + unsigned m) (shl x m). +Proof. + intros. apply same_bits_eq; intros. + rewrite bits_zero_ext, ! bits_shl by omega. + destruct (zlt i (unsigned m)). +- rewrite zlt_true by omega; auto. +- rewrite bits_zero_ext by omega. + destruct (zlt (i - unsigned m) n); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. +Qed. + +Lemma shl_sign_ext: + forall n m x, 0 < n -> + shl (sign_ext n x) m = sign_ext (n + unsigned m) (shl x m). +Proof. + intros. generalize (unsigned_range m); intros. + apply same_bits_eq; intros. + rewrite bits_sign_ext, ! bits_shl by omega. + destruct (zlt i (n + unsigned m)). +- rewrite bits_shl by auto. destruct (zlt i (unsigned m)); auto. + rewrite bits_sign_ext by omega. f_equal. apply zlt_true. omega. +- rewrite zlt_false by omega. rewrite bits_shl by omega. rewrite zlt_false by omega. + rewrite bits_sign_ext by omega. f_equal. rewrite zlt_false by omega. omega. +Qed. + +Lemma shru_zero_ext: + forall n m x, 0 <= n -> + shru (zero_ext (n + unsigned m) x) m = zero_ext n (shru x m). +Proof. + intros. bit_solve. +- destruct (zlt (i + unsigned m) zwordsize). +* destruct (zlt i n); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. +* destruct (zlt i n); auto. +- generalize (unsigned_range m); omega. +- omega. +Qed. + +Lemma shru_zero_ext_0: + forall n m x, n <= unsigned m -> + shru (zero_ext n x) m = zero. +Proof. + intros. bit_solve. +- destruct (zlt (i + unsigned m) zwordsize); auto. + apply zlt_false. omega. +- generalize (unsigned_range m); omega. +Qed. + +Lemma shr_sign_ext: + forall n m x, 0 < n -> n + unsigned m < zwordsize -> + shr (sign_ext (n + unsigned m) x) m = sign_ext n (shr x m). +Proof. + intros. generalize (unsigned_range m); intros. + apply same_bits_eq; intros. + rewrite bits_sign_ext, bits_shr by auto. + rewrite bits_sign_ext, bits_shr. +- f_equal. + destruct (zlt i n), (zlt (i + unsigned m) zwordsize). ++ apply zlt_true; omega. ++ apply zlt_true; omega. ++ rewrite zlt_false by omega. rewrite zlt_true by omega. omega. ++ rewrite zlt_false by omega. rewrite zlt_true by omega. omega. +- destruct (zlt i n); omega. +- destruct (zlt (i + unsigned m) zwordsize); omega. +Qed. + +Lemma zero_ext_shru_min: + forall s x n, ltu n iwordsize = true -> + zero_ext s (shru x n) = zero_ext (Z.min s (zwordsize - unsigned n)) (shru x n). +Proof. + intros. apply ltu_iwordsize_inv in H. + apply Z.min_case_strong; intros; auto. + bit_solve; try omega. + destruct (zlt i (zwordsize - unsigned n)). + rewrite zlt_true by omega. auto. + destruct (zlt i s); auto. rewrite zlt_false by omega; auto. +Qed. + +Lemma sign_ext_shr_min: + forall s x n, ltu n iwordsize = true -> + sign_ext s (shr x n) = sign_ext (Z.min s (zwordsize - unsigned n)) (shr x n). +Proof. + intros. apply ltu_iwordsize_inv in H. + rewrite Z.min_comm. + destruct (Z.min_spec (zwordsize - unsigned n) s) as [[A B] | [A B]]; rewrite B; auto. + apply same_bits_eq; intros. rewrite ! bits_sign_ext by auto. + destruct (zlt i (zwordsize - unsigned n)). + rewrite zlt_true by omega. auto. + assert (C: testbit (shr x n) (zwordsize - unsigned n - 1) = testbit x (zwordsize - 1)). + { rewrite bits_shr by omega. rewrite zlt_true by omega. f_equal; omega. } + rewrite C. destruct (zlt i s); rewrite bits_shr by omega. + rewrite zlt_false by omega. auto. + rewrite zlt_false by omega. auto. +Qed. + +Lemma shl_zero_ext_min: + forall s x n, ltu n iwordsize = true -> + shl (zero_ext s x) n = shl (zero_ext (Z.min s (zwordsize - unsigned n)) x) n. +Proof. + intros. apply ltu_iwordsize_inv in H. + apply Z.min_case_strong; intros; auto. + apply same_bits_eq; intros. rewrite ! bits_shl by auto. + destruct (zlt i (unsigned n)); auto. + rewrite ! bits_zero_ext by omega. + destruct (zlt (i - unsigned n) s). + rewrite zlt_true by omega; auto. + rewrite zlt_false by omega; auto. +Qed. + +Lemma shl_sign_ext_min: + forall s x n, ltu n iwordsize = true -> + shl (sign_ext s x) n = shl (sign_ext (Z.min s (zwordsize - unsigned n)) x) n. +Proof. + intros. apply ltu_iwordsize_inv in H. + rewrite Z.min_comm. + destruct (Z.min_spec (zwordsize - unsigned n) s) as [[A B] | [A B]]; rewrite B; auto. + apply same_bits_eq; intros. rewrite ! bits_shl by auto. + destruct (zlt i (unsigned n)); auto. + rewrite ! bits_sign_ext by omega. f_equal. + destruct (zlt (i - unsigned n) s). + rewrite zlt_true by omega; auto. + omegaContradiction. +Qed. + (** ** Properties of [one_bits] (decomposition in sum of powers of two) *) Theorem one_bits_range: @@ -3533,7 +3006,7 @@ Proof. auto with ints. decEq. apply Z_one_bits_powerserie. auto with ints. unfold one_bits. - generalize (Z_one_bits_range (unsigned x)). + generalize (Z_one_bits_range wordsize (unsigned x)). generalize (Z_one_bits wordsize (unsigned x) 0). induction l. intros; reflexivity. @@ -3541,7 +3014,8 @@ Proof. apply eqm_add. rewrite shl_mul_two_p. rewrite mul_commut. rewrite mul_one. apply eqm_unsigned_repr_r. rewrite unsigned_repr. auto with ints. - generalize (H a (in_eq _ _)). generalize wordsize_max_unsigned. omega. + generalize (H a (in_eq _ _)). change (Z.of_nat wordsize) with zwordsize. + generalize wordsize_max_unsigned. omega. auto with ints. intros; apply H; auto with coqlib. Qed. @@ -3741,8 +3215,7 @@ Proof. intros. rewrite <- negb_orb. rewrite <- not_ltu. rewrite negb_involutive. auto. Qed. - -(** Non-overlapping test *) +(** ** Non-overlapping test *) Definition no_overlap (ofs1: int) (sz1: Z) (ofs2: int) (sz2: Z) : bool := let x1 := unsigned ofs1 in let x2 := unsigned ofs2 in @@ -3768,94 +3241,10 @@ Proof. intros [C|C] [D|D]; omega. Qed. -(** Size of integers, in bits. *) - -Definition Zsize (x: Z) : Z := - match x with - | Zpos p => Zpos (Pos.size p) - | _ => 0 - end. +(** ** Size of integers, in bits. *) Definition size (x: int) : Z := Zsize (unsigned x). -Remark Zsize_pos: forall x, 0 <= Zsize x. -Proof. - destruct x; simpl. omega. compute; intuition congruence. omega. -Qed. - -Remark Zsize_pos': forall x, 0 < x -> 0 < Zsize x. -Proof. - destruct x; simpl; intros; try discriminate. compute; auto. -Qed. - -Lemma Zsize_shiftin: - forall b x, 0 < x -> Zsize (Zshiftin b x) = Z.succ (Zsize x). -Proof. - intros. destruct x; compute in H; try discriminate. - destruct b. - change (Zshiftin true (Zpos p)) with (Zpos (p~1)). - simpl. f_equal. rewrite Pos.add_1_r; auto. - change (Zshiftin false (Zpos p)) with (Zpos (p~0)). - simpl. f_equal. rewrite Pos.add_1_r; auto. -Qed. - -Lemma Ztestbit_size_1: - forall x, 0 < x -> Z.testbit x (Z.pred (Zsize x)) = true. -Proof. - intros x0 POS0; pattern x0; apply Zshiftin_pos_ind; auto. - intros. rewrite Zsize_shiftin; auto. - replace (Z.pred (Z.succ (Zsize x))) with (Z.succ (Z.pred (Zsize x))) by omega. - rewrite Ztestbit_shiftin_succ. auto. generalize (Zsize_pos' x H); omega. -Qed. - -Lemma Ztestbit_size_2: - forall x, 0 <= x -> forall i, i >= Zsize x -> Z.testbit x i = false. -Proof. - intros x0 POS0. destruct (zeq x0 0). - - subst x0; intros. apply Ztestbit_0. - - pattern x0; apply Zshiftin_pos_ind. - + simpl. intros. change 1 with (Zshiftin true 0). rewrite Ztestbit_shiftin. - rewrite zeq_false. apply Ztestbit_0. omega. omega. - + intros. rewrite Zsize_shiftin in H1; auto. - generalize (Zsize_pos' _ H); intros. - rewrite Ztestbit_shiftin. rewrite zeq_false. apply H0. omega. - omega. omega. - + omega. -Qed. - -Lemma Zsize_interval_1: - forall x, 0 <= x -> 0 <= x < two_p (Zsize x). -Proof. - intros. - assert (x = x mod (two_p (Zsize x))). - apply equal_same_bits; intros. - rewrite Ztestbit_mod_two_p; auto. - destruct (zlt i (Zsize x)). auto. apply Ztestbit_size_2; auto. - apply Zsize_pos; auto. - rewrite H0 at 1. rewrite H0 at 3. apply Z_mod_lt. apply two_p_gt_ZERO. apply Zsize_pos; auto. -Qed. - -Lemma Zsize_interval_2: - forall x n, 0 <= n -> 0 <= x < two_p n -> n >= Zsize x. -Proof. - intros. set (N := Z.to_nat n). - assert (Z.of_nat N = n) by (apply Z2Nat.id; auto). - rewrite <- H1 in H0. rewrite <- two_power_nat_two_p in H0. - destruct (zeq x 0). - subst x; simpl; omega. - destruct (zlt n (Zsize x)); auto. - exploit (Ztestbit_above N x (Z.pred (Zsize x))). auto. omega. - rewrite Ztestbit_size_1. congruence. omega. -Qed. - -Lemma Zsize_monotone: - forall x y, 0 <= x <= y -> Zsize x <= Zsize y. -Proof. - intros. apply Z.ge_le. apply Zsize_interval_2. apply Zsize_pos. - exploit (Zsize_interval_1 y). omega. - omega. -Qed. - Theorem size_zero: size zero = 0. Proof. unfold size; rewrite unsigned_zero; auto. @@ -3933,10 +3322,11 @@ Proof. assert (0 <= Z.min (size a) (size b)). generalize (size_range a) (size_range b). zify; omega. apply bits_size_3. auto. intros. - rewrite bits_and. zify. subst z z0. destruct H1. - rewrite (bits_size_2 a). auto. omega. - rewrite (bits_size_2 b). apply andb_false_r. omega. - omega. + rewrite bits_and by omega. + rewrite andb_false_iff. + generalize (bits_size_2 a i). + generalize (bits_size_2 b i). + zify; intuition. Qed. Corollary and_interval: @@ -4305,6 +3695,190 @@ Proof. unfold shr, shr'; rewrite <- A; auto. Qed. +Theorem shru'_shl': + forall x y z, Int.ltu y iwordsize' = true -> Int.ltu z iwordsize' = true -> + shru' (shl' x y) z = + if Int.ltu z y then shl' (zero_ext (zwordsize - Int.unsigned y) x) (Int.sub y z) + else zero_ext (zwordsize - Int.unsigned z) (shru' x (Int.sub z y)). +Proof. + intros. apply Int.ltu_inv in H; apply Int.ltu_inv in H0. + change (Int.unsigned iwordsize') with zwordsize in *. + unfold Int.ltu. set (Y := Int.unsigned y) in *; set (Z := Int.unsigned z) in *. + apply same_bits_eq; intros. rewrite bits_shru' by auto. fold Z. + destruct (zlt Z Y). +- assert (A: Int.unsigned (Int.sub y z) = Y - Z). + { apply Int.unsigned_repr. assert (zwordsize < Int.max_unsigned) by reflexivity. omega. } + symmetry; rewrite bits_shl', A by omega. + destruct (zlt (i + Z) zwordsize). ++ rewrite bits_shl' by omega. fold Y. + destruct (zlt i (Y - Z)); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. + rewrite bits_zero_ext by omega. rewrite zlt_true by omega. f_equal; omega. ++ rewrite bits_zero_ext by omega. rewrite ! zlt_false by omega. auto. +- assert (A: Int.unsigned (Int.sub z y) = Z - Y). + { apply Int.unsigned_repr. assert (zwordsize < Int.max_unsigned) by reflexivity. omega. } + rewrite bits_zero_ext, bits_shru', A by omega. + destruct (zlt (i + Z) zwordsize); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. + rewrite bits_shl' by omega. fold Y. + destruct (zlt (i + Z) Y). ++ rewrite zlt_false by omega. auto. ++ rewrite zlt_true by omega. f_equal; omega. +Qed. + +Theorem shr'_shl': + forall x y z, Int.ltu y iwordsize' = true -> Int.ltu z iwordsize' = true -> + shr' (shl' x y) z = + if Int.ltu z y then shl' (sign_ext (zwordsize - Int.unsigned y) x) (Int.sub y z) + else sign_ext (zwordsize - Int.unsigned z) (shr' x (Int.sub z y)). +Proof. + intros. apply Int.ltu_inv in H; apply Int.ltu_inv in H0. + change (Int.unsigned iwordsize') with zwordsize in *. + unfold Int.ltu. set (Y := Int.unsigned y) in *; set (Z := Int.unsigned z) in *. + apply same_bits_eq; intros. rewrite bits_shr' by auto. fold Z. + rewrite bits_shl' by (destruct (zlt (i + Z) zwordsize); omega). fold Y. + destruct (zlt Z Y). +- assert (A: Int.unsigned (Int.sub y z) = Y - Z). + { apply Int.unsigned_repr. assert (zwordsize < Int.max_unsigned) by reflexivity. omega. } + rewrite bits_shl', A by omega. + destruct (zlt i (Y - Z)). ++ apply zlt_true. destruct (zlt (i + Z) zwordsize); omega. ++ rewrite zlt_false by (destruct (zlt (i + Z) zwordsize); omega). + rewrite bits_sign_ext by omega. f_equal. + destruct (zlt (i + Z) zwordsize). + rewrite zlt_true by omega. omega. + rewrite zlt_false by omega. omega. +- assert (A: Int.unsigned (Int.sub z y) = Z - Y). + { apply Int.unsigned_repr. assert (zwordsize < Int.max_unsigned) by reflexivity. omega. } + rewrite bits_sign_ext by omega. + rewrite bits_shr' by (destruct (zlt i (zwordsize - Z)); omega). + rewrite A. rewrite zlt_false by (destruct (zlt (i + Z) zwordsize); omega). + f_equal. destruct (zlt i (zwordsize - Z)). ++ rewrite ! zlt_true by omega. omega. ++ rewrite ! zlt_false by omega. rewrite zlt_true by omega. omega. +Qed. + +Lemma shl'_zero_ext: + forall n m x, 0 <= n -> + shl' (zero_ext n x) m = zero_ext (n + Int.unsigned m) (shl' x m). +Proof. + intros. apply same_bits_eq; intros. + rewrite bits_zero_ext, ! bits_shl' by omega. + destruct (zlt i (Int.unsigned m)). +- rewrite zlt_true by omega; auto. +- rewrite bits_zero_ext by omega. + destruct (zlt (i - Int.unsigned m) n); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. +Qed. + +Lemma shl'_sign_ext: + forall n m x, 0 < n -> + shl' (sign_ext n x) m = sign_ext (n + Int.unsigned m) (shl' x m). +Proof. + intros. generalize (Int.unsigned_range m); intros. + apply same_bits_eq; intros. + rewrite bits_sign_ext, ! bits_shl' by omega. + destruct (zlt i (n + Int.unsigned m)). +- rewrite bits_shl' by auto. destruct (zlt i (Int.unsigned m)); auto. + rewrite bits_sign_ext by omega. f_equal. apply zlt_true. omega. +- rewrite zlt_false by omega. rewrite bits_shl' by omega. rewrite zlt_false by omega. + rewrite bits_sign_ext by omega. f_equal. rewrite zlt_false by omega. omega. +Qed. + +Lemma shru'_zero_ext: + forall n m x, 0 <= n -> + shru' (zero_ext (n + Int.unsigned m) x) m = zero_ext n (shru' x m). +Proof. + intros. generalize (Int.unsigned_range m); intros. + bit_solve; [|omega]. rewrite bits_shru', bits_zero_ext, bits_shru' by omega. + destruct (zlt (i + Int.unsigned m) zwordsize). +* destruct (zlt i n); [rewrite zlt_true by omega|rewrite zlt_false by omega]; auto. +* destruct (zlt i n); auto. +Qed. + +Lemma shru'_zero_ext_0: + forall n m x, n <= Int.unsigned m -> + shru' (zero_ext n x) m = zero. +Proof. + intros. generalize (Int.unsigned_range m); intros. + bit_solve. rewrite bits_shru', bits_zero_ext by omega. + destruct (zlt (i + Int.unsigned m) zwordsize); auto. + apply zlt_false. omega. +Qed. + +Lemma shr'_sign_ext: + forall n m x, 0 < n -> n + Int.unsigned m < zwordsize -> + shr' (sign_ext (n + Int.unsigned m) x) m = sign_ext n (shr' x m). +Proof. + intros. generalize (Int.unsigned_range m); intros. + apply same_bits_eq; intros. + rewrite bits_sign_ext, bits_shr' by auto. + rewrite bits_sign_ext, bits_shr'. +- f_equal. + destruct (zlt i n), (zlt (i + Int.unsigned m) zwordsize). ++ apply zlt_true; omega. ++ apply zlt_true; omega. ++ rewrite zlt_false by omega. rewrite zlt_true by omega. omega. ++ rewrite zlt_false by omega. rewrite zlt_true by omega. omega. +- destruct (zlt i n); omega. +- destruct (zlt (i + Int.unsigned m) zwordsize); omega. +Qed. + +Lemma zero_ext_shru'_min: + forall s x n, Int.ltu n iwordsize' = true -> + zero_ext s (shru' x n) = zero_ext (Z.min s (zwordsize - Int.unsigned n)) (shru' x n). +Proof. + intros. apply Int.ltu_inv in H. change (Int.unsigned iwordsize') with zwordsize in H. + apply Z.min_case_strong; intros; auto. + bit_solve; try omega. rewrite ! bits_shru' by omega. + destruct (zlt i (zwordsize - Int.unsigned n)). + rewrite zlt_true by omega. auto. + destruct (zlt i s); auto. rewrite zlt_false by omega; auto. +Qed. + +Lemma sign_ext_shr'_min: + forall s x n, Int.ltu n iwordsize' = true -> + sign_ext s (shr' x n) = sign_ext (Z.min s (zwordsize - Int.unsigned n)) (shr' x n). +Proof. + intros. apply Int.ltu_inv in H. change (Int.unsigned iwordsize') with zwordsize in H. + rewrite Z.min_comm. + destruct (Z.min_spec (zwordsize - Int.unsigned n) s) as [[A B] | [A B]]; rewrite B; auto. + apply same_bits_eq; intros. rewrite ! bits_sign_ext by auto. + destruct (zlt i (zwordsize - Int.unsigned n)). + rewrite zlt_true by omega. auto. + assert (C: testbit (shr' x n) (zwordsize - Int.unsigned n - 1) = testbit x (zwordsize - 1)). + { rewrite bits_shr' by omega. rewrite zlt_true by omega. f_equal; omega. } + rewrite C. destruct (zlt i s); rewrite bits_shr' by omega. + rewrite zlt_false by omega. auto. + rewrite zlt_false by omega. auto. +Qed. + +Lemma shl'_zero_ext_min: + forall s x n, Int.ltu n iwordsize' = true -> + shl' (zero_ext s x) n = shl' (zero_ext (Z.min s (zwordsize - Int.unsigned n)) x) n. +Proof. + intros. apply Int.ltu_inv in H. change (Int.unsigned iwordsize') with zwordsize in H. + apply Z.min_case_strong; intros; auto. + apply same_bits_eq; intros. rewrite ! bits_shl' by auto. + destruct (zlt i (Int.unsigned n)); auto. + rewrite ! bits_zero_ext by omega. + destruct (zlt (i - Int.unsigned n) s). + rewrite zlt_true by omega; auto. + rewrite zlt_false by omega; auto. +Qed. + +Lemma shl'_sign_ext_min: + forall s x n, Int.ltu n iwordsize' = true -> + shl' (sign_ext s x) n = shl' (sign_ext (Z.min s (zwordsize - Int.unsigned n)) x) n. +Proof. + intros. apply Int.ltu_inv in H. change (Int.unsigned iwordsize') with zwordsize in H. + rewrite Z.min_comm. + destruct (Z.min_spec (zwordsize - Int.unsigned n) s) as [[A B] | [A B]]; rewrite B; auto. + apply same_bits_eq; intros. rewrite ! bits_shl' by auto. + destruct (zlt i (Int.unsigned n)); auto. + rewrite ! bits_sign_ext by omega. f_equal. + destruct (zlt (i - Int.unsigned n) s). + rewrite zlt_true by omega; auto. + omegaContradiction. +Qed. + (** Powers of two with exponents given as 32-bit ints *) Definition one_bits' (x: int) : list Int.int := @@ -4321,7 +3895,7 @@ Theorem one_bits'_range: Proof. intros. destruct (list_in_map_inv _ _ _ H) as [i0 [EQ IN]]. - exploit Z_one_bits_range; eauto. intros R. + exploit Z_one_bits_range; eauto. fold zwordsize. intros R. unfold Int.ltu. rewrite EQ. rewrite Int.unsigned_repr. change (Int.unsigned iwordsize') with zwordsize. apply zlt_true. omega. assert (zwordsize < Int.max_unsigned) by reflexivity. omega. @@ -4380,7 +3954,7 @@ Lemma is_power2'_correct: Proof. unfold is_power2'; intros. destruct (Z_one_bits wordsize (unsigned n) 0) as [ | i [ | ? ?]] eqn:B; inv H. - rewrite (Z_one_bits_powerserie (unsigned n)) by (apply unsigned_range). + rewrite (Z_one_bits_powerserie wordsize (unsigned n)) by (apply unsigned_range). rewrite Int.unsigned_repr. rewrite B; simpl. omega. assert (0 <= i < zwordsize). { apply Z_one_bits_range with (unsigned n). rewrite B; auto with coqlib. } diff --git a/lib/IntvSets.v b/lib/IntvSets.v index 78c20cc5..b97d9882 100644 --- a/lib/IntvSets.v +++ b/lib/IntvSets.v @@ -102,7 +102,7 @@ Proof. simpl. rewrite IHok. tauto. destruct (zlt h0 l). simpl. tauto. - rewrite IHok. intuition. + rewrite IHok. intuition idtac. assert (l0 <= x < h0 \/ l <= x < h) by xomega. tauto. left; xomega. left; xomega. @@ -190,7 +190,7 @@ Module PTree <: TREE. | Leaf : tree A | Node : tree A -> option A -> tree A -> tree A. - Arguments Leaf [A]. + Arguments Leaf {A}. Arguments Node [A]. Scheme tree_ind := Induction for tree Sort Prop. diff --git a/lib/Ordered.v b/lib/Ordered.v index bcf24cbd..1adbd330 100644 --- a/lib/Ordered.v +++ b/lib/Ordered.v @@ -21,6 +21,8 @@ Require Import Coqlib. Require Import Maps. Require Import Integers. +Create HintDb ordered_type. + (** The ordered type of positive numbers *) Module OrderedPositive <: OrderedType. @@ -173,17 +175,17 @@ Definition eq (x y: t) := Lemma eq_refl : forall x : t, eq x x. Proof. - intros; split; auto. + intros; split; auto with ordered_type. Qed. Lemma eq_sym : forall x y : t, eq x y -> eq y x. Proof. - unfold eq; intros. intuition auto. + unfold eq; intros. intuition auto with ordered_type. Qed. Lemma eq_trans : forall x y z : t, eq x y -> eq y z -> eq x z. Proof. - unfold eq; intros. intuition eauto. + unfold eq; intros. intuition eauto with ordered_type. Qed. Definition lt (x y: t) := @@ -201,7 +203,7 @@ Proof. case (A.compare (fst x) (fst z)); intro. assumption. generalize (A.lt_not_eq H2); intro. elim H5. - apply A.eq_trans with (fst z). auto. auto. + apply A.eq_trans with (fst z). auto. auto with ordered_type. generalize (@A.lt_not_eq (fst z) (fst y)); intro. elim H5. apply A.lt_trans with (fst x); auto. apply A.eq_sym; auto. diff --git a/lib/Zbits.v b/lib/Zbits.v new file mode 100644 index 00000000..27586aff --- /dev/null +++ b/lib/Zbits.v @@ -0,0 +1,1101 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Additional operations and proofs about binary integers, + on top of the ZArith standard library. *) + +Require Import Psatz Zquot. +Require Import Coqlib. + +(** ** Modulo arithmetic *) + +(** We define and state properties of equality and arithmetic modulo a + positive integer. *) + +Section EQ_MODULO. + +Variable modul: Z. +Hypothesis modul_pos: modul > 0. + +Definition eqmod (x y: Z) : Prop := exists k, x = k * modul + y. + +Lemma eqmod_refl: forall x, eqmod x x. +Proof. + intros; red. exists 0. omega. +Qed. + +Lemma eqmod_refl2: forall x y, x = y -> eqmod x y. +Proof. + intros. subst y. apply eqmod_refl. +Qed. + +Lemma eqmod_sym: forall x y, eqmod x y -> eqmod y x. +Proof. + intros x y [k EQ]; red. exists (-k). subst x. ring. +Qed. + +Lemma eqmod_trans: forall x y z, eqmod x y -> eqmod y z -> eqmod x z. +Proof. + intros x y z [k1 EQ1] [k2 EQ2]; red. + exists (k1 + k2). subst x; subst y. ring. +Qed. + +Lemma eqmod_small_eq: + forall x y, eqmod x y -> 0 <= x < modul -> 0 <= y < modul -> x = y. +Proof. + intros x y [k EQ] I1 I2. + generalize (Zdiv_unique _ _ _ _ EQ I2). intro. + rewrite (Z.div_small x modul I1) in H. subst k. omega. +Qed. + +Lemma eqmod_mod_eq: + forall x y, eqmod x y -> x mod modul = y mod modul. +Proof. + intros x y [k EQ]. subst x. + rewrite Z.add_comm. apply Z_mod_plus. auto. +Qed. + +Lemma eqmod_mod: + forall x, eqmod x (x mod modul). +Proof. + intros; red. exists (x / modul). + rewrite Z.mul_comm. apply Z_div_mod_eq. auto. +Qed. + +Lemma eqmod_add: + forall a b c d, eqmod a b -> eqmod c d -> eqmod (a + c) (b + d). +Proof. + intros a b c d [k1 EQ1] [k2 EQ2]; red. + subst a; subst c. exists (k1 + k2). ring. +Qed. + +Lemma eqmod_neg: + forall x y, eqmod x y -> eqmod (-x) (-y). +Proof. + intros x y [k EQ]; red. exists (-k). rewrite EQ. ring. +Qed. + +Lemma eqmod_sub: + forall a b c d, eqmod a b -> eqmod c d -> eqmod (a - c) (b - d). +Proof. + intros a b c d [k1 EQ1] [k2 EQ2]; red. + subst a; subst c. exists (k1 - k2). ring. +Qed. + +Lemma eqmod_mult: + forall a b c d, eqmod a c -> eqmod b d -> eqmod (a * b) (c * d). +Proof. + intros a b c d [k1 EQ1] [k2 EQ2]; red. + subst a; subst b. + exists (k1 * k2 * modul + c * k2 + k1 * d). + ring. +Qed. + +End EQ_MODULO. + +Lemma eqmod_divides: + forall n m x y, eqmod n x y -> Z.divide m n -> eqmod m x y. +Proof. + intros. destruct H as [k1 EQ1]. destruct H0 as [k2 EQ2]. + exists (k1*k2). rewrite <- Z.mul_assoc. rewrite <- EQ2. auto. +Qed. + +(** ** Fast normalization modulo [2^n] *) + +Fixpoint P_mod_two_p (p: positive) (n: nat) {struct n} : Z := + match n with + | O => 0 + | S m => + match p with + | xH => 1 + | xO q => Z.double (P_mod_two_p q m) + | xI q => Z.succ_double (P_mod_two_p q m) + end + end. + +Definition Z_mod_two_p (x: Z) (n: nat) : Z := + match x with + | Z0 => 0 + | Zpos p => P_mod_two_p p n + | Zneg p => let r := P_mod_two_p p n in if zeq r 0 then 0 else two_power_nat n - r + end. + +Lemma P_mod_two_p_range: + forall n p, 0 <= P_mod_two_p p n < two_power_nat n. +Proof. + induction n; simpl; intros. + - rewrite two_power_nat_O. omega. + - rewrite two_power_nat_S. destruct p. + + generalize (IHn p). rewrite Z.succ_double_spec. omega. + + generalize (IHn p). rewrite Z.double_spec. omega. + + generalize (two_power_nat_pos n). omega. +Qed. + +Lemma P_mod_two_p_eq: + forall n p, P_mod_two_p p n = (Zpos p) mod (two_power_nat n). +Proof. + assert (forall n p, exists y, Zpos p = y * two_power_nat n + P_mod_two_p p n). + { + induction n; simpl; intros. + - rewrite two_power_nat_O. exists (Zpos p). ring. + - rewrite two_power_nat_S. destruct p. + + destruct (IHn p) as [y EQ]. exists y. + change (Zpos p~1) with (2 * Zpos p + 1). rewrite EQ. + rewrite Z.succ_double_spec. ring. + + destruct (IHn p) as [y EQ]. exists y. + change (Zpos p~0) with (2 * Zpos p). rewrite EQ. + rewrite (Z.double_spec (P_mod_two_p p n)). ring. + + exists 0; omega. + } + intros. + destruct (H n p) as [y EQ]. + symmetry. apply Zmod_unique with y. auto. apply P_mod_two_p_range. +Qed. + +Lemma Z_mod_two_p_range: + forall n x, 0 <= Z_mod_two_p x n < two_power_nat n. +Proof. + intros; unfold Z_mod_two_p. generalize (two_power_nat_pos n); intros. + destruct x. + - intuition. + - apply P_mod_two_p_range. + - set (r := P_mod_two_p p n). + assert (0 <= r < two_power_nat n) by apply P_mod_two_p_range. + destruct (zeq r 0). + + intuition. + + Psatz.lia. +Qed. + +Lemma Z_mod_two_p_eq: + forall n x, Z_mod_two_p x n = x mod (two_power_nat n). +Proof. + intros. unfold Z_mod_two_p. generalize (two_power_nat_pos n); intros. + destruct x. + - rewrite Zmod_0_l. auto. + - apply P_mod_two_p_eq. + - generalize (P_mod_two_p_range n p) (P_mod_two_p_eq n p). intros A B. + exploit (Z_div_mod_eq (Zpos p) (two_power_nat n)); auto. intros C. + set (q := Zpos p / two_power_nat n) in *. + set (r := P_mod_two_p p n) in *. + rewrite <- B in C. + change (Z.neg p) with (- (Z.pos p)). destruct (zeq r 0). + + symmetry. apply Zmod_unique with (-q). rewrite C; rewrite e. Psatz.lia. + intuition. + + symmetry. apply Zmod_unique with (-q - 1). rewrite C. Psatz.lia. + intuition. +Qed. + +(** ** Bit-level operations and properties *) + +(** Shift [x] left by one and insert [b] as the low bit of the result. *) + +Definition Zshiftin (b: bool) (x: Z) : Z := + if b then Z.succ_double x else Z.double x. + +Remark Ztestbit_0: forall n, Z.testbit 0 n = false. +Proof Z.testbit_0_l. + +Remark Ztestbit_1: forall n, Z.testbit 1 n = zeq n 0. +Proof. + intros. destruct n; simpl; auto. +Qed. + +Remark Ztestbit_m1: forall n, 0 <= n -> Z.testbit (-1) n = true. +Proof. + intros. destruct n; simpl; auto. +Qed. + +Remark Zshiftin_spec: + forall b x, Zshiftin b x = 2 * x + (if b then 1 else 0). +Proof. + unfold Zshiftin; intros. destruct b. + - rewrite Z.succ_double_spec. omega. + - rewrite Z.double_spec. omega. +Qed. + +Remark Zshiftin_inj: + forall b1 x1 b2 x2, + Zshiftin b1 x1 = Zshiftin b2 x2 -> b1 = b2 /\ x1 = x2. +Proof. + intros. rewrite !Zshiftin_spec in H. + destruct b1; destruct b2. + split; [auto|omega]. + omegaContradiction. + omegaContradiction. + split; [auto|omega]. +Qed. + +Remark Zdecomp: + forall x, x = Zshiftin (Z.odd x) (Z.div2 x). +Proof. + intros. destruct x; simpl. + - auto. + - destruct p; auto. + - destruct p; auto. simpl. rewrite Pos.pred_double_succ. auto. +Qed. + +Remark Ztestbit_shiftin: + forall b x n, + 0 <= n -> + Z.testbit (Zshiftin b x) n = if zeq n 0 then b else Z.testbit x (Z.pred n). +Proof. + intros. rewrite Zshiftin_spec. destruct (zeq n 0). + - subst n. destruct b. + + apply Z.testbit_odd_0. + + rewrite Z.add_0_r. apply Z.testbit_even_0. + - assert (0 <= Z.pred n) by omega. + set (n' := Z.pred n) in *. + replace n with (Z.succ n') by (unfold n'; omega). + destruct b. + + apply Z.testbit_odd_succ; auto. + + rewrite Z.add_0_r. apply Z.testbit_even_succ; auto. +Qed. + +Remark Ztestbit_shiftin_base: + forall b x, Z.testbit (Zshiftin b x) 0 = b. +Proof. + intros. rewrite Ztestbit_shiftin. apply zeq_true. omega. +Qed. + +Remark Ztestbit_shiftin_succ: + forall b x n, 0 <= n -> Z.testbit (Zshiftin b x) (Z.succ n) = Z.testbit x n. +Proof. + intros. rewrite Ztestbit_shiftin. rewrite zeq_false. rewrite Z.pred_succ. auto. + omega. omega. +Qed. + +Lemma Zshiftin_ind: + forall (P: Z -> Prop), + P 0 -> + (forall b x, 0 <= x -> P x -> P (Zshiftin b x)) -> + forall x, 0 <= x -> P x. +Proof. + intros. destruct x. + - auto. + - induction p. + + change (P (Zshiftin true (Z.pos p))). auto. + + change (P (Zshiftin false (Z.pos p))). auto. + + change (P (Zshiftin true 0)). apply H0. omega. auto. + - compute in H1. intuition congruence. +Qed. + +Lemma Zshiftin_pos_ind: + forall (P: Z -> Prop), + P 1 -> + (forall b x, 0 < x -> P x -> P (Zshiftin b x)) -> + forall x, 0 < x -> P x. +Proof. + intros. destruct x; simpl in H1; try discriminate. + induction p. + + change (P (Zshiftin true (Z.pos p))). auto. + + change (P (Zshiftin false (Z.pos p))). auto. + + auto. +Qed. + +(** ** Bit-wise decomposition ([Z.testbit]) *) + +Remark Ztestbit_eq: + forall n x, 0 <= n -> + Z.testbit x n = if zeq n 0 then Z.odd x else Z.testbit (Z.div2 x) (Z.pred n). +Proof. + intros. rewrite (Zdecomp x) at 1. apply Ztestbit_shiftin; auto. +Qed. + +Remark Ztestbit_base: + forall x, Z.testbit x 0 = Z.odd x. +Proof. + intros. rewrite Ztestbit_eq. apply zeq_true. omega. +Qed. + +Remark Ztestbit_succ: + forall n x, 0 <= n -> Z.testbit x (Z.succ n) = Z.testbit (Z.div2 x) n. +Proof. + intros. rewrite Ztestbit_eq. rewrite zeq_false. rewrite Z.pred_succ. auto. + omega. omega. +Qed. + +Lemma eqmod_same_bits: + forall n x y, + (forall i, 0 <= i < Z.of_nat n -> Z.testbit x i = Z.testbit y i) -> + eqmod (two_power_nat n) x y. +Proof. + induction n; intros. + - change (two_power_nat 0) with 1. exists (x-y); ring. + - rewrite two_power_nat_S. + assert (eqmod (two_power_nat n) (Z.div2 x) (Z.div2 y)). + apply IHn. intros. rewrite <- !Ztestbit_succ. apply H. rewrite Nat2Z.inj_succ; omega. + omega. omega. + destruct H0 as [k EQ]. + exists k. rewrite (Zdecomp x). rewrite (Zdecomp y). + replace (Z.odd y) with (Z.odd x). + rewrite EQ. rewrite !Zshiftin_spec. ring. + exploit (H 0). rewrite Nat2Z.inj_succ; omega. + rewrite !Ztestbit_base. auto. +Qed. + +Lemma same_bits_eqmod: + forall n x y i, + eqmod (two_power_nat n) x y -> 0 <= i < Z.of_nat n -> + Z.testbit x i = Z.testbit y i. +Proof. + induction n; intros. + - simpl in H0. omegaContradiction. + - rewrite Nat2Z.inj_succ in H0. rewrite two_power_nat_S in H. + rewrite !(Ztestbit_eq i); intuition. + destruct H as [k EQ]. + assert (EQ': Zshiftin (Z.odd x) (Z.div2 x) = + Zshiftin (Z.odd y) (k * two_power_nat n + Z.div2 y)). + { + rewrite (Zdecomp x) in EQ. rewrite (Zdecomp y) in EQ. + rewrite EQ. rewrite !Zshiftin_spec. ring. + } + exploit Zshiftin_inj; eauto. intros [A B]. + destruct (zeq i 0). + + auto. + + apply IHn. exists k; auto. omega. +Qed. + +Lemma equal_same_bits: + forall x y, + (forall i, 0 <= i -> Z.testbit x i = Z.testbit y i) -> + x = y. +Proof Z.bits_inj'. + +Lemma Z_one_complement: + forall i, 0 <= i -> + forall x, Z.testbit (-x-1) i = negb (Z.testbit x i). +Proof. + intros i0 POS0. pattern i0. apply Zlt_0_ind; auto. + intros i IND POS x. + rewrite (Zdecomp x). set (y := Z.div2 x). + replace (- Zshiftin (Z.odd x) y - 1) + with (Zshiftin (negb (Z.odd x)) (- y - 1)). + rewrite !Ztestbit_shiftin; auto. + destruct (zeq i 0). auto. apply IND. omega. + rewrite !Zshiftin_spec. destruct (Z.odd x); simpl negb; ring. +Qed. + +Lemma Ztestbit_above: + forall n x i, + 0 <= x < two_power_nat n -> + i >= Z.of_nat n -> + Z.testbit x i = false. +Proof. + induction n; intros. + - change (two_power_nat 0) with 1 in H. + replace x with 0 by omega. + apply Z.testbit_0_l. + - rewrite Nat2Z.inj_succ in H0. rewrite Ztestbit_eq. rewrite zeq_false. + apply IHn. rewrite two_power_nat_S in H. rewrite (Zdecomp x) in H. + rewrite Zshiftin_spec in H. destruct (Z.odd x); omega. + omega. omega. omega. +Qed. + +Lemma Ztestbit_above_neg: + forall n x i, + -two_power_nat n <= x < 0 -> + i >= Z.of_nat n -> + Z.testbit x i = true. +Proof. + intros. set (y := -x-1). + assert (Z.testbit y i = false). + apply Ztestbit_above with n. + unfold y; omega. auto. + unfold y in H1. rewrite Z_one_complement in H1. + change true with (negb false). rewrite <- H1. rewrite negb_involutive; auto. + omega. +Qed. + +Lemma Zsign_bit: + forall n x, + 0 <= x < two_power_nat (S n) -> + Z.testbit x (Z.of_nat n) = if zlt x (two_power_nat n) then false else true. +Proof. + induction n; intros. + - change (two_power_nat 1) with 2 in H. + assert (x = 0 \/ x = 1) by omega. + destruct H0; subst x; reflexivity. + - rewrite Nat2Z.inj_succ. rewrite Ztestbit_eq. rewrite zeq_false. rewrite Z.pred_succ. + rewrite IHn. rewrite two_power_nat_S. + destruct (zlt (Z.div2 x) (two_power_nat n)); rewrite (Zdecomp x); rewrite Zshiftin_spec. + rewrite zlt_true. auto. destruct (Z.odd x); omega. + rewrite zlt_false. auto. destruct (Z.odd x); omega. + rewrite (Zdecomp x) in H; rewrite Zshiftin_spec in H. + rewrite two_power_nat_S in H. destruct (Z.odd x); omega. + omega. omega. +Qed. + +Lemma Ztestbit_le: + forall x y, + 0 <= y -> + (forall i, 0 <= i -> Z.testbit x i = true -> Z.testbit y i = true) -> + x <= y. +Proof. + intros x y0 POS0; revert x; pattern y0; apply Zshiftin_ind; auto; intros. + - replace x with 0. omega. apply equal_same_bits; intros. + rewrite Ztestbit_0. destruct (Z.testbit x i) as [] eqn:E; auto. + exploit H; eauto. rewrite Ztestbit_0. auto. + - assert (Z.div2 x0 <= x). + { apply H0. intros. exploit (H1 (Z.succ i)). + omega. rewrite Ztestbit_succ; auto. rewrite Ztestbit_shiftin_succ; auto. + } + rewrite (Zdecomp x0). rewrite !Zshiftin_spec. + destruct (Z.odd x0) as [] eqn:E1; destruct b as [] eqn:E2; try omega. + exploit (H1 0). omega. rewrite Ztestbit_base; auto. + rewrite Ztestbit_shiftin_base. congruence. +Qed. + +Lemma Ztestbit_mod_two_p: + forall n x i, + 0 <= n -> 0 <= i -> + Z.testbit (x mod (two_p n)) i = if zlt i n then Z.testbit x i else false. +Proof. + intros n0 x i N0POS. revert x i; pattern n0; apply natlike_ind; auto. + - intros. change (two_p 0) with 1. rewrite Zmod_1_r. rewrite Z.testbit_0_l. + rewrite zlt_false; auto. omega. + - intros. rewrite two_p_S; auto. + replace (x0 mod (2 * two_p x)) + with (Zshiftin (Z.odd x0) (Z.div2 x0 mod two_p x)). + rewrite Ztestbit_shiftin; auto. rewrite (Ztestbit_eq i x0); auto. destruct (zeq i 0). + + rewrite zlt_true; auto. omega. + + rewrite H0. destruct (zlt (Z.pred i) x). + * rewrite zlt_true; auto. omega. + * rewrite zlt_false; auto. omega. + * omega. + + rewrite (Zdecomp x0) at 3. set (x1 := Z.div2 x0). symmetry. + apply Zmod_unique with (x1 / two_p x). + rewrite !Zshiftin_spec. rewrite Z.add_assoc. f_equal. + transitivity (2 * (two_p x * (x1 / two_p x) + x1 mod two_p x)). + f_equal. apply Z_div_mod_eq. apply two_p_gt_ZERO; auto. + ring. + rewrite Zshiftin_spec. exploit (Z_mod_lt x1 (two_p x)). apply two_p_gt_ZERO; auto. + destruct (Z.odd x0); omega. +Qed. + +Corollary Ztestbit_two_p_m1: + forall n i, 0 <= n -> 0 <= i -> + Z.testbit (two_p n - 1) i = if zlt i n then true else false. +Proof. + intros. replace (two_p n - 1) with ((-1) mod (two_p n)). + rewrite Ztestbit_mod_two_p; auto. destruct (zlt i n); auto. apply Ztestbit_m1; auto. + apply Zmod_unique with (-1). ring. + exploit (two_p_gt_ZERO n). auto. omega. +Qed. + +Corollary Ztestbit_neg_two_p: + forall n i, 0 <= n -> 0 <= i -> + Z.testbit (- (two_p n)) i = if zlt i n then false else true. +Proof. + intros. + replace (- two_p n) with (- (two_p n - 1) - 1) by omega. + rewrite Z_one_complement by auto. + rewrite Ztestbit_two_p_m1 by auto. + destruct (zlt i n); auto. +Qed. + +Lemma Z_add_is_or: + forall i, 0 <= i -> + forall x y, + (forall j, 0 <= j <= i -> Z.testbit x j && Z.testbit y j = false) -> + Z.testbit (x + y) i = Z.testbit x i || Z.testbit y i. +Proof. + intros i0 POS0. pattern i0. apply Zlt_0_ind; auto. + intros i IND POS x y EXCL. + rewrite (Zdecomp x) in *. rewrite (Zdecomp y) in *. + transitivity (Z.testbit (Zshiftin (Z.odd x || Z.odd y) (Z.div2 x + Z.div2 y)) i). + - f_equal. rewrite !Zshiftin_spec. + exploit (EXCL 0). omega. rewrite !Ztestbit_shiftin_base. intros. +Opaque Z.mul. + destruct (Z.odd x); destruct (Z.odd y); simpl in *; discriminate || ring. + - rewrite !Ztestbit_shiftin; auto. + destruct (zeq i 0). + + auto. + + apply IND. omega. intros. + exploit (EXCL (Z.succ j)). omega. + rewrite !Ztestbit_shiftin_succ. auto. + omega. omega. +Qed. + +(** ** Zero and sign extensions *) + +(** In pseudo-code: +<< + Fixpoint Zzero_ext (n: Z) (x: Z) : Z := + if zle n 0 then + 0 + else + Zshiftin (Z.odd x) (Zzero_ext (Z.pred n) (Z.div2 x)). + Fixpoint Zsign_ext (n: Z) (x: Z) : Z := + if zle n 1 then + if Z.odd x then -1 else 0 + else + Zshiftin (Z.odd x) (Zzero_ext (Z.pred n) (Z.div2 x)). +>> + We encode this [nat]-like recursion using the [Z.iter] iteration + function, in order to make the [Zzero_ext] and [Zsign_ext] + functions efficiently executable within Coq. +*) + +Definition Zzero_ext (n: Z) (x: Z) : Z := + Z.iter n + (fun rec x => Zshiftin (Z.odd x) (rec (Z.div2 x))) + (fun x => 0) + x. + +Definition Zsign_ext (n: Z) (x: Z) : Z := + Z.iter (Z.pred n) + (fun rec x => Zshiftin (Z.odd x) (rec (Z.div2 x))) + (fun x => if Z.odd x && zlt 0 n then -1 else 0) + x. + +Lemma Ziter_base: + forall (A: Type) n (f: A -> A) x, n <= 0 -> Z.iter n f x = x. +Proof. + intros. unfold Z.iter. destruct n; auto. compute in H. elim H; auto. +Qed. + +Lemma Ziter_succ: + forall (A: Type) n (f: A -> A) x, + 0 <= n -> Z.iter (Z.succ n) f x = f (Z.iter n f x). +Proof. + intros. destruct n; simpl. + - auto. + - rewrite Pos.add_1_r. apply Pos.iter_succ. + - compute in H. elim H; auto. +Qed. + +Lemma Znatlike_ind: + forall (P: Z -> Prop), + (forall n, n <= 0 -> P n) -> + (forall n, 0 <= n -> P n -> P (Z.succ n)) -> + forall n, P n. +Proof. + intros. destruct (zle 0 n). + apply natlike_ind; auto. apply H; omega. + apply H. omega. +Qed. + +Lemma Zzero_ext_spec: + forall n x i, 0 <= i -> + Z.testbit (Zzero_ext n x) i = if zlt i n then Z.testbit x i else false. +Proof. + unfold Zzero_ext. induction n using Znatlike_ind. + - intros. rewrite Ziter_base; auto. + rewrite zlt_false. rewrite Ztestbit_0; auto. omega. + - intros. rewrite Ziter_succ; auto. + rewrite Ztestbit_shiftin; auto. + rewrite (Ztestbit_eq i x); auto. + destruct (zeq i 0). + + subst i. rewrite zlt_true; auto. omega. + + rewrite IHn. destruct (zlt (Z.pred i) n). + rewrite zlt_true; auto. omega. + rewrite zlt_false; auto. omega. + omega. +Qed. + +Lemma Zsign_ext_spec: + forall n x i, 0 <= i -> + Z.testbit (Zsign_ext n x) i = Z.testbit x (if zlt i n then i else n - 1). +Proof. + intros n0 x i I0. unfold Zsign_ext. + unfold proj_sumbool; destruct (zlt 0 n0) as [N0|N0]; simpl. +- revert x i I0. pattern n0. apply Zlt_lower_bound_ind with (z := 1); [ | omega ]. + unfold Zsign_ext. intros. + destruct (zeq x 1). + + subst x; simpl. + replace (if zlt i 1 then i else 0) with 0. + rewrite Ztestbit_base. + destruct (Z.odd x0); [ apply Ztestbit_m1; auto | apply Ztestbit_0 ]. + destruct (zlt i 1); omega. + + set (x1 := Z.pred x). replace x1 with (Z.succ (Z.pred x1)) by omega. + rewrite Ziter_succ by (unfold x1; omega). rewrite Ztestbit_shiftin by auto. + destruct (zeq i 0). + * subst i. rewrite zlt_true. rewrite Ztestbit_base; auto. omega. + * rewrite H by (unfold x1; omega). + unfold x1; destruct (zlt (Z.pred i) (Z.pred x)). + ** rewrite zlt_true by omega. + rewrite (Ztestbit_eq i x0) by omega. + rewrite zeq_false by omega. auto. + ** rewrite zlt_false by omega. + rewrite (Ztestbit_eq (x - 1) x0) by omega. + rewrite zeq_false by omega. auto. +- rewrite Ziter_base by omega. rewrite andb_false_r. + rewrite Z.testbit_0_l, Z.testbit_neg_r. auto. + destruct (zlt i n0); omega. +Qed. + +(** [Zzero_ext n x] is [x modulo 2^n] *) + +Lemma Zzero_ext_mod: + forall n x, 0 <= n -> Zzero_ext n x = x mod (two_p n). +Proof. + intros. apply equal_same_bits; intros. + rewrite Zzero_ext_spec, Ztestbit_mod_two_p by auto. auto. +Qed. + +(** [Zzero_ext n x] is the unique integer congruent to [x] modulo [2^n] in the range [0...2^n-1]. *) + +Lemma Zzero_ext_range: + forall n x, 0 <= n -> 0 <= Zzero_ext n x < two_p n. +Proof. + intros. rewrite Zzero_ext_mod; auto. apply Z_mod_lt. apply two_p_gt_ZERO. omega. +Qed. + +Lemma eqmod_Zzero_ext: + forall n x, 0 <= n -> eqmod (two_p n) (Zzero_ext n x) x. +Proof. + intros. rewrite Zzero_ext_mod; auto. apply eqmod_sym. apply eqmod_mod. + apply two_p_gt_ZERO. omega. +Qed. + +(** Relation between [Zsign_ext n x] and (Zzero_ext n x] *) + +Lemma Zsign_ext_zero_ext: + forall n, 0 <= n -> forall x, + Zsign_ext n x = Zzero_ext n x - (if Z.testbit x (n - 1) then two_p n else 0). +Proof. + intros. apply equal_same_bits; intros. + rewrite Zsign_ext_spec by auto. + destruct (Z.testbit x (n - 1)) eqn:SIGNBIT. +- set (n' := - two_p n). + replace (Zzero_ext n x - two_p n) with (Zzero_ext n x + n') by (unfold n'; omega). + rewrite Z_add_is_or; auto. + rewrite Zzero_ext_spec by auto. unfold n'; rewrite Ztestbit_neg_two_p by omega. + destruct (zlt i n). rewrite orb_false_r; auto. auto. + intros. rewrite Zzero_ext_spec by omega. unfold n'; rewrite Ztestbit_neg_two_p by omega. + destruct (zlt j n); auto using andb_false_r. +- replace (Zzero_ext n x - 0) with (Zzero_ext n x) by omega. + rewrite Zzero_ext_spec by auto. + destruct (zlt i n); auto. +Qed. + +(** [Zsign_ext n x] is the unique integer congruent to [x] modulo [2^n] + in the range [-2^(n-1)...2^(n-1) - 1]. *) + +Lemma Zsign_ext_range: + forall n x, 0 < n -> -two_p (n-1) <= Zsign_ext n x < two_p (n-1). +Proof. + intros. + assert (A: 0 <= Zzero_ext n x < two_p n) by (apply Zzero_ext_range; omega). + assert (B: Z.testbit (Zzero_ext n x) (n - 1) = + if zlt (Zzero_ext n x) (two_p (n - 1)) then false else true). + { set (N := Z.to_nat (n - 1)). + generalize (Zsign_bit N (Zzero_ext n x)). + rewrite ! two_power_nat_two_p. + rewrite inj_S. unfold N; rewrite Z2Nat.id by omega. + intros X; apply X. replace (Z.succ (n - 1)) with n by omega. exact A. + } + assert (C: two_p n = 2 * two_p (n - 1)). + { rewrite <- two_p_S by omega. f_equal; omega. } + rewrite Zzero_ext_spec, zlt_true in B by omega. + rewrite Zsign_ext_zero_ext by omega. rewrite B. + destruct (zlt (Zzero_ext n x) (two_p (n - 1))); omega. +Qed. + +Lemma eqmod_Zsign_ext: + forall n x, 0 <= n -> + eqmod (two_p n) (Zsign_ext n x) x. +Proof. + intros. rewrite Zsign_ext_zero_ext by auto. + apply eqmod_trans with (x - 0). + apply eqmod_sub. + apply eqmod_Zzero_ext; omega. + exists (if Z.testbit x (n - 1) then 1 else 0). destruct (Z.testbit x (n - 1)); ring. + apply eqmod_refl2; omega. +Qed. + +(** ** Decomposition of a number as a sum of powers of two. *) + +Fixpoint Z_one_bits (n: nat) (x: Z) (i: Z) {struct n}: list Z := + match n with + | O => nil + | S m => + if Z.odd x + then i :: Z_one_bits m (Z.div2 x) (i+1) + else Z_one_bits m (Z.div2 x) (i+1) + end. + +Fixpoint powerserie (l: list Z): Z := + match l with + | nil => 0 + | x :: xs => two_p x + powerserie xs + end. + +Lemma Z_one_bits_powerserie: + forall n x, 0 <= x < two_power_nat n -> x = powerserie (Z_one_bits n x 0). +Proof. + assert (forall n x i, + 0 <= i -> + 0 <= x < two_power_nat n -> + x * two_p i = powerserie (Z_one_bits n x i)). + { + induction n; intros. + simpl. rewrite two_power_nat_O in H0. + assert (x = 0) by omega. subst x. omega. + rewrite two_power_nat_S in H0. simpl Z_one_bits. + rewrite (Zdecomp x) in H0. rewrite Zshiftin_spec in H0. + assert (EQ: Z.div2 x * two_p (i + 1) = powerserie (Z_one_bits n (Z.div2 x) (i + 1))). + apply IHn. omega. + destruct (Z.odd x); omega. + rewrite two_p_is_exp in EQ. change (two_p 1) with 2 in EQ. + rewrite (Zdecomp x) at 1. rewrite Zshiftin_spec. + destruct (Z.odd x); simpl powerserie; rewrite <- EQ; ring. + omega. omega. + } + intros. rewrite <- H. change (two_p 0) with 1. omega. + omega. exact H0. +Qed. + +Lemma Z_one_bits_range: + forall n x i, In i (Z_one_bits n x 0) -> 0 <= i < Z.of_nat n. +Proof. + assert (forall n x i j, + In j (Z_one_bits n x i) -> i <= j < i + Z.of_nat n). + { + induction n; simpl In. + tauto. + intros x i j. rewrite Nat2Z.inj_succ. + assert (In j (Z_one_bits n (Z.div2 x) (i + 1)) -> i <= j < i + Z.succ (Z.of_nat n)). + intros. exploit IHn; eauto. omega. + destruct (Z.odd x); simpl. + intros [A|B]. subst j. omega. auto. + auto. + } + intros. generalize (H n x 0 i H0). omega. +Qed. + +Remark Z_one_bits_zero: + forall n i, Z_one_bits n 0 i = nil. +Proof. + induction n; intros; simpl; auto. +Qed. + +Remark Z_one_bits_two_p: + forall n x i, + 0 <= x < Z.of_nat n -> + Z_one_bits n (two_p x) i = (i + x) :: nil. +Proof. + induction n; intros; simpl. simpl in H. omegaContradiction. + rewrite Nat2Z.inj_succ in H. + assert (x = 0 \/ 0 < x) by omega. destruct H0. + subst x; simpl. decEq. omega. apply Z_one_bits_zero. + assert (Z.odd (two_p x) = false /\ Z.div2 (two_p x) = two_p (x-1)). + apply Zshiftin_inj. rewrite <- Zdecomp. rewrite !Zshiftin_spec. + rewrite <- two_p_S. rewrite Z.add_0_r. f_equal; omega. omega. + destruct H1 as [A B]; rewrite A; rewrite B. + rewrite IHn. f_equal; omega. omega. +Qed. + +(** ** Recognition of powers of two *) + +Fixpoint P_is_power2 (p: positive) : bool := + match p with + | xH => true + | xO q => P_is_power2 q + | xI q => false + end. + +Definition Z_is_power2 (x: Z) : option Z := + match x with + | Z0 => None + | Zpos p => if P_is_power2 p then Some (Z.log2 x) else None + | Zneg _ => None + end. + +Remark P_is_power2_sound: + forall p, P_is_power2 p = true -> Z.pos p = two_p (Z.log2 (Z.pos p)). +Proof. + induction p; simpl P_is_power2; intros. +- discriminate. +- change (Z.pos p~0) with (2 * Z.pos p). apply IHp in H. + rewrite Z.log2_double by xomega. rewrite two_p_S. congruence. + apply Z.log2_nonneg. +- reflexivity. +Qed. + +Lemma Z_is_power2_nonneg: + forall x i, Z_is_power2 x = Some i -> 0 <= i. +Proof. + unfold Z_is_power2; intros. destruct x; try discriminate. + destruct (P_is_power2 p) eqn:P; try discriminate. + replace i with (Z.log2 (Z.pos p)) by congruence. apply Z.log2_nonneg. +Qed. + +Lemma Z_is_power2_sound: + forall x i, Z_is_power2 x = Some i -> x = two_p i /\ i = Z.log2 x. +Proof. + unfold Z_is_power2; intros. destruct x; try discriminate. + destruct (P_is_power2 p) eqn:P; try discriminate. + apply P_is_power2_sound in P. rewrite P; split; congruence. +Qed. + +Corollary Z_is_power2_range: + forall n x i, + 0 <= n -> 0 <= x < two_p n -> Z_is_power2 x = Some i -> 0 <= i < n. +Proof. + intros. + assert (x <> 0) by (red; intros; subst x; discriminate). + apply Z_is_power2_sound in H1. destruct H1 as [P Q]. subst i. + split. apply Z.log2_nonneg. apply Z.log2_lt_pow2. omega. rewrite <- two_p_equiv; tauto. +Qed. + +Lemma Z_is_power2_complete: + forall i, 0 <= i -> Z_is_power2 (two_p i) = Some i. +Proof. +Opaque Z.log2. + assert (A: forall x i, Z_is_power2 x = Some i -> Z_is_power2 (2 * x) = Some (Z.succ i)). + { destruct x; simpl; intros; try discriminate. + change (2 * Z.pos p) with (Z.pos (xO p)); simpl. + destruct (P_is_power2 p); inv H. rewrite <- Z.log2_double by xomega. auto. + } + induction i using Znatlike_ind; intros. +- replace i with 0 by omega. reflexivity. +- rewrite two_p_S by omega. apply A. apply IHi; omega. +Qed. + +Definition Z_is_power2m1 (x: Z) : option Z := Z_is_power2 (Z.succ x). + +Lemma Z_is_power2m1_nonneg: + forall x i, Z_is_power2m1 x = Some i -> 0 <= i. +Proof. + unfold Z_is_power2m1; intros. eapply Z_is_power2_nonneg; eauto. +Qed. + +Lemma Z_is_power2m1_sound: + forall x i, Z_is_power2m1 x = Some i -> x = two_p i - 1. +Proof. + unfold Z_is_power2m1; intros. apply Z_is_power2_sound in H. omega. +Qed. + +Lemma Z_is_power2m1_complete: + forall i, 0 <= i -> Z_is_power2m1 (two_p i - 1) = Some i. +Proof. + intros. unfold Z_is_power2m1. replace (Z.succ (two_p i - 1)) with (two_p i) by omega. + apply Z_is_power2_complete; auto. +Qed. + +Lemma Z_is_power2m1_range: + forall n x i, + 0 <= n -> 0 <= x < two_p n -> Z_is_power2m1 x = Some i -> 0 <= i <= n. +Proof. + intros. destruct (zeq x (two_p n - 1)). +- subst x. rewrite Z_is_power2m1_complete in H1 by auto. inv H1; omega. +- unfold Z_is_power2m1 in H1. apply (Z_is_power2_range n (Z.succ x) i) in H1; omega. +Qed. + +(** ** Relation between bitwise operations and multiplications / divisions by powers of 2 *) + +(** Left shifts and multiplications by powers of 2. *) + +Lemma Zshiftl_mul_two_p: + forall x n, 0 <= n -> Z.shiftl x n = x * two_p n. +Proof. + intros. destruct n; simpl. + - omega. + - pattern p. apply Pos.peano_ind. + + change (two_power_pos 1) with 2. simpl. ring. + + intros. rewrite Pos.iter_succ. rewrite H0. + rewrite Pplus_one_succ_l. rewrite two_power_pos_is_exp. + change (two_power_pos 1) with 2. ring. + - compute in H. congruence. +Qed. + +(** Right shifts and divisions by powers of 2. *) + +Lemma Zshiftr_div_two_p: + forall x n, 0 <= n -> Z.shiftr x n = x / two_p n. +Proof. + intros. destruct n; unfold Z.shiftr; simpl. + - rewrite Zdiv_1_r. auto. + - pattern p. apply Pos.peano_ind. + + change (two_power_pos 1) with 2. simpl. apply Zdiv2_div. + + intros. rewrite Pos.iter_succ. rewrite H0. + rewrite Pplus_one_succ_l. rewrite two_power_pos_is_exp. + change (two_power_pos 1) with 2. + rewrite Zdiv2_div. rewrite Z.mul_comm. apply Zdiv_Zdiv. + rewrite two_power_pos_nat. apply two_power_nat_pos. omega. + - compute in H. congruence. +Qed. + +(** ** Properties of [shrx] (signed division by a power of 2) *) + +Lemma Zquot_Zdiv: + forall x y, + y > 0 -> + Z.quot x y = if zlt x 0 then (x + y - 1) / y else x / y. +Proof. + intros. destruct (zlt x 0). + - symmetry. apply Zquot_unique_full with ((x + y - 1) mod y - (y - 1)). + + red. right; split. omega. + exploit (Z_mod_lt (x + y - 1) y); auto. + rewrite Z.abs_eq. omega. omega. + + transitivity ((y * ((x + y - 1) / y) + (x + y - 1) mod y) - (y-1)). + rewrite <- Z_div_mod_eq. ring. auto. ring. + - apply Zquot_Zdiv_pos; omega. +Qed. + +Lemma Zdiv_shift: + forall x y, y > 0 -> + (x + (y - 1)) / y = x / y + if zeq (Z.modulo x y) 0 then 0 else 1. +Proof. + intros. generalize (Z_div_mod_eq x y H). generalize (Z_mod_lt x y H). + set (q := x / y). set (r := x mod y). intros. + destruct (zeq r 0). + apply Zdiv_unique with (y - 1). rewrite H1. rewrite e. ring. omega. + apply Zdiv_unique with (r - 1). rewrite H1. ring. omega. +Qed. + +(** ** Size of integers, in bits. *) + +Definition Zsize (x: Z) : Z := + match x with + | Zpos p => Zpos (Pos.size p) + | _ => 0 + end. + +Remark Zsize_pos: forall x, 0 <= Zsize x. +Proof. + destruct x; simpl. omega. compute; intuition congruence. omega. +Qed. + +Remark Zsize_pos': forall x, 0 < x -> 0 < Zsize x. +Proof. + destruct x; simpl; intros; try discriminate. compute; auto. +Qed. + +Lemma Zsize_shiftin: + forall b x, 0 < x -> Zsize (Zshiftin b x) = Z.succ (Zsize x). +Proof. + intros. destruct x; compute in H; try discriminate. + destruct b. + change (Zshiftin true (Zpos p)) with (Zpos (p~1)). + simpl. f_equal. rewrite Pos.add_1_r; auto. + change (Zshiftin false (Zpos p)) with (Zpos (p~0)). + simpl. f_equal. rewrite Pos.add_1_r; auto. +Qed. + +Lemma Ztestbit_size_1: + forall x, 0 < x -> Z.testbit x (Z.pred (Zsize x)) = true. +Proof. + intros x0 POS0; pattern x0; apply Zshiftin_pos_ind; auto. + intros. rewrite Zsize_shiftin; auto. + replace (Z.pred (Z.succ (Zsize x))) with (Z.succ (Z.pred (Zsize x))) by omega. + rewrite Ztestbit_shiftin_succ. auto. generalize (Zsize_pos' x H); omega. +Qed. + +Lemma Ztestbit_size_2: + forall x, 0 <= x -> forall i, i >= Zsize x -> Z.testbit x i = false. +Proof. + intros x0 POS0. destruct (zeq x0 0). + - subst x0; intros. apply Ztestbit_0. + - pattern x0; apply Zshiftin_pos_ind. + + simpl. intros. change 1 with (Zshiftin true 0). rewrite Ztestbit_shiftin. + rewrite zeq_false. apply Ztestbit_0. omega. omega. + + intros. rewrite Zsize_shiftin in H1; auto. + generalize (Zsize_pos' _ H); intros. + rewrite Ztestbit_shiftin. rewrite zeq_false. apply H0. omega. + omega. omega. + + omega. +Qed. + +Lemma Zsize_interval_1: + forall x, 0 <= x -> 0 <= x < two_p (Zsize x). +Proof. + intros. + assert (x = x mod (two_p (Zsize x))). + apply equal_same_bits; intros. + rewrite Ztestbit_mod_two_p; auto. + destruct (zlt i (Zsize x)). auto. apply Ztestbit_size_2; auto. + apply Zsize_pos; auto. + rewrite H0 at 1. rewrite H0 at 3. apply Z_mod_lt. apply two_p_gt_ZERO. apply Zsize_pos; auto. +Qed. + +Lemma Zsize_interval_2: + forall x n, 0 <= n -> 0 <= x < two_p n -> n >= Zsize x. +Proof. + intros. set (N := Z.to_nat n). + assert (Z.of_nat N = n) by (apply Z2Nat.id; auto). + rewrite <- H1 in H0. rewrite <- two_power_nat_two_p in H0. + destruct (zeq x 0). + subst x; simpl; omega. + destruct (zlt n (Zsize x)); auto. + exploit (Ztestbit_above N x (Z.pred (Zsize x))). auto. omega. + rewrite Ztestbit_size_1. congruence. omega. +Qed. + +Lemma Zsize_monotone: + forall x y, 0 <= x <= y -> Zsize x <= Zsize y. +Proof. + intros. apply Z.ge_le. apply Zsize_interval_2. apply Zsize_pos. + exploit (Zsize_interval_1 y). omega. + omega. +Qed. + +(** ** Bit insertion, bit extraction *) + +(** Extract and optionally sign-extend bits [from...from+len-1] of [x] *) +Definition Zextract_u (x: Z) (from: Z) (len: Z) : Z := + Zzero_ext len (Z.shiftr x from). + +Definition Zextract_s (x: Z) (from: Z) (len: Z) : Z := + Zsign_ext len (Z.shiftr x from). + +Lemma Zextract_u_spec: + forall x from len i, + 0 <= from -> 0 <= len -> 0 <= i -> + Z.testbit (Zextract_u x from len) i = + if zlt i len then Z.testbit x (from + i) else false. +Proof. + unfold Zextract_u; intros. rewrite Zzero_ext_spec, Z.shiftr_spec by auto. + rewrite Z.add_comm. auto. +Qed. + +Lemma Zextract_s_spec: + forall x from len i, + 0 <= from -> 0 < len -> 0 <= i -> + Z.testbit (Zextract_s x from len) i = + Z.testbit x (from + (if zlt i len then i else len - 1)). +Proof. + unfold Zextract_s; intros. rewrite Zsign_ext_spec by auto. rewrite Z.shiftr_spec. + rewrite Z.add_comm. auto. + destruct (zlt i len); omega. +Qed. + +(** Insert bits [0...len-1] of [y] into bits [to...to+len-1] of [x] *) + +Definition Zinsert (x y: Z) (to: Z) (len: Z) : Z := + let mask := Z.shiftl (two_p len - 1) to in + Z.lor (Z.land (Z.shiftl y to) mask) (Z.ldiff x mask). + +Lemma Zinsert_spec: + forall x y to len i, + 0 <= to -> 0 <= len -> 0 <= i -> + Z.testbit (Zinsert x y to len) i = + if zle to i && zlt i (to + len) + then Z.testbit y (i - to) + else Z.testbit x i. +Proof. + unfold Zinsert; intros. set (mask := two_p len - 1). + assert (M: forall j, 0 <= j -> Z.testbit mask j = if zlt j len then true else false). + { intros; apply Ztestbit_two_p_m1; auto. } + rewrite Z.lor_spec, Z.land_spec, Z.ldiff_spec by auto. + destruct (zle to i). +- rewrite ! Z.shiftl_spec by auto. rewrite ! M by omega. + unfold proj_sumbool; destruct (zlt (i - to) len); simpl; + rewrite andb_true_r, andb_false_r. ++ rewrite zlt_true by omega. apply orb_false_r. ++ rewrite zlt_false by omega; auto. +- rewrite ! Z.shiftl_spec_low by omega. simpl. apply andb_true_r. +Qed. @@ -1,10 +1,7 @@ #!/bin/sh -# Start Proof General with the right -I options +# Start Proof General with the right Coq version # Use the Makefile to rebuild dependencies if needed -# Recompile the modified file after coqide editing - -PWD=`pwd` -INCLUDES=`make print-includes` +# Recompile the modified file after editing make -q ${1}o || { make -n ${1}o | grep -v "\\b${1}\\b" | \ @@ -15,16 +12,5 @@ make -q ${1}o || { COQPROGNAME="${COQBIN}coqtop" -COQPROGARGS="" -for arg in $INCLUDES; do - case "$arg" in - -I|-R|-as|compcert*) - COQPROGARGS="$COQPROGARGS \"$arg\"";; - *) - COQPROGARGS="$COQPROGARGS \"$PWD/$arg\"";; - esac -done - -emacs --eval "(setq coq-prog-name \"$COQPROGNAME\")" \ - --eval "(setq coq-prog-args '($COQPROGARGS))" $1 \ +emacs --eval "(setq coq-prog-name \"$COQPROGNAME\")" $1 \ && make ${1}o diff --git a/powerpc/Archi.v b/powerpc/Archi.v index d792e4fe..10f38391 100644 --- a/powerpc/Archi.v +++ b/powerpc/Archi.v @@ -16,7 +16,7 @@ (** Architecture-dependent parameters for PowerPC *) -Require Import ZArith. +Require Import ZArith List. (*From Flocq*) Require Import Binary Bits. @@ -30,6 +30,10 @@ Definition align_float64 := 8%Z. (** Can we use the 64-bit extensions to the PowerPC architecture? *) Parameter ppc64 : bool. +(** Should single-precision FP arguments passed on stack be passed + as singles or use double FP format. *) +Parameter single_passed_as_single : bool. + Definition splitlong := negb ppc64. Lemma splitlong_ptr32: splitlong = true -> ptr64 = false. @@ -37,24 +41,33 @@ Proof. reflexivity. Qed. -Definition default_nan_64 : { x : binary64 | is_nan _ _ x = true } := - exist _ (B754_nan 53 1024 false (iter_nat 51 _ xO xH) (eq_refl true)) (eq_refl true). +Definition default_nan_64 := (false, iter_nat 51 _ xO xH). +Definition default_nan_32 := (false, iter_nat 22 _ xO xH). + +(* Always choose the first NaN argument, if any *) + +Definition choose_nan_64 (l: list (bool * positive)) : bool * positive := + match l with nil => default_nan_64 | n :: _ => n end. + +Definition choose_nan_32 (l: list (bool * positive)) : bool * positive := + match l with nil => default_nan_32 | n :: _ => n end. -Definition choose_binop_pl_64 (pl1 pl2 : positive) := - false. (**r always choose first NaN *) +Lemma choose_nan_64_idem: forall n, + choose_nan_64 (n :: n :: nil) = choose_nan_64 (n :: nil). +Proof. auto. Qed. -Definition default_nan_32 : { x : binary32 | is_nan _ _ x = true } := - exist _ (B754_nan 24 128 false (iter_nat 22 _ xO xH) (eq_refl true)) (eq_refl true). +Lemma choose_nan_32_idem: forall n, + choose_nan_32 (n :: n :: nil) = choose_nan_32 (n :: nil). +Proof. auto. Qed. -Definition choose_binop_pl_32 (pl1 pl2 : positive) := - false. (**r always choose first NaN *) +Definition fma_order {A: Type} (x y z: A) := (x, z, y). -Definition fpu_returns_default_qNaN := false. +Definition fma_invalid_mul_is_nan := false. Definition float_of_single_preserves_sNaN := true. Global Opaque ptr64 big_endian splitlong - default_nan_64 choose_binop_pl_64 - default_nan_32 choose_binop_pl_32 - fpu_returns_default_qNaN + default_nan_64 choose_nan_64 + default_nan_32 choose_nan_32 + fma_order fma_invalid_mul_is_nan float_of_single_preserves_sNaN. diff --git a/powerpc/Asm.v b/powerpc/Asm.v index ad24f563..4fb38ff8 100644 --- a/powerpc/Asm.v +++ b/powerpc/Asm.v @@ -231,6 +231,7 @@ Inductive instruction : Type := | Pfres: freg -> freg -> instruction (**r approximate inverse *) | Pfsel: freg -> freg -> freg -> freg -> instruction (**r FP conditional move *) | Pisel: ireg -> ireg -> ireg -> crbit -> instruction (**r integer select *) + | Pfsel_gen: freg -> freg -> freg -> crbit -> instruction (**r floating point select *) | Pisync: instruction (**r ISYNC barrier *) | Picbi: ireg -> ireg -> instruction (**r instruction cache invalidate *) | Picbtls: int -> ireg -> ireg -> instruction (**r instruction cache block touch and lock set *) @@ -860,6 +861,20 @@ Definition exec_instr (f: function) (i: instruction) (rs: regset) (m: mem) : out Next (nextinstr (rs#rd <- (Val.subf rs#r1 rs#r2))) m | Pfsubs rd r1 r2 => Next (nextinstr (rs#rd <- (Val.subfs rs#r1 rs#r2))) m + | Pisel rd r1 r2 bit => + let v := + match rs#(reg_of_crbit bit) with + | Vint n => if Int.eq n Int.zero then rs#r2 else (gpr_or_zero rs r1) + | _ => Vundef + end in + Next (nextinstr (rs #rd <- v #GPR0 <- Vundef)) m + | Pfsel_gen rd r1 r2 bit => + let v := + match rs#(reg_of_crbit bit) with + | Vint n => if Int.eq n Int.zero then rs#r2 else rs#r1 + | _ => Vundef + end in + Next (nextinstr (rs #rd <- v #GPR0 <- Vundef)) m | Plbz rd cst r1 => load1 Mint8unsigned rd cst r1 rs m | Plbzx rd r1 r2 => @@ -1073,7 +1088,6 @@ Definition exec_instr (f: function) (i: instruction) (rs: regset) (m: mem) : out | Pfrsqrte _ _ | Pfres _ _ | Pfsel _ _ _ _ - | Pisel _ _ _ _ | Plwarx _ _ _ | Plwbrx _ _ _ | Picbi _ _ diff --git a/powerpc/AsmToJSON.ml b/powerpc/AsmToJSON.ml index ee3eaca8..f4d4285a 100644 --- a/powerpc/AsmToJSON.ml +++ b/powerpc/AsmToJSON.ml @@ -17,12 +17,10 @@ open AST open BinNums open Camlcoq open Json -open Format open JsonAST let pp_reg pp t n = - let s = sprintf "%s%s" t n in - pp_jsingle_object pp "Register" pp_jstring s + pp_jsingle_object pp "Register" pp_jstring (t ^ n) let pp_ireg pp reg = pp_reg pp "r" (TargetPrinter.int_reg_name reg) @@ -31,8 +29,8 @@ let pp_freg pp reg = pp_reg pp "f" (TargetPrinter.float_reg_name reg) let preg_annot = function - | IR r -> sprintf "r%s" (TargetPrinter.int_reg_name r) - | FR r -> sprintf "f%s" (TargetPrinter.float_reg_name r) + | IR r -> "r" ^ (TargetPrinter.int_reg_name r) + | FR r -> "f" ^ (TargetPrinter.float_reg_name r) | _ -> assert false let pp_constant pp c = @@ -86,28 +84,31 @@ let pp_arg pp = function | Atom a -> pp_atom_constant pp a | String s -> pp_jsingle_object pp "String" pp_jstring s -let mnemonic_names =["Padd"; "Paddc"; "Padde"; "Paddi"; "Paddic"; "Paddis"; "Paddze"; "Pand_"; - "Pandc"; "Pandi_"; "Pandis_"; "Pannot"; "Pb"; "Pbctr"; "Pbctrl"; "Pbdnz"; - "Pbf"; "Pbl"; "Pblr"; "Pbs"; "Pbt"; "Pbtbl"; "Pcmpb"; "Pcmpd"; "Pcmpdi"; - "Pcmpld"; "Pcmpldi"; "Pcmplw"; "Pcmplwi"; "Pcmpw"; "Pcmpwi"; "Pcntlzd"; - "Pcntlzw"; "Pcreqv"; "Pcror"; "Pcrxor"; "Pdcbf"; "Pdcbi"; "Pdcbt"; - "Pdcbtls"; "Pdcbtst"; "Pdcbz"; "Pdivd"; "Pdivdu"; "Pdivw"; "Pdivwu"; - "Peieio"; "Peqv"; "Pextsb"; "Pextsh"; "Pextsw"; "Pfabs"; "Pfadd"; "Pfadds"; - "Pfcfid"; "Pfcmpu"; "Pfctidz"; "Pfctiw"; "Pfctiwz"; "Pfdiv"; "Pfdivs"; - "Pfmadd"; "Pfmr"; "Pfmsub"; "Pfmul"; "Pfmuls"; "Pfneg"; "Pfnmadd"; - "Pfnmsub"; "Pfres"; "Pfrsp"; "Pfrsqrte"; "Pfsel"; "Pfsqrt"; "Pfsub"; - "Pfsubs"; "Picbi"; "Picbtls"; "Pinlineasm"; "Pisel"; "Pisync"; "Plabel"; - "Plbz"; "Plbzx"; "Pld"; "Pldbrx"; "Pldi"; "Pldx"; "Plfd"; "Plfdx"; "Plfi"; "Plfis"; - "Plfs"; "Plfsx"; "Plha"; "Plhax"; "Plhbrx"; "Plhz"; "Plhzx"; "Plwarx"; - "Plwbrx"; "Plwsync"; "Plwz"; "Plwzu"; "Plwzx"; "Pmbar"; "Pmfcr"; "Pmflr"; - "Pmfspr"; "Pmr"; "Pmtctr"; "Pmtlr"; "Pmtspr"; "Pmulhd"; "Pmulhdu"; "Pmulhw"; - "Pmulhwu"; "Pmulld"; "Pmulli"; "Pmullw"; "Pnand"; "Pnor"; "Por"; "Porc"; - "Pori"; "Poris"; "Prldicl"; "Prldimi"; "Prldinm"; "Prlwimi"; "Prlwinm"; - "Psld"; "Pslw"; "Psrad"; "Psradi"; "Psraw"; "Psrawi"; "Psrd"; "Psrw"; - "Pstb"; "Pstbx"; "Pstd"; "Pstdbrx"; "Pstdu"; "Pstdx"; "Pstfd"; "Pstfdu"; "Pstfdx"; - "Pstfs"; "Pstfsx"; "Psth"; "Psthbrx"; "Psthx"; "Pstw"; "Pstwbrx"; "Pstwcx_"; - "Pstwu"; "Pstwux"; "Pstwx"; "Psubfc"; "Psubfe"; "Psubfic"; "Psubfze"; - "Psync"; "Ptrap"; "Pxor"; "Pxori"; "Pxoris"] +module StringSet = Set.Make(String) + +let mnemonic_names = StringSet.of_list + ["Padd"; "Paddc"; "Padde"; "Paddi"; "Paddic"; "Paddis"; "Paddze"; "Pand_"; + "Pandc"; "Pandi_"; "Pandis_"; "Pannot"; "Pb"; "Pbctr"; "Pbctrl"; "Pbdnz"; + "Pbf"; "Pbl"; "Pblr"; "Pbs"; "Pbt"; "Pbtbl"; "Pcmpb"; "Pcmpd"; "Pcmpdi"; + "Pcmpld"; "Pcmpldi"; "Pcmplw"; "Pcmplwi"; "Pcmpw"; "Pcmpwi"; "Pcntlzd"; + "Pcntlzw"; "Pcreqv"; "Pcror"; "Pcrxor"; "Pdcbf"; "Pdcbi"; "Pdcbt"; + "Pdcbtls"; "Pdcbtst"; "Pdcbz"; "Pdivd"; "Pdivdu"; "Pdivw"; "Pdivwu"; + "Peieio"; "Peqv"; "Pextsb"; "Pextsh"; "Pextsw"; "Pfabs"; "Pfadd"; "Pfadds"; + "Pfcfid"; "Pfcmpu"; "Pfctidz"; "Pfctiw"; "Pfctiwz"; "Pfdiv"; "Pfdivs"; + "Pfmadd"; "Pfmr"; "Pfmsub"; "Pfmul"; "Pfmuls"; "Pfneg"; "Pfnmadd"; + "Pfnmsub"; "Pfres"; "Pfrsp"; "Pfrsqrte"; "Pfsel"; "Pfsqrt"; "Pfsub"; + "Pfsubs"; "Picbi"; "Picbtls"; "Pinlineasm"; "Pisel"; "Pisync"; "Plabel"; + "Plbz"; "Plbzx"; "Pld"; "Pldbrx"; "Pldi"; "Pldx"; "Plfd"; "Plfdx"; "Plfi"; + "Plfis"; "Plfs"; "Plfsx"; "Plha"; "Plhax"; "Plhbrx"; "Plhz"; "Plhzx"; + "Plwarx"; "Plwbrx"; "Plwsync"; "Plwz"; "Plwzu"; "Plwzx"; "Pmbar"; "Pmfcr"; + "Pmflr"; "Pmfspr"; "Pmr"; "Pmtctr"; "Pmtlr"; "Pmtspr"; "Pmulhd"; "Pmulhdu"; + "Pmulhw"; "Pmulhwu"; "Pmulld"; "Pmulli"; "Pmullw"; "Pnand"; "Pnor"; "Por"; + "Porc"; "Pori"; "Poris"; "Prldicl"; "Prldimi"; "Prldinm"; "Prlwimi"; + "Prlwinm"; "Psld"; "Pslw"; "Psrad"; "Psradi"; "Psraw"; "Psrawi"; "Psrd"; + "Psrw"; "Pstb"; "Pstbx"; "Pstd"; "Pstdbrx"; "Pstdu"; "Pstdx"; "Pstfd"; + "Pstfdu"; "Pstfdx"; "Pstfs"; "Pstfsx"; "Psth"; "Psthbrx"; "Psthx"; "Pstw"; + "Pstwbrx"; "Pstwcx_"; "Pstwu"; "Pstwux"; "Pstwx"; "Psubfc"; "Psubfe"; + "Psubfic"; "Psubfze"; "Psync"; "Ptrap"; "Pxor"; "Pxori"; "Pxoris"] let pp_instructions pp ic = let ic = List.filter (fun s -> match s with @@ -126,7 +127,7 @@ let pp_instructions pp ic = | Pcfi_rel_offset _ -> false | _ -> true) ic in let instruction pp n args = - assert (List.mem n mnemonic_names); + assert (StringSet.mem n mnemonic_names); pp_jobject_start pp; pp_jmember ~first:true pp "Instruction Name" pp_jstring n; pp_jmember pp "Args" (pp_jarray pp_arg) args; @@ -228,6 +229,7 @@ let pp_instructions pp ic = | Pfres (fr1,fr2) -> instruction pp "Pfres" [Freg fr1; Freg fr2] | Pfsel (fr1,fr2,fr3,fr4) -> instruction pp "Pfsel" [Freg fr1; Freg fr2; Freg fr3; Freg fr4] | Pisel (ir1,ir2,ir3,cr) -> instruction pp "Pisel" [Ireg ir1; Ireg ir2; Ireg ir3; Crbit cr] + | Pfsel_gen _ -> assert false (* Should not occur *) | Picbi (ir1,ir2) -> instruction pp "Picbi" [Ireg ir1; Ireg ir2] | Picbtls (n,ir1,ir2) -> instruction pp "Picbtls" [Constant (Cint n);Ireg ir1; Ireg ir2] | Pisync -> instruction pp "Pisync" [] @@ -250,7 +252,7 @@ let pp_instructions pp ic = | Plhbrx (ir1,ir2,ir3) -> instruction pp "Plhbrx" [Ireg ir1; Ireg ir2; Ireg ir3] | Plhz (ir1,c,ir2) -> instruction pp "Plhz" [Ireg ir1; Constant c; Ireg ir2] | Plhzx (ir1,ir2,ir3) -> instruction pp "Plhzx" [Ireg ir1; Ireg ir2; Ireg ir3] - | Pldi (ir,c) -> instruction pp "Pldi" [Ireg ir; Long c] (* FIXME Cint is too small, we need Clong *) + | Pldi (ir,c) -> instruction pp "Pldi" [Ireg ir; Long c] | Plmake _ (* Should not occur *) | Pllo _ (* Should not occur *) | Plhi _ -> assert false (* Should not occur *) @@ -384,8 +386,8 @@ let print_if prog sourcename = | Some f -> let f = Filename.concat !sdump_folder f in let oc = open_out_bin f in - pp_ast (formatter_of_out_channel oc) pp_instructions prog sourcename; + pp_ast oc pp_instructions prog sourcename; close_out oc let pp_mnemonics pp = - pp_mnemonics pp mnemonic_names + pp_mnemonics pp (StringSet.elements mnemonic_names) diff --git a/powerpc/Asmexpand.ml b/powerpc/Asmexpand.ml index 49a0d237..ce88778c 100644 --- a/powerpc/Asmexpand.ml +++ b/powerpc/Asmexpand.ml @@ -14,7 +14,7 @@ of the PPC assembly code. *) open Camlcoq -open Integers +open! Integers open AST open Asm open Asmexpandaux @@ -30,20 +30,22 @@ let eref = (* Useful constants and helper functions *) -let _0 = Integers.Int.zero -let _1 = Integers.Int.one +let _0 = Int.zero +let _1 = Int.one let _2 = coqint_of_camlint 2l let _4 = coqint_of_camlint 4l let _6 = coqint_of_camlint 6l let _8 = coqint_of_camlint 8l +let _16 = coqint_of_camlint 16l let _31 = coqint_of_camlint 31l let _32 = coqint_of_camlint 32l let _64 = coqint_of_camlint 64l let _m1 = coqint_of_camlint (-1l) let _m4 = coqint_of_camlint (-4l) let _m8 = coqint_of_camlint (-8l) +let _m16 = coqint_of_camlint (-16l) -let _0L = Integers.Int64.zero +let _0L = Int64.zero let _32L = coqint_of_camlint64 32L let _64L = coqint_of_camlint64 64L let _m1L = coqint_of_camlint64 (-1L) @@ -56,6 +58,15 @@ let emit_loadimm r n = let emit_addimm rd rs n = List.iter emit (Asmgen.addimm rd rs n []) +(* Numbering of bits in the CR register *) + +let num_crbit = function + | CRbit_0 -> 0 + | CRbit_1 -> 1 + | CRbit_2 -> 2 + | CRbit_3 -> 3 + | CRbit_6 -> 6 + (* Handling of annotations *) let expand_annot_val kind txt targ args res = @@ -77,8 +88,6 @@ let expand_annot_val kind txt targ args res = So, use 64-bit accesses only if alignment >= 4. Note that lfd and stfd cannot trap on ill-formed floats. *) - - let offset_in_range ofs = Int.eq (Asmgen.high_s ofs) _0 @@ -410,10 +419,21 @@ let expand_builtin_va_start r = let expand_int64_arith conflict rl fn = if conflict then (fn GPR0; emit (Pmr(rl, GPR0))) else fn rl -(* Expansion of integer conditional moves (__builtin_*sel) *) +(* Expansion of integer conditional moves (__builtin_*sel and Pisel) *) (* The generated code works equally well with 32-bit integer registers and with 64-bit integer registers. *) +let expand_integer_cond_move_1 a2 a3 res = + (* GPR0 is -1 (all ones) if condition is true, 0 if it is false *) + if res <> a3 then begin + emit (Pand_ (res, a2, GPR0)); + emit (Pandc (GPR0, a3, GPR0)) + end else begin + emit (Pandc (res, a3, GPR0)); + emit (Pand_ (GPR0, a2, GPR0)) + end; + emit (Por (res, res, GPR0)) + let expand_integer_cond_move a1 a2 a3 res = if a2 = a3 then emit (Pmr (res, a2)) @@ -423,15 +443,37 @@ let expand_integer_cond_move a1 a2 a3 res = end else begin (* a1 has type _Bool, hence it is 0 or 1 *) emit (Psubfic (GPR0, a1, Cint _0)); - (* r0 = -1 (all ones) if a1 is true, r0 = 0 if a1 is false *) - if res <> a3 then begin - emit (Pand_ (res, a2, GPR0)); - emit (Pandc (GPR0, a3, GPR0)) - end else begin - emit (Pandc (res, a3, GPR0)); - emit (Pand_ (GPR0, a2, GPR0)) - end; - emit (Por (res, res, GPR0)) + expand_integer_cond_move_1 a2 a3 res + end + + +(* Expansion of floating point conditional moves (Pfcmove) *) + +let expand_float_cond_move bit a2 a3 res = + emit (Pmfcr GPR0); + emit (Prlwinm(GPR0, GPR0, Z.of_uint (4 + num_crbit bit), _8)); + emit (Pstfdu (a3, Cint (_m16), GPR1)); + emit (Pcfi_adjust _16); + emit (Pstfd (a2, Cint (_8), GPR1)); + emit (Plfdx (res, GPR1, GPR0)); + emit (Paddi (GPR1, GPR1, (Cint _16))); + emit (Pcfi_adjust _m16) + + + +(* Symmetrically, we emulate the "isel" instruction on PPC processors + that do not have it. *) + +let expand_isel bit a2 a3 res = + if a2 = a3 then + emit (Pmr (res, a2)) + else if eref then + emit (Pisel (res, a2, a3, bit)) + else begin + emit (Pmfcr GPR0); + emit (Prlwinm(GPR0, GPR0, Z.of_uint (1 + num_crbit bit), _1)); + emit (Psubfic (GPR0, GPR0, Cint _0)); + expand_integer_cond_move_1 a2 a3 res end (* Convert integer constant into GPR with corresponding number *) @@ -512,6 +554,26 @@ let expand_builtin_inline name args res = emit (Plabel lbl2) | "__builtin_cmpb", [BA(IR a1); BA(IR a2)], BR(IR res) -> emit (Pcmpb (res,a1,a2)) + | "__builtin_bswap64", [BA_splitlong(BA(IR ah), BA(IR al))], + BR_splitlong(BR(IR rh), BR(IR rl))-> + assert (not Archi.ppc64); + emit (Pstwu(ah, Cint _m8, GPR1)); + emit (Pcfi_adjust _8); + emit (Pstwu(al, Cint _m8, GPR1)); + emit (Pcfi_adjust _8); + emit (Plwbrx(rh, GPR0, GPR1)); + emit (Paddi(GPR1, GPR1, Cint _8)); + emit (Pcfi_adjust _m8); + emit (Plwbrx(rl, GPR0, GPR1)); + emit (Paddi(GPR1, GPR1, Cint _8)); + emit (Pcfi_adjust _m8) + | "__builtin_bswap64", [BA(IR a1)], BR(IR res) -> + assert (Archi.ppc64); + emit (Pstdu(a1, Cint _m8, GPR1)); + emit (Pcfi_adjust _8); + emit (Pldbrx(res, GPR0, GPR1)); + emit (Paddi(GPR1, GPR1, Cint _8)); + emit (Pcfi_adjust _m8) | ("__builtin_bswap" | "__builtin_bswap32"), [BA(IR a1)], BR(IR res) -> emit (Pstwu(a1, Cint _m8, GPR1)); emit (Pcfi_adjust _8); @@ -772,13 +834,6 @@ let set_cr6 sg = (* Expand instructions *) -let num_crbit = function - | CRbit_0 -> 0 - | CRbit_1 -> 1 - | CRbit_2 -> 2 - | CRbit_3 -> 3 - | CRbit_6 -> 6 - let expand_instruction instr = match instr with | Pallocframe(sz, ofs,retofs) -> @@ -797,7 +852,7 @@ let expand_instruction instr = if variadic then begin emit (Pmflr GPR0); emit (Pbl(intern_string "__compcert_va_saveregs", - {sig_args = []; sig_res = None; sig_cc = cc_default})); + {sig_args = []; sig_res = Tvoid; sig_cc = cc_default})); emit (Pmtlr GPR0) end; current_function_stacksize := sz; @@ -874,6 +929,10 @@ let expand_instruction instr = emit (Pcfi_adjust _m8); | Pfxdp(r1, r2) -> if r1 <> r2 then emit(Pfmr(r1, r2)) + | Pisel(rd, r1, r2, bit) -> + expand_isel bit r1 r2 rd + | Pfsel_gen (rd, r1, r2, bit) -> + expand_float_cond_move bit r1 r2 rd | Plmake(r1, rhi, rlo) -> if r1 = rlo then emit (Prldimi(r1, rhi, _32L, upper32)) diff --git a/powerpc/Asmgen.v b/powerpc/Asmgen.v index 8c296f0a..a686414a 100644 --- a/powerpc/Asmgen.v +++ b/powerpc/Asmgen.v @@ -125,17 +125,35 @@ Definition rolm (r1 r2: ireg) (amount mask: int) (k: code) := Definition low64_u (n: int64) := Int64.zero_ext 16 n. Definition low64_s (n: int64) := Int64.sign_ext 16 n. -Definition loadimm64 (r: ireg) (n: int64) (k: code) := +Definition loadimm64_32s (r: ireg) (n: int64) (k: code) := let lo_u := low64_u n in let lo_s := low64_s n in - let hi_s := Int64.sign_ext 16 (Int64.shr n (Int64.repr 16)) in + let hi_s := low64_s (Int64.shr n (Int64.repr 16)) in if Int64.eq n lo_s then Paddi64 r GPR0 n :: k - else if Int64.eq n (Int64.or (Int64.shl hi_s (Int64.repr 16)) lo_u) then - Paddis64 r GPR0 hi_s :: Pori64 r r lo_u :: k + else + Paddis64 r GPR0 hi_s :: Pori64 r r lo_u :: k. + +Definition loadimm64 (r: ireg) (n: int64) (k: code) := + if Int64.eq n (Int64.sign_ext 32 n) then + loadimm64_32s r n k else Pldi r n :: k. +(** [loadimm64_notemp] is a variant of [loadimm64] that uses no temporary + register. The code it produces is larger and slower than the code + produced by [loadimm64], but it is sometimes useful to preserve all registers + except the destination. *) + +Definition loadimm64_notemp (r: ireg) (n: int64) (k: code) := + if Int64.eq n (Int64.sign_ext 32 n) then + loadimm64_32s r n k + else + loadimm64_32s r (Int64.shru n (Int64.repr 32)) + (Prldinm r r (Int.repr 32) (Int64.shl Int64.mone (Int64.repr 32)) :: + Poris64 r r (low64_u (Int64.shru n (Int64.repr 16))) :: + Pori64 r r (low64_u n) :: k). + Definition opimm64 (insn2: ireg -> ireg -> ireg -> instruction) (insn1: ireg -> ireg -> int64 -> instruction) (r1 r2: ireg) (n: int64) (ok: bool) (k: code) := @@ -261,18 +279,14 @@ Definition transl_cond do r1 <- ireg_of a1; if Int64.eq n (low64_s n) then OK (Pcmpdi r1 n :: k) - else if ireg_eq r1 GPR12 then - OK (Pmr GPR0 GPR12 :: loadimm64 GPR12 n (Pcmpd GPR0 GPR12 :: k)) else - OK (loadimm64 GPR0 n (Pcmpd r1 GPR0 :: k)) + OK (loadimm64_notemp GPR0 n (Pcmpd r1 GPR0 :: k)) | Ccompluimm c n, a1 :: nil => do r1 <- ireg_of a1; if Int64.eq n (low64_u n) then OK (Pcmpldi r1 n :: k) - else if ireg_eq r1 GPR12 then - OK (Pmr GPR0 GPR12 :: loadimm64 GPR12 n (Pcmpld GPR0 GPR12 :: k)) else - OK (loadimm64 GPR0 n (Pcmpld r1 GPR0 :: k)) + OK (loadimm64_notemp GPR0 n (Pcmpld r1 GPR0 :: k)) | _, _ => Error(msg "Asmgen.transl_cond") end. @@ -390,6 +404,28 @@ Definition transl_cond_op else Pxori r' r' (Cint Int.one) :: k) end. +(** Translation of a select operation *) + +Definition transl_select_op + (cond: condition) (args: list mreg) (r1 r2 rd: ireg) (k: code) := + if ireg_eq r1 r2 then + OK (Pmr rd r1 :: k) + else + (let p := crbit_for_cond cond in + let r1' := if snd p then r1 else r2 in + let r2' := if snd p then r2 else r1 in + transl_cond cond args (Pisel rd r1' r2' (fst p) :: k)). + +Definition transl_fselect_op + (cond: condition) (args: list mreg) (r1 r2 rd: freg) (k: code) := + if freg_eq r1 r2 then + OK (Pfmr rd r1 :: k) + else + (let p := crbit_for_cond cond in + let r1' := if snd p then r1 else r2 in + let r2' := if snd p then r2 else r1 in + transl_cond cond args (Pfsel_gen rd r1' r2' (fst p) :: k)). + (** Translation of the arithmetic operation [r <- op(args)]. The corresponding instructions are prepended to [k]. *) @@ -596,6 +632,17 @@ Definition transl_op do r1 <- ireg_of a1; do r <- ireg_of res; OK (Plhi r r1 :: k) | Ocmp cmp, _ => transl_cond_op cmp args res k + | Osel cmp ty, a1 :: a2 :: args => + match preg_of res with + | IR r1 => + do r1 <- ireg_of a1; do r2 <- ireg_of a2; do r <- ireg_of res; + transl_select_op cmp args r1 r2 r k + | FR r => + do r1 <- freg_of a1; do r2 <- freg_of a2; do r <- freg_of res; + transl_fselect_op cmp args r1 r2 r k + | _ => + Error (msg "Asmgen.Osel") + end (*c PPC64 operations *) | Olongconst n, nil => do r <- ireg_of res; OK (loadimm64 r n k) diff --git a/powerpc/Asmgenproof.v b/powerpc/Asmgenproof.v index 8ad28aea..d653633c 100644 --- a/powerpc/Asmgenproof.v +++ b/powerpc/Asmgenproof.v @@ -179,14 +179,28 @@ Proof. Qed. Hint Resolve rolm_label: labels. +Remark loadimm64_32s_label: + forall r n k, tail_nolabel k (loadimm64_32s r n k). +Proof. + unfold loadimm64_32s; intros. destruct Int64.eq; TailNoLabel. +Qed. +Hint Resolve loadimm64_32s_label: labels. + Remark loadimm64_label: forall r n k, tail_nolabel k (loadimm64 r n k). Proof. - unfold loadimm64; intros. - destruct Int64.eq. TailNoLabel. destruct Int64.eq; TailNoLabel. + unfold loadimm64; intros. destruct Int64.eq; TailNoLabel. Qed. Hint Resolve loadimm64_label: labels. +Remark loadimm64_notemp_label: + forall r n k, tail_nolabel k (loadimm64_notemp r n k). +Proof. + unfold loadimm64_notemp; intros. destruct Int64.eq; TailNoLabel. + eapply tail_nolabel_trans; TailNoLabel. +Qed. +Hint Resolve loadimm64_notemp_label: labels. + Remark loadind_label: forall base ofs ty dst k c, loadind base ofs ty dst k = OK c -> tail_nolabel k c. @@ -234,6 +248,24 @@ Proof. destruct (snd (crbit_for_cond c0)); TailNoLabel. Qed. +Remark transl_select_op_label: + forall cond args r1 r2 rd k c, + transl_select_op cond args r1 r2 rd k = OK c -> tail_nolabel k c. +Proof. + unfold transl_select_op; intros. destruct (ireg_eq r1 r2). + TailNoLabel. + eapply tail_nolabel_trans. eapply transl_cond_label; eauto. TailNoLabel. +Qed. + +Remark transl_fselect_op_label: + forall cond args r1 r2 rd k c, + transl_fselect_op cond args r1 r2 rd k = OK c -> tail_nolabel k c. +Proof. + unfold transl_fselect_op; intros. destruct (freg_eq r1 r2). + TailNoLabel. + eapply tail_nolabel_trans. eapply transl_cond_label; eauto. TailNoLabel. +Qed. + Remark transl_op_label: forall op args r k c, transl_op op args r k = OK c -> tail_nolabel k c. @@ -261,6 +293,7 @@ Opaque Int.eq. destruct Int64.eq. TailNoLabel. destruct ireg_eq; [apply tail_nolabel_cons; unfold nolabel;auto|]; eapply tail_nolabel_trans; TailNoLabel. - eapply transl_cond_op_label; eauto. +- destruct (preg_of r); monadInv H. eapply transl_select_op_label; eauto. eapply transl_fselect_op_label; eauto. Qed. Remark transl_memory_access_label: diff --git a/powerpc/Asmgenproof1.v b/powerpc/Asmgenproof1.v index c18757b2..20cf9c1d 100644 --- a/powerpc/Asmgenproof1.v +++ b/powerpc/Asmgenproof1.v @@ -16,6 +16,7 @@ Require Import Coqlib. Require Import Errors. Require Import Maps. Require Import AST. +Require Import Zbits. Require Import Integers. Require Import Floats. Require Import Values. @@ -80,13 +81,13 @@ Proof. unfold Int.modu, Int.zero. decEq. change 0 with (0 mod 65536). change (Int.unsigned (Int.repr 65536)) with 65536. - apply Int.eqmod_mod_eq. omega. - unfold x, low_s. eapply Int.eqmod_trans. - apply Int.eqmod_divides with Int.modulus. + apply eqmod_mod_eq. omega. + unfold x, low_s. eapply eqmod_trans. + apply eqmod_divides with Int.modulus. unfold Int.sub. apply Int.eqm_unsigned_repr_l. apply Int.eqm_refl. exists 65536. compute; auto. replace 0 with (Int.unsigned n - Int.unsigned n) by omega. - apply Int.eqmod_sub. apply Int.eqmod_refl. apply Int.eqmod_sign_ext'. + apply eqmod_sub. apply eqmod_refl. apply Int.eqmod_sign_ext'. compute; auto. rewrite H0 in H. rewrite Int.add_zero in H. rewrite <- H. unfold x. rewrite Int.sub_add_opp. rewrite Int.add_assoc. @@ -531,6 +532,40 @@ Qed. (** Load int64 constant. *) +Lemma loadimm64_32s_correct: + forall r n k rs m, + exists rs', + exec_straight ge fn (loadimm64_32s r n k) rs m k rs' m + /\ rs'#r = Vlong (Int64.sign_ext 32 n) + /\ forall r': preg, r' <> r -> r' <> PC -> rs'#r' = rs#r'. +Proof. + unfold loadimm64_32s; intros. predSpec Int64.eq Int64.eq_spec n (low64_s n). + - econstructor; split; [|split]. + + apply exec_straight_one. simpl; eauto. auto. + + Simpl. rewrite Int64.add_zero_l. rewrite H. unfold low64_s. + rewrite Int64.sign_ext_widen by omega. auto. + + intros; Simpl. + - econstructor; split; [|split]. + + eapply exec_straight_two. simpl; eauto. simpl; eauto. auto. auto. + + Simpl. simpl. f_equal. rewrite Int64.add_zero_l. + apply Int64.same_bits_eq; intros. assert (Int64.zwordsize = 64) by auto. + rewrite Int64.bits_or, Int64.bits_shl by auto. + unfold low64_s, low64_u. + rewrite Int64.bits_zero_ext by omega. + change (Int64.unsigned (Int64.repr 16)) with 16. + destruct (zlt i 16). + * rewrite Int64.bits_sign_ext by omega. rewrite zlt_true by omega. auto. + * rewrite ! Int64.bits_sign_ext by omega. rewrite orb_false_r. + destruct (zlt i 32). + ** rewrite zlt_true by omega. rewrite Int64.bits_shr by omega. + change (Int64.unsigned (Int64.repr 16)) with 16. + rewrite zlt_true by omega. f_equal; omega. + ** rewrite zlt_false by omega. rewrite Int64.bits_shr by omega. + change (Int64.unsigned (Int64.repr 16)) with 16. + reflexivity. + + intros; Simpl. +Qed. + Lemma loadimm64_correct: forall r n k rs m, exists rs', @@ -539,20 +574,78 @@ Lemma loadimm64_correct: /\ forall r': preg, r' <> r -> r' <> GPR12 -> r' <> PC -> rs'#r' = rs#r'. Proof. intros. unfold loadimm64. - set (hi_s := Int64.sign_ext 16 (Int64.shr n (Int64.repr 16))). - set (hi' := Int64.shl hi_s (Int64.repr 16)). - destruct (Int64.eq n (low64_s n)). - (* addi *) - econstructor; split. apply exec_straight_one. simpl; eauto. auto. - rewrite Int64.add_zero_l. intuition Simpl. - (* addis + ori *) - predSpec Int64.eq Int64.eq_spec n (Int64.or hi' (low64_u n)). - econstructor; split. eapply exec_straight_two. simpl; eauto. simpl; eauto. auto. auto. - split. Simpl. rewrite Int64.add_zero_l. simpl; f_equal; auto. - intros. Simpl. - (* ldi *) - econstructor; split. apply exec_straight_one. simpl; eauto. auto. - intuition Simpl. + predSpec Int64.eq Int64.eq_spec n (Int64.sign_ext 32 n). + - destruct (loadimm64_32s_correct r n k rs m) as (rs' & A & B & C). + exists rs'; intuition auto. congruence. + - econstructor; split. apply exec_straight_one. simpl; eauto. auto. + intuition Simpl. +Qed. + +(** Alternate load int64 immediate *) + +Lemma loadimm64_notemp_correct: + forall r n k rs m, + exists rs', + exec_straight ge fn (loadimm64_notemp r n k) rs m k rs' m + /\ rs'#r = Vlong n + /\ forall r': preg, r' <> r -> r' <> PC -> rs'#r' = rs#r'. +Proof. + intros. unfold loadimm64_notemp. + predSpec Int64.eq Int64.eq_spec n (Int64.sign_ext 32 n). +- destruct (loadimm64_32s_correct r n k rs m) as (rs' & A & B & C). + exists rs'; intuition auto. congruence. +- set (n2 := Int64.shru n (Int64.repr 32)). + set (n1 := Int64.zero_ext 16 (Int64.shru n (Int64.repr 16))). + set (n0 := Int64.zero_ext 16 n). + set (mi := Int64.shl n1 (Int64.repr 16)). + set (hi := Int64.shl (Int64.sign_ext 32 n2) (Int64.repr 32)). + assert (HI: forall i, 0 <= i < Int64.zwordsize -> + Int64.testbit hi i = if zlt i 32 then false else Int64.testbit n i). + { intros. unfold hi. assert (Int64.zwordsize = 64) by auto. + rewrite Int64.bits_shl by auto. + change (Int64.unsigned (Int64.repr 32)) with 32. + destruct (zlt i 32); auto. + rewrite Int64.bits_sign_ext by omega. + rewrite zlt_true by omega. + unfold n2. rewrite Int64.bits_shru by omega. + change (Int64.unsigned (Int64.repr 32)) with 32. + rewrite zlt_true by omega. f_equal; omega. + } + assert (MI: forall i, 0 <= i < Int64.zwordsize -> + Int64.testbit mi i = + if zlt i 16 then false + else if zlt i 32 then Int64.testbit n i else false). + { intros. unfold mi. assert (Int64.zwordsize = 64) by auto. + rewrite Int64.bits_shl by auto. + change (Int64.unsigned (Int64.repr 16)) with 16. + destruct (zlt i 16); auto. + unfold n1. rewrite Int64.bits_zero_ext by omega. + rewrite Int64.bits_shru by omega. + destruct (zlt i 32). + rewrite zlt_true by omega. + change (Int64.unsigned (Int64.repr 16)) with 16. + rewrite zlt_true by omega. f_equal; omega. + rewrite zlt_false by omega. auto. + } + assert (EQ: Int64.or (Int64.or hi mi) n0 = n). + { apply Int64.same_bits_eq; intros. + rewrite ! Int64.bits_or by auto. + unfold n0; rewrite Int64.bits_zero_ext by omega. + rewrite HI, MI by auto. + destruct (zlt i 16). + rewrite zlt_true by omega. auto. + destruct (zlt i 32); rewrite ! orb_false_r; auto. + } + edestruct (loadimm64_32s_correct r n2) as (rs' & A & B & C). + econstructor; split; [|split]. + + eapply exec_straight_trans. eexact A. + eapply exec_straight_three. + simpl. rewrite B. simpl; auto. + simpl; auto. + simpl; auto. + reflexivity. reflexivity. reflexivity. + + Simpl. simpl. f_equal. rewrite <- Int64.shl_rolm by auto. exact EQ. + + intros; Simpl. Qed. (** Add int64 immediate. *) @@ -889,7 +982,7 @@ Lemma transl_cond_correct_1: (if snd (crbit_for_cond cond) then Val.of_optbool (eval_condition cond (map rs (map preg_of args)) m) else Val.notbool (Val.of_optbool (eval_condition cond (map rs (map preg_of args)) m))) - /\ forall r, important_preg r = true -> preg_notin r (destroyed_by_cond cond) -> rs'#r = rs#r. + /\ forall r, important_preg r = true -> rs'#r = rs#r. Proof. intros. Opaque Int.eq. @@ -991,20 +1084,12 @@ Opaque Int.eq. auto with asmgen. - (* Ccomplimm *) rewrite <- Val.notbool_negb_3. rewrite <- Val.negate_cmpl_bool. - destruct (Int64.eq i (low64_s i)); [|destruct (ireg_eq x GPR12)]; inv EQ0. + destruct (Int64.eq i (low64_s i)); inv EQ0. + destruct (compare_slong_spec rs (rs x) (Vlong i)) as [A [B [C D]]]. econstructor; split. apply exec_straight_one. simpl; reflexivity. reflexivity. split. case c0; simpl; auto. auto with asmgen. -+ destruct (loadimm64_correct GPR12 i (Pcmpd GPR0 GPR12 :: k) (nextinstr (rs#GPR0 <- (rs#GPR12))) m) as [rs1 [EX1 [RES1 OTH1]]]. - destruct (compare_slong_spec rs1 (rs GPR12) (Vlong i)) as [A [B [C D]]]. - assert (SAME: rs1 GPR0 = rs GPR12) by (apply OTH1; eauto with asmgen). - econstructor; split. - eapply exec_straight_step. simpl;reflexivity. reflexivity. - eapply exec_straight_trans. eexact EX1. eapply exec_straight_one. simpl;reflexivity. reflexivity. - split. rewrite RES1, SAME. destruct c0; simpl; auto. - simpl; intros. rewrite RES1, SAME. rewrite D by eauto with asmgen. rewrite OTH1 by eauto with asmgen. Simpl. -+ destruct (loadimm64_correct GPR0 i (Pcmpd x GPR0 :: k) rs m) as [rs1 [EX1 [RES1 OTH1]]]. ++ destruct (loadimm64_notemp_correct GPR0 i (Pcmpd x GPR0 :: k) rs m) as [rs1 [EX1 [RES1 OTH1]]]. destruct (compare_slong_spec rs1 (rs x) (Vlong i)) as [A [B [C D]]]. assert (SAME: rs1 x = rs x) by (apply OTH1; eauto with asmgen). econstructor; split. @@ -1013,20 +1098,12 @@ Opaque Int.eq. simpl; intros. rewrite RES1, SAME. rewrite D; eauto with asmgen. - (* Ccompluimm *) rewrite <- Val.notbool_negb_3. rewrite <- Val.negate_cmplu_bool. - destruct (Int64.eq i (low64_u i)); [|destruct (ireg_eq x GPR12)]; inv EQ0. + destruct (Int64.eq i (low64_u i)); inv EQ0. + destruct (compare_ulong_spec rs m (rs x) (Vlong i)) as [A [B [C D]]]. econstructor; split. apply exec_straight_one. simpl; reflexivity. reflexivity. split. case c0; simpl; auto. auto with asmgen. -+ destruct (loadimm64_correct GPR12 i (Pcmpld GPR0 GPR12 :: k) (nextinstr (rs#GPR0 <- (rs#GPR12))) m) as [rs1 [EX1 [RES1 OTH1]]]. - destruct (compare_ulong_spec rs1 m (rs GPR12) (Vlong i)) as [A [B [C D]]]. - assert (SAME: rs1 GPR0 = rs GPR12) by (apply OTH1; eauto with asmgen). - econstructor; split. - eapply exec_straight_step. simpl;reflexivity. reflexivity. - eapply exec_straight_trans. eexact EX1. eapply exec_straight_one. simpl;reflexivity. reflexivity. - split. rewrite RES1, SAME. destruct c0; simpl; auto. - simpl; intros. rewrite RES1, SAME. rewrite D by eauto with asmgen. rewrite OTH1 by eauto with asmgen. Simpl. -+ destruct (loadimm64_correct GPR0 i (Pcmpld x GPR0 :: k) rs m) as [rs1 [EX1 [RES1 OTH1]]]. ++ destruct (loadimm64_notemp_correct GPR0 i (Pcmpld x GPR0 :: k) rs m) as [rs1 [EX1 [RES1 OTH1]]]. destruct (compare_ulong_spec rs1 m (rs x) (Vlong i)) as [A [B [C D]]]. assert (SAME: rs1 x = rs x) by (apply OTH1; eauto with asmgen). econstructor; split. @@ -1045,7 +1122,7 @@ Lemma transl_cond_correct_2: (if snd (crbit_for_cond cond) then Val.of_bool b else Val.notbool (Val.of_bool b)) - /\ forall r, important_preg r = true -> preg_notin r (destroyed_by_cond cond) -> rs'#r = rs#r. + /\ forall r, important_preg r = true -> rs'#r = rs#r. Proof. intros. replace (Val.of_bool b) @@ -1072,7 +1149,8 @@ Proof. exploit transl_cond_correct_2. eauto. eapply eval_condition_lessdef. eapply preg_vals; eauto. eauto. eauto. intros [rs' [A [B C]]]. - exists rs'; split. eauto. split. auto. apply agree_undef_regs with rs; auto. intros r D. + exists rs'; split. eauto. split. auto. + apply agree_undef_regs with rs; auto. intros r D E. apply C. apply important_data_preg_1; auto. Qed. @@ -1180,6 +1258,66 @@ Proof. intuition Simpl. rewrite RES1. destruct (eval_condition c rs ## (preg_of ## rl) m). destruct b; auto. auto. Qed. + +Lemma transl_select_op_correct: + forall cond args ty r1 r2 rd k rs m c, + transl_select_op cond args r1 r2 rd k = OK c -> + important_preg rd = true -> important_preg r1 = true -> important_preg r2 = true -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ Val.lessdef (Val.select (eval_condition cond (map rs (map preg_of args)) m) rs#r1 rs#r2 ty) rs'#rd + /\ forall r, important_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros until c. intros TR IMP1 IMP2 IMP3. + unfold transl_select_op in TR. + destruct (ireg_eq r1 r2). + - inv TR. econstructor; split; [|split]. + + apply exec_straight_one. simpl; eauto. auto. + + Simpl. destruct (eval_condition cond rs ## (preg_of ## args) m) as [[]|]; simpl; auto using Val.lessdef_normalize. + + intros; Simpl. + - destruct (transl_cond_correct_1 cond args _ rs m _ TR) as (rs1 & A & B & C). + set (bit := fst (crbit_for_cond cond)) in *. + set (dir := snd (crbit_for_cond cond)) in *. + set (ob := eval_condition cond rs##(preg_of##args) m) in *. + econstructor; split; [|split]. + + eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. + reflexivity. + + Simpl. + rewrite <- (C r1), <- (C r2) by auto. + rewrite B, gpr_or_zero_not_zero. + destruct dir; destruct ob as [[]|]; simpl; auto using Val.lessdef_normalize. + destruct dir; intros e; subst; discriminate. + + intros. Simpl. +Qed. + +Lemma transl_fselect_op_correct: + forall cond args ty r1 r2 rd k rs m c, + transl_fselect_op cond args r1 r2 rd k = OK c -> + important_preg rd = true -> important_preg r1 = true -> important_preg r2 = true -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ Val.lessdef (Val.select (eval_condition cond (map rs (map preg_of args)) m) rs#r1 rs#r2 ty) rs'#rd + /\ forall r, important_preg r = true -> r <> rd -> rs'#r = rs#r. +Proof. + intros until c. intros TR IMP1 IMP2 IMP3. + unfold transl_fselect_op in TR. + destruct (freg_eq r1 r2). + - inv TR. econstructor; split; [|split]. + + apply exec_straight_one. simpl; eauto. auto. + + Simpl. destruct (eval_condition cond rs ## (preg_of ## args) m) as [[]|]; simpl; auto using Val.lessdef_normalize. + + intros; Simpl. + - destruct (transl_cond_correct_1 cond args _ rs m _ TR) as (rs1 & A & B & C). + set (bit := fst (crbit_for_cond cond)) in *. + set (dir := snd (crbit_for_cond cond)) in *. + set (ob := eval_condition cond rs##(preg_of##args) m) in *. + econstructor; split; [|split]. + + eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. + reflexivity. + + Simpl. + rewrite <- (C r1), <- (C r2) by auto. + rewrite B. destruct dir; destruct ob as [[]|]; simpl; auto using Val.lessdef_normalize. + + intros. Simpl. +Qed. (** Translation of arithmetic operations. *) @@ -1377,6 +1515,18 @@ Opaque Val.add. (* Ocmp *) - destruct (transl_cond_op_correct c0 args res k rs m c) as [rs' [A [B C]]]; auto. exists rs'; auto with asmgen. + (* Osel *) +- assert (X: forall mr r, ireg_of mr = OK r -> important_preg r = true). + { intros. apply ireg_of_eq in H0. apply important_data_preg_1. rewrite <- H0. + auto with asmgen. } + assert (Y: forall mr r, freg_of mr = OK r -> important_preg r = true). + { intros. apply freg_of_eq in H0. apply important_data_preg_1. rewrite <- H0. + auto with asmgen. } + destruct (preg_of res) eqn:RES; monadInv H; rewrite <- RES. + + rewrite (ireg_of_eq _ _ EQ), (ireg_of_eq _ _ EQ0), (ireg_of_eq _ _ EQ1) in *. + destruct (transl_select_op_correct _ _ t _ _ _ _ rs m _ EQ3) as (rs' & A & B & C); eauto. + + rewrite (freg_of_eq _ _ EQ), (freg_of_eq _ _ EQ0), (freg_of_eq _ _ EQ1) in *. + destruct (transl_fselect_op_correct _ _ t _ _ _ _ rs m _ EQ3) as (rs' & A & B & C); eauto. Qed. Lemma transl_op_correct: diff --git a/powerpc/Builtins1.v b/powerpc/Builtins1.v new file mode 100644 index 00000000..53c83d7e --- /dev/null +++ b/powerpc/Builtins1.v @@ -0,0 +1,33 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Platform-specific built-in functions *) + +Require Import String Coqlib. +Require Import AST Integers Floats Values. +Require Import Builtins0. + +Inductive platform_builtin : Type := . + +Local Open Scope string_scope. + +Definition platform_builtin_table : list (string * platform_builtin) := + nil. + +Definition platform_builtin_sig (b: platform_builtin) : signature := + match b with end. + +Definition platform_builtin_sem (b: platform_builtin) : builtin_sem (sig_res (platform_builtin_sig b)) := + match b with end. diff --git a/powerpc/CBuiltins.ml b/powerpc/CBuiltins.ml index 11b7aef9..e29a41f1 100644 --- a/powerpc/CBuiltins.ml +++ b/powerpc/CBuiltins.ml @@ -18,11 +18,11 @@ open C let builtins = { - Builtins.typedefs = [ + builtin_typedefs = [ "__builtin_va_list", TArray(TInt(IUInt, []), Some 3L, []) ]; - Builtins.functions = [ + builtin_functions = [ (* Integer arithmetic *) "__builtin_mulhw", (TInt(IInt, []), [TInt(IInt, []); TInt(IInt, [])], false); diff --git a/powerpc/ConstpropOp.vp b/powerpc/ConstpropOp.vp index 2d492b66..8e90a849 100644 --- a/powerpc/ConstpropOp.vp +++ b/powerpc/ConstpropOp.vp @@ -16,13 +16,14 @@ Require Import Coqlib Compopts. Require Import AST Integers Floats. Require Import Op Registers. -Require Import ValueDomain. +Require Import ValueDomain ValueAOp. (** * Converting known values to constants *) Definition const_for_result (a: aval) : option operation := match a with | I n => Some(Ointconst n) + | L n => if Archi.ppc64 then Some (Olongconst n) else None | F n => if Compopts.generate_float_constants tt then Some(Ofloatconst n) else None | FS n => if Compopts.generate_float_constants tt then Some(Osingleconst n) else None | Ptr(Gl id ofs) => Some (Oaddrsymbol id ofs) @@ -95,6 +96,15 @@ Nondetfunction make_cmp (c: condition) (args: list reg) (vl: list aval) := make_cmp_base c args vl end. +Definition make_select (c: condition) (ty: typ) + (r1 r2: reg) (args: list reg) (vl: list aval) := + match resolve_branch (eval_static_condition c vl) with + | Some b => (Omove, (if b then r1 else r2) :: nil) + | None => + let (c', args') := cond_strength_reduction c args vl in + (Osel c' ty, r1 :: r2 :: args') + end. + Definition make_addimm (n: int) (r: reg) := if Int.eq n Int.zero then (Omove, r :: nil) @@ -303,6 +313,7 @@ Nondetfunction op_strength_reduction | Oshrl, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shrlimm n2 r1 r2 | Oshrlu, r1 :: r2 :: nil, v1 :: I n2 :: nil => make_shrluimm n2 r1 r2 | Ocmp c, args, vl => make_cmp c args vl + | Osel c ty, r1 :: r2 :: args, v1 :: v2 :: vl => make_select c ty r1 r2 args vl | Omulf, r1 :: r2 :: nil, v1 :: F n2 :: nil => make_mulfimm n2 r1 r1 r2 | Omulf, r1 :: r2 :: nil, F n1 :: v2 :: nil => make_mulfimm n1 r2 r1 r2 | Omulfs, r1 :: r2 :: nil, v1 :: FS n2 :: nil => make_mulfsimm n2 r1 r1 r2 diff --git a/powerpc/ConstpropOpproof.v b/powerpc/ConstpropOpproof.v index fe061e5b..8687b056 100644 --- a/powerpc/ConstpropOpproof.v +++ b/powerpc/ConstpropOpproof.v @@ -14,7 +14,7 @@ Require Import Coqlib Compopts. Require Import Integers Floats Values Memory Globalenvs Events. -Require Import Op Registers RTL ValueDomain. +Require Import Op Registers RTL ValueDomain ValueAOp ValueAnalysis. Require Import ConstpropOp. Local Transparent Archi.ptr64. @@ -101,6 +101,8 @@ Proof. destruct a; inv H; SimplVM. - (* integer *) exists (Vint n); auto. +- (* long *) + destruct (Archi.ppc64); inv H2. exists (Vlong n); auto. - (* float *) destruct (generate_float_constants tt); inv H2. exists (Vfloat f); auto. - (* single *) @@ -211,6 +213,28 @@ Proof. - apply make_cmp_base_correct; auto. Qed. +Lemma make_select_correct: + forall c ty r1 r2 args vl, + vl = map (fun r => AE.get r ae) args -> + let (op', args') := make_select c ty r1 r2 args vl in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op' rs##args' m = Some v + /\ Val.lessdef (Val.select (eval_condition c rs##args m) rs#r1 rs#r2 ty) v. +Proof. + unfold make_select; intros. + destruct (resolve_branch (eval_static_condition c vl)) as [b|] eqn:RB. +- exists (if b then rs#r1 else rs#r2); split. ++ simpl. destruct b; auto. ++ destruct (eval_condition c rs##args m) as [b'|] eqn:EC; simpl; auto. + assert (b = b'). + { eapply resolve_branch_sound; eauto. + rewrite <- EC. apply eval_static_condition_sound with bc. + subst vl. exact (aregs_sound _ _ _ args MATCH). } + subst b'. apply Val.lessdef_normalize. +- generalize (cond_strength_reduction_correct c args vl H). + destruct (cond_strength_reduction c args vl) as [cond' args']; intros EQ. + econstructor; split. simpl; eauto. rewrite EQ; auto. +Qed. + Lemma make_addimm_correct: forall n r, let (op, args) := make_addimm n r in @@ -715,6 +739,8 @@ Proof. InvApproxRegs; SimplVM; inv H0. apply make_shrluimm_correct; auto. (* cmp *) inv H0. apply make_cmp_correct; auto. +(* select *) + inv H0. apply make_select_correct; congruence. (* mulf *) InvApproxRegs; SimplVM; inv H0. rewrite <- H2. apply make_mulfimm_correct; auto. InvApproxRegs; SimplVM; inv H0. fold (Val.mulf (Vfloat n1) rs#r2). diff --git a/powerpc/Conventions1.v b/powerpc/Conventions1.v index 1de55c1a..5c9cbd4f 100644 --- a/powerpc/Conventions1.v +++ b/powerpc/Conventions1.v @@ -117,18 +117,16 @@ Definition dummy_float_reg := F0. (**r Used in [Coloring]. *) We treat a function without result as a function with one integer result. *) Definition loc_result_32 (s: signature) : rpair mreg := - match s.(sig_res) with - | None => One R3 - | Some (Tint | Tany32) => One R3 - | Some (Tfloat | Tsingle | Tany64) => One F1 - | Some Tlong => Twolong R3 R4 + match proj_sig_res s with + | Tint | Tany32 => One R3 + | Tfloat | Tsingle | Tany64 => One F1 + | Tlong => Twolong R3 R4 end. Definition loc_result_64 (s: signature) : rpair mreg := - match s.(sig_res) with - | None => One R3 - | Some (Tint | Tlong | Tany32 | Tany64) => One R3 - | Some (Tfloat | Tsingle) => One F1 + match proj_sig_res s with + | Tint | Tlong | Tany32 | Tany64 => One R3 + | Tfloat | Tsingle => One F1 end. Definition loc_result := @@ -140,8 +138,8 @@ Lemma loc_result_type: forall sig, subtype (proj_sig_res sig) (typ_rpair mreg_type (loc_result sig)) = true. Proof. - intros. unfold proj_sig_res, loc_result, loc_result_32, loc_result_64, mreg_type. - destruct Archi.ptr64 eqn:?; destruct (sig_res sig) as [[]|]; destruct Archi.ppc64; simpl; auto. + intros. unfold loc_result, loc_result_32, loc_result_64, mreg_type. + destruct Archi.ptr64 eqn:?; destruct (proj_sig_res sig); destruct Archi.ppc64; simpl; auto. Qed. (** The result locations are caller-save registers *) @@ -151,7 +149,7 @@ Lemma loc_result_caller_save: forall_rpair (fun r => is_callee_save r = false) (loc_result s). Proof. intros. unfold loc_result, loc_result_32, loc_result_64, is_callee_save; - destruct Archi.ptr64; destruct (sig_res s) as [[]|]; simpl; auto. + destruct Archi.ptr64; destruct (proj_sig_res s); simpl; auto. Qed. (** If the result is in a pair of registers, those registers are distinct and have type [Tint] at least. *) @@ -161,13 +159,13 @@ Lemma loc_result_pair: match loc_result sg with | One _ => True | Twolong r1 r2 => - r1 <> r2 /\ sg.(sig_res) = Some Tlong + r1 <> r2 /\ proj_sig_res sg = Tlong /\ subtype Tint (mreg_type r1) = true /\ subtype Tint (mreg_type r2) = true /\ Archi.ptr64 = false end. Proof. intros; unfold loc_result, loc_result_32, loc_result_64, mreg_type; - destruct Archi.ptr64; destruct (sig_res sg) as [[]|]; destruct Archi.ppc64; simpl; auto. + destruct Archi.ptr64; destruct (proj_sig_res sg); destruct Archi.ppc64; simpl; auto. split; auto. congruence. split; auto. congruence. Qed. @@ -177,7 +175,7 @@ Qed. Lemma loc_result_exten: forall s1 s2, s1.(sig_res) = s2.(sig_res) -> loc_result s1 = loc_result s2. Proof. - intros. unfold loc_result, loc_result_32, loc_result_64. + intros. unfold loc_result, loc_result_32, loc_result_64, proj_sig_res. destruct Archi.ptr64; rewrite H; auto. Qed. @@ -210,7 +208,16 @@ Fixpoint loc_arguments_rec | Some ireg => One (R ireg) :: loc_arguments_rec tys (ir + 1) fr ofs end - | (Tfloat | Tsingle | Tany64) as ty :: tys => + | Tsingle as ty :: tys => + match list_nth_z float_param_regs fr with + | None => + let ty := if Archi.single_passed_as_single then Tsingle else Tany64 in + let ofs := align ofs (typesize ty) in + One (S Outgoing ofs ty) :: loc_arguments_rec tys ir fr (ofs + (typesize ty)) + | Some freg => + One (R freg) :: loc_arguments_rec tys ir (fr + 1) ofs + end + | (Tfloat | Tany64) as ty :: tys => match list_nth_z float_param_regs fr with | None => let ofs := align ofs 2 in @@ -238,33 +245,6 @@ Fixpoint loc_arguments_rec Definition loc_arguments (s: signature) : list (rpair loc) := loc_arguments_rec s.(sig_args) 0 0 0. -(** [size_arguments s] returns the number of [Outgoing] slots used - to call a function with signature [s]. *) - -Fixpoint size_arguments_rec (tyl: list typ) (ir fr ofs: Z) {struct tyl} : Z := - match tyl with - | nil => ofs - | (Tint | Tany32) :: tys => - match list_nth_z int_param_regs ir with - | None => size_arguments_rec tys ir fr (ofs + 1) - | Some ireg => size_arguments_rec tys (ir + 1) fr ofs - end - | (Tfloat | Tsingle | Tany64) :: tys => - match list_nth_z float_param_regs fr with - | None => size_arguments_rec tys ir fr (align ofs 2 + 2) - | Some freg => size_arguments_rec tys ir (fr + 1) ofs - end - | Tlong :: tys => - let ir := align ir 2 in - match list_nth_z int_param_regs ir, list_nth_z int_param_regs (ir + 1) with - | Some r1, Some r2 => size_arguments_rec tys (ir + 2) fr ofs - | _, _ => size_arguments_rec tys ir fr (align ofs 2 + 2) - end - end. - -Definition size_arguments (s: signature) : Z := - size_arguments_rec s.(sig_args) 0 0 0. - (** Argument locations are either caller-save registers or [Outgoing] stack slots at nonnegative offsets. *) @@ -324,12 +304,14 @@ Opaque list_nth_z. apply align_divides; omega. apply Z.divide_1_l. apply Z.divide_1_l. eapply Y; eauto. omega. - (* single *) + assert (ofs <= align ofs 1) by (apply align_le; omega). assert (ofs <= align ofs 2) by (apply align_le; omega). destruct (list_nth_z float_param_regs fr) as [r|] eqn:E; destruct H. subst. right. eapply list_nth_z_in; eauto. eapply IHtyl; eauto. - subst. split. omega. apply Z.divide_1_l. - eapply Y; eauto. omega. + subst. split. destruct Archi.single_passed_as_single; simpl; omega. + destruct Archi.single_passed_as_single; simpl; apply Z.divide_1_l. + eapply Y; eauto. destruct Archi.single_passed_as_single; simpl; omega. - (* any32 *) destruct (list_nth_z int_param_regs ir) as [r|] eqn:E; destruct H. subst. left. eapply list_nth_z_in; eauto. @@ -361,107 +343,14 @@ Qed. Hint Resolve loc_arguments_acceptable: locs. -(** The offsets of [Outgoing] arguments are below [size_arguments s]. *) - -Remark size_arguments_rec_above: - forall tyl ir fr ofs0, - ofs0 <= size_arguments_rec tyl ir fr ofs0. -Proof. - induction tyl; simpl; intros. - omega. - destruct a. - destruct (list_nth_z int_param_regs ir); eauto. apply Z.le_trans with (ofs0 + 1); auto; omega. - destruct (list_nth_z float_param_regs fr); eauto. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. - set (ir' := align ir 2). - destruct (list_nth_z int_param_regs ir'); eauto. - destruct (list_nth_z int_param_regs (ir' + 1)); eauto. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. - destruct (list_nth_z float_param_regs fr); eauto. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. - destruct (list_nth_z int_param_regs ir); eauto. apply Z.le_trans with (ofs0 + 1); auto; omega. - destruct (list_nth_z float_param_regs fr); eauto. - apply Z.le_trans with (align ofs0 2). apply align_le; omega. - apply Z.le_trans with (align ofs0 2 + 2); auto; omega. -Qed. - -Lemma size_arguments_above: - forall s, size_arguments s >= 0. -Proof. - intros; unfold size_arguments. apply Z.le_ge. - apply size_arguments_rec_above. -Qed. - -Lemma loc_arguments_bounded: - forall (s: signature) (ofs: Z) (ty: typ), - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments s)) -> - ofs + typesize ty <= size_arguments s. -Proof. - intros. - assert (forall tyl ir fr ofs0, - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments_rec tyl ir fr ofs0)) -> - ofs + typesize ty <= size_arguments_rec tyl ir fr ofs0). -{ - induction tyl; simpl; intros. - elim H0. - destruct a. -- (* int *) - destruct (list_nth_z int_param_regs ir); destruct H0. - congruence. - eauto. - inv H0. apply size_arguments_rec_above. - eauto. -- (* float *) - destruct (list_nth_z float_param_regs fr); destruct H0. - congruence. - eauto. - inv H0. apply size_arguments_rec_above. eauto. -- (* long *) - set (ir' := align ir 2) in *. - assert (DFL: - In (S Outgoing ofs ty) (regs_of_rpairs - ((if Archi.ptr64 - then One (S Outgoing (align ofs0 2) Tlong) - else Twolong (S Outgoing (align ofs0 2) Tint) - (S Outgoing (align ofs0 2 + 1) Tint)) - :: loc_arguments_rec tyl ir' fr (align ofs0 2 + 2))) -> - ofs + typesize ty <= size_arguments_rec tyl ir' fr (align ofs0 2 + 2)). - { destruct Archi.ptr64; intros IN. - - destruct IN. inv H1. apply size_arguments_rec_above. auto. - - destruct IN. inv H1. transitivity (align ofs0 2 + 2). simpl; omega. apply size_arguments_rec_above. - destruct H1. inv H1. transitivity (align ofs0 2 + 2). simpl; omega. apply size_arguments_rec_above. - auto. } - destruct (list_nth_z int_param_regs ir'); auto. - destruct (list_nth_z int_param_regs (ir' + 1)); auto. - destruct H0. congruence. destruct H0. congruence. eauto. -- (* single *) - destruct (list_nth_z float_param_regs fr); destruct H0. - congruence. - eauto. - inv H0. transitivity (align ofs0 2 + 2). simpl; omega. apply size_arguments_rec_above. - eauto. -- (* any32 *) - destruct (list_nth_z int_param_regs ir); destruct H0. - congruence. - eauto. - inv H0. apply size_arguments_rec_above. - eauto. -- (* any64 *) - destruct (list_nth_z float_param_regs fr); destruct H0. - congruence. - eauto. - inv H0. apply size_arguments_rec_above. eauto. - } - eauto. -Qed. - Lemma loc_arguments_main: loc_arguments signature_main = nil. Proof. reflexivity. Qed. + +(** ** Normalization of function results *) + +(** No normalization needed. *) + +Definition return_value_needs_normalization (t: rettype) := false. diff --git a/powerpc/Machregs.v b/powerpc/Machregs.v index 53d99e2f..e7c8758b 100644 --- a/powerpc/Machregs.v +++ b/powerpc/Machregs.v @@ -159,11 +159,7 @@ Definition register_by_name (s: string) : option mreg := (** ** Destroyed registers, preferred registers *) -Definition destroyed_by_cond (cond: condition): list mreg := - match cond with - | Ccomplimm _ _ | Ccompluimm _ _ => R12 :: nil - | _ => nil - end. +Definition destroyed_by_cond (cond: condition): list mreg := nil. Definition destroyed_by_op (op: operation): list mreg := match op with diff --git a/powerpc/NeedOp.v b/powerpc/NeedOp.v index 9a579cc5..5ea09bd8 100644 --- a/powerpc/NeedOp.v +++ b/powerpc/NeedOp.v @@ -65,6 +65,7 @@ Definition needs_of_operation (op: operation) (nv: nval): list nval := | Ofloatofwords | Omakelong => op2 (default nv) | Olowlong | Ohighlong => op1 (default nv) | Ocmp c => needs_of_condition c + | Osel c ty => nv :: nv :: needs_of_condition c end. Definition operation_is_redundant (op: operation) (nv: nval): bool := @@ -147,6 +148,10 @@ Proof. erewrite needs_of_condition_sound by eauto. subst v; simpl. auto with na. subst v; auto with na. +- destruct (eval_condition c args m) as [b|] eqn:EC. + erewrite needs_of_condition_sound by eauto. + apply select_sound; auto. + simpl; auto with na. Qed. Lemma operation_is_redundant_sound: diff --git a/powerpc/Op.v b/powerpc/Op.v index e6f942c1..0f082c1f 100644 --- a/powerpc/Op.v +++ b/powerpc/Op.v @@ -150,8 +150,9 @@ Inductive operation : Type := | Olowlong: operation (**r [rd = low-word(r1)] *) | Ohighlong: operation (**r [rd = high-word(r1)] *) (*c Boolean tests: *) - | Ocmp: condition -> operation. (**r [rd = 1] if condition holds, [rd = 0] otherwise. *) - + | Ocmp: condition -> operation (**r [rd = 1] if condition holds, [rd = 0] otherwise. *) + | Osel: condition -> typ -> operation. + (**r [rd = rs1] if condition holds, [rd = rs2] otherwise. *) (** Addressing modes. [r1], [r2], etc, are the arguments to the addressing. *) @@ -173,7 +174,7 @@ Proof. Defined. Definition beq_operation: forall (x y: operation), bool. - generalize Int.eq_dec Int64.eq_dec Ptrofs.eq_dec ident_eq Float.eq_dec Float32.eq_dec eq_condition; boolean_equality. + generalize Int.eq_dec Int64.eq_dec Ptrofs.eq_dec ident_eq Float.eq_dec Float32.eq_dec typ_eq eq_condition; boolean_equality. Defined. Definition eq_operation (x y: operation): {x=y} + {x<>y}. @@ -306,6 +307,7 @@ Definition eval_operation | Olowlong, v1::nil => Some(Val.loword v1) | Ohighlong, v1::nil => Some(Val.hiword v1) | Ocmp c, _ => Some(Val.of_optbool (eval_condition c vl m)) + | Osel c ty, v1::v2::vl => Some(Val.select (eval_condition c vl m) v1 v2 ty) | _, _ => None end. @@ -455,6 +457,7 @@ Definition type_of_operation (op: operation) : list typ * typ := | Olowlong => (Tlong :: nil, Tint) | Ohighlong => (Tlong :: nil, Tint) | Ocmp c => (type_of_condition c, Tint) + | Osel c ty => (ty :: ty :: type_of_condition c, ty) end. Definition type_of_addressing (addr: addressing) : list typ := @@ -575,6 +578,7 @@ Proof with (try exact I; try reflexivity). destruct v0... destruct v0... destruct (eval_condition c vl m); simpl... destruct b... + unfold Val.select. destruct (eval_condition c vl m). apply Val.normalize_type. exact I. Qed. End SOUNDNESS. @@ -727,22 +731,40 @@ Definition is_trivial_op (op: operation) : bool := (** Operations that depend on the memory state. *) +Definition condition_depends_on_memory (c: condition) : bool := + match c with + | Ccompu _ => true + | Ccompuimm _ _ => true + | Ccomplu _ => Archi.ppc64 + | Ccompluimm _ _ => Archi.ppc64 + | _ => false + end. + Definition op_depends_on_memory (op: operation) : bool := match op with - | Ocmp (Ccompu _) => true - | Ocmp (Ccompuimm _ _) => true - | Ocmp (Ccomplu _) => Archi.ppc64 - | Ocmp (Ccompluimm _ _) => Archi.ppc64 + | Ocmp c => condition_depends_on_memory c + | Osel c ty => condition_depends_on_memory c | _ => false end. +Lemma condition_depends_on_memory_correct: + forall c args m1 m2, + condition_depends_on_memory c = false -> + eval_condition c args m1 = eval_condition c args m2. +Proof. + intros. destruct c; simpl; auto; discriminate. +Qed. + Lemma op_depends_on_memory_correct: forall (F V: Type) (ge: Genv.t F V) sp op args m1 m2, op_depends_on_memory op = false -> eval_operation ge sp op args m1 = eval_operation ge sp op args m2. Proof. - intros until m2. destruct op; simpl; try congruence. unfold eval_condition. - destruct c; simpl; auto; try discriminate. + intros until m2. destruct op; simpl; try congruence; intros C. +- f_equal; f_equal; apply condition_depends_on_memory_correct; auto. +- destruct args; auto. destruct args; auto. + rewrite (condition_depends_on_memory_correct c args m1 m2 C). + auto. Qed. (** Global variables mentioned in an operation or addressing mode *) @@ -989,6 +1011,9 @@ Proof. exploit eval_condition_inj; eauto. intros EQ; rewrite EQ. destruct b; simpl; constructor. simpl; constructor. + apply Val.select_inject; auto. + destruct (eval_condition c vl1 m1) eqn:?; auto. + right; symmetry; eapply eval_condition_inj; eauto. Qed. Lemma eval_addressing_inj: diff --git a/powerpc/PrintOp.ml b/powerpc/PrintOp.ml index cffaafdb..8d7f17ab 100644 --- a/powerpc/PrintOp.ml +++ b/powerpc/PrintOp.ml @@ -110,6 +110,10 @@ let print_operation reg pp = function | Olowlong, [r1] -> fprintf pp "lowlong(%a)" reg r1 | Ohighlong, [r1] -> fprintf pp "highlong(%a)" reg r1 | Ocmp c, args -> print_condition reg pp (c, args) + | Osel (c, ty), r1::r2::args -> + fprintf pp "%a ?%s %a : %a" + (print_condition reg) (c, args) + (PrintAST.name_of_type ty) reg r1 reg r2 | Olongconst n, [] -> fprintf pp "%LdL" (camlint64_of_coqint n) | Ocast32signed, [r1] -> fprintf pp "int32signed(%a)" reg r1 | Ocast32unsigned, [r1] -> fprintf pp "int32unsigned(%a)" reg r1 diff --git a/powerpc/SelectLongproof.v b/powerpc/SelectLongproof.v index a214d131..f16c967e 100644 --- a/powerpc/SelectLongproof.v +++ b/powerpc/SelectLongproof.v @@ -12,7 +12,7 @@ (** Correctness of instruction selection for 64-bit integer operations *) -Require Import String Coqlib Maps Integers Floats Errors. +Require Import String Coqlib Maps Zbits Integers Floats Errors. Require Archi. Require Import AST Values Memory Globalenvs Events. Require Import Cminor Op CminorSel. @@ -221,11 +221,11 @@ Proof. change (Int64.unsigned Int64.iwordsize) with 64. f_equal. rewrite Int.unsigned_repr. - apply Int.eqmod_mod_eq. omega. - apply Int.eqmod_trans with a. - apply Int.eqmod_divides with Int.modulus. apply Int.eqm_sym. apply Int.eqm_unsigned_repr. + apply eqmod_mod_eq. omega. + apply eqmod_trans with a. + apply eqmod_divides with Int.modulus. apply Int.eqm_sym. apply Int.eqm_unsigned_repr. exists (two_p (32-6)); auto. - apply Int.eqmod_divides with Int64.modulus. apply Int64.eqm_unsigned_repr. + apply eqmod_divides with Int64.modulus. apply Int64.eqm_unsigned_repr. exists (two_p (64-6)); auto. assert (0 <= Int.unsigned (Int.repr a) mod 64 < 64) by (apply Z_mod_lt; omega). assert (64 < Int.max_unsigned) by (compute; auto). diff --git a/powerpc/SelectOp.vp b/powerpc/SelectOp.vp index d2ca408a..ba6612e8 100644 --- a/powerpc/SelectOp.vp +++ b/powerpc/SelectOp.vp @@ -38,11 +38,9 @@ Require Import Coqlib. Require Import Compopts. -Require Import AST. -Require Import Integers. -Require Import Floats. -Require Import Op. -Require Import CminorSel. +Require Import AST Integers Floats Builtins. +Require Import Op CminorSel. +Require Archi. Local Open Scope cminorsel_scope. @@ -516,6 +514,19 @@ Definition singleofintu (e: expr) := Definition singleoffloat (e: expr) := Eop Osingleoffloat (e ::: Enil). Definition floatofsingle (e: expr) := Eop Ofloatofsingle (e ::: Enil). +(** ** Selection *) + +Definition select (ty: typ) (cond: condition) (args: exprlist) (e1 e2: expr) := + if match ty with + | Tint => true + | Tfloat => true + | Tsingle => true + | Tlong => Archi.ppc64 + | _ => false + end + then Some (Eop (Osel cond ty) (e1 ::: e2 ::: args)) + else None. + (** ** Recognition of addressing modes for load and store operations *) Definition can_use_Aindexed2 (chunk: memory_chunk): bool := @@ -552,3 +563,8 @@ Nondetfunction builtin_arg (e: expr) := | Eop Oadd (e1:::e2:::Enil) => BA_addptr (BA e1) (BA e2) | _ => BA e end. + +(** Platform-specific known builtins *) + +Definition platform_builtin (b: platform_builtin) (args: exprlist) : option expr := + None. diff --git a/powerpc/SelectOpproof.v b/powerpc/SelectOpproof.v index 5f87d9b9..c3eda068 100644 --- a/powerpc/SelectOpproof.v +++ b/powerpc/SelectOpproof.v @@ -13,17 +13,10 @@ (** Correctness of instruction selection for operators *) Require Import Coqlib. -Require Import Maps. +Require Import AST Integers Floats. +Require Import Values Memory Builtins Globalenvs. +Require Import Cminor Op CminorSel. Require Import Compopts. -Require Import AST. -Require Import Integers. -Require Import Floats. -Require Import Values. -Require Import Memory. -Require Import Globalenvs. -Require Import Cminor. -Require Import Op. -Require Import CminorSel. Require Import SelectOp. Local Open Scope cminorsel_scope. @@ -812,7 +805,7 @@ Qed. Theorem eval_cast8unsigned: unary_constructor_sound cast8unsigned (Val.zero_ext 8). Proof. red; intros. unfold cast8unsigned. - rewrite Val.zero_ext_and. apply eval_andimm; auto. compute; auto. + rewrite Val.zero_ext_and. apply eval_andimm; auto. omega. Qed. Theorem eval_cast16signed: unary_constructor_sound cast16signed (Val.sign_ext 16). @@ -825,7 +818,7 @@ Qed. Theorem eval_cast16unsigned: unary_constructor_sound cast16unsigned (Val.zero_ext 16). Proof. red; intros. unfold cast16unsigned. - rewrite Val.zero_ext_and. apply eval_andimm; auto. compute; auto. + rewrite Val.zero_ext_and. apply eval_andimm; auto. omega. Qed. Theorem eval_singleoffloat: unary_constructor_sound singleoffloat Val.singleoffloat. @@ -1000,6 +993,27 @@ Proof. exists (Val.singleoffloat v); split. EvalOp. inv D; auto. Qed. +Theorem eval_select: + forall le ty cond al vl a1 v1 a2 v2 a b, + select ty cond al a1 a2 = Some a -> + eval_exprlist ge sp e m le al vl -> + eval_expr ge sp e m le a1 v1 -> + eval_expr ge sp e m le a2 v2 -> + eval_condition cond vl m = Some b -> + + exists v, + eval_expr ge sp e m le a v + /\ Val.lessdef (Val.select (Some b) v1 v2 ty) v. +Proof. + unfold select; intros. + destruct (match ty with Tint => true | Tfloat => true | Tsingle => true | Tlong => Archi.ppc64 | _ => false end); inv H. + exists (Val.select (Some b) v1 v2 ty); split. + apply eval_Eop with (v1 :: v2 :: vl). + constructor; auto. constructor; auto. + simpl. rewrite H3; auto. + auto. +Qed. + Theorem eval_addressing: forall le chunk a v b ofs, eval_expr ge sp e m le a v -> @@ -1044,4 +1058,16 @@ Proof. - constructor; auto. Qed. +(** Platform-specific known builtins *) + +Theorem eval_platform_builtin: + forall bf al a vl v le, + platform_builtin bf al = Some a -> + eval_exprlist ge sp e m le al vl -> + platform_builtin_sem bf vl = Some v -> + exists v', eval_expr ge sp e m le a v' /\ Val.lessdef v v'. +Proof. + intros. discriminate. +Qed. + End CMCONSTR. diff --git a/powerpc/Stacklayout.v b/powerpc/Stacklayout.v index cb3806bd..d5539b70 100644 --- a/powerpc/Stacklayout.v +++ b/powerpc/Stacklayout.v @@ -68,7 +68,7 @@ Lemma frame_env_separated: ** range sp (fe_ofs_callee_save fe) (size_callee_save_area b (fe_ofs_callee_save fe)) ** P. Proof. -Local Opaque Z.add Z.mul sepconj range. +Local Opaque Z.add Z.mul sepconj range'. intros; simpl. set (ol := align (8 + 4 * b.(bound_outgoing)) 8). set (ora := ol + 4 * b.(bound_local)). diff --git a/powerpc/TargetPrinter.ml b/powerpc/TargetPrinter.ml index c1aaa55d..0f608d25 100644 --- a/powerpc/TargetPrinter.ml +++ b/powerpc/TargetPrinter.ml @@ -118,13 +118,22 @@ module Linux_System : SYSTEM = let name_of_section = function | Section_text -> ".text" | Section_data i -> - if i then ".data" else "COMM" + if i then + ".data" + else + common_section ~sec:".section .bss" () | Section_small_data i -> - if i then ".section .sdata,\"aw\",@progbits" else "COMM" + if i then + ".section .sdata,\"aw\",@progbits" + else + common_section ~sec:".section .sbss,\"aw\",@nobits" () | Section_const i -> - if i then ".rodata" else "COMM" + if i || (not !Clflags.option_fcommon) then ".rodata" else "COMM" | Section_small_const i -> - if i then ".section .sdata2,\"a\",@progbits" else "COMM" + if i || (not !Clflags.option_fcommon) then + ".section .sdata2,\"a\",@progbits" + else + "COMM" | Section_string -> ".rodata" | Section_literal -> ".section .rodata.cst8,\"aM\",@progbits,8" | Section_jumptable -> ".text" @@ -209,7 +218,7 @@ module Diab_System : SYSTEM = let name_of_section = function | Section_text -> ".text" - | Section_data i -> if i then ".data" else "COMM" + | Section_data i -> if i then ".data" else common_section () | Section_small_data i -> if i then ".sdata" else ".sbss" | Section_const _ -> ".text" | Section_small_const _ -> ".sdata2" @@ -331,7 +340,7 @@ module Target (System : SYSTEM):TARGET = let ireg_or_zero oc r = if r = GPR0 then output_string oc "0" else ireg oc r - let preg oc = function + let preg_asm oc ty = function | IR r -> ireg oc r | FR r -> freg oc r | _ -> assert false @@ -604,6 +613,7 @@ module Target (System : SYSTEM):TARGET = fprintf oc " fsel %a, %a, %a, %a\n" freg r1 freg r2 freg r3 freg r4 | Pisel (r1,r2,r3,cr) -> fprintf oc " isel %a, %a, %a, %a\n" ireg r1 ireg r2 ireg r3 crbit cr + | Pfsel_gen _ -> assert false | Picbi (r1,r2) -> fprintf oc " icbi %a, %a\n" ireg r1 ireg r2 | Picbtls (n,r1,r2) -> @@ -853,7 +863,7 @@ module Target (System : SYSTEM):TARGET = (P.to_int kind) (extern_atom txt) args | EF_inline_asm(txt, sg, clob) -> fprintf oc "%s begin inline assembly\n\t" comment; - print_inline_asm preg oc (camlstring_of_coqstring txt) sg args res; + print_inline_asm preg_asm oc (camlstring_of_coqstring txt) sg args res; fprintf oc "%s end inline assembly\n" comment | _ -> assert false diff --git a/powerpc/ValueAOp.v b/powerpc/ValueAOp.v index f7f65e9e..a270d857 100644 --- a/powerpc/ValueAOp.v +++ b/powerpc/ValueAOp.v @@ -141,6 +141,7 @@ Definition eval_static_operation (op: operation) (vl: list aval): aval := | Olowlong, v1::nil => loword v1 | Ohighlong, v1::nil => hiword v1 | Ocmp c, _ => of_optbool (eval_static_condition c vl) + | Osel c ty, v1::v2::vl => select (eval_static_condition c vl) v1 v2 | _, _ => Vbot end. @@ -211,6 +212,7 @@ Proof. apply rolml_sound; auto. apply floatofwords_sound; auto. apply of_optbool_sound. eapply eval_static_condition_sound; eauto. + apply select_sound; auto. eapply eval_static_condition_sound; eauto. Qed. End SOUNDNESS. diff --git a/powerpc/extractionMachdep.v b/powerpc/extractionMachdep.v index 7482435f..a3e945bf 100644 --- a/powerpc/extractionMachdep.v +++ b/powerpc/extractionMachdep.v @@ -34,3 +34,6 @@ Extract Constant Archi.ppc64 => | ""e5500"" -> true | _ -> false end". + +(* Choice of passing of single *) +Extract Constant Archi.single_passed_as_single => "Configuration.gnu_toolchain". diff --git a/riscV/Archi.v b/riscV/Archi.v index 3758d686..61d129d0 100644 --- a/riscV/Archi.v +++ b/riscV/Archi.v @@ -16,7 +16,7 @@ (** Architecture-dependent parameters for RISC-V *) -Require Import ZArith. +Require Import ZArith List. (*From Flocq*) Require Import Binary Bits. @@ -40,26 +40,33 @@ Qed. except the MSB, a.k.a. the quiet bit." Exceptions are operations manipulating signs. *) -Definition default_nan_64 : { x : binary64 | is_nan _ _ x = true } := - exist _ (B754_nan 53 1024 false (iter_nat 51 _ xO xH) (eq_refl true)) (eq_refl true). +Definition default_nan_64 := (false, iter_nat 51 _ xO xH). +Definition default_nan_32 := (false, iter_nat 22 _ xO xH). -Definition choose_binop_pl_64 (pl1 pl2 : positive) := - false. (**r irrelevant *) +Definition choose_nan_64 (l: list (bool * positive)) : bool * positive := + default_nan_64. -Definition default_nan_32 : { x : binary32 | is_nan _ _ x = true } := - exist _ (B754_nan 24 128 false (iter_nat 22 _ xO xH) (eq_refl true)) (eq_refl true). +Definition choose_nan_32 (l: list (bool * positive)) : bool * positive := + default_nan_32. -Definition choose_binop_pl_32 (pl1 pl2 : positive) := - false. (**r irrelevant *) +Lemma choose_nan_64_idem: forall n, + choose_nan_64 (n :: n :: nil) = choose_nan_64 (n :: nil). +Proof. auto. Qed. -Definition fpu_returns_default_qNaN := true. +Lemma choose_nan_32_idem: forall n, + choose_nan_32 (n :: n :: nil) = choose_nan_32 (n :: nil). +Proof. auto. Qed. + +Definition fma_order {A: Type} (x y z: A) := (x, y, z). + +Definition fma_invalid_mul_is_nan := false. Definition float_of_single_preserves_sNaN := false. Global Opaque ptr64 big_endian splitlong - default_nan_64 choose_binop_pl_64 - default_nan_32 choose_binop_pl_32 - fpu_returns_default_qNaN + default_nan_64 choose_nan_64 + default_nan_32 choose_nan_32 + fma_order fma_invalid_mul_is_nan float_of_single_preserves_sNaN. (** Whether to generate position-independent code or not *) diff --git a/riscV/Asm.v b/riscV/Asm.v index 1d8fda11..dc410a3b 100644 --- a/riscV/Asm.v +++ b/riscV/Asm.v @@ -369,7 +369,7 @@ lbl: - [Ploadfi rd fval]: similar to [Ploadli] but loads a double FP constant fval into a float register rd. -- [Ploadsi rd fval]: similar to [Ploadli] but loads a singe FP constant fval +- [Ploadsi rd fval]: similar to [Ploadli] but loads a single FP constant fval into a float register rd. - [Pallocframe sz pos]: in the formal semantics, this diff --git a/riscV/Asmexpand.ml b/riscV/Asmexpand.ml index 3e734747..7e36abf8 100644 --- a/riscV/Asmexpand.ml +++ b/riscV/Asmexpand.ml @@ -23,7 +23,7 @@ open Asm open Asmexpandaux open AST open Camlcoq -open !Integers +open! Integers exception Error of string @@ -63,44 +63,44 @@ let expand_storeind_ptr src base ofs = let int_param_regs = [| X10; X11; X12; X13; X14; X15; X16; X17 |] let float_param_regs = [| F10; F11; F12; F13; F14; F15; F16; F17 |] -let rec fixup_variadic_call pos tyl = - if pos < 8 then +let rec fixup_variadic_call ri rf tyl = + if ri < 8 then match tyl with | [] -> () | (Tint | Tany32) :: tyl -> - fixup_variadic_call (pos + 1) tyl + fixup_variadic_call (ri + 1) rf tyl | Tsingle :: tyl -> - let rs =float_param_regs.(pos) - and rd = int_param_regs.(pos) in + let rs = float_param_regs.(rf) + and rd = int_param_regs.(ri) in emit (Pfmvxs(rd, rs)); - fixup_variadic_call (pos + 1) tyl + fixup_variadic_call (ri + 1) (rf + 1) tyl | Tlong :: tyl -> - let pos' = if Archi.ptr64 then pos + 1 else align pos 2 + 2 in - fixup_variadic_call pos' tyl + let ri' = if Archi.ptr64 then ri + 1 else align ri 2 + 2 in + fixup_variadic_call ri' rf tyl | (Tfloat | Tany64) :: tyl -> if Archi.ptr64 then begin - let rs = float_param_regs.(pos) - and rd = int_param_regs.(pos) in + let rs = float_param_regs.(rf) + and rd = int_param_regs.(ri) in emit (Pfmvxd(rd, rs)); - fixup_variadic_call (pos + 1) tyl + fixup_variadic_call (ri + 1) (rf + 1) tyl end else begin - let pos = align pos 2 in - if pos < 8 then begin - let rs = float_param_regs.(pos) - and rd1 = int_param_regs.(pos) - and rd2 = int_param_regs.(pos + 1) in + let ri = align ri 2 in + if ri < 8 then begin + let rs = float_param_regs.(rf) + and rd1 = int_param_regs.(ri) + and rd2 = int_param_regs.(ri + 1) in emit (Paddiw(X2, X X2, Integers.Int.neg _16)); emit (Pfsd(rs, X2, Ofsimm _0)); emit (Plw(rd1, X2, Ofsimm _0)); emit (Plw(rd2, X2, Ofsimm _4)); emit (Paddiw(X2, X X2, _16)); - fixup_variadic_call (pos + 2) tyl + fixup_variadic_call (ri + 2) (rf + 1) tyl end end let fixup_call sg = - if sg.sig_cc.cc_vararg then fixup_variadic_call 0 sg.sig_args + if sg.sig_cc.cc_vararg then fixup_variadic_call 0 0 sg.sig_args (* Handling of annotations *) @@ -483,7 +483,7 @@ let expand_instruction instr = emit (Pmv (X30, X2)); if sg.sig_cc.cc_vararg then begin let n = arguments_size sg in - let extra_sz = if n >= 8 then 0 else align 16 ((8 - n) * wordsize) in + let extra_sz = if n >= 8 then 0 else align ((8 - n) * wordsize) 16 in let full_sz = Z.add sz (Z.of_uint extra_sz) in expand_addptrofs X2 X2 (Ptrofs.repr (Z.neg full_sz)); expand_storeind_ptr X30 X2 ofs; @@ -501,7 +501,7 @@ let expand_instruction instr = let extra_sz = if sg.sig_cc.cc_vararg then begin let n = arguments_size sg in - if n >= 8 then 0 else align 16 ((8 - n) * wordsize) + if n >= 8 then 0 else align ((8 - n) * wordsize) 16 end else 0 in expand_addptrofs X2 X2 (Ptrofs.repr (Z.add sz (Z.of_uint extra_sz))) diff --git a/riscV/Asmgenproof1.v b/riscV/Asmgenproof1.v index 7f070c12..c20c4e49 100644 --- a/riscV/Asmgenproof1.v +++ b/riscV/Asmgenproof1.v @@ -16,7 +16,7 @@ (* *********************************************************************) Require Import Coqlib Errors Maps. -Require Import AST Integers Floats Values Memory Globalenvs. +Require Import AST Zbits Integers Floats Values Memory Globalenvs. Require Import Op Locations Mach Conventions. Require Import Asm Asmgen Asmgenproof0. @@ -33,16 +33,16 @@ Proof. predSpec Int.eq Int.eq_spec n lo. - auto. - set (m := Int.sub n lo). - assert (A: Int.eqmod (two_p 12) (Int.unsigned lo) (Int.unsigned n)) by (apply Int.eqmod_sign_ext'; compute; auto). - assert (B: Int.eqmod (two_p 12) (Int.unsigned n - Int.unsigned lo) 0). + assert (A: eqmod (two_p 12) (Int.unsigned lo) (Int.unsigned n)) by (apply Int.eqmod_sign_ext'; compute; auto). + assert (B: eqmod (two_p 12) (Int.unsigned n - Int.unsigned lo) 0). { replace 0 with (Int.unsigned n - Int.unsigned n) by omega. - auto using Int.eqmod_sub, Int.eqmod_refl. } - assert (C: Int.eqmod (two_p 12) (Int.unsigned m) 0). - { apply Int.eqmod_trans with (Int.unsigned n - Int.unsigned lo); auto. - apply Int.eqmod_divides with Int.modulus. apply Int.eqm_sym; apply Int.eqm_unsigned_repr. + auto using eqmod_sub, eqmod_refl. } + assert (C: eqmod (two_p 12) (Int.unsigned m) 0). + { apply eqmod_trans with (Int.unsigned n - Int.unsigned lo); auto. + apply eqmod_divides with Int.modulus. apply Int.eqm_sym; apply Int.eqm_unsigned_repr. exists (two_p (32-12)); auto. } assert (D: Int.modu m (Int.repr 4096) = Int.zero). - { apply Int.eqmod_mod_eq in C. unfold Int.modu. + { apply eqmod_mod_eq in C. unfold Int.modu. change (Int.unsigned (Int.repr 4096)) with (two_p 12). rewrite C. reflexivity. apply two_p_gt_ZERO; omega. } @@ -400,22 +400,6 @@ Ltac ArgsInv := | [ H: freg_of _ = OK _ |- _ ] => simpl in *; rewrite (freg_of_eq _ _ H) in * end). -Inductive exec_straight_opt: code -> regset -> mem -> code -> regset -> mem -> Prop := - | exec_straight_opt_refl: forall c rs m, - exec_straight_opt c rs m c rs m - | exec_straight_opt_intro: forall c1 rs1 m1 c2 rs2 m2, - exec_straight ge fn c1 rs1 m1 c2 rs2 m2 -> - exec_straight_opt c1 rs1 m1 c2 rs2 m2. - -Remark exec_straight_opt_right: - forall c3 rs3 m3 c1 rs1 m1 c2 rs2 m2, - exec_straight_opt c1 rs1 m1 c2 rs2 m2 -> - exec_straight ge fn c2 rs2 m2 c3 rs3 m3 -> - exec_straight ge fn c1 rs1 m1 c3 rs3 m3. -Proof. - destruct 1; intros. auto. eapply exec_straight_trans; eauto. -Qed. - Lemma transl_cbranch_correct_1: forall cond args lbl k c m ms b sp rs m', transl_cbranch cond args lbl k = OK c -> @@ -423,7 +407,7 @@ Lemma transl_cbranch_correct_1: agree ms sp rs -> Mem.extends m m' -> exists rs', exists insn, - exec_straight_opt c rs m' (insn :: k) rs' m' + exec_straight_opt ge fn c rs m' (insn :: k) rs' m' /\ exec_instr ge fn insn rs' m' = eval_branch fn lbl rs' m' (Some b) /\ forall r, r <> PC -> r <> X31 -> rs'#r = rs#r. Proof. @@ -518,7 +502,7 @@ Lemma transl_cbranch_correct_true: agree ms sp rs -> Mem.extends m m' -> exists rs', exists insn, - exec_straight_opt c rs m' (insn :: k) rs' m' + exec_straight_opt ge fn c rs m' (insn :: k) rs' m' /\ exec_instr ge fn insn rs' m' = goto_label fn lbl rs' m' /\ forall r, r <> PC -> r <> X31 -> rs'#r = rs#r. Proof. @@ -1108,7 +1092,7 @@ Lemma indexed_memory_access_correct: forall mk_instr base ofs k rs m, base <> X31 -> exists base' ofs' rs', - exec_straight_opt (indexed_memory_access mk_instr base ofs k) rs m + exec_straight_opt ge fn (indexed_memory_access mk_instr base ofs k) rs m (mk_instr base' ofs' :: k) rs' m /\ Val.offset_ptr rs'#base' (eval_offset ge ofs') = Val.offset_ptr rs#base ofs /\ forall r, r <> PC -> r <> X31 -> rs'#r = rs#r. @@ -1258,7 +1242,7 @@ Lemma transl_memory_access_correct: transl_memory_access mk_instr addr args k = OK c -> eval_addressing ge rs#SP addr (map rs (map preg_of args)) = Some v -> exists base ofs rs', - exec_straight_opt c rs m (mk_instr base ofs :: k) rs' m + exec_straight_opt ge fn c rs m (mk_instr base ofs :: k) rs' m /\ Val.offset_ptr rs'#base (eval_offset ge ofs) = v /\ forall r, r <> PC -> r <> X31 -> rs'#r = rs#r. Proof. diff --git a/riscV/Builtins1.v b/riscV/Builtins1.v new file mode 100644 index 00000000..53c83d7e --- /dev/null +++ b/riscV/Builtins1.v @@ -0,0 +1,33 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Platform-specific built-in functions *) + +Require Import String Coqlib. +Require Import AST Integers Floats Values. +Require Import Builtins0. + +Inductive platform_builtin : Type := . + +Local Open Scope string_scope. + +Definition platform_builtin_table : list (string * platform_builtin) := + nil. + +Definition platform_builtin_sig (b: platform_builtin) : signature := + match b with end. + +Definition platform_builtin_sem (b: platform_builtin) : builtin_sem (sig_res (platform_builtin_sig b)) := + match b with end. diff --git a/riscV/CBuiltins.ml b/riscV/CBuiltins.ml index 0c981d11..a2087cb7 100644 --- a/riscV/CBuiltins.ml +++ b/riscV/CBuiltins.ml @@ -18,16 +18,13 @@ open C let builtins = { - Builtins.typedefs = [ + builtin_typedefs = [ "__builtin_va_list", TPtr(TVoid [], []) ]; - Builtins.functions = [ + builtin_functions = [ (* Synchronization *) "__builtin_fence", (TVoid [], [], false); - (* Integer arithmetic *) - "__builtin_bswap64", - (TInt(IULongLong, []), [TInt(IULongLong, [])], false); (* Float arithmetic *) "__builtin_fmadd", (TFloat(FDouble, []), diff --git a/riscV/Conventions1.v b/riscV/Conventions1.v index df7ddfd2..17326139 100644 --- a/riscV/Conventions1.v +++ b/riscV/Conventions1.v @@ -105,7 +105,9 @@ Definition is_float_reg (r: mreg) := of function arguments), but this leaves much liberty in choosing actual locations. To ensure binary interoperability of code generated by our compiler with libraries compiled by another compiler, we - implement the standard RISC-V conventions. *) + implement the standard RISC-V conventions as found here: + https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md +*) (** ** Location of function result *) @@ -115,11 +117,10 @@ Definition is_float_reg (r: mreg) := with one integer result. *) Definition loc_result (s: signature) : rpair mreg := - match s.(sig_res) with - | None => One R10 - | Some (Tint | Tany32) => One R10 - | Some (Tfloat | Tsingle | Tany64) => One F10 - | Some Tlong => if Archi.ptr64 then One R10 else Twolong R11 R10 + match proj_sig_res s with + | Tint | Tany32 => One R10 + | Tfloat | Tsingle | Tany64 => One F10 + | Tlong => if Archi.ptr64 then One R10 else Twolong R11 R10 end. (** The result registers have types compatible with that given in the signature. *) @@ -128,8 +129,8 @@ Lemma loc_result_type: forall sig, subtype (proj_sig_res sig) (typ_rpair mreg_type (loc_result sig)) = true. Proof. - intros. unfold proj_sig_res, loc_result, mreg_type; - destruct (sig_res sig) as [[]|]; auto; destruct Archi.ptr64; auto. + intros. unfold loc_result, mreg_type; + destruct (proj_sig_res sig); auto; destruct Archi.ptr64; auto. Qed. (** The result locations are caller-save registers *) @@ -139,7 +140,7 @@ Lemma loc_result_caller_save: forall_rpair (fun r => is_callee_save r = false) (loc_result s). Proof. intros. unfold loc_result, is_callee_save; - destruct (sig_res s) as [[]|]; simpl; auto; destruct Archi.ptr64; simpl; auto. + destruct (proj_sig_res s); simpl; auto; destruct Archi.ptr64; simpl; auto. Qed. (** If the result is in a pair of registers, those registers are distinct and have type [Tint] at least. *) @@ -149,13 +150,13 @@ Lemma loc_result_pair: match loc_result sg with | One _ => True | Twolong r1 r2 => - r1 <> r2 /\ sg.(sig_res) = Some Tlong + r1 <> r2 /\ proj_sig_res sg = Tlong /\ subtype Tint (mreg_type r1) = true /\ subtype Tint (mreg_type r2) = true /\ Archi.ptr64 = false end. Proof. intros. - unfold loc_result; destruct (sig_res sg) as [[]|]; auto. + unfold loc_result; destruct (proj_sig_res sg); auto. unfold mreg_type; destruct Archi.ptr64; auto. split; auto. congruence. Qed. @@ -165,43 +166,37 @@ Qed. Lemma loc_result_exten: forall s1 s2, s1.(sig_res) = s2.(sig_res) -> loc_result s1 = loc_result s2. Proof. - intros. unfold loc_result. rewrite H; auto. + intros. unfold loc_result, proj_sig_res. rewrite H; auto. Qed. (** ** Location of function arguments *) -(** The RISC-V ABI states the following convention for passing arguments +(** The RISC-V ABI states the following conventions for passing arguments to a function: -- Arguments are passed in registers when possible. - -- Up to eight integer registers (ai: int_param_regs) and up to eight - floating-point registers (fai: float_param_regs) are used for this - purpose. - -- If the arguments to a function are conceptualized as fields of a C - struct, each with pointer alignment, the argument registers are a - shadow of the first eight pointer-words of that struct. If argument - i < 8 is a floating-point type, it is passed in floating-point - register fa_i; otherwise, it is passed in integer register a_i. - -- When primitive arguments twice the size of a pointer-word are passed - on the stack, they are naturally aligned. When they are passed in the - integer registers, they reside in an aligned even-odd register pair, - with the even register holding the least-significant bits. - -- Floating-point arguments to variadic functions (except those that - are explicitly named in the parameter list) are passed in integer - registers. - -- The portion of the conceptual struct that is not passed in argument - registers is passed on the stack. The stack pointer sp points to the - first argument not passed in a register. - -The bit about variadic functions doesn't quite fit CompCert's model. -We do our best by passing the FP arguments in registers, as usual, -and reserving the corresponding integer registers, so that fixup -code can be introduced in the Asmexpand pass. +- RV64, not variadic: pass the first 8 integer arguments in + integer registers (a1...a8: int_param_regs), the first 8 FP arguments + in FP registers (fa1...fa8: float_param_regs), and the remaining + arguments on the stack, in 8-byte slots. + +- RV32, not variadic: same, but arguments of 64-bit integer type + are passed in two consecutive integer registers (a(i), a(i+1)) + or in a(8) and on a 32-bit word on the stack. Stack-allocated + arguments are aligned to their natural alignment. + +- RV64, variadic: pass the first 8 arguments in integer registers + (a1...a8), including FP arguments; pass the remaining arguments on + the stack, in 8-byte slots. + +- RV32, variadic: same, but arguments of 64-bit types (integers as well + as floats) are passed in two consecutive aligned integer registers + (a(2i), a(2i+1)). + +The passing of FP arguments to variadic functions in integer registers +doesn't quite fit CompCert's model. We do our best by passing the FP +arguments in registers, as usual, and reserving the corresponding +integer registers, so that fixup code can be introduced in the +Asmexpand pass. *) Definition int_param_regs := @@ -209,80 +204,84 @@ Definition int_param_regs := Definition float_param_regs := F10 :: F11 :: F12 :: F13 :: F14 :: F15 :: F16 :: F17 :: nil. -Definition one_arg (regs: list mreg) (rn: Z) (ofs: Z) (ty: typ) - (rec: Z -> Z -> list (rpair loc)) := - match list_nth_z regs rn with +Definition int_arg (ri rf ofs: Z) (ty: typ) + (rec: Z -> Z -> Z -> list (rpair loc)) := + match list_nth_z int_param_regs ri with | Some r => - One(R r) :: rec (rn + 1) ofs + One(R r) :: rec (ri + 1) rf ofs | None => - let ofs := align ofs (typealign ty) in - One(S Outgoing ofs ty) :: rec rn (ofs + (if Archi.ptr64 then 2 else typesize ty)) + let ofs := align ofs (typesize ty) in + One(S Outgoing ofs ty) + :: rec ri rf (ofs + (if Archi.ptr64 then 2 else typesize ty)) end. -Definition two_args (regs: list mreg) (rn: Z) (ofs: Z) - (rec: Z -> Z -> list (rpair loc)) := - let rn := align rn 2 in - match list_nth_z regs rn, list_nth_z regs (rn + 1) with - | Some r1, Some r2 => - Twolong (R r2) (R r1) :: rec (rn + 2) ofs - | _, _ => - let ofs := align ofs 2 in - Twolong (S Outgoing (ofs + 1) Tint) (S Outgoing ofs Tint) :: - rec rn (ofs + 2) +Definition float_arg (va: bool) (ri rf ofs: Z) (ty: typ) + (rec: Z -> Z -> Z -> list (rpair loc)) := + match list_nth_z float_param_regs rf with + | Some r => + if va then + (let ri' := (* reserve 1 or 2 aligned integer registers *) + if Archi.ptr64 || zeq (typesize ty) 1 then ri + 1 else align ri 2 + 2 in + if zle ri' 8 then + (* we have enough integer registers, put argument in FP reg + and fixup code will put it in one or two integer regs *) + One (R r) :: rec ri' (rf + 1) ofs + else + (* we are out of integer registers, pass argument on stack *) + let ofs := align ofs (typesize ty) in + One(S Outgoing ofs ty) + :: rec ri' rf (ofs + (if Archi.ptr64 then 2 else typesize ty))) + else + One (R r) :: rec ri (rf + 1) ofs + | None => + let ofs := align ofs (typesize ty) in + One(S Outgoing ofs ty) + :: rec ri rf (ofs + (if Archi.ptr64 then 2 else typesize ty)) end. -Definition hybrid_arg (regs: list mreg) (rn: Z) (ofs: Z) (ty: typ) - (rec: Z -> Z -> list (rpair loc)) := - let rn := align rn 2 in - match list_nth_z regs rn with - | Some r => - One (R r) :: rec (rn + 2) ofs - | None => +Definition split_long_arg (va: bool) (ri rf ofs: Z) + (rec: Z -> Z -> Z -> list (rpair loc)) := + let ri := if va then align ri 2 else ri in + match list_nth_z int_param_regs ri, list_nth_z int_param_regs (ri + 1) with + | Some r1, Some r2 => + Twolong (R r2) (R r1) :: rec (ri + 2) rf ofs + | Some r1, None => + Twolong (S Outgoing ofs Tint) (R r1) :: rec (ri + 1) rf (ofs + 1) + | None, _ => let ofs := align ofs 2 in - One (S Outgoing ofs ty) :: rec rn (ofs + 2) + Twolong (S Outgoing (ofs + 1) Tint) (S Outgoing ofs Tint) :: + rec ri rf (ofs + 2) end. Fixpoint loc_arguments_rec (va: bool) - (tyl: list typ) (r ofs: Z) {struct tyl} : list (rpair loc) := + (tyl: list typ) (ri rf ofs: Z) {struct tyl} : list (rpair loc) := match tyl with | nil => nil | (Tint | Tany32) as ty :: tys => - one_arg int_param_regs r ofs ty (loc_arguments_rec va tys) + (* pass in one integer register or on stack *) + int_arg ri rf ofs ty (loc_arguments_rec va tys) | Tsingle as ty :: tys => - one_arg float_param_regs r ofs ty (loc_arguments_rec va tys) + (* pass in one FP register or on stack. + If vararg, reserve 1 integer register. *) + float_arg va ri rf ofs ty (loc_arguments_rec va tys) | Tlong as ty :: tys => - if Archi.ptr64 - then one_arg int_param_regs r ofs ty (loc_arguments_rec va tys) - else two_args int_param_regs r ofs (loc_arguments_rec va tys) + if Archi.ptr64 then + (* pass in one integer register or on stack *) + int_arg ri rf ofs ty (loc_arguments_rec va tys) + else + (* pass in register pair or on stack; align register pair if vararg *) + split_long_arg va ri rf ofs(loc_arguments_rec va tys) | (Tfloat | Tany64) as ty :: tys => - if va && negb Archi.ptr64 - then hybrid_arg float_param_regs r ofs ty (loc_arguments_rec va tys) - else one_arg float_param_regs r ofs ty (loc_arguments_rec va tys) + (* pass in one FP register or on stack. + If vararg, reserve 1 or 2 integer registers. *) + float_arg va ri rf ofs ty (loc_arguments_rec va tys) end. (** [loc_arguments s] returns the list of locations where to store arguments when calling a function with signature [s]. *) Definition loc_arguments (s: signature) : list (rpair loc) := - loc_arguments_rec s.(sig_cc).(cc_vararg) s.(sig_args) 0 0. - -(** [size_arguments s] returns the number of [Outgoing] slots used - to call a function with signature [s]. *) - -Definition max_outgoing_1 (accu: Z) (l: loc) : Z := - match l with - | S Outgoing ofs ty => Z.max accu (ofs + typesize ty) - | _ => accu - end. - -Definition max_outgoing_2 (accu: Z) (rl: rpair loc) : Z := - match rl with - | One l => max_outgoing_1 accu l - | Twolong l1 l2 => max_outgoing_1 (max_outgoing_1 accu l1) l2 - end. - -Definition size_arguments (s: signature) : Z := - List.fold_left max_outgoing_2 (loc_arguments s) 0. + loc_arguments_rec s.(sig_cc).(cc_vararg) s.(sig_args) 0 0 0. (** Argument locations are either non-temporary registers or [Outgoing] stack slots at nonnegative offsets. *) @@ -295,90 +294,87 @@ Definition loc_argument_acceptable (l: loc) : Prop := end. Lemma loc_arguments_rec_charact: - forall va tyl rn ofs p, + forall va tyl ri rf ofs p, ofs >= 0 -> - In p (loc_arguments_rec va tyl rn ofs) -> forall_rpair loc_argument_acceptable p. + In p (loc_arguments_rec va tyl ri rf ofs) -> forall_rpair loc_argument_acceptable p. Proof. set (OK := fun (l: list (rpair loc)) => forall p, In p l -> forall_rpair loc_argument_acceptable p). - set (OKF := fun (f: Z -> Z -> list (rpair loc)) => - forall rn ofs, ofs >= 0 -> OK (f rn ofs)). - set (OKREGS := fun (l: list mreg) => forall r, In r l -> is_callee_save r = false). - assert (AL: forall ofs ty, ofs >= 0 -> align ofs (typealign ty) >= 0). + set (OKF := fun (f: Z -> Z -> Z -> list (rpair loc)) => + forall ri rf ofs, ofs >= 0 -> OK (f ri rf ofs)). + assert (CSI: forall r, In r int_param_regs -> is_callee_save r = false). + { decide_goal. } + assert (CSF: forall r, In r float_param_regs -> is_callee_save r = false). + { decide_goal. } + assert (AL: forall ofs ty, ofs >= 0 -> align ofs (typesize ty) >= 0). { intros. - assert (ofs <= align ofs (typealign ty)) by (apply align_le; apply typealign_pos). + assert (ofs <= align ofs (typesize ty)) by (apply align_le; apply typesize_pos). omega. } + assert (ALD: forall ofs ty, ofs >= 0 -> (typealign ty | align ofs (typesize ty))). + { intros. eapply Z.divide_trans. apply typealign_typesize. + apply align_divides. apply typesize_pos. } assert (SK: (if Archi.ptr64 then 2 else 1) > 0). { destruct Archi.ptr64; omega. } assert (SKK: forall ty, (if Archi.ptr64 then 2 else typesize ty) > 0). { intros. destruct Archi.ptr64. omega. apply typesize_pos. } - assert (A: forall regs rn ofs ty f, - OKREGS regs -> OKF f -> ofs >= 0 -> OK (one_arg regs rn ofs ty f)). - { intros until f; intros OR OF OO; red; unfold one_arg; intros. - destruct (list_nth_z regs rn) as [r|] eqn:NTH; destruct H. - - subst p; simpl. apply OR. eapply list_nth_z_in; eauto. + assert (A: forall ri rf ofs ty f, + OKF f -> ofs >= 0 -> OK (int_arg ri rf ofs ty f)). + { intros until f; intros OF OO; red; unfold int_arg; intros. + destruct (list_nth_z int_param_regs ri) as [r|] eqn:NTH; destruct H. + - subst p; simpl. apply CSI. eapply list_nth_z_in; eauto. - eapply OF; eauto. - subst p; simpl. auto using align_divides, typealign_pos. - eapply OF; [idtac|eauto]. generalize (AL ofs ty OO) (SKK ty); omega. } - assert (B: forall regs rn ofs f, - OKREGS regs -> OKF f -> ofs >= 0 -> OK (two_args regs rn ofs f)). - { intros until f; intros OR OF OO; unfold two_args. - set (rn' := align rn 2). + assert (B: forall va ri rf ofs ty f, + OKF f -> ofs >= 0 -> OK (float_arg va ri rf ofs ty f)). + { intros until f; intros OF OO; red; unfold float_arg; intros. + destruct (list_nth_z float_param_regs rf) as [r|] eqn:NTH. + - set (ri' := if Archi.ptr64 || zeq (typesize ty) 1 then ri + 1 else align ri 2 + 2) in *. + destruct va; [destruct (zle ri' 8)|idtac]; destruct H. + + subst p; simpl. apply CSF. eapply list_nth_z_in; eauto. + + eapply OF; eauto. + + subst p; repeat split; auto. + + eapply OF; [idtac|eauto]. generalize (AL ofs ty OO) (SKK ty); omega. + + subst p; simpl. apply CSF. eapply list_nth_z_in; eauto. + + eapply OF; eauto. + - destruct H. + + subst p; repeat split; auto. + + eapply OF; [idtac|eauto]. generalize (AL ofs ty OO) (SKK ty); omega. + } + assert (C: forall va ri rf ofs f, + OKF f -> ofs >= 0 -> OK (split_long_arg va ri rf ofs f)). + { intros until f; intros OF OO; unfold split_long_arg. + set (ri' := if va then align ri 2 else ri). set (ofs' := align ofs 2). assert (OO': ofs' >= 0) by (apply (AL ofs Tlong); auto). - assert (DFL: OK (Twolong (S Outgoing (ofs' + 1) Tint) (S Outgoing ofs' Tint) - :: f rn' (ofs' + 2))). - { red; simpl; intros. destruct H. - - subst p; simpl. - repeat split; auto using Z.divide_1_l. omega. - - eapply OF; [idtac|eauto]. omega. - } - destruct (list_nth_z regs rn') as [r1|] eqn:NTH1; - destruct (list_nth_z regs (rn' + 1)) as [r2|] eqn:NTH2; - try apply DFL. - red; simpl; intros; destruct H. - - subst p; simpl. split; apply OR; eauto using list_nth_z_in. - - eapply OF; [idtac|eauto]. auto. + destruct (list_nth_z int_param_regs ri') as [r1|] eqn:NTH1; + [destruct (list_nth_z int_param_regs (ri'+1)) as [r2|] eqn:NTH2 | idtac]. + - red; simpl; intros; destruct H. + + subst p; split; apply CSI; eauto using list_nth_z_in. + + eapply OF; [idtac|eauto]. omega. + - red; simpl; intros; destruct H. + + subst p; split. split; auto using Z.divide_1_l. apply CSI; eauto using list_nth_z_in. + + eapply OF; [idtac|eauto]. omega. + - red; simpl; intros; destruct H. + + subst p; repeat split; auto using Z.divide_1_l. omega. + + eapply OF; [idtac|eauto]. omega. } - assert (C: forall regs rn ofs ty f, - OKREGS regs -> OKF f -> ofs >= 0 -> typealign ty = 1 -> OK (hybrid_arg regs rn ofs ty f)). - { intros until f; intros OR OF OO OTY; unfold hybrid_arg; red; intros. - set (rn' := align rn 2) in *. - destruct (list_nth_z regs rn') as [r|] eqn:NTH; destruct H. - - subst p; simpl. apply OR. eapply list_nth_z_in; eauto. - - eapply OF; eauto. - - subst p; simpl. rewrite OTY. split. apply (AL ofs Tlong OO). apply Z.divide_1_l. - - eapply OF; [idtac|eauto]. generalize (AL ofs Tlong OO); simpl; omega. - } - assert (D: OKREGS int_param_regs). - { red. decide_goal. } - assert (E: OKREGS float_param_regs). - { red. decide_goal. } - - cut (forall va tyl rn ofs, ofs >= 0 -> OK (loc_arguments_rec va tyl rn ofs)). + cut (forall va tyl ri rf ofs, ofs >= 0 -> OK (loc_arguments_rec va tyl ri rf ofs)). unfold OK. eauto. induction tyl as [ | ty1 tyl]; intros until ofs; intros OO; simpl. - red; simpl; tauto. - destruct ty1. + (* int *) apply A; auto. -+ (* float *) - destruct (va && negb Archi.ptr64). - apply C; auto. - apply A; auto. ++ (* float *) apply B; auto. + (* long *) destruct Archi.ptr64. apply A; auto. - apply B; auto. -+ (* single *) - apply A; auto. -+ (* any32 *) - apply A; auto. -+ (* any64 *) - destruct (va && negb Archi.ptr64). apply C; auto. - apply A; auto. ++ (* single *) apply B; auto. ++ (* any32 *) apply A; auto. ++ (* any64 *) apply B; auto. Qed. Lemma loc_arguments_acceptable: @@ -388,54 +384,14 @@ Proof. unfold loc_arguments; intros. eapply loc_arguments_rec_charact; eauto. omega. Qed. -(** The offsets of [Outgoing] arguments are below [size_arguments s]. *) - -Remark fold_max_outgoing_above: - forall l n, fold_left max_outgoing_2 l n >= n. -Proof. - assert (A: forall n l, max_outgoing_1 n l >= n). - { intros; unfold max_outgoing_1. destruct l as [_ | []]; xomega. } - induction l; simpl; intros. - - omega. - - eapply Zge_trans. eauto. - destruct a; simpl. apply A. eapply Zge_trans; eauto. -Qed. - -Lemma size_arguments_above: - forall s, size_arguments s >= 0. -Proof. - intros. apply fold_max_outgoing_above. -Qed. - -Lemma loc_arguments_bounded: - forall (s: signature) (ofs: Z) (ty: typ), - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments s)) -> - ofs + typesize ty <= size_arguments s. -Proof. - intros until ty. - assert (A: forall n l, n <= max_outgoing_1 n l). - { intros; unfold max_outgoing_1. destruct l as [_ | []]; xomega. } - assert (B: forall p n, - In (S Outgoing ofs ty) (regs_of_rpair p) -> - ofs + typesize ty <= max_outgoing_2 n p). - { intros. destruct p; simpl in H; intuition; subst; simpl. - - xomega. - - eapply Z.le_trans. 2: apply A. xomega. - - xomega. } - assert (C: forall l n, - In (S Outgoing ofs ty) (regs_of_rpairs l) -> - ofs + typesize ty <= fold_left max_outgoing_2 l n). - { induction l; simpl; intros. - - contradiction. - - rewrite in_app_iff in H. destruct H. - + eapply Z.le_trans. eapply B; eauto. apply Z.ge_le. apply fold_max_outgoing_above. - + apply IHl; auto. - } - apply C. -Qed. - Lemma loc_arguments_main: loc_arguments signature_main = nil. Proof. reflexivity. Qed. + +(** ** Normalization of function results *) + +(** No normalization needed. *) + +Definition return_value_needs_normalization (t: rettype) := false. diff --git a/riscV/SelectOp.vp b/riscV/SelectOp.vp index bb8af2ed..99806006 100644 --- a/riscV/SelectOp.vp +++ b/riscV/SelectOp.vp @@ -44,11 +44,8 @@ Require Archi. Require Import Coqlib. Require Import Compopts. -Require Import AST. -Require Import Integers. -Require Import Floats. -Require Import Op. -Require Import CminorSel. +Require Import AST Integers Floats Builtins. +Require Import Op CminorSel. Local Open Scope cminorsel_scope. @@ -420,6 +417,12 @@ Definition singleofintu (e: expr) := Eop Osingleofintu (e ::: Enil). Definition singleoffloat (e: expr) := Eop Osingleoffloat (e ::: Enil). Definition floatofsingle (e: expr) := Eop Ofloatofsingle (e ::: Enil). +(** ** Selection *) + +Definition select (ty: typ) (cond: condition) (args: exprlist) (e1 e2: expr) + : option expr + := None. + (** ** Recognition of addressing modes for load and store operations *) Nondetfunction addressing (chunk: memory_chunk) (e: expr) := @@ -448,3 +451,8 @@ Nondetfunction builtin_arg (e: expr) := if Archi.ptr64 then BA_addptr (BA e1) (BA_long n) else BA e | _ => BA e end. + +(** Platform-specific known builtins *) + +Definition platform_builtin (b: platform_builtin) (args: exprlist) : option expr := + None. diff --git a/riscV/SelectOpproof.v b/riscV/SelectOpproof.v index 90f077db..593be1ed 100644 --- a/riscV/SelectOpproof.v +++ b/riscV/SelectOpproof.v @@ -17,17 +17,10 @@ (** Correctness of instruction selection for operators *) -Require Import Coqlib. -Require Import Maps. -Require Import AST. -Require Import Integers. -Require Import Floats. -Require Import Values. -Require Import Memory. -Require Import Globalenvs. -Require Import Cminor. -Require Import Op. -Require Import CminorSel. +Require Import Coqlib Zbits. +Require Import AST Integers Floats. +Require Import Values Memory Builtins Globalenvs. +Require Import Cminor Op CminorSel. Require Import SelectOp. Local Open Scope cminorsel_scope. @@ -372,7 +365,7 @@ Proof. change (Int.ltu (Int.repr 32) Int64.iwordsize') with true; simpl. apply Val.lessdef_same. f_equal. transitivity (Int.repr (Z.shiftr (Int.signed i * Int.signed i0) 32)). - unfold Int.mulhs; f_equal. rewrite Int.Zshiftr_div_two_p by omega. reflexivity. + unfold Int.mulhs; f_equal. rewrite Zshiftr_div_two_p by omega. reflexivity. apply Int.same_bits_eq; intros n N. change Int.zwordsize with 32 in *. assert (N1: 0 <= n < 64) by omega. @@ -400,7 +393,7 @@ Proof. change (Int.ltu (Int.repr 32) Int64.iwordsize') with true; simpl. apply Val.lessdef_same. f_equal. transitivity (Int.repr (Z.shiftr (Int.unsigned i * Int.unsigned i0) 32)). - unfold Int.mulhu; f_equal. rewrite Int.Zshiftr_div_two_p by omega. reflexivity. + unfold Int.mulhu; f_equal. rewrite Zshiftr_div_two_p by omega. reflexivity. apply Int.same_bits_eq; intros n N. change Int.zwordsize with 32 in *. assert (N1: 0 <= n < 64) by omega. @@ -770,7 +763,7 @@ Qed. Theorem eval_cast8unsigned: unary_constructor_sound cast8unsigned (Val.zero_ext 8). Proof. red; intros until x. unfold cast8unsigned. - rewrite Val.zero_ext_and. apply eval_andimm. compute; auto. + rewrite Val.zero_ext_and. apply eval_andimm. omega. Qed. Theorem eval_cast16signed: unary_constructor_sound cast16signed (Val.sign_ext 16). @@ -783,7 +776,7 @@ Qed. Theorem eval_cast16unsigned: unary_constructor_sound cast16unsigned (Val.zero_ext 16). Proof. red; intros until x. unfold cast8unsigned. - rewrite Val.zero_ext_and. apply eval_andimm. compute; auto. + rewrite Val.zero_ext_and. apply eval_andimm. omega. Qed. Theorem eval_intoffloat: @@ -872,6 +865,20 @@ Proof. red; intros. unfold floatofsingle. TrivialExists. Qed. +Theorem eval_select: + forall le ty cond al vl a1 v1 a2 v2 a b, + select ty cond al a1 a2 = Some a -> + eval_exprlist ge sp e m le al vl -> + eval_expr ge sp e m le a1 v1 -> + eval_expr ge sp e m le a2 v2 -> + eval_condition cond vl m = Some b -> + exists v, + eval_expr ge sp e m le a v + /\ Val.lessdef (Val.select (Some b) v1 v2 ty) v. +Proof. + unfold select; intros; discriminate. +Qed. + Theorem eval_addressing: forall le chunk a v b ofs, eval_expr ge sp e m le a v -> @@ -922,4 +929,16 @@ Proof. - constructor; auto. Qed. +(** Platform-specific known builtins *) + +Theorem eval_platform_builtin: + forall bf al a vl v le, + platform_builtin bf al = Some a -> + eval_exprlist ge sp e m le al vl -> + platform_builtin_sem bf vl = Some v -> + exists v', eval_expr ge sp e m le a v' /\ Val.lessdef v v'. +Proof. + intros. discriminate. +Qed. + End CMCONSTR. diff --git a/riscV/TargetPrinter.ml b/riscV/TargetPrinter.ml index 19704bad..64bcea4c 100644 --- a/riscV/TargetPrinter.ml +++ b/riscV/TargetPrinter.ml @@ -93,7 +93,7 @@ module Target : TARGET = | X0 -> output_string oc "x0" | X r -> ireg oc r - let preg oc = function + let preg_asm oc ty = function | IR r -> ireg oc r | FR r -> freg oc r | _ -> assert false @@ -108,9 +108,9 @@ module Target : TARGET = let name_of_section = function | Section_text -> ".text" | Section_data i | Section_small_data i -> - if i then ".data" else "COMM" + if i then ".data" else common_section () | Section_const i | Section_small_const i -> - if i then ".section .rodata" else "COMM" + if i || (not !Clflags.option_fcommon) then ".section .rodata" else "COMM" | Section_string -> ".section .rodata" | Section_literal -> ".section .rodata" | Section_jumptable -> ".section .rodata" @@ -582,7 +582,7 @@ module Target : TARGET = (P.to_int kind) (extern_atom txt) args | EF_inline_asm(txt, sg, clob) -> fprintf oc "%s begin inline assembly\n\t" comment; - print_inline_asm preg oc (camlstring_of_coqstring txt) sg args res; + print_inline_asm preg_asm oc (camlstring_of_coqstring txt) sg args res; fprintf oc "%s end inline assembly\n" comment | _ -> assert false diff --git a/runtime/Makefile b/runtime/Makefile index 27ad6e8c..6777995d 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -22,6 +22,8 @@ ifeq ($(ARCH),x86_64) OBJS=i64_dtou.o i64_utod.o i64_utof.o vararg.o else ifeq ($(ARCH),powerpc64) OBJS=i64_dtou.o i64_stof.o i64_utod.o i64_utof.o vararg.o +else ifeq ($(ARCH),aarch64) +OBJS=vararg.o else OBJS=i64_dtos.o i64_dtou.o i64_sar.o i64_sdiv.o i64_shl.o \ i64_shr.o i64_smod.o i64_stod.o i64_stof.o \ @@ -70,16 +72,16 @@ clean:: ifeq ($(strip $(HAS_RUNTIME_LIB)),true) install:: - install -d $(LIBDIR) - install -m 0644 $(LIB) $(LIBDIR) + install -d $(DESTDIR)$(LIBDIR) + install -m 0644 $(LIB) $(DESTDIR)$(LIBDIR) else install:: endif ifeq ($(strip $(HAS_STANDARD_HEADERS)),true) install:: - install -d $(LIBDIR)/include - install -m 0644 $(INCLUDES) $(LIBDIR)/include + install -d $(DESTDIR)$(LIBDIR)/include + install -m 0644 $(INCLUDES) $(DESTDIR)$(LIBDIR)/include else install:: endif diff --git a/runtime/aarch64/sysdeps.h b/runtime/aarch64/sysdeps.h new file mode 100644 index 00000000..0cee9ae3 --- /dev/null +++ b/runtime/aarch64/sysdeps.h @@ -0,0 +1,45 @@ +// ***************************************************************** +// +// The Compcert verified compiler +// +// Xavier Leroy, Collège de France and INRIA Paris +// +// Copyright (c) Institut National de Recherche en Informatique et +// en Automatique. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the <organization> nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT +// HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ********************************************************************* + +// System dependencies + +#define FUNCTION(f) \ + .text; \ + .balign 16; \ + .globl f; \ +f: + +#define ENDFUNCTION(f) \ + .type f, @function; .size f, . - f + diff --git a/runtime/aarch64/vararg.S b/runtime/aarch64/vararg.S new file mode 100644 index 00000000..b7347d65 --- /dev/null +++ b/runtime/aarch64/vararg.S @@ -0,0 +1,109 @@ +// ***************************************************************** +// +// The Compcert verified compiler +// +// Xavier Leroy, Collège de France and INRIA Paris +// +// Copyright (c) Institut National de Recherche en Informatique et +// en Automatique. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the <organization> nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT +// HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ********************************************************************* + +// Helper functions for variadic functions <stdarg.h>. AArch64 version. + +#include "sysdeps.h" + +// typedef struct __va_list { +// void *__stack; // next stack parameter +// void *__gr_top; // top of the save area for int regs +// void *__vr_top; // top of the save area for float regs +// int__gr_offs; // offset from gr_top to next int reg +// int__vr_offs; // offset from gr_top to next FP reg +// } +// typedef struct __va_list va_list; // struct passed by reference +// unsigned int __compcert_va_int32(va_list * ap); +// unsigned long long __compcert_va_int64(va_list * ap); +// double __compcert_va_float64(va_list * ap); + +FUNCTION(__compcert_va_int32) + ldr w1, [x0, #24] // w1 = gr_offs + cbz w1, 1f + // gr_offs is not zero: load from int save area and update gr_offs + ldr x2, [x0, #8] // x2 = gr_top + ldr w2, [x2, w1, sxtw] // w2 = the next integer + add w1, w1, #8 + str w1, [x0, #24] // update gr_offs + mov w0, w2 + ret + // gr_offs is zero: load from stack save area and update stack pointer +1: ldr x1, [x0, #0] // x1 = stack + ldr w2, [x1, #0] // w2 = the next integer + add x1, x1, #8 + str x1, [x0, #0] // update stack + mov w0, w2 + ret +ENDFUNCTION(__compcert_va_int32) + +FUNCTION(__compcert_va_int64) + ldr w1, [x0, #24] // w1 = gr_offs + cbz w1, 1f + // gr_offs is not zero: load from int save area and update gr_offs + ldr x2, [x0, #8] // x2 = gr_top + ldr x2, [x2, w1, sxtw] // w2 = the next long integer + add w1, w1, #8 + str w1, [x0, #24] // update gr_offs + mov x0, x2 + ret + // gr_offs is zero: load from stack save area and update stack pointer +1: ldr x1, [x0, #0] // x1 = stack + ldr x2, [x1, #0] // w2 = the next long integer + add x1, x1, #8 + str x1, [x0, #0] // update stack + mov x0, x2 + ret +ENDFUNCTION(__compcert_va_int64) + +FUNCTION(__compcert_va_float64) + ldr w1, [x0, #28] // w1 = vr_offs + cbz w1, 1f + // vr_offs is not zero: load from float save area and update vr_offs + ldr x2, [x0, #16] // x2 = vr_top + ldr d0, [x2, w1, sxtw] // d0 = the next float + add w1, w1, #16 + str w1, [x0, #28] // update vr_offs + ret + // gr_offs is zero: load from stack save area and update stack pointer +1: ldr x1, [x0, #0] // x1 = stack + ldr d0, [x1, #0] // d0 = the next float + add x1, x1, #8 + str x1, [x0, #0] // update stack + ret +ENDFUNCTION(__compcert_va_float64) + +// Right now we pass structs by reference. This is not ABI conformant. +FUNCTION(__compcert_va_composite) + b __compcert_va_int64 +ENDFUNCTION(__compcert_va_composite) diff --git a/runtime/arm/i64_stof.S b/runtime/arm/i64_stof.S index bcfa471c..11e00a2a 100644 --- a/runtime/arm/i64_stof.S +++ b/runtime/arm/i64_stof.S @@ -39,12 +39,11 @@ @@@ Conversion from signed 64-bit integer to single float FUNCTION(__compcert_i64_stof) - @ Check whether -2^53 <= X < 2^53 - ASR r2, Reg0HI, #21 - ASR r3, Reg0HI, #31 @ (r2,r3) = X >> 53 + @ Check whether -2^53 <= X < 2^53 + ASR r2, Reg0HI, #21 @ r2 = high 32 bits of X >> 53 + @ -2^53 <= X < 2^53 iff r2 is -1 or 0, that is, iff r2 + 1 is 0 or 1 adds r2, r2, #1 - adc r3, r3, #0 @ (r2,r3) = X >> 53 + 1 - cmp r3, #2 + cmp r2, #2 blo 1f @ X is large enough that double rounding can occur. @ Avoid it by nudging X away from the points where double rounding diff --git a/runtime/powerpc/i64_stof.s b/runtime/powerpc/i64_stof.s index 97fa6bb8..ea23a1c8 100644 --- a/runtime/powerpc/i64_stof.s +++ b/runtime/powerpc/i64_stof.s @@ -43,20 +43,19 @@ __compcert_i64_stof: mflr r9 # Check whether -2^53 <= X < 2^53 - srawi r5, r3, 31 - srawi r6, r3, 21 # (r5,r6) = X >> 53 - addic r6, r6, 1 - addze r5, r5 # (r5,r6) = (X >> 53) + 1 + srawi r5, r3, 21 # r5 = high 32 bits of X >> 53 + # -2^53 <= X < 2^53 iff r5 is -1 or 0, that is, iff r5 + 1 is 0 or 1 + addi r5, r5, 1 cmplwi r5, 2 blt 1f # X is large enough that double rounding can occur. # Avoid it by nudging X away from the points where double rounding # occurs (the "round to odd" technique) - rlwinm r0, r4, 0, 21, 31 # extract bits 0 to 11 of X - addi r0, r0, 0x7FF # r0 = (X & 0x7FF) + 0x7FF - # bit 12 of r0 is 0 if all low 12 bits of X are 0, 1 otherwise - # bits 13-31 of r0 are 0 - or r4, r4, r0 # correct bit number 12 of X + rlwinm r5, r4, 0, 21, 31 # extract bits 0 to 11 of X + addi r5, r5, 0x7FF # r5 = (X & 0x7FF) + 0x7FF + # bit 12 of r5 is 0 if all low 12 bits of X are 0, 1 otherwise + # bits 13-31 of r5 are 0 + or r4, r4, r5 # correct bit number 12 of X rlwinm r4, r4, 0, 0, 20 # set to 0 bits 0 to 11 of X # Convert to double, then round to single 1: bl __compcert_i64_stod diff --git a/runtime/powerpc/i64_utof.s b/runtime/powerpc/i64_utof.s index cdb2f867..4a2a172b 100644 --- a/runtime/powerpc/i64_utof.s +++ b/runtime/powerpc/i64_utof.s @@ -48,11 +48,11 @@ __compcert_i64_utof: # X is large enough that double rounding can occur. # Avoid it by nudging X away from the points where double rounding # occurs (the "round to odd" technique) - rlwinm r0, r4, 0, 21, 31 # extract bits 0 to 11 of X - addi r0, r0, 0x7FF # r0 = (X & 0x7FF) + 0x7FF - # bit 12 of r0 is 0 if all low 12 bits of X are 0, 1 otherwise - # bits 13-31 of r0 are 0 - or r4, r4, r0 # correct bit number 12 of X + rlwinm r5, r4, 0, 21, 31 # extract bits 0 to 11 of X + addi r5, r5, 0x7FF # r5 = (X & 0x7FF) + 0x7FF + # bit 12 of r5 is 0 if all low 12 bits of X are 0, 1 otherwise + # bits 13-31 of r5 are 0 + or r4, r4, r5 # correct bit number 12 of X rlwinm r4, r4, 0, 0, 20 # set to 0 bits 0 to 11 of X # Convert to double, then round to single 1: bl __compcert_i64_utod diff --git a/runtime/powerpc64/i64_utof.s b/runtime/powerpc64/i64_utof.s index cdb2f867..4a2a172b 100644 --- a/runtime/powerpc64/i64_utof.s +++ b/runtime/powerpc64/i64_utof.s @@ -48,11 +48,11 @@ __compcert_i64_utof: # X is large enough that double rounding can occur. # Avoid it by nudging X away from the points where double rounding # occurs (the "round to odd" technique) - rlwinm r0, r4, 0, 21, 31 # extract bits 0 to 11 of X - addi r0, r0, 0x7FF # r0 = (X & 0x7FF) + 0x7FF - # bit 12 of r0 is 0 if all low 12 bits of X are 0, 1 otherwise - # bits 13-31 of r0 are 0 - or r4, r4, r0 # correct bit number 12 of X + rlwinm r5, r4, 0, 21, 31 # extract bits 0 to 11 of X + addi r5, r5, 0x7FF # r5 = (X & 0x7FF) + 0x7FF + # bit 12 of r5 is 0 if all low 12 bits of X are 0, 1 otherwise + # bits 13-31 of r5 are 0 + or r4, r4, r5 # correct bit number 12 of X rlwinm r4, r4, 0, 0, 20 # set to 0 bits 0 to 11 of X # Convert to double, then round to single 1: bl __compcert_i64_utod diff --git a/test/c/Makefile b/test/c/Makefile index 51a8f105..4b521bb5 100644 --- a/test/c/Makefile +++ b/test/c/Makefile @@ -7,8 +7,7 @@ CFLAGS=-O1 -Wall LIBS=$(LIBMATH) -TIME=xtime -o /dev/null -mintime 2.0 # Xavier's hack -#TIME=time >/dev/null # Otherwise +TIME=ocaml unix.cma ../../tools/xtime.ml -o /dev/null -mintime 2.0 -minruns 4 PROGS=fib integr qsort fft fftsp fftw sha1 sha3 aes almabench \ lists binarytrees fannkuch knucleotide mandelbrot nbody \ @@ -48,12 +47,12 @@ test_gcc: bench_gcc: @for i in $(PROGS); do \ - echo -n "$$i: "; $(TIME) ./$$i.gcc; \ + $(TIME) -name $$i -- ./$$i.gcc; \ done bench: @for i in $(PROGS); do \ - echo -n "$$i: "; $(TIME) ./$$i.compcert; \ + $(TIME) -name $$i -- ./$$i.compcert; \ done clean: diff --git a/test/c/aes.c b/test/c/aes.c index 0aa02595..16f02e47 100644 --- a/test/c/aes.c +++ b/test/c/aes.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include "../endian.h" #define MAXKC (256/32) #define MAXKB (256/8) @@ -36,15 +37,6 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; -#if defined(__ppc__) || defined(__PPC__) || defined(__ARMEB__) -#define ARCH_BIG_ENDIAN -#elif defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__) \ - || defined(__riscv) -#undef ARCH_BIG_ENDIAN -#else -#error "unknown endianness" -#endif - #ifdef ARCH_BIG_ENDIAN #define GETU32(pt) (*(u32 *)(pt)) #define PUTU32(ct,st) (*(u32 *)(ct) = (st)) diff --git a/test/c/chomp.c b/test/c/chomp.c index c88cef5c..728e7a01 100644 --- a/test/c/chomp.c +++ b/test/c/chomp.c @@ -106,7 +106,7 @@ void dump_play(struct _play *play) /* and for the entire game tree */ int get_value(int *data) /* get the value (0 or 1) for a specific _data */ { struct _play *search; - search = game_tree; /* start at the begginig */ + search = game_tree; /* start at the beginning */ while (! equal_data(search -> state,data)) /* until you find a match */ search = search -> next; /* take next element */ return search -> value; /* return its value */ @@ -138,7 +138,7 @@ void show_list(struct _list *list) /* show the entire list of moves */ } } -void show_play(struct _play *play) /* to diplay the whole tree */ +void show_play(struct _play *play) /* to display the whole tree */ { while (play != NULL) { @@ -154,7 +154,7 @@ void show_play(struct _play *play) /* to diplay the whole tree */ int in_wanted(int *data) /* checks if the current _data is in the wanted list */ { struct _list *current; - current = wanted; /* start at the begginig */ + current = wanted; /* start at the beginning */ while (current != NULL) /* unitl the last one */ { if (equal_data(current -> data,data)) break; /* break if found */ diff --git a/test/clightgen/issue319.c b/test/clightgen/issue319.c new file mode 100644 index 00000000..be9f3f7e --- /dev/null +++ b/test/clightgen/issue319.c @@ -0,0 +1,12 @@ +/* Dollar signs in identifiers */ + +int c$d = 42; + +int a$b(int x$$) { + return c$d + x$$; +} + +int main(int argc, const char *argv[]) +{ + return a$b(6); +} diff --git a/test/compression/Makefile b/test/compression/Makefile index 2e14e646..e8f3cf4d 100644 --- a/test/compression/Makefile +++ b/test/compression/Makefile @@ -3,7 +3,7 @@ include ../../Makefile.config CC=../../ccomp CFLAGS=$(CCOMPOPTS) -U__GNUC__ -stdlib ../../runtime -dclight -dasm LIBS= -TIME=xtime -o /dev/null -mintime 1.0 +TIME=ocaml unix.cma ../../tools/xtime.ml -mintime 2.0 -minruns 2 EXE=arcode lzw lzss @@ -48,8 +48,7 @@ test: bench: @rm -f $(TESTCOMPR) @for i in $(EXE); do \ - echo -n "$$i: "; \ - $(TIME) sh -c "./$$i -c -i $(TESTFILE) -o $(TESTCOMPR) && ./$$i -d -i $(TESTCOMPR) -o /dev/null"; \ + $(TIME) -name $$i -- sh -c "./$$i -c -i $(TESTFILE) -o $(TESTCOMPR) && ./$$i -d -i $(TESTCOMPR) -o /dev/null"; \ done @rm -f $(TESTCOMPR) diff --git a/test/endian.h b/test/endian.h new file mode 100644 index 00000000..8be2850c --- /dev/null +++ b/test/endian.h @@ -0,0 +1,8 @@ +#if defined(__ppc__) || defined(__PPC__) || defined(__ARMEB__) +#define ARCH_BIG_ENDIAN +#elif defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__) \ + || defined(__riscv) || defined(__aarch64__) +#undef ARCH_BIG_ENDIAN +#else +#error "unknown endianness" +#endif diff --git a/test/raytracer/Makefile b/test/raytracer/Makefile index 8f6541a1..24461bd1 100644 --- a/test/raytracer/Makefile +++ b/test/raytracer/Makefile @@ -3,7 +3,7 @@ include ../../Makefile.config CC=../../ccomp CFLAGS=$(CCOMPOPTS) -stdlib ../../runtime -dparse -dclight -dasm -fstruct-return LIBS=$(LIBMATH) -TIME=xtime +TIME=ocaml unix.cma ../../tools/xtime.ml -mintime 2.0 -minruns 4 OBJS=memory.o gmllexer.o gmlparser.o eval.o \ arrays.o vector.o matrix.o object.o intersect.o surface.o light.o \ @@ -30,4 +30,4 @@ test: fi bench: - @echo -n "raytracer: "; $(TIME) sh -c './render < kal.gml' + @$(TIME) -name raytracer -- sh -c './render < kal.gml' diff --git a/test/regression/Makefile b/test/regression/Makefile index 760ee570..8e8d8762 100644 --- a/test/regression/Makefile +++ b/test/regression/Makefile @@ -10,13 +10,13 @@ LIBS=$(LIBMATH) # Can run, both in compiled mode and in interpreter mode, # and have reference output in Results -TESTS=int32 int64 floats floats-basics \ +TESTS=int32 int64 floats floats-basics floats-lit \ expr1 expr6 funptr2 initializers initializers2 initializers3 \ volatile1 volatile2 volatile3 volatile4 \ funct3 expr5 struct7 struct8 struct11 struct12 casts1 casts2 char1 \ sizeof1 sizeof2 binops bool for1 for2 switch switch2 compound \ decl1 interop1 bitfields9 ptrs3 \ - parsing krfun + parsing krfun ifconv # Can run, but only in compiled mode, and have reference output in Results diff --git a/test/regression/Results/builtins-aarch64 b/test/regression/Results/builtins-aarch64 new file mode 100644 index 00000000..c70432d8 --- /dev/null +++ b/test/regression/Results/builtins-aarch64 @@ -0,0 +1,15 @@ +bswap(12345678) = 78563412 +bswap16(1234) = 3412 +bswap64(123456789abcdef0) = f0debc9a78563412 +clz(12345678) = 3 +clzll(12345678) = 35 +clzll(1234567812345678) = 3 +cls(1234567) = 10 +cls(-9999) = 17 +clsll(1234567) = 42 +clsll(-9999) = 49 +fsqrt(3.141590) = 1.772453 +fmadd(3.141590, 2.718000, 1.414000) = 9.952842 +fmsub(3.141590, 2.718000, 1.414000) = -7.124842 +fnmadd(3.141590, 2.718000, 1.414000) = -9.952842 +fnmsub(3.141590, 2.718000, 1.414000) = 7.124842 diff --git a/test/regression/Results/floats-lit b/test/regression/Results/floats-lit new file mode 100644 index 00000000..6cde72fb --- /dev/null +++ b/test/regression/Results/floats-lit @@ -0,0 +1,2 @@ +--- Double-precision test +--- Single-precision test diff --git a/test/regression/Results/ifconv b/test/regression/Results/ifconv new file mode 100644 index 00000000..38019fe6 --- /dev/null +++ b/test/regression/Results/ifconv @@ -0,0 +1,26 @@ +test1(0,1,12,34) = 12 +test1(1,0,45,67) = 67 +test2(0,1,12,34) = 12 +test2(1,0,45,67) = 67 +test3(0,1,12,34) = 12 +test3(1,0,45,67) = 67 +test4(0,1,12,34) = 12 +test4(1,0,45,67) = 67 +test5(0,1,12) = 13 +test5(1,0,45) = 44 +test6(NULL) = 0 +test6(&i) = 1244 +test7(1,0) = -1 +test7(-100,4) = -25 +test8(0) = 0 +test8(1) = -72 +ltest1(-1, 0, 123LL, 456LL) = 124 +ltest1(1, 0, 123LL, 456LL) = 114 +dmax(0.0, 3.14) = 3.140000 +dmax(1.0, -2.718) = 1.000000 +dabs(1.0) = 1.000000 +dabs(-2.718) = 2.718000 +smin(0.0, 3.14) = 0.000000 +smin(1.0, -2.718) = -2.718000 +sdoz(1.0, 0.5) = 0.500000 +sdoz(0.0, 3.14) = 0.000000 diff --git a/test/regression/Results/int64 b/test/regression/Results/int64 index af444cf6..ae8a3cc1 100644 --- a/test/regression/Results/int64 +++ b/test/regression/Results/int64 @@ -335,6 +335,48 @@ utof x = 0 stof x = 0 x = 0 +y = 52ce6b4000000063 +-x = 0 +x + y = 52ce6b4000000063 +x - y = ad3194bfffffff9d +x * y = 0 +x /u y = 0 +x %u y = 0 +x /s y = 0 +x %s y = 0 +x /u y2 = 0 +x %u y2 = 0 +x /s y3 = 0 +x %s y3 = 0 +x /u 3 = 0 +x %u 3 = 0 +x /s 3 = 0 +x %s 3 = 0 +x /u 5 = 0 +x %u 5 = 0 +x /s 5 = 0 +x %s 5 = 0 +x /u 11 = 0 +x %u 11 = 0 +x /s 11 = 0 +x %s 11 = 0 +~x = ffffffffffffffff +x & y = 0 +x | y = 52ce6b4000000063 +x ^ y = 52ce6b4000000063 +x << i = 0 +x >>u i = 0 +x >>s i = 0 +x cmpu y = lt +x cmps y = lt +utod x = 0 +dtou f = 0 +stod x = 0 +dtos f = 0 +utof x = 0 +stof x = 0 + +x = 0 y = 14057b7ef767814f -x = 0 x + y = 14057b7ef767814f @@ -755,6 +797,48 @@ utof x = 3f800000 stof x = 3f800000 x = 1 +y = 52ce6b4000000063 +-x = ffffffffffffffff +x + y = 52ce6b4000000064 +x - y = ad3194bfffffff9e +x * y = 52ce6b4000000063 +x /u y = 0 +x %u y = 1 +x /s y = 0 +x %s y = 1 +x /u y2 = 0 +x %u y2 = 1 +x /s y3 = 0 +x %s y3 = 1 +x /u 3 = 0 +x %u 3 = 1 +x /s 3 = 0 +x %s 3 = 1 +x /u 5 = 0 +x %u 5 = 1 +x /s 5 = 0 +x %s 5 = 1 +x /u 11 = 0 +x %u 11 = 1 +x /s 11 = 0 +x %s 11 = 1 +~x = fffffffffffffffe +x & y = 1 +x | y = 52ce6b4000000063 +x ^ y = 52ce6b4000000062 +x << i = 800000000 +x >>u i = 0 +x >>s i = 0 +x cmpu y = lt +x cmps y = lt +utod x = 3ff0000000000000 +dtou f = 0 +stod x = 3ff0000000000000 +dtos f = 0 +utof x = 3f800000 +stof x = 3f800000 + +x = 1 y = 9af678222e728119 -x = ffffffffffffffff x + y = 9af678222e72811a @@ -1175,6 +1259,48 @@ utof x = 5f800000 stof x = bf800000 x = ffffffffffffffff +y = 52ce6b4000000063 +-x = 1 +x + y = 52ce6b4000000062 +x - y = ad3194bfffffff9c +x * y = ad3194bfffffff9d +x /u y = 3 +x %u y = 794be3ffffffed6 +x /s y = 0 +x %s y = ffffffffffffffff +x /u y2 = 3176fe836 +x %u y2 = 3683607f +x /s y3 = 0 +x %s y3 = ffffffffffffffff +x /u 3 = 5555555555555555 +x %u 3 = 0 +x /s 3 = 0 +x %s 3 = ffffffffffffffff +x /u 5 = 3333333333333333 +x %u 5 = 0 +x /s 5 = 0 +x %s 5 = ffffffffffffffff +x /u 11 = 1745d1745d1745d1 +x %u 11 = 4 +x /s 11 = 0 +x %s 11 = ffffffffffffffff +~x = 0 +x & y = 52ce6b4000000063 +x | y = ffffffffffffffff +x ^ y = ad3194bfffffff9c +x << i = fffffff800000000 +x >>u i = 1fffffff +x >>s i = ffffffffffffffff +x cmpu y = gt +x cmps y = lt +utod x = 43f0000000000000 +dtou f = 68db8bac710cb +stod x = bff0000000000000 +dtos f = 0 +utof x = 5f800000 +stof x = bf800000 + +x = ffffffffffffffff y = 62354cda6226d1f3 -x = 1 x + y = 62354cda6226d1f2 @@ -1595,6 +1721,48 @@ utof x = 4f000000 stof x = 4f000000 x = 7fffffff +y = 52ce6b4000000063 +-x = ffffffff80000001 +x + y = 52ce6b4080000062 +x - y = ad3194c07fffff9c +x * y = ad3194f17fffff9d +x /u y = 0 +x %u y = 7fffffff +x /s y = 0 +x %s y = 7fffffff +x /u y2 = 1 +x %u y2 = 2d3194bf +x /s y3 = 1 +x %s y3 = 2d3194bf +x /u 3 = 2aaaaaaa +x %u 3 = 1 +x /s 3 = 2aaaaaaa +x %s 3 = 1 +x /u 5 = 19999999 +x %u 5 = 2 +x /s 5 = 19999999 +x %s 5 = 2 +x /u 11 = ba2e8ba +x %u 11 = 1 +x /s 11 = ba2e8ba +x %s 11 = 1 +~x = ffffffff80000000 +x & y = 63 +x | y = 52ce6b407fffffff +x ^ y = 52ce6b407fffff9c +x << i = fffffff800000000 +x >>u i = 0 +x >>s i = 0 +x cmpu y = lt +x cmps y = lt +utod x = 41dfffffffc00000 +dtou f = 346dc +stod x = 41dfffffffc00000 +dtos f = 346dc +utof x = 4f000000 +stof x = 4f000000 + +x = 7fffffff y = 144093704fadba5d -x = ffffffff80000001 x + y = 14409370cfadba5c @@ -2015,6 +2183,48 @@ utof x = 4f000000 stof x = 4f000000 x = 80000000 +y = 52ce6b4000000063 +-x = ffffffff80000000 +x + y = 52ce6b4080000063 +x - y = ad3194c07fffff9d +x * y = 3180000000 +x /u y = 0 +x %u y = 80000000 +x /s y = 0 +x %s y = 80000000 +x /u y2 = 1 +x %u y2 = 2d3194c0 +x /s y3 = 1 +x %s y3 = 2d3194c0 +x /u 3 = 2aaaaaaa +x %u 3 = 2 +x /s 3 = 2aaaaaaa +x %s 3 = 2 +x /u 5 = 19999999 +x %u 5 = 3 +x /s 5 = 19999999 +x %s 5 = 3 +x /u 11 = ba2e8ba +x %u 11 = 2 +x /s 11 = ba2e8ba +x %s 11 = 2 +~x = ffffffff7fffffff +x & y = 0 +x | y = 52ce6b4080000063 +x ^ y = 52ce6b4080000063 +x << i = 0 +x >>u i = 0 +x >>s i = 0 +x cmpu y = lt +x cmps y = lt +utod x = 41e0000000000000 +dtou f = 346dc +stod x = 41e0000000000000 +dtos f = 346dc +utof x = 4f000000 +stof x = 4f000000 + +x = 80000000 y = 7b985bc1e7bce4d7 -x = ffffffff80000000 x + y = 7b985bc267bce4d7 @@ -2435,6 +2645,48 @@ utof x = 5f000000 stof x = 5f000000 x = 7fffffffffffffff +y = 52ce6b4000000063 +-x = 8000000000000001 +x + y = d2ce6b4000000062 +x - y = 2d3194bfffffff9c +x * y = 2d3194bfffffff9d +x /u y = 1 +x %u y = 2d3194bfffffff9c +x /s y = 1 +x %s y = 2d3194bfffffff9c +x /u y2 = 18bb7f41b +x %u y2 = 1b41b03f +x /s y3 = 18bb7f41b +x %s y3 = 1b41b03f +x /u 3 = 2aaaaaaaaaaaaaaa +x %u 3 = 1 +x /s 3 = 2aaaaaaaaaaaaaaa +x %s 3 = 1 +x /u 5 = 1999999999999999 +x %u 5 = 2 +x /s 5 = 1999999999999999 +x %s 5 = 2 +x /u 11 = ba2e8ba2e8ba2e8 +x %u 11 = 7 +x /s 11 = ba2e8ba2e8ba2e8 +x %s 11 = 7 +~x = 8000000000000000 +x & y = 52ce6b4000000063 +x | y = 7fffffffffffffff +x ^ y = 2d3194bfffffff9c +x << i = fffffff800000000 +x >>u i = fffffff +x >>s i = fffffff +x cmpu y = gt +x cmps y = gt +utod x = 43e0000000000000 +dtou f = 346dc5d638865 +stod x = 43e0000000000000 +dtos f = 346dc5d638865 +utof x = 5f000000 +stof x = 5f000000 + +x = 7fffffffffffffff y = a220229ec164ffe1 -x = 8000000000000001 x + y = 2220229ec164ffe0 @@ -2855,6 +3107,48 @@ utof x = 5f000000 stof x = df000000 x = 8000000000000000 +y = 52ce6b4000000063 +-x = 8000000000000000 +x + y = d2ce6b4000000063 +x - y = 2d3194bfffffff9d +x * y = 8000000000000000 +x /u y = 1 +x %u y = 2d3194bfffffff9d +x /s y = ffffffffffffffff +x %s y = d2ce6b4000000063 +x /u y2 = 18bb7f41b +x %u y2 = 1b41b040 +x /s y3 = fffffffe74480be5 +x %s y3 = ffffffffe4be4fc0 +x /u 3 = 2aaaaaaaaaaaaaaa +x %u 3 = 2 +x /s 3 = d555555555555556 +x %s 3 = fffffffffffffffe +x /u 5 = 1999999999999999 +x %u 5 = 3 +x /s 5 = e666666666666667 +x %s 5 = fffffffffffffffd +x /u 11 = ba2e8ba2e8ba2e8 +x %u 11 = 8 +x /s 11 = f45d1745d1745d18 +x %s 11 = fffffffffffffff8 +~x = 7fffffffffffffff +x & y = 0 +x | y = d2ce6b4000000063 +x ^ y = d2ce6b4000000063 +x << i = 0 +x >>u i = 10000000 +x >>s i = fffffffff0000000 +x cmpu y = gt +x cmps y = lt +utod x = 43e0000000000000 +dtou f = 346dc5d638865 +stod x = c3e0000000000000 +dtos f = fffcb923a29c779b +utof x = 5f000000 +stof x = df000000 + +x = 8000000000000000 y = c73aa0d9a415dfb -x = 8000000000000000 x + y = 8c73aa0d9a415dfb @@ -3275,6 +3569,48 @@ utof x = 4f800000 stof x = 4f800000 x = 100000003 +y = 52ce6b4000000063 +-x = fffffffefffffffd +x + y = 52ce6b4100000066 +x - y = ad3194c0ffffffa0 +x * y = f86b422300000129 +x /u y = 0 +x %u y = 100000003 +x /s y = 0 +x %s y = 100000003 +x /u y2 = 3 +x %u y2 = 794be43 +x /s y3 = 3 +x %s y3 = 794be43 +x /u 3 = 55555556 +x %u 3 = 1 +x /s 3 = 55555556 +x %s 3 = 1 +x /u 5 = 33333333 +x %u 5 = 4 +x /s 5 = 33333333 +x %s 5 = 4 +x /u 11 = 1745d174 +x %u 11 = 7 +x /s 11 = 1745d174 +x %s 11 = 7 +~x = fffffffefffffffc +x & y = 3 +x | y = 52ce6b4100000063 +x ^ y = 52ce6b4100000060 +x << i = 1800000000 +x >>u i = 0 +x >>s i = 0 +x cmpu y = lt +x cmps y = lt +utod x = 41f0000000300000 +dtou f = 68db8 +stod x = 41f0000000300000 +dtos f = 68db8 +utof x = 4f800000 +stof x = 4f800000 + +x = 100000003 y = e9bcd26890f095a5 -x = fffffffefffffffd x + y = e9bcd26990f095a8 @@ -3358,47 +3694,467 @@ dtos f = 14bb101261e18 utof x = 5e4a72c9 stof x = 5e4a72c9 -x = 8362aa9340fe215f -y = f986342416ec8002 --x = 7c9d556cbf01dea1 -x + y = 7ce8deb757eaa161 -x - y = 89dc766f2a11a15d -x * y = e4a2b426803fc2be +x = 52ce6b4000000063 +y = 0 +-x = ad3194bfffffff9d +x + y = 52ce6b4000000063 +x - y = 52ce6b4000000063 +x * y = 0 x /u y = 0 -x %u y = 8362aa9340fe215f -x /s y = 13 -x %s y = fe6ccbe58d70a139 -x /u y2 = 86cb918b -x %u y2 = 910b6dd3 -x /s y3 = 133e437097 -x %s y3 = fffffffffe99a023 -x /u 3 = 2bcb8e3115aa0b1f -x %u 3 = 2 -x /s 3 = d67638dbc054b5cb -x %s 3 = fffffffffffffffe -x /u 5 = 1a46eeea4032d379 -x %u 5 = 2 -x /s 5 = e713bbb70cffa047 -x %s 5 = fffffffffffffffc -x /u 11 = bf1b26a7a45a5f1 -x %u 11 = 4 -x /s 11 = f4abe0f61d2e6020 -x %s 11 = ffffffffffffffff -~x = 7c9d556cbf01dea0 -x & y = 8102200000ec0002 -x | y = fbe6beb756fea15f -x ^ y = 7ae49eb75612a15d -x << i = d8aaa4d03f8857c -x >>u i = 20d8aaa4d03f8857 -x >>s i = e0d8aaa4d03f8857 +x %u y = 0 +x /s y = 0 +x %s y = 0 +x /u y2 = 0 +x %u y2 = 0 +x /s y3 = 0 +x %s y3 = 0 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 0 +x | y = 52ce6b4000000063 +x ^ y = 52ce6b4000000063 +x << i = 52ce6b4000000063 +x >>u i = 52ce6b4000000063 +x >>s i = 52ce6b4000000063 +x cmpu y = gt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 1 +-x = ad3194bfffffff9d +x + y = 52ce6b4000000064 +x - y = 52ce6b4000000062 +x * y = 52ce6b4000000063 +x /u y = 52ce6b4000000063 +x %u y = 0 +x /s y = 52ce6b4000000063 +x %s y = 0 +x /u y2 = 0 +x %u y2 = 0 +x /s y3 = 0 +x %s y3 = 0 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 1 +x | y = 52ce6b4000000063 +x ^ y = 52ce6b4000000062 +x << i = a59cd680000000c6 +x >>u i = 296735a000000031 +x >>s i = 296735a000000031 +x cmpu y = gt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = ffffffffffffffff +-x = ad3194bfffffff9d +x + y = 52ce6b4000000062 +x - y = 52ce6b4000000064 +x * y = ad3194bfffffff9d +x /u y = 0 +x %u y = 52ce6b4000000063 +x /s y = ad3194bfffffff9d +x %s y = 0 +x /u y2 = 52ce6b40 +x %u y2 = 52ce6ba3 +x /s y3 = ad3194bfffffff9d +x %s y3 = 0 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 52ce6b4000000063 +x | y = ffffffffffffffff +x ^ y = ad3194bfffffff9c +x << i = 8000000000000000 +x >>u i = 0 +x >>s i = 0 +x cmpu y = lt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 7fffffff +-x = ad3194bfffffff9d +x + y = 52ce6b4080000062 +x - y = 52ce6b3f80000064 +x * y = ad3194f17fffff9d +x /u y = a59cd681 +x %u y = 259cd6e4 +x /s y = a59cd681 +x %s y = 259cd6e4 +x /u y2 = 0 +x %u y2 = 0 +x /s y3 = 0 +x %s y3 = 0 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 63 +x | y = 52ce6b407fffffff +x ^ y = 52ce6b407fffff9c +x << i = 8000000000000000 +x >>u i = 0 +x >>s i = 0 +x cmpu y = gt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 80000000 +-x = ad3194bfffffff9d +x + y = 52ce6b4080000063 +x - y = 52ce6b3f80000063 +x * y = 3180000000 +x /u y = a59cd680 +x %u y = 63 +x /s y = a59cd680 +x %s y = 63 +x /u y2 = 0 +x %u y2 = 0 +x /s y3 = 0 +x %s y3 = 0 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 0 +x | y = 52ce6b4080000063 +x ^ y = 52ce6b4080000063 +x << i = 52ce6b4000000063 +x >>u i = 52ce6b4000000063 +x >>s i = 52ce6b4000000063 +x cmpu y = gt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 7fffffffffffffff +-x = ad3194bfffffff9d +x + y = d2ce6b4000000062 +x - y = d2ce6b4000000064 +x * y = 2d3194bfffffff9d +x /u y = 0 +x %u y = 52ce6b4000000063 +x /s y = 0 +x %s y = 52ce6b4000000063 +x /u y2 = a59cd681 +x %u y2 = 259cd6e4 +x /s y3 = a59cd681 +x %s y3 = 259cd6e4 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 52ce6b4000000063 +x | y = 7fffffffffffffff +x ^ y = 2d3194bfffffff9c +x << i = 8000000000000000 +x >>u i = 0 +x >>s i = 0 x cmpu y = lt x cmps y = lt -utod x = 43e06c5552681fc4 -dtou f = 35d0c262d14d7 -stod x = c3df27555b2fc078 -dtos f = fffccf536b66040d -utof x = 5f0362ab -stof x = def93aab +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 8000000000000000 +-x = ad3194bfffffff9d +x + y = d2ce6b4000000063 +x - y = d2ce6b4000000063 +x * y = 8000000000000000 +x /u y = 0 +x %u y = 52ce6b4000000063 +x /s y = 0 +x %s y = 52ce6b4000000063 +x /u y2 = a59cd680 +x %u y2 = 63 +x /s y3 = ffffffff5a632980 +x %s y3 = 63 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 0 +x | y = d2ce6b4000000063 +x ^ y = d2ce6b4000000063 +x << i = 52ce6b4000000063 +x >>u i = 52ce6b4000000063 +x >>s i = 52ce6b4000000063 +x cmpu y = lt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 100000003 +-x = ad3194bfffffff9d +x + y = 52ce6b4100000066 +x - y = 52ce6b3f00000060 +x * y = f86b422300000129 +x /u y = 52ce6b3f +x %u y = 794bea6 +x /s y = 52ce6b3f +x %s y = 794bea6 +x /u y2 = 52ce6b4000000063 +x %u y2 = 0 +x /s y3 = 52ce6b4000000063 +x %s y3 = 0 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 3 +x | y = 52ce6b4100000063 +x ^ y = 52ce6b4100000060 +x << i = 96735a0000000318 +x >>u i = a59cd680000000c +x >>s i = a59cd680000000c +x cmpu y = gt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 52ce6b4000000063 +-x = ad3194bfffffff9d +x + y = a59cd680000000c6 +x - y = 0 +x * y = ba6f38000002649 +x /u y = 1 +x %u y = 0 +x /s y = 1 +x %s y = 0 +x /u y2 = 100000000 +x %u y2 = 63 +x /s y3 = 100000000 +x %s y3 = 63 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 52ce6b4000000063 +x | y = 52ce6b4000000063 +x ^ y = 0 +x << i = 31800000000 +x >>u i = a59cd68 +x >>s i = a59cd68 +x cmpu y = eq +x cmps y = eq +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = 52ce6b4000000063 +y = 8362aa9340fe215f +-x = ad3194bfffffff9d +x + y = d63115d340fe21c2 +x - y = cf6bc0acbf01df04 +x * y = 8f1503b22246e7bd +x /u y = 0 +x %u y = 52ce6b4000000063 +x /s y = 0 +x %s y = 52ce6b4000000063 +x /u y2 = a158656f +x %u y2 = 5640ba6 +x /s y3 = ffffffff55e35d11 +x %s y3 = 5f2245a0 +x /u 3 = 1b9a23c000000021 +x %u 3 = 0 +x /s 3 = 1b9a23c000000021 +x %s 3 = 0 +x /u 5 = 108faf0ccccccce0 +x %u 5 = 3 +x /s 5 = 108faf0ccccccce0 +x %s 5 = 3 +x /u 11 = 7872105d1745d20 +x %u 11 = 3 +x /s 11 = 7872105d1745d20 +x %s 11 = 3 +~x = ad3194bfffffff9c +x & y = 2422a0000000043 +x | y = d3eeebd340fe217f +x ^ y = d1acc1d340fe213c +x << i = 3180000000 +x >>u i = a59cd680 +x >>s i = a59cd680 +x cmpu y = lt +x cmps y = gt +utod x = 43d4b39ad0000000 +dtou f = 21eadf559b3d0 +stod x = 43d4b39ad0000000 +dtos f = 21eadf559b3d0 +utof x = 5ea59cd7 +stof x = 5ea59cd7 + +x = f986342416ec8002 +y = 52ce6b4000000063 +-x = 679cbdbe9137ffe +x + y = 4c549f6416ec8065 +x - y = a6b7c8e416ec7f9f +x * y = b9230074dd7580c6 +x /u y = 3 +x %u y = 11af26416ec7ed9 +x /s y = 0 +x %s y = f986342416ec8002 +x /u y2 = 3036abea3 +x %u y2 = 164b642 +x /s y3 = ffffffffebfad66d +x %s y3 = ffffffffcae155c2 +x /u 3 = 532cbc0c07a42aab +x %u 3 = 1 +x /s 3 = fdd766b6b24ed556 +x %s 3 = 0 +x /u 5 = 31e7a40737c8e666 +x %u 5 = 4 +x /s 5 = feb470d40495b334 +x %s 5 = fffffffffffffffe +x /u 11 = 16af1c0347e6f45d +x %u 11 = 3 +x /s 11 = ff694a8eeacfae8c +x %s 11 = fffffffffffffffe +~x = 679cbdbe9137ffd +x & y = 5086200000000002 +x | y = fbce7f6416ec8063 +x ^ y = ab485f6416ec8061 +x << i = b764001000000000 +x >>u i = 1f30c684 +x >>s i = ffffffffff30c684 +x cmpu y = gt +x cmps y = lt +utod x = 43ef30c68482dd90 +dtou f = 6634832136daf +stod x = c399e72f6fa44e00 +dtos f = ffffd58f774c5ce4 +utof x = 5f798634 +stof x = dccf397b x = 368083376ba4ffa9 y = 6912b247b79a4904 @@ -7558,3 +8314,45 @@ dtos f = b3fdf698d581 utof x = 5ddbb784 stof x = 5ddbb784 +x = ca9a47c1649d27a7 +y = d56d650045e652aa +-x = 3565b83e9b62d859 +x + y = a007acc1aa837a51 +x - y = f52ce2c11eb6d4fd +x * y = 630e3c88ca19d2e6 +x /u y = 0 +x %u y = ca9a47c1649d27a7 +x /s y = 1 +x %s y = f52ce2c11eb6d4fd +x /u y2 = f3042098 +x %u y2 = 6b092fa7 +x /s y3 = 141176486 +x %s y3 = ffffffffdee649a7 +x /u 3 = 4388c295cc34628d +x %u 3 = 0 +x /s 3 = ee336d4076df0d38 +x %s 3 = ffffffffffffffff +x /u 5 = 2885418d141f6e54 +x %u 5 = 3 +x /s 5 = f5520e59e0ec3b22 +x %s 5 = fffffffffffffffd +x /u 11 = 126b1dcbc3541ae0 +x %u 11 = 7 +x /s 11 = fb254c57663cd510 +x %s 11 = fffffffffffffff7 +~x = 3565b83e9b62d858 +x & y = c0084500448402a2 +x | y = dfff67c165ff77af +x ^ y = 1ff722c1217b750d +x << i = 749e9c0000000000 +x >>u i = 32a691 +x >>s i = fffffffffff2a691 +x cmpu y = lt +x cmps y = lt +utod x = 43e95348f82c93a5 +dtou f = 52fc6dac31674 +stod x = c3cab2dc1f4db16c +dtos f = fffea20e1ffc05aa +utof x = 5f4a9a48 +stof x = de5596e1 + diff --git a/test/regression/Results/interop1 b/test/regression/Results/interop1 index 990dfe9d..6e32c1cb 100644 --- a/test/regression/Results/interop1 +++ b/test/regression/Results/interop1 @@ -1,4 +1,8 @@ --- CompCert calling native: +si8u: 177 +si8s: -79 +si16u: 64305 +si16s: -1231 s1: { a = 'a' } s2: { a = 'a', b = 'b' } s3: { a = 'a', b = 'b', c = ' c' } @@ -44,6 +48,10 @@ ru6: { a = 55555, b = 666 } ru7: { a = -10001, b = -789, c = 'z' } ru8: { a = 'x', b = 12345 } --- native calling CompCert: +si8u: 177 +si8s: -79 +si16u: 64305 +si16s: -1231 s1: { a = 'a' } s2: { a = 'a', b = 'b' } s3: { a = 'a', b = 'b', c = ' c' } diff --git a/test/regression/builtins-aarch64.c b/test/regression/builtins-aarch64.c new file mode 100644 index 00000000..2cfa2d09 --- /dev/null +++ b/test/regression/builtins-aarch64.c @@ -0,0 +1,47 @@ +/* Fun with builtin functions */ + +#include <stdio.h> + +int main(int argc, char ** argv) +{ + unsigned int x = 0x12345678; + unsigned int y = 0xDEADBEEF; + unsigned long long xx = 0x1234567812345678ULL; + unsigned long long yy = 0x1234567800000000ULL; + unsigned long long zz = 0x123456789ABCDEF0ULL; + unsigned z; + double a = 3.14159; + double b = 2.718; + double c = 1.414; + unsigned short s = 0x1234; + signed int u = 1234567; + signed int v = -9999; + + printf("bswap(%x) = %x\n", x, __builtin_bswap(x)); + printf("bswap16(%x) = %x\n", s, __builtin_bswap16(s)); + printf("bswap64(%llx) = %llx\n", zz, __builtin_bswap64(zz)); + printf("clz(%x) = %d\n", x, __builtin_clz(x)); + printf("clzll(%llx) = %d\n", (unsigned long long) x, __builtin_clzll(x)); + printf("clzll(%llx) = %d\n", xx, __builtin_clzll(xx)); + printf("cls(%d) = %d\n", u, __builtin_cls(u)); + printf("cls(%d) = %d\n", v, __builtin_cls(v)); + printf("clsll(%lld) = %d\n", (signed long long) u, __builtin_clsll(u)); + printf("clsll(%lld) = %d\n", (signed long long) v, __builtin_clsll(v)); + + printf("fsqrt(%f) = %f\n", a, __builtin_fsqrt(a)); + printf("fmadd(%f, %f, %f) = %f\n", a, b, c, __builtin_fmadd(a, b, c)); + printf("fmsub(%f, %f, %f) = %f\n", a, b, c, __builtin_fmsub(a, b, c)); + printf("fnmadd(%f, %f, %f) = %f\n", a, b, c, __builtin_fnmadd(a, b, c)); + printf("fnmsub(%f, %f, %f) = %f\n", a, b, c, __builtin_fnmsub(a, b, c)); + + /* Make sure that ignoring the result of a builtin + doesn't cause an internal error */ + (void) __builtin_bswap(x); + (void) __builtin_fsqrt(a); + return 0; +} + + + + + diff --git a/test/regression/builtins-arm.c b/test/regression/builtins-arm.c index 709343ce..d06e8e5e 100644 --- a/test/regression/builtins-arm.c +++ b/test/regression/builtins-arm.c @@ -2,14 +2,15 @@ #include <stdio.h> +unsigned int x = 0x12345678; +unsigned int y = 0xDEADBEEF; +unsigned long long xx = 0x1234567812345678ULL; +double a = 3.14159; +unsigned short s = 0x1234; + int main(int argc, char ** argv) { - unsigned int x = 0x12345678; - unsigned int y = 0xDEADBEEF; - unsigned long long xx = 0x1234567812345678ULL; unsigned z; - double a = 3.14159; - unsigned short s = 0x1234; printf("bswap(%x) = %x\n", x, __builtin_bswap(x)); printf("bswap16(%x) = %x\n", s, __builtin_bswap16(s)); diff --git a/test/regression/builtins-powerpc.c b/test/regression/builtins-powerpc.c index 23e9d191..8fd5818b 100644 --- a/test/regression/builtins-powerpc.c +++ b/test/regression/builtins-powerpc.c @@ -9,16 +9,17 @@ char * check_relative_error(double exact, double actual, double precision) return fabs(relative_error) <= precision ? "OK" : "ERROR"; } +unsigned int x = 0x12345678; +unsigned int y = 0xDEADBEEF; +unsigned long long xx = 0x1234567812345678ULL; +double a = 3.14159; +double b = 2.718; +double c = 1.414; +unsigned short s = 0x1234; + int main(int argc, char ** argv) { - unsigned int x = 0x12345678; - unsigned int y = 0xDEADBEEF; - unsigned long long xx = 0x1234567812345678ULL; unsigned z; - double a = 3.14159; - double b = 2.718; - double c = 1.414; - unsigned short s = 0x1234; printf("mulhw(%x, %x) = %x\n", x, y, __builtin_mulhw(x, y)); printf("mulhwu(%x, %x) = %x\n", x, y, __builtin_mulhwu(x, y)); diff --git a/test/regression/builtins-riscV.c b/test/regression/builtins-riscV.c index a302a6c4..c34fdf2c 100644 --- a/test/regression/builtins-riscV.c +++ b/test/regression/builtins-riscV.c @@ -2,15 +2,15 @@ #include <stdio.h> +unsigned int x = 0x12345678; +unsigned short s = 0x1234; +unsigned long long zz = 0x123456789ABCDEF0ULL; +double a = 3.14159; +double b = 2.718; +double c = 1.414; + int main(int argc, char ** argv) { - unsigned int x = 0x12345678; - unsigned short s = 0x1234; - unsigned long long zz = 0x123456789ABCDEF0ULL; - double a = 3.14159; - double b = 2.718; - double c = 1.414; - printf("bswap16(%x) = %x\n", s, __builtin_bswap16(s)); printf("bswap32(%x) = %x\n", x, __builtin_bswap32(x)); printf("bswap64(%llx) = %llx\n", zz, __builtin_bswap64(zz)); diff --git a/test/regression/builtins-x86.c b/test/regression/builtins-x86.c index 1ba213e7..6233f9fd 100644 --- a/test/regression/builtins-x86.c +++ b/test/regression/builtins-x86.c @@ -2,18 +2,19 @@ #include <stdio.h> +unsigned int x = 0x12345678; +unsigned int y = 0xDEADBEEF; +unsigned long long xx = 0x1234567812345678ULL; +unsigned long long yy = 0x1234567800000000ULL; +unsigned long long zz = 0x123456789ABCDEF0ULL; +double a = 3.14159; +double b = 2.718; +double c = 1.414; +unsigned short s = 0x1234; + int main(int argc, char ** argv) { - unsigned int x = 0x12345678; - unsigned int y = 0xDEADBEEF; - unsigned long long xx = 0x1234567812345678ULL; - unsigned long long yy = 0x1234567800000000ULL; - unsigned long long zz = 0x123456789ABCDEF0ULL; unsigned z; - double a = 3.14159; - double b = 2.718; - double c = 1.414; - unsigned short s = 0x1234; printf("bswap(%x) = %x\n", x, __builtin_bswap(x)); printf("bswap16(%x) = %x\n", s, __builtin_bswap16(s)); diff --git a/test/regression/extasm.c b/test/regression/extasm.c index babc57f1..297178d1 100644 --- a/test/regression/extasm.c +++ b/test/regression/extasm.c @@ -5,14 +5,16 @@ int clobbers(int x, int z) { int y; asm("TEST0 out:%0 in:%1" : "=r"(y) : "r"(x) : "cc" -#if defined(__x86_64__) +#if defined(ARCH_x86) && defined(MODEL_64) , "rax", "rdx", "rbx" -#elif defined(__i386__) +#elif defined(ARCH_x86) && !defined(MODEL_64) , "eax", "edx", "ebx" -#elif defined(__arm__) +#elif defined(ARCH_arm) , "r0", "r1", "r4" -#elif defined(__PPC__) +#elif defined(ARCH_powerpc) , "r0", "r3", "r4", "r31" +#elif defined(ARCH_aarch64) + , "x0", "x1", "x16", "x29", "x30" #endif ); return y + z; @@ -21,7 +23,8 @@ int clobbers(int x, int z) #if (defined(ARCH_x86) && defined(MODEL_64)) \ || (defined(ARCH_riscV) && defined(MODEL_64)) \ || (defined(ARCH_powerpc) && defined(MODEL_ppc64)) \ - || (defined(ARCH_powerpc) && defined(MODEL_e5500)) + || (defined(ARCH_powerpc) && defined(MODEL_e5500)) \ + || defined(ARCH_aarch64) #define SIXTYFOUR #else #undef SIXTYFOUR @@ -33,6 +36,7 @@ int main() void * y; long long z; double f; + float sf; char c[16]; /* No inputs, no outputs */ @@ -72,6 +76,15 @@ int main() #ifdef FAILURES asm("FAIL4 a:%[a]" : "=r"(x) : [z]"i"(0)); #endif + /* One argument of each type */ + asm("TEST15 int32 %0" : : "r" (x)); +#ifdef SIXTYFOUR + asm("TEST15 int64 %0" : : "r" (z)); +#else + asm("TEST15 int64 %Q0 / %R0" : : "r" (z)); +#endif + asm("TEST15 float64 %0" : : "r" (f)); + asm("TEST15 float32 %0" : : "r" (sf)); /* Various failures */ #ifdef FAILURES asm("FAIL5 out:%0,%1" : "=r"(x), "=r"(y)); diff --git a/test/regression/floats-basics.c b/test/regression/floats-basics.c index a7ba3623..876a0d42 100644 --- a/test/regression/floats-basics.c +++ b/test/regression/floats-basics.c @@ -1,18 +1,10 @@ -#include<stdio.h> -#include<stdlib.h> +#include <stdio.h> +#include <stdlib.h> +#include "../endian.h" #define STR_EXPAND(tok) #tok #define STR(tok) STR_EXPAND(tok) -#if defined(__ppc__) || defined(__PPC__) || defined(__ARMEB__) -#define ARCH_BIG_ENDIAN -#elif defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__) \ - || defined(__riscv) -#undef ARCH_BIG_ENDIAN -#else -#error "unknown endianness" -#endif - union converter64 { double dbl; struct { diff --git a/test/regression/floats-lit.c b/test/regression/floats-lit.c new file mode 100644 index 00000000..a1098faf --- /dev/null +++ b/test/regression/floats-lit.c @@ -0,0 +1,559 @@ +#include <stdio.h> + +int error = 0; + +void dbl(double x, unsigned long long bits) +{ + union { double d; unsigned long long i; } u; + u.d = x; + if (u.i != bits) { + printf("Error: expected 0x%016llx, got 0x%016llx\n", bits, u.i); + error = 1; + } +} + +void sng(float x, unsigned int bits) +{ + union { float f; unsigned int i; } u; + u.f = x; + if (u.i != bits) { + printf("Error: expected 0x%08x, got 0x%08x\n", bits, u.i); + error = 1; + } +} + +void testdbl(void) +{ + printf("--- Double-precision test\n"); + dbl(0.0, 0ULL); + dbl((-0.0), 0x8000000000000000ULL); + // The following are the "fmov immediate" of aarch64 + // They include +1.0 and -1.0 + dbl(0x1p-3, 0x3fc0000000000000ULL); + dbl(0x1.1p-3, 0x3fc1000000000000ULL); + dbl(0x1.2p-3, 0x3fc2000000000000ULL); + dbl(0x1.3p-3, 0x3fc3000000000000ULL); + dbl(0x1.4p-3, 0x3fc4000000000000ULL); + dbl(0x1.5p-3, 0x3fc5000000000000ULL); + dbl(0x1.6p-3, 0x3fc6000000000000ULL); + dbl(0x1.7p-3, 0x3fc7000000000000ULL); + dbl(0x1.8p-3, 0x3fc8000000000000ULL); + dbl(0x1.9p-3, 0x3fc9000000000000ULL); + dbl(0x1.ap-3, 0x3fca000000000000ULL); + dbl(0x1.bp-3, 0x3fcb000000000000ULL); + dbl(0x1.cp-3, 0x3fcc000000000000ULL); + dbl(0x1.dp-3, 0x3fcd000000000000ULL); + dbl(0x1.ep-3, 0x3fce000000000000ULL); + dbl(0x1.fp-3, 0x3fcf000000000000ULL); + dbl(0x1p-2, 0x3fd0000000000000ULL); + dbl(0x1.1p-2, 0x3fd1000000000000ULL); + dbl(0x1.2p-2, 0x3fd2000000000000ULL); + dbl(0x1.3p-2, 0x3fd3000000000000ULL); + dbl(0x1.4p-2, 0x3fd4000000000000ULL); + dbl(0x1.5p-2, 0x3fd5000000000000ULL); + dbl(0x1.6p-2, 0x3fd6000000000000ULL); + dbl(0x1.7p-2, 0x3fd7000000000000ULL); + dbl(0x1.8p-2, 0x3fd8000000000000ULL); + dbl(0x1.9p-2, 0x3fd9000000000000ULL); + dbl(0x1.ap-2, 0x3fda000000000000ULL); + dbl(0x1.bp-2, 0x3fdb000000000000ULL); + dbl(0x1.cp-2, 0x3fdc000000000000ULL); + dbl(0x1.dp-2, 0x3fdd000000000000ULL); + dbl(0x1.ep-2, 0x3fde000000000000ULL); + dbl(0x1.fp-2, 0x3fdf000000000000ULL); + dbl(0x1p-1, 0x3fe0000000000000ULL); + dbl(0x1.1p-1, 0x3fe1000000000000ULL); + dbl(0x1.2p-1, 0x3fe2000000000000ULL); + dbl(0x1.3p-1, 0x3fe3000000000000ULL); + dbl(0x1.4p-1, 0x3fe4000000000000ULL); + dbl(0x1.5p-1, 0x3fe5000000000000ULL); + dbl(0x1.6p-1, 0x3fe6000000000000ULL); + dbl(0x1.7p-1, 0x3fe7000000000000ULL); + dbl(0x1.8p-1, 0x3fe8000000000000ULL); + dbl(0x1.9p-1, 0x3fe9000000000000ULL); + dbl(0x1.ap-1, 0x3fea000000000000ULL); + dbl(0x1.bp-1, 0x3feb000000000000ULL); + dbl(0x1.cp-1, 0x3fec000000000000ULL); + dbl(0x1.dp-1, 0x3fed000000000000ULL); + dbl(0x1.ep-1, 0x3fee000000000000ULL); + dbl(0x1.fp-1, 0x3fef000000000000ULL); + dbl(0x1p+0, 0x3ff0000000000000ULL); + dbl(0x1.1p+0, 0x3ff1000000000000ULL); + dbl(0x1.2p+0, 0x3ff2000000000000ULL); + dbl(0x1.3p+0, 0x3ff3000000000000ULL); + dbl(0x1.4p+0, 0x3ff4000000000000ULL); + dbl(0x1.5p+0, 0x3ff5000000000000ULL); + dbl(0x1.6p+0, 0x3ff6000000000000ULL); + dbl(0x1.7p+0, 0x3ff7000000000000ULL); + dbl(0x1.8p+0, 0x3ff8000000000000ULL); + dbl(0x1.9p+0, 0x3ff9000000000000ULL); + dbl(0x1.ap+0, 0x3ffa000000000000ULL); + dbl(0x1.bp+0, 0x3ffb000000000000ULL); + dbl(0x1.cp+0, 0x3ffc000000000000ULL); + dbl(0x1.dp+0, 0x3ffd000000000000ULL); + dbl(0x1.ep+0, 0x3ffe000000000000ULL); + dbl(0x1.fp+0, 0x3fff000000000000ULL); + dbl(0x1p+1, 0x4000000000000000ULL); + dbl(0x1.1p+1, 0x4001000000000000ULL); + dbl(0x1.2p+1, 0x4002000000000000ULL); + dbl(0x1.3p+1, 0x4003000000000000ULL); + dbl(0x1.4p+1, 0x4004000000000000ULL); + dbl(0x1.5p+1, 0x4005000000000000ULL); + dbl(0x1.6p+1, 0x4006000000000000ULL); + dbl(0x1.7p+1, 0x4007000000000000ULL); + dbl(0x1.8p+1, 0x4008000000000000ULL); + dbl(0x1.9p+1, 0x4009000000000000ULL); + dbl(0x1.ap+1, 0x400a000000000000ULL); + dbl(0x1.bp+1, 0x400b000000000000ULL); + dbl(0x1.cp+1, 0x400c000000000000ULL); + dbl(0x1.dp+1, 0x400d000000000000ULL); + dbl(0x1.ep+1, 0x400e000000000000ULL); + dbl(0x1.fp+1, 0x400f000000000000ULL); + dbl(0x1p+2, 0x4010000000000000ULL); + dbl(0x1.1p+2, 0x4011000000000000ULL); + dbl(0x1.2p+2, 0x4012000000000000ULL); + dbl(0x1.3p+2, 0x4013000000000000ULL); + dbl(0x1.4p+2, 0x4014000000000000ULL); + dbl(0x1.5p+2, 0x4015000000000000ULL); + dbl(0x1.6p+2, 0x4016000000000000ULL); + dbl(0x1.7p+2, 0x4017000000000000ULL); + dbl(0x1.8p+2, 0x4018000000000000ULL); + dbl(0x1.9p+2, 0x4019000000000000ULL); + dbl(0x1.ap+2, 0x401a000000000000ULL); + dbl(0x1.bp+2, 0x401b000000000000ULL); + dbl(0x1.cp+2, 0x401c000000000000ULL); + dbl(0x1.dp+2, 0x401d000000000000ULL); + dbl(0x1.ep+2, 0x401e000000000000ULL); + dbl(0x1.fp+2, 0x401f000000000000ULL); + dbl(0x1p+3, 0x4020000000000000ULL); + dbl(0x1.1p+3, 0x4021000000000000ULL); + dbl(0x1.2p+3, 0x4022000000000000ULL); + dbl(0x1.3p+3, 0x4023000000000000ULL); + dbl(0x1.4p+3, 0x4024000000000000ULL); + dbl(0x1.5p+3, 0x4025000000000000ULL); + dbl(0x1.6p+3, 0x4026000000000000ULL); + dbl(0x1.7p+3, 0x4027000000000000ULL); + dbl(0x1.8p+3, 0x4028000000000000ULL); + dbl(0x1.9p+3, 0x4029000000000000ULL); + dbl(0x1.ap+3, 0x402a000000000000ULL); + dbl(0x1.bp+3, 0x402b000000000000ULL); + dbl(0x1.cp+3, 0x402c000000000000ULL); + dbl(0x1.dp+3, 0x402d000000000000ULL); + dbl(0x1.ep+3, 0x402e000000000000ULL); + dbl(0x1.fp+3, 0x402f000000000000ULL); + dbl(0x1p+4, 0x4030000000000000ULL); + dbl(0x1.1p+4, 0x4031000000000000ULL); + dbl(0x1.2p+4, 0x4032000000000000ULL); + dbl(0x1.3p+4, 0x4033000000000000ULL); + dbl(0x1.4p+4, 0x4034000000000000ULL); + dbl(0x1.5p+4, 0x4035000000000000ULL); + dbl(0x1.6p+4, 0x4036000000000000ULL); + dbl(0x1.7p+4, 0x4037000000000000ULL); + dbl(0x1.8p+4, 0x4038000000000000ULL); + dbl(0x1.9p+4, 0x4039000000000000ULL); + dbl(0x1.ap+4, 0x403a000000000000ULL); + dbl(0x1.bp+4, 0x403b000000000000ULL); + dbl(0x1.cp+4, 0x403c000000000000ULL); + dbl(0x1.dp+4, 0x403d000000000000ULL); + dbl(0x1.ep+4, 0x403e000000000000ULL); + dbl(0x1.fp+4, 0x403f000000000000ULL); + dbl((-0x1p-3), 0xbfc0000000000000ULL); + dbl((-0x1.1p-3), 0xbfc1000000000000ULL); + dbl((-0x1.2p-3), 0xbfc2000000000000ULL); + dbl((-0x1.3p-3), 0xbfc3000000000000ULL); + dbl((-0x1.4p-3), 0xbfc4000000000000ULL); + dbl((-0x1.5p-3), 0xbfc5000000000000ULL); + dbl((-0x1.6p-3), 0xbfc6000000000000ULL); + dbl((-0x1.7p-3), 0xbfc7000000000000ULL); + dbl((-0x1.8p-3), 0xbfc8000000000000ULL); + dbl((-0x1.9p-3), 0xbfc9000000000000ULL); + dbl((-0x1.ap-3), 0xbfca000000000000ULL); + dbl((-0x1.bp-3), 0xbfcb000000000000ULL); + dbl((-0x1.cp-3), 0xbfcc000000000000ULL); + dbl((-0x1.dp-3), 0xbfcd000000000000ULL); + dbl((-0x1.ep-3), 0xbfce000000000000ULL); + dbl((-0x1.fp-3), 0xbfcf000000000000ULL); + dbl((-0x1p-2), 0xbfd0000000000000ULL); + dbl((-0x1.1p-2), 0xbfd1000000000000ULL); + dbl((-0x1.2p-2), 0xbfd2000000000000ULL); + dbl((-0x1.3p-2), 0xbfd3000000000000ULL); + dbl((-0x1.4p-2), 0xbfd4000000000000ULL); + dbl((-0x1.5p-2), 0xbfd5000000000000ULL); + dbl((-0x1.6p-2), 0xbfd6000000000000ULL); + dbl((-0x1.7p-2), 0xbfd7000000000000ULL); + dbl((-0x1.8p-2), 0xbfd8000000000000ULL); + dbl((-0x1.9p-2), 0xbfd9000000000000ULL); + dbl((-0x1.ap-2), 0xbfda000000000000ULL); + dbl((-0x1.bp-2), 0xbfdb000000000000ULL); + dbl((-0x1.cp-2), 0xbfdc000000000000ULL); + dbl((-0x1.dp-2), 0xbfdd000000000000ULL); + dbl((-0x1.ep-2), 0xbfde000000000000ULL); + dbl((-0x1.fp-2), 0xbfdf000000000000ULL); + dbl((-0x1p-1), 0xbfe0000000000000ULL); + dbl((-0x1.1p-1), 0xbfe1000000000000ULL); + dbl((-0x1.2p-1), 0xbfe2000000000000ULL); + dbl((-0x1.3p-1), 0xbfe3000000000000ULL); + dbl((-0x1.4p-1), 0xbfe4000000000000ULL); + dbl((-0x1.5p-1), 0xbfe5000000000000ULL); + dbl((-0x1.6p-1), 0xbfe6000000000000ULL); + dbl((-0x1.7p-1), 0xbfe7000000000000ULL); + dbl((-0x1.8p-1), 0xbfe8000000000000ULL); + dbl((-0x1.9p-1), 0xbfe9000000000000ULL); + dbl((-0x1.ap-1), 0xbfea000000000000ULL); + dbl((-0x1.bp-1), 0xbfeb000000000000ULL); + dbl((-0x1.cp-1), 0xbfec000000000000ULL); + dbl((-0x1.dp-1), 0xbfed000000000000ULL); + dbl((-0x1.ep-1), 0xbfee000000000000ULL); + dbl((-0x1.fp-1), 0xbfef000000000000ULL); + dbl((-0x1p+0), 0xbff0000000000000ULL); + dbl((-0x1.1p+0), 0xbff1000000000000ULL); + dbl((-0x1.2p+0), 0xbff2000000000000ULL); + dbl((-0x1.3p+0), 0xbff3000000000000ULL); + dbl((-0x1.4p+0), 0xbff4000000000000ULL); + dbl((-0x1.5p+0), 0xbff5000000000000ULL); + dbl((-0x1.6p+0), 0xbff6000000000000ULL); + dbl((-0x1.7p+0), 0xbff7000000000000ULL); + dbl((-0x1.8p+0), 0xbff8000000000000ULL); + dbl((-0x1.9p+0), 0xbff9000000000000ULL); + dbl((-0x1.ap+0), 0xbffa000000000000ULL); + dbl((-0x1.bp+0), 0xbffb000000000000ULL); + dbl((-0x1.cp+0), 0xbffc000000000000ULL); + dbl((-0x1.dp+0), 0xbffd000000000000ULL); + dbl((-0x1.ep+0), 0xbffe000000000000ULL); + dbl((-0x1.fp+0), 0xbfff000000000000ULL); + dbl((-0x1p+1), 0xc000000000000000ULL); + dbl((-0x1.1p+1), 0xc001000000000000ULL); + dbl((-0x1.2p+1), 0xc002000000000000ULL); + dbl((-0x1.3p+1), 0xc003000000000000ULL); + dbl((-0x1.4p+1), 0xc004000000000000ULL); + dbl((-0x1.5p+1), 0xc005000000000000ULL); + dbl((-0x1.6p+1), 0xc006000000000000ULL); + dbl((-0x1.7p+1), 0xc007000000000000ULL); + dbl((-0x1.8p+1), 0xc008000000000000ULL); + dbl((-0x1.9p+1), 0xc009000000000000ULL); + dbl((-0x1.ap+1), 0xc00a000000000000ULL); + dbl((-0x1.bp+1), 0xc00b000000000000ULL); + dbl((-0x1.cp+1), 0xc00c000000000000ULL); + dbl((-0x1.dp+1), 0xc00d000000000000ULL); + dbl((-0x1.ep+1), 0xc00e000000000000ULL); + dbl((-0x1.fp+1), 0xc00f000000000000ULL); + dbl((-0x1p+2), 0xc010000000000000ULL); + dbl((-0x1.1p+2), 0xc011000000000000ULL); + dbl((-0x1.2p+2), 0xc012000000000000ULL); + dbl((-0x1.3p+2), 0xc013000000000000ULL); + dbl((-0x1.4p+2), 0xc014000000000000ULL); + dbl((-0x1.5p+2), 0xc015000000000000ULL); + dbl((-0x1.6p+2), 0xc016000000000000ULL); + dbl((-0x1.7p+2), 0xc017000000000000ULL); + dbl((-0x1.8p+2), 0xc018000000000000ULL); + dbl((-0x1.9p+2), 0xc019000000000000ULL); + dbl((-0x1.ap+2), 0xc01a000000000000ULL); + dbl((-0x1.bp+2), 0xc01b000000000000ULL); + dbl((-0x1.cp+2), 0xc01c000000000000ULL); + dbl((-0x1.dp+2), 0xc01d000000000000ULL); + dbl((-0x1.ep+2), 0xc01e000000000000ULL); + dbl((-0x1.fp+2), 0xc01f000000000000ULL); + dbl((-0x1p+3), 0xc020000000000000ULL); + dbl((-0x1.1p+3), 0xc021000000000000ULL); + dbl((-0x1.2p+3), 0xc022000000000000ULL); + dbl((-0x1.3p+3), 0xc023000000000000ULL); + dbl((-0x1.4p+3), 0xc024000000000000ULL); + dbl((-0x1.5p+3), 0xc025000000000000ULL); + dbl((-0x1.6p+3), 0xc026000000000000ULL); + dbl((-0x1.7p+3), 0xc027000000000000ULL); + dbl((-0x1.8p+3), 0xc028000000000000ULL); + dbl((-0x1.9p+3), 0xc029000000000000ULL); + dbl((-0x1.ap+3), 0xc02a000000000000ULL); + dbl((-0x1.bp+3), 0xc02b000000000000ULL); + dbl((-0x1.cp+3), 0xc02c000000000000ULL); + dbl((-0x1.dp+3), 0xc02d000000000000ULL); + dbl((-0x1.ep+3), 0xc02e000000000000ULL); + dbl((-0x1.fp+3), 0xc02f000000000000ULL); + dbl((-0x1p+4), 0xc030000000000000ULL); + dbl((-0x1.1p+4), 0xc031000000000000ULL); + dbl((-0x1.2p+4), 0xc032000000000000ULL); + dbl((-0x1.3p+4), 0xc033000000000000ULL); + dbl((-0x1.4p+4), 0xc034000000000000ULL); + dbl((-0x1.5p+4), 0xc035000000000000ULL); + dbl((-0x1.6p+4), 0xc036000000000000ULL); + dbl((-0x1.7p+4), 0xc037000000000000ULL); + dbl((-0x1.8p+4), 0xc038000000000000ULL); + dbl((-0x1.9p+4), 0xc039000000000000ULL); + dbl((-0x1.ap+4), 0xc03a000000000000ULL); + dbl((-0x1.bp+4), 0xc03b000000000000ULL); + dbl((-0x1.cp+4), 0xc03c000000000000ULL); + dbl((-0x1.dp+4), 0xc03d000000000000ULL); + dbl((-0x1.ep+4), 0xc03e000000000000ULL); + dbl((-0x1.fp+4), 0xc03f000000000000ULL); +} + +void testsng(void) +{ + printf("--- Single-precision test\n"); + sng(0x0p+0, 0x0U); + sng(-0x0p+0, 0x80000000U); + sng(0x1p-3, 0x3e000000U); + sng(0x1.1p-3, 0x3e080000U); + sng(0x1.2p-3, 0x3e100000U); + sng(0x1.3p-3, 0x3e180000U); + sng(0x1.4p-3, 0x3e200000U); + sng(0x1.5p-3, 0x3e280000U); + sng(0x1.6p-3, 0x3e300000U); + sng(0x1.7p-3, 0x3e380000U); + sng(0x1.8p-3, 0x3e400000U); + sng(0x1.9p-3, 0x3e480000U); + sng(0x1.ap-3, 0x3e500000U); + sng(0x1.bp-3, 0x3e580000U); + sng(0x1.cp-3, 0x3e600000U); + sng(0x1.dp-3, 0x3e680000U); + sng(0x1.ep-3, 0x3e700000U); + sng(0x1.fp-3, 0x3e780000U); + sng(0x1p-2, 0x3e800000U); + sng(0x1.1p-2, 0x3e880000U); + sng(0x1.2p-2, 0x3e900000U); + sng(0x1.3p-2, 0x3e980000U); + sng(0x1.4p-2, 0x3ea00000U); + sng(0x1.5p-2, 0x3ea80000U); + sng(0x1.6p-2, 0x3eb00000U); + sng(0x1.7p-2, 0x3eb80000U); + sng(0x1.8p-2, 0x3ec00000U); + sng(0x1.9p-2, 0x3ec80000U); + sng(0x1.ap-2, 0x3ed00000U); + sng(0x1.bp-2, 0x3ed80000U); + sng(0x1.cp-2, 0x3ee00000U); + sng(0x1.dp-2, 0x3ee80000U); + sng(0x1.ep-2, 0x3ef00000U); + sng(0x1.fp-2, 0x3ef80000U); + sng(0x1p-1, 0x3f000000U); + sng(0x1.1p-1, 0x3f080000U); + sng(0x1.2p-1, 0x3f100000U); + sng(0x1.3p-1, 0x3f180000U); + sng(0x1.4p-1, 0x3f200000U); + sng(0x1.5p-1, 0x3f280000U); + sng(0x1.6p-1, 0x3f300000U); + sng(0x1.7p-1, 0x3f380000U); + sng(0x1.8p-1, 0x3f400000U); + sng(0x1.9p-1, 0x3f480000U); + sng(0x1.ap-1, 0x3f500000U); + sng(0x1.bp-1, 0x3f580000U); + sng(0x1.cp-1, 0x3f600000U); + sng(0x1.dp-1, 0x3f680000U); + sng(0x1.ep-1, 0x3f700000U); + sng(0x1.fp-1, 0x3f780000U); + sng(0x1p+0, 0x3f800000U); + sng(0x1.1p+0, 0x3f880000U); + sng(0x1.2p+0, 0x3f900000U); + sng(0x1.3p+0, 0x3f980000U); + sng(0x1.4p+0, 0x3fa00000U); + sng(0x1.5p+0, 0x3fa80000U); + sng(0x1.6p+0, 0x3fb00000U); + sng(0x1.7p+0, 0x3fb80000U); + sng(0x1.8p+0, 0x3fc00000U); + sng(0x1.9p+0, 0x3fc80000U); + sng(0x1.ap+0, 0x3fd00000U); + sng(0x1.bp+0, 0x3fd80000U); + sng(0x1.cp+0, 0x3fe00000U); + sng(0x1.dp+0, 0x3fe80000U); + sng(0x1.ep+0, 0x3ff00000U); + sng(0x1.fp+0, 0x3ff80000U); + sng(0x1p+1, 0x40000000U); + sng(0x1.1p+1, 0x40080000U); + sng(0x1.2p+1, 0x40100000U); + sng(0x1.3p+1, 0x40180000U); + sng(0x1.4p+1, 0x40200000U); + sng(0x1.5p+1, 0x40280000U); + sng(0x1.6p+1, 0x40300000U); + sng(0x1.7p+1, 0x40380000U); + sng(0x1.8p+1, 0x40400000U); + sng(0x1.9p+1, 0x40480000U); + sng(0x1.ap+1, 0x40500000U); + sng(0x1.bp+1, 0x40580000U); + sng(0x1.cp+1, 0x40600000U); + sng(0x1.dp+1, 0x40680000U); + sng(0x1.ep+1, 0x40700000U); + sng(0x1.fp+1, 0x40780000U); + sng(0x1p+2, 0x40800000U); + sng(0x1.1p+2, 0x40880000U); + sng(0x1.2p+2, 0x40900000U); + sng(0x1.3p+2, 0x40980000U); + sng(0x1.4p+2, 0x40a00000U); + sng(0x1.5p+2, 0x40a80000U); + sng(0x1.6p+2, 0x40b00000U); + sng(0x1.7p+2, 0x40b80000U); + sng(0x1.8p+2, 0x40c00000U); + sng(0x1.9p+2, 0x40c80000U); + sng(0x1.ap+2, 0x40d00000U); + sng(0x1.bp+2, 0x40d80000U); + sng(0x1.cp+2, 0x40e00000U); + sng(0x1.dp+2, 0x40e80000U); + sng(0x1.ep+2, 0x40f00000U); + sng(0x1.fp+2, 0x40f80000U); + sng(0x1p+3, 0x41000000U); + sng(0x1.1p+3, 0x41080000U); + sng(0x1.2p+3, 0x41100000U); + sng(0x1.3p+3, 0x41180000U); + sng(0x1.4p+3, 0x41200000U); + sng(0x1.5p+3, 0x41280000U); + sng(0x1.6p+3, 0x41300000U); + sng(0x1.7p+3, 0x41380000U); + sng(0x1.8p+3, 0x41400000U); + sng(0x1.9p+3, 0x41480000U); + sng(0x1.ap+3, 0x41500000U); + sng(0x1.bp+3, 0x41580000U); + sng(0x1.cp+3, 0x41600000U); + sng(0x1.dp+3, 0x41680000U); + sng(0x1.ep+3, 0x41700000U); + sng(0x1.fp+3, 0x41780000U); + sng(0x1p+4, 0x41800000U); + sng(0x1.1p+4, 0x41880000U); + sng(0x1.2p+4, 0x41900000U); + sng(0x1.3p+4, 0x41980000U); + sng(0x1.4p+4, 0x41a00000U); + sng(0x1.5p+4, 0x41a80000U); + sng(0x1.6p+4, 0x41b00000U); + sng(0x1.7p+4, 0x41b80000U); + sng(0x1.8p+4, 0x41c00000U); + sng(0x1.9p+4, 0x41c80000U); + sng(0x1.ap+4, 0x41d00000U); + sng(0x1.bp+4, 0x41d80000U); + sng(0x1.cp+4, 0x41e00000U); + sng(0x1.dp+4, 0x41e80000U); + sng(0x1.ep+4, 0x41f00000U); + sng(0x1.fp+4, 0x41f80000U); + sng(-0x1p-3, 0xbe000000U); + sng(-0x1.1p-3, 0xbe080000U); + sng(-0x1.2p-3, 0xbe100000U); + sng(-0x1.3p-3, 0xbe180000U); + sng(-0x1.4p-3, 0xbe200000U); + sng(-0x1.5p-3, 0xbe280000U); + sng(-0x1.6p-3, 0xbe300000U); + sng(-0x1.7p-3, 0xbe380000U); + sng(-0x1.8p-3, 0xbe400000U); + sng(-0x1.9p-3, 0xbe480000U); + sng(-0x1.ap-3, 0xbe500000U); + sng(-0x1.bp-3, 0xbe580000U); + sng(-0x1.cp-3, 0xbe600000U); + sng(-0x1.dp-3, 0xbe680000U); + sng(-0x1.ep-3, 0xbe700000U); + sng(-0x1.fp-3, 0xbe780000U); + sng(-0x1p-2, 0xbe800000U); + sng(-0x1.1p-2, 0xbe880000U); + sng(-0x1.2p-2, 0xbe900000U); + sng(-0x1.3p-2, 0xbe980000U); + sng(-0x1.4p-2, 0xbea00000U); + sng(-0x1.5p-2, 0xbea80000U); + sng(-0x1.6p-2, 0xbeb00000U); + sng(-0x1.7p-2, 0xbeb80000U); + sng(-0x1.8p-2, 0xbec00000U); + sng(-0x1.9p-2, 0xbec80000U); + sng(-0x1.ap-2, 0xbed00000U); + sng(-0x1.bp-2, 0xbed80000U); + sng(-0x1.cp-2, 0xbee00000U); + sng(-0x1.dp-2, 0xbee80000U); + sng(-0x1.ep-2, 0xbef00000U); + sng(-0x1.fp-2, 0xbef80000U); + sng(-0x1p-1, 0xbf000000U); + sng(-0x1.1p-1, 0xbf080000U); + sng(-0x1.2p-1, 0xbf100000U); + sng(-0x1.3p-1, 0xbf180000U); + sng(-0x1.4p-1, 0xbf200000U); + sng(-0x1.5p-1, 0xbf280000U); + sng(-0x1.6p-1, 0xbf300000U); + sng(-0x1.7p-1, 0xbf380000U); + sng(-0x1.8p-1, 0xbf400000U); + sng(-0x1.9p-1, 0xbf480000U); + sng(-0x1.ap-1, 0xbf500000U); + sng(-0x1.bp-1, 0xbf580000U); + sng(-0x1.cp-1, 0xbf600000U); + sng(-0x1.dp-1, 0xbf680000U); + sng(-0x1.ep-1, 0xbf700000U); + sng(-0x1.fp-1, 0xbf780000U); + sng(-0x1p+0, 0xbf800000U); + sng(-0x1.1p+0, 0xbf880000U); + sng(-0x1.2p+0, 0xbf900000U); + sng(-0x1.3p+0, 0xbf980000U); + sng(-0x1.4p+0, 0xbfa00000U); + sng(-0x1.5p+0, 0xbfa80000U); + sng(-0x1.6p+0, 0xbfb00000U); + sng(-0x1.7p+0, 0xbfb80000U); + sng(-0x1.8p+0, 0xbfc00000U); + sng(-0x1.9p+0, 0xbfc80000U); + sng(-0x1.ap+0, 0xbfd00000U); + sng(-0x1.bp+0, 0xbfd80000U); + sng(-0x1.cp+0, 0xbfe00000U); + sng(-0x1.dp+0, 0xbfe80000U); + sng(-0x1.ep+0, 0xbff00000U); + sng(-0x1.fp+0, 0xbff80000U); + sng(-0x1p+1, 0xc0000000U); + sng(-0x1.1p+1, 0xc0080000U); + sng(-0x1.2p+1, 0xc0100000U); + sng(-0x1.3p+1, 0xc0180000U); + sng(-0x1.4p+1, 0xc0200000U); + sng(-0x1.5p+1, 0xc0280000U); + sng(-0x1.6p+1, 0xc0300000U); + sng(-0x1.7p+1, 0xc0380000U); + sng(-0x1.8p+1, 0xc0400000U); + sng(-0x1.9p+1, 0xc0480000U); + sng(-0x1.ap+1, 0xc0500000U); + sng(-0x1.bp+1, 0xc0580000U); + sng(-0x1.cp+1, 0xc0600000U); + sng(-0x1.dp+1, 0xc0680000U); + sng(-0x1.ep+1, 0xc0700000U); + sng(-0x1.fp+1, 0xc0780000U); + sng(-0x1p+2, 0xc0800000U); + sng(-0x1.1p+2, 0xc0880000U); + sng(-0x1.2p+2, 0xc0900000U); + sng(-0x1.3p+2, 0xc0980000U); + sng(-0x1.4p+2, 0xc0a00000U); + sng(-0x1.5p+2, 0xc0a80000U); + sng(-0x1.6p+2, 0xc0b00000U); + sng(-0x1.7p+2, 0xc0b80000U); + sng(-0x1.8p+2, 0xc0c00000U); + sng(-0x1.9p+2, 0xc0c80000U); + sng(-0x1.ap+2, 0xc0d00000U); + sng(-0x1.bp+2, 0xc0d80000U); + sng(-0x1.cp+2, 0xc0e00000U); + sng(-0x1.dp+2, 0xc0e80000U); + sng(-0x1.ep+2, 0xc0f00000U); + sng(-0x1.fp+2, 0xc0f80000U); + sng(-0x1p+3, 0xc1000000U); + sng(-0x1.1p+3, 0xc1080000U); + sng(-0x1.2p+3, 0xc1100000U); + sng(-0x1.3p+3, 0xc1180000U); + sng(-0x1.4p+3, 0xc1200000U); + sng(-0x1.5p+3, 0xc1280000U); + sng(-0x1.6p+3, 0xc1300000U); + sng(-0x1.7p+3, 0xc1380000U); + sng(-0x1.8p+3, 0xc1400000U); + sng(-0x1.9p+3, 0xc1480000U); + sng(-0x1.ap+3, 0xc1500000U); + sng(-0x1.bp+3, 0xc1580000U); + sng(-0x1.cp+3, 0xc1600000U); + sng(-0x1.dp+3, 0xc1680000U); + sng(-0x1.ep+3, 0xc1700000U); + sng(-0x1.fp+3, 0xc1780000U); + sng(-0x1p+4, 0xc1800000U); + sng(-0x1.1p+4, 0xc1880000U); + sng(-0x1.2p+4, 0xc1900000U); + sng(-0x1.3p+4, 0xc1980000U); + sng(-0x1.4p+4, 0xc1a00000U); + sng(-0x1.5p+4, 0xc1a80000U); + sng(-0x1.6p+4, 0xc1b00000U); + sng(-0x1.7p+4, 0xc1b80000U); + sng(-0x1.8p+4, 0xc1c00000U); + sng(-0x1.9p+4, 0xc1c80000U); + sng(-0x1.ap+4, 0xc1d00000U); + sng(-0x1.bp+4, 0xc1d80000U); + sng(-0x1.cp+4, 0xc1e00000U); + sng(-0x1.dp+4, 0xc1e80000U); + sng(-0x1.ep+4, 0xc1f00000U); + sng(-0x1.fp+4, 0xc1f80000U); +} + + +int main() +{ + testdbl(); + testsng(); + return error; +} diff --git a/test/regression/floats.c b/test/regression/floats.c index 84c4e062..58c202ae 100644 --- a/test/regression/floats.c +++ b/test/regression/floats.c @@ -1,17 +1,9 @@ -#include<stdio.h> +#include <stdio.h> +#include "../endian.h" #define STR_EXPAND(tok) #tok #define STR(tok) STR_EXPAND(tok) -#if defined(__ppc__) || defined(__PPC__) || defined(__ARMEB__) -#define ARCH_BIG_ENDIAN -#elif defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__) \ - || defined(__riscv) -#undef ARCH_BIG_ENDIAN -#else -#error "unknown endianness" -#endif - union converter64 { double dbl; struct { diff --git a/test/regression/ifconv.c b/test/regression/ifconv.c new file mode 100644 index 00000000..e12a394c --- /dev/null +++ b/test/regression/ifconv.c @@ -0,0 +1,149 @@ +#include <stdio.h> + +/* Several equivalent forms that should be turned into cmov */ + +int test1(int x, int y, int a, int b) +{ + return x < y ? a : b; +} + +int test2(int x, int y, int a, int b) +{ + int r; + if (x < y) { r = a; } else { r = b; } + return r; +} + +int test3(int x, int y, int a, int b) +{ + int r = b; + if (x < y) { r = a; } + return r; +} + +int test4(int x, int y, int a, int b) +{ + int r = a; + if (x < y) { /*skip*/; } else { r = b; } + return r; +} + +/* A more advanced example */ + +int test5(int x, int y, int a) +{ + return x < y ? a + 1 : a - 1; +} + +/* Unsafe operations should not be turned into cmov */ + +int test6(int * p) +{ + return p == NULL ? 0 : *p + 10; +} + +int test7(int a, int b) +{ + return b == 0 ? -1 : a / b; +} + +/* Very large operations should not be turned into cmov */ + +int test8(int a) +{ + return a == 0 ? 0 : a*a*a*a - 2*a*a*a + 10*a*a + 42*a - 123; +} + +/* Some examples with 64-bit integers */ + +long long ltest1(int x, int y, long long a, long long b) +{ + return x < y ? a + 1 : b >> 2; +} + +/* Some examples with floating-point */ + +double dmax(double x, double y) +{ + return x >= y ? x : y; +} + +double dabs(double x) +{ + return x < 0.0 ? -x : x; +} + +float smin(float x, float y) +{ + return x <= y ? x : y; +} + +float sdoz(float x, float y) +{ + return x >= y ? x - y : 0.0f; +} + +/* Examples where constant propagation should take place */ + +int constprop1(int x) +{ + int n = 0; + return n ? x : 42; +} + +int constprop2(int x) +{ + int n = 1; + return n ? x : 42; +} + +int constprop3(int x, int y) +{ + int n = 0; + return x < n ? y - 1 : y + 1; +} + +/* Test harness */ + +#define TESTI(call) printf(#call " = %d\n", call) +#define TESTL(call) printf(#call " = %lld\n", call) +#define TESTF(call) printf(#call " = %f\n", call) + + +int main() +{ + int i = 1234; + TESTI(test1(0,1,12,34)); + TESTI(test1(1,0,45,67)); + TESTI(test2(0,1,12,34)); + TESTI(test2(1,0,45,67)); + TESTI(test3(0,1,12,34)); + TESTI(test3(1,0,45,67)); + TESTI(test4(0,1,12,34)); + TESTI(test4(1,0,45,67)); + TESTI(test5(0,1,12)); + TESTI(test5(1,0,45)); + TESTI(test6(NULL)); + TESTI(test6(&i)); + TESTI(test7(1,0)); + TESTI(test7(-100,4)); + TESTI(test8(0)); + TESTI(test8(1)); + + TESTL(ltest1(-1, 0, 123LL, 456LL)); + TESTL(ltest1(1, 0, 123LL, 456LL)); + + TESTF(dmax(0.0, 3.14)); + TESTF(dmax(1.0, -2.718)); + + TESTF(dabs(1.0)); + TESTF(dabs(-2.718)); + + TESTF(smin(0.0, 3.14)); + TESTF(smin(1.0, -2.718)); + + TESTF(sdoz(1.0, 0.5)); + TESTF(sdoz(0.0, 3.14)); + + return 0; +} diff --git a/test/regression/int64.c b/test/regression/int64.c index d9785e95..0da9602d 100644 --- a/test/regression/int64.c +++ b/test/regression/int64.c @@ -103,7 +103,8 @@ u64 special_values[] = { 0x80000000LLU, 0x7FFFFFFFFFFFFFFFLLU, 0x8000000000000000LLU, - 0x100000003LLU + 0x100000003LLU, + 0x52ce6b4000000063LLU }; #define NUM_SPECIAL_VALUES (sizeof(special_values) / sizeof(u64)) diff --git a/test/regression/interop1.c b/test/regression/interop1.c index a39f449c..6836b89e 100644 --- a/test/regression/interop1.c +++ b/test/regression/interop1.c @@ -195,6 +195,17 @@ RETURN(ru6,U6,init_U6) RETURN(ru7,U7,init_U7) RETURN(ru8,U8,init_U8) +/* Returning small integers */ + +#define SMALLINT(name,ty) \ +extern ty THEM(name)(int); \ +ty US(name)(int x) { return x * x; } + +SMALLINT(si8u, unsigned char) +SMALLINT(si8s, signed char) +SMALLINT(si16u, unsigned short) +SMALLINT(si16s, signed short) + /* Test function, calling the functions compiled by the other compiler */ #define CALLPRINT(name,ty,init) \ @@ -207,6 +218,10 @@ RETURN(ru8,U8,init_U8) extern void THEM(test) (void); void US(test) (void) { + printf("si8u: %d\n", THEM(si8u)(12345)); + printf("si8s: %d\n", THEM(si8s)(12345)); + printf("si16u: %d\n", THEM(si16u)(1234567)); + printf("si16s: %d\n", THEM(si16s)(1234567)); CALLPRINT(s1,S1,init_S1) CALLPRINT(s2,S2,init_S2) CALLPRINT(s3,S3,init_S3) diff --git a/test/spass/Makefile b/test/spass/Makefile index 0e89d6d1..d512ea95 100644 --- a/test/spass/Makefile +++ b/test/spass/Makefile @@ -24,11 +24,10 @@ clean: test: $(SIMU) ./spass small_problem.dfg | grep 'Proof found' -TIME=xtime -o /dev/null # Xavier's hack -#TIME=time >/dev/null # Otherwise +TIME=ocaml unix.cma ../../tools/xtime.ml -o /dev/null -mintime 5.0 bench: - @echo -n "spass: "; $(TIME) ./spass problem.dfg + @$(TIME) -name spass -- ./spass problem.dfg depend: gcc -MM $(SRCS) > .depend diff --git a/tools/ndfun.ml b/tools/ndfun.ml index 2b8bcb19..b6a87ede 100644 --- a/tools/ndfun.ml +++ b/tools/ndfun.ml @@ -41,7 +41,9 @@ let trim s = let str_match n re s = if not (Str.string_match re s 0) then [||] else begin let res = Array.make (n+1) "" in - for i = 1 to n do res.(i) <- Str.matched_group i s done; + for i = 1 to n do + res.(i) <- (try Str.matched_group i s with Not_found -> "") + done; for i = 1 to n do res.(i) <- trim res.(i) done; res end @@ -87,6 +89,11 @@ let match_temps args = let parenpats p = "(" ^ Str.global_replace re_comma ") (" p ^ ")" +(* "foo, bar, gee" -> "_ _ _" *) + +let underscores_for s = + Str.global_replace re_arg "_" (remove_commas s) + (* Extract the bound variables in a pattern. Heuristic: any identifier that starts with a lowercase letter and is not "nil". *) @@ -123,7 +130,7 @@ let re_nd = Str.regexp( let re_split_cases = Str.regexp "|" -let re_case = Str.regexp "\\(.*\\)=>\\(.*\\)" +let re_case = Str.regexp "\\([^?]*\\)\\(\\?\\?\\(.*\\)\\)?=>\\(.*\\)" let re_default_pat = Str.regexp "[ _,]*$" @@ -165,16 +172,20 @@ let transl_ndfun filename lineno s = (* Adding each case *) let numcase = ref 0 in let transl_case s = - let res = str_match 2 re_case s in + let res = str_match 4 re_case s in if Array.length res = 0 then error filename lineno ("ill-formed case: " ^ s); - let patlist = res.(1) and rhs = res.(2) in + let patlist = res.(1) and guard = res.(3) and rhs = res.(4) in let bv = boundvarspat patlist in if not (Str.string_match re_default_pat patlist 0) then begin incr numcase; bprintf a " | %s_case%d: forall %s, %s_cases %s\n" name !numcase bv name (parenpats patlist); - bprintf b " | %s => %s_case%d %s\n" patlist name !numcase bv; + if guard = "" then + bprintf b " | %s => %s_case%d %s\n" patlist name !numcase bv + else + bprintf b " | %s => if %s then %s_case%d %s else %s_default %s\n" + patlist guard name !numcase bv name (underscores_for args); bprintf c " | %s_case%d %s => (* %s *) \n" name !numcase bv patlist; bprintf c " %s\n" rhs end else begin diff --git a/tools/xtime.ml b/tools/xtime.ml new file mode 100644 index 00000000..fbb25a49 --- /dev/null +++ b/tools/xtime.ml @@ -0,0 +1,101 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, INRIA Paris-Rocquencourt *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(* Timing the execution of a command, with more options than the + standard Unix "time" utility. *) + +open Printf + +let outfile = ref "" +let errfile = ref "" +let command_name = ref "" +let num_runs = ref 1 +let min_runs = ref 0 +let min_time = ref 0.0 +let print_sys = ref false + +let error fmt = + eprintf "Error: "; kfprintf (fun _ -> exit 2) stderr fmt + +let open_file out dfl = + if out = "" + then dfl + else Unix.(openfile out [O_WRONLY; O_CREAT; O_TRUNC] 0o666) + +let close_file out fd = + if out <> "" then Unix.close fd + +let run1 (cmd, args) = + let fd_out = open_file !outfile Unix.stdout in + let fd_err = open_file !errfile Unix.stderr in + let pid = + Unix.create_process cmd (Array.of_list (cmd :: args)) + Unix.stdin fd_out fd_err in + close_file !outfile fd_out; + close_file !errfile fd_err; + let (_, st) = Unix.waitpid [] pid in + match st with + | Unix.WEXITED 127 -> error "cannot execute '%s'\n" cmd + | Unix.WSIGNALED signo -> error "terminated by signal %d\n" signo + | _ -> () + +let run (cmd, arg) = + let rec repeat n = + run1 (cmd, arg); + if (!min_time > 0.0 && Unix.((times()).tms_cutime) < !min_time) + || (!min_runs > 0 && n < !min_runs) + || n < !num_runs + then repeat (n + 1) + else n in + let n = repeat 1 in + let ts = Unix.times() in + let cmdname = if !command_name <> "" then !command_name else cmd in + if !print_sys then + Printf.printf "%.3f usr + %.3f sys %s\n" + (ts.Unix.tms_cutime /. float n) + (ts.Unix.tms_cstime /. float n) + cmdname + else + Printf.printf "%.3f %s\n" + (ts.Unix.tms_cutime /. float n) + cmdname + +let _ = + let cmd_and_args = ref [] in + Arg.parse [ + "-o", Arg.Set_string outfile, + " <file> Redirect standard output of command to <file>"; + "-e", Arg.Set_string outfile, + " <file> Redirect standard error of command to <file>"; + "-name", Arg.Set_string command_name, + " <name> Name of command to report along with the time"; + "-repeat", Arg.Int (fun n -> num_runs := n), + " <N> Run the command N times"; + "-mintime", Arg.Float (fun f -> min_time := f), + " <T> Repeatedly run the command for a total duration of at least T seconds"; + "-minruns", Arg.Int (fun n -> num_runs := n), + " <N> Run the command at least N times (to be used in conjunction with -mintime)"; + "-sys", Arg.Set print_sys, + " Print system time (spent in the OS) in addition to user time (spent in the command)"; + "--", Arg.Rest (fun s -> cmd_and_args := s :: !cmd_and_args), + " <executable> <arguments> Specify the executable to time, with its arguments" + ] + (fun s -> raise (Arg.Bad (sprintf "Don't know what to do with '%s'" s))) + "Usage: xtime [options] -- <executable> [arguments].\n\nOptions are:"; + match List.rev !cmd_and_args with + | [] -> + error "No command to execute\n" + | cmd :: args -> + Unix.handle_unix_error run (cmd, args) @@ -851,11 +851,12 @@ Definition exec_instr (f: function) (i: instruction) (rs: regset) (m: mem) : out | Ptestq_ri r1 n => Next (nextinstr (compare_longs (Val.andl (rs r1) (Vlong n)) (Vlong Int64.zero) rs m)) m | Pcmov c rd r1 => - match eval_testcond c rs with - | Some true => Next (nextinstr (rs#rd <- (rs#r1))) m - | Some false => Next (nextinstr rs) m - | None => Next (nextinstr (rs#rd <- Vundef)) m - end + let v := + match eval_testcond c rs with + | Some b => if b then rs#r1 else rs#rd + | None => Vundef + end in + Next (nextinstr (rs#rd <- v)) m | Psetcc c rd => Next (nextinstr (rs#rd <- (Val.of_optbool (eval_testcond c rs)))) m (** Arithmetic operations over double-precision floats *) diff --git a/x86/Asmexpand.ml b/x86/Asmexpand.ml index 16426ce3..b8353046 100644 --- a/x86/Asmexpand.ml +++ b/x86/Asmexpand.ml @@ -251,7 +251,7 @@ let expand_builtin_va_start_32 r = invalid_arg "Fatal error: va_start used in non-vararg function"; let ofs = Int32.(add (add !PrintAsmaux.current_function_stacksize 4l) - (mul 4l (Z.to_int32 (Conventions1.size_arguments + (mul 4l (Z.to_int32 (Conventions.size_arguments (get_current_function_sig ()))))) in emit (Pleal (RAX, linear_addr RSP (Z.of_uint32 ofs))); emit (Pmovl_mr (linear_addr r _0z, RAX)) @@ -506,7 +506,7 @@ let expand_instruction instr = (* Save the registers *) emit (Pleaq (R10, linear_addr RSP (Z.of_uint save_regs))); emit (Pcall_s (intern_string "__compcert_va_saveregs", - {sig_args = []; sig_res = None; sig_cc = cc_default})) + {sig_args = []; sig_res = Tvoid; sig_cc = cc_default})) end; (* Stack chaining *) let fullsz = sz + 8 in diff --git a/x86/Asmgen.v b/x86/Asmgen.v index dedbfbe6..73e3263e 100644 --- a/x86/Asmgen.v +++ b/x86/Asmgen.v @@ -305,6 +305,35 @@ Definition mk_jcc (cond: extcond) (lbl: label) (k: code) := | Cond_or c1 c2 => Pjcc c1 lbl :: Pjcc c2 lbl :: k end. +Definition negate_testcond (c: testcond) : testcond := + match c with + | Cond_e => Cond_ne | Cond_ne => Cond_e + | Cond_b => Cond_ae | Cond_be => Cond_a + | Cond_ae => Cond_b | Cond_a => Cond_be + | Cond_l => Cond_ge | Cond_le => Cond_g + | Cond_ge => Cond_l | Cond_g => Cond_le + | Cond_p => Cond_np | Cond_np => Cond_p + end. + +Definition mk_sel (cond: extcond) (rd r2: ireg) (k: code) := + match cond with + | Cond_base c => + OK (Pcmov (negate_testcond c) rd r2 :: k) + | Cond_and c1 c2 => + OK (Pcmov (negate_testcond c1) rd r2 :: + Pcmov (negate_testcond c2) rd r2 :: k) + | Cond_or c1 c2 => + Error (msg "Asmgen.mk_sel") (**r should never happen, see [SelectOp.select] *) + end. + +Definition transl_sel + (cond: condition) (args: list mreg) (rd r2: ireg) (k: code) : res code := + if ireg_eq rd r2 then + OK (Pmov_rr rd r2 :: k) (* must generate one instruction... *) + else + do k1 <- mk_sel (testcond_for_condition cond) rd r2 k; + transl_cond cond args k1. + (** Translation of the arithmetic operation [r <- op(args)]. The corresponding instructions are prepended to [k]. *) @@ -597,6 +626,10 @@ Definition transl_op | Ocmp c, args => do r <- ireg_of res; transl_cond c args (mk_setcc (testcond_for_condition c) r k) + | Osel c ty, a1 :: a2 :: args => + assertion (mreg_eq a1 res); + do r <- ireg_of res; do r2 <- ireg_of a2; + transl_sel c args r r2 k | _, _ => Error(msg "Asmgen.transl_op") end. diff --git a/x86/Asmgenproof.v b/x86/Asmgenproof.v index 3aa87a4c..f1fd41e3 100644 --- a/x86/Asmgenproof.v +++ b/x86/Asmgenproof.v @@ -194,6 +194,14 @@ Proof. intros. destruct xc; simpl; TailNoLabel. Qed. +Remark mk_sel_label: + forall xc rd r2 k c, + mk_sel xc rd r2 k = OK c -> + tail_nolabel k c. +Proof. + unfold mk_sel; intros; destruct xc; inv H; TailNoLabel. +Qed. + Remark transl_cond_label: forall cond args k c, transl_cond cond args k = OK c -> @@ -221,6 +229,9 @@ Proof. destruct (Float32.eq_dec n Float32.zero); TailNoLabel. destruct (normalize_addrmode_64 x) as [am' [delta|]]; TailNoLabel. eapply tail_nolabel_trans. eapply transl_cond_label; eauto. eapply mk_setcc_label. + unfold transl_sel in EQ2. destruct (ireg_eq x x0); monadInv EQ2. + TailNoLabel. + eapply tail_nolabel_trans. eapply transl_cond_label; eauto. eapply mk_sel_label; eauto. Qed. Remark transl_load_label: @@ -706,7 +717,7 @@ Opaque loadind. intros. simpl in TR. destruct (transl_cond_correct tge tf cond args _ _ rs0 m' TR) as [rs' [A [B C]]]. - rewrite EC in B. + rewrite EC in B. destruct B as [B _]. destruct (testcond_for_condition cond); simpl in *. (* simple jcc *) exists (Pjcc c1 lbl); exists k; exists rs'. @@ -744,7 +755,7 @@ Opaque loadind. left; eapply exec_straight_steps; eauto. intros. simpl in TR. destruct (transl_cond_correct tge tf cond args _ _ rs0 m' TR) as [rs' [A [B C]]]. - rewrite EC in B. + rewrite EC in B. destruct B as [B _]. destruct (testcond_for_condition cond); simpl in *. (* simple jcc *) econstructor; split. diff --git a/x86/Asmgenproof1.v b/x86/Asmgenproof1.v index 904debdc..fd88954e 100644 --- a/x86/Asmgenproof1.v +++ b/x86/Asmgenproof1.v @@ -208,7 +208,8 @@ Proof. set (x' := Int.add x tnm1). set (rs2 := nextinstr (compare_ints (Vint x) (Vint Int.zero) rs1 m)). set (rs3 := nextinstr (rs2#RCX <- (Vint x'))). - set (rs4 := nextinstr (if Int.lt x Int.zero then rs3#RAX <- (Vint x') else rs3)). + set (v' := if Int.lt x Int.zero then Vint x' else Vint x). + set (rs4 := nextinstr (rs3#RAX <- v')). set (rs5 := nextinstr_nf (rs4#RAX <- (Val.shr rs4#RAX (Vint n)))). assert (rs3#RAX = Vint x). unfold rs3. Simplifs. assert (rs3#RCX = Vint x'). unfold rs3. Simplifs. @@ -218,13 +219,12 @@ Proof. change (rs2 RAX) with (rs1 RAX). rewrite A. simpl. rewrite Int.repr_unsigned. rewrite Int.add_zero_l. auto. auto. apply exec_straight_step with rs4 m. simpl. - rewrite Int.lt_sub_overflow. unfold rs4. destruct (Int.lt x Int.zero); simpl; auto. - unfold rs4. destruct (Int.lt x Int.zero); simpl; auto. + rewrite Int.lt_sub_overflow. unfold rs4, v'. rewrite H, H0. destruct (Int.lt x Int.zero); simpl; auto. + auto. apply exec_straight_one. auto. auto. split. unfold rs5. Simplifs. unfold rs4. rewrite nextinstr_inv; auto with asmgen. - destruct (Int.lt x Int.zero). rewrite Pregmap.gss. rewrite A; auto. rewrite A; rewrite H; auto. + rewrite Pregmap.gss. unfold v'. rewrite A. reflexivity. intros. unfold rs5. Simplifs. unfold rs4. Simplifs. - transitivity (rs3#r). destruct (Int.lt x Int.zero). Simplifs. auto. unfold rs3. Simplifs. unfold rs2. Simplifs. unfold compare_ints. Simplifs. Qed. @@ -913,6 +913,7 @@ Lemma transl_cond_correct: /\ match eval_condition cond (map rs (map preg_of args)) m with | None => True | Some b => eval_extcond (testcond_for_condition cond) rs' = Some b + /\ eval_extcond (testcond_for_condition (negate_condition cond)) rs' = Some (negb b) end /\ forall r, data_preg r = true -> rs'#r = rs r. Proof. @@ -921,58 +922,78 @@ Proof. - (* comp *) simpl. rewrite (ireg_of_eq _ _ EQ). rewrite (ireg_of_eq _ _ EQ1). econstructor. split. apply exec_straight_one. simpl. eauto. auto. - split. destruct (Val.cmp_bool c0 (rs x) (rs x0)) eqn:?; auto. + split. destruct (Val.cmp_bool c0 (rs x) (rs x0)) eqn:?; auto. split. + eapply testcond_for_signed_comparison_32_correct; eauto. eapply testcond_for_signed_comparison_32_correct; eauto. + rewrite Val.negate_cmp_bool, Heqo; auto. intros. unfold compare_ints. Simplifs. - (* compu *) simpl. rewrite (ireg_of_eq _ _ EQ). rewrite (ireg_of_eq _ _ EQ1). econstructor. split. apply exec_straight_one. simpl. eauto. auto. - split. destruct (Val.cmpu_bool (Mem.valid_pointer m) c0 (rs x) (rs x0)) eqn:?; auto. + split. destruct (Val.cmpu_bool (Mem.valid_pointer m) c0 (rs x) (rs x0)) eqn:?; auto. split. eapply testcond_for_unsigned_comparison_32_correct; eauto. + eapply testcond_for_unsigned_comparison_32_correct; eauto. + rewrite Val.negate_cmpu_bool, Heqo; auto. intros. unfold compare_ints. Simplifs. - (* compimm *) simpl. rewrite (ireg_of_eq _ _ EQ). destruct (Int.eq_dec n Int.zero). econstructor; split. apply exec_straight_one. simpl; eauto. auto. - split. destruct (rs x); simpl; auto. subst. rewrite Int.and_idem. + split. destruct (rs x); simpl; auto. subst. rewrite Int.and_idem. split. + eapply testcond_for_signed_comparison_32_correct; eauto. eapply testcond_for_signed_comparison_32_correct; eauto. + rewrite Val.negate_cmp_bool; auto. intros. unfold compare_ints. Simplifs. econstructor; split. apply exec_straight_one. simpl; eauto. auto. - split. destruct (Val.cmp_bool c0 (rs x) (Vint n)) eqn:?; auto. + split. destruct (Val.cmp_bool c0 (rs x) (Vint n)) eqn:?; auto. split. eapply testcond_for_signed_comparison_32_correct; eauto. + eapply testcond_for_signed_comparison_32_correct; eauto. + rewrite Val.negate_cmp_bool, Heqo; auto. intros. unfold compare_ints. Simplifs. - (* compuimm *) simpl. rewrite (ireg_of_eq _ _ EQ). econstructor. split. apply exec_straight_one. simpl. eauto. auto. - split. destruct (Val.cmpu_bool (Mem.valid_pointer m) c0 (rs x) (Vint n)) eqn:?; auto. + split. destruct (Val.cmpu_bool (Mem.valid_pointer m) c0 (rs x) (Vint n)) eqn:?; auto; split. + eapply testcond_for_unsigned_comparison_32_correct; eauto. eapply testcond_for_unsigned_comparison_32_correct; eauto. + rewrite Val.negate_cmpu_bool, Heqo; auto. intros. unfold compare_ints. Simplifs. - (* compl *) simpl. rewrite (ireg_of_eq _ _ EQ). rewrite (ireg_of_eq _ _ EQ1). econstructor. split. apply exec_straight_one. simpl. eauto. auto. - split. destruct (Val.cmpl_bool c0 (rs x) (rs x0)) eqn:?; auto. + split. destruct (Val.cmpl_bool c0 (rs x) (rs x0)) eqn:?; auto. split. eapply testcond_for_signed_comparison_64_correct; eauto. + eapply testcond_for_signed_comparison_64_correct; eauto. + rewrite Val.negate_cmpl_bool, Heqo; auto. intros. unfold compare_longs. Simplifs. - (* complu *) simpl. rewrite (ireg_of_eq _ _ EQ). rewrite (ireg_of_eq _ _ EQ1). econstructor. split. apply exec_straight_one. simpl. eauto. auto. - split. destruct (Val.cmplu_bool (Mem.valid_pointer m) c0 (rs x) (rs x0)) eqn:?; auto. + split. destruct (Val.cmplu_bool (Mem.valid_pointer m) c0 (rs x) (rs x0)) eqn:?; auto. split. + eapply testcond_for_unsigned_comparison_64_correct; eauto. eapply testcond_for_unsigned_comparison_64_correct; eauto. + rewrite Val.negate_cmplu_bool, Heqo; auto. intros. unfold compare_longs. Simplifs. - (* compimm *) simpl. rewrite (ireg_of_eq _ _ EQ). destruct (Int64.eq_dec n Int64.zero). econstructor; split. apply exec_straight_one. simpl; eauto. auto. - split. destruct (rs x); simpl; auto. subst. rewrite Int64.and_idem. + split. destruct (rs x); simpl; auto. subst. rewrite Int64.and_idem. split. eapply testcond_for_signed_comparison_64_correct; eauto. + eapply testcond_for_signed_comparison_64_correct; eauto. + rewrite Val.negate_cmpl_bool; auto. intros. unfold compare_longs. Simplifs. econstructor; split. apply exec_straight_one. simpl; eauto. auto. - split. destruct (Val.cmpl_bool c0 (rs x) (Vlong n)) eqn:?; auto. + split. destruct (Val.cmpl_bool c0 (rs x) (Vlong n)) eqn:?; auto. split. + eapply testcond_for_signed_comparison_64_correct; eauto. eapply testcond_for_signed_comparison_64_correct; eauto. + rewrite Val.negate_cmpl_bool, Heqo; auto. intros. unfold compare_longs. Simplifs. - (* compuimm *) simpl. rewrite (ireg_of_eq _ _ EQ). econstructor. split. apply exec_straight_one. simpl. eauto. auto. - split. destruct (Val.cmplu_bool (Mem.valid_pointer m) c0 (rs x) (Vlong n)) eqn:?; auto. + split. destruct (Val.cmplu_bool (Mem.valid_pointer m) c0 (rs x) (Vlong n)) eqn:?; auto. split. eapply testcond_for_unsigned_comparison_64_correct; eauto. + eapply testcond_for_unsigned_comparison_64_correct; eauto. + rewrite Val.negate_cmplu_bool, Heqo; auto. intros. unfold compare_longs. Simplifs. - (* compf *) simpl. rewrite (freg_of_eq _ _ EQ). rewrite (freg_of_eq _ _ EQ1). @@ -981,7 +1002,9 @@ Proof. destruct c0; simpl; auto. unfold nextinstr. rewrite Pregmap.gss. rewrite compare_floats_inv; auto with asmgen. split. destruct (rs x); destruct (rs x0); simpl; auto. - repeat rewrite swap_floats_commut. apply testcond_for_float_comparison_correct. + repeat rewrite swap_floats_commut. split. + apply testcond_for_float_comparison_correct. + apply testcond_for_neg_float_comparison_correct. intros. Simplifs. apply compare_floats_inv; auto with asmgen. - (* notcompf *) simpl. rewrite (freg_of_eq _ _ EQ). rewrite (freg_of_eq _ _ EQ1). @@ -990,7 +1013,9 @@ Proof. destruct c0; simpl; auto. unfold nextinstr. rewrite Pregmap.gss. rewrite compare_floats_inv; auto with asmgen. split. destruct (rs x); destruct (rs x0); simpl; auto. - repeat rewrite swap_floats_commut. apply testcond_for_neg_float_comparison_correct. + repeat rewrite swap_floats_commut. split. + apply testcond_for_neg_float_comparison_correct. + rewrite negb_involutive. apply testcond_for_float_comparison_correct. intros. Simplifs. apply compare_floats_inv; auto with asmgen. - (* compfs *) simpl. rewrite (freg_of_eq _ _ EQ). rewrite (freg_of_eq _ _ EQ1). @@ -999,7 +1024,9 @@ Proof. destruct c0; simpl; auto. unfold nextinstr. rewrite Pregmap.gss. rewrite compare_floats32_inv; auto with asmgen. split. destruct (rs x); destruct (rs x0); simpl; auto. - repeat rewrite swap_floats_commut. apply testcond_for_float32_comparison_correct. + repeat rewrite swap_floats_commut. split. + apply testcond_for_float32_comparison_correct. + apply testcond_for_neg_float32_comparison_correct. intros. Simplifs. apply compare_floats32_inv; auto with asmgen. - (* notcompfs *) simpl. rewrite (freg_of_eq _ _ EQ). rewrite (freg_of_eq _ _ EQ1). @@ -1008,7 +1035,9 @@ Proof. destruct c0; simpl; auto. unfold nextinstr. rewrite Pregmap.gss. rewrite compare_floats32_inv; auto with asmgen. split. destruct (rs x); destruct (rs x0); simpl; auto. - repeat rewrite swap_floats_commut. apply testcond_for_neg_float32_comparison_correct. + repeat rewrite swap_floats_commut. split. + apply testcond_for_neg_float32_comparison_correct. + rewrite negb_involutive. apply testcond_for_float32_comparison_correct. intros. Simplifs. apply compare_floats32_inv; auto with asmgen. - (* maskzero *) simpl. rewrite (ireg_of_eq _ _ EQ). @@ -1133,6 +1162,94 @@ Proof. intuition Simplifs. Qed. +Definition negate_extcond (xc: extcond) : extcond := + match xc with + | Cond_base c => Cond_base (negate_testcond c) + | Cond_and c1 c2 => Cond_or (negate_testcond c1) (negate_testcond c2) + | Cond_or c1 c2 => Cond_and (negate_testcond c1) (negate_testcond c2) + end. + +Remark negate_testcond_for_condition: + forall cond, + negate_extcond (testcond_for_condition cond) = testcond_for_condition (negate_condition cond). +Proof. + intros. destruct cond; try destruct c; reflexivity. +Qed. + +Lemma mk_sel_correct: + forall xc ty rd r2 k c ob rs m, + mk_sel xc rd r2 k = OK c -> + rd <> r2 -> + match ob with + | Some b => eval_extcond xc rs = Some b /\ eval_extcond (negate_extcond xc) rs = Some (negb b) + | None => True + end -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ Val.lessdef (Val.select ob rs#rd rs#r2 ty) rs'#rd + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs r. +Proof. + intros. destruct xc; monadInv H; simpl in H1. +- econstructor; split. + eapply exec_straight_one. reflexivity. reflexivity. + set (v := match eval_testcond (negate_testcond c0) rs with + | Some true => rs r2 + | Some false => rs rd + | None => Vundef + end). + split. rewrite nextinstr_inv, Pregmap.gss by eauto with asmgen. + destruct ob; simpl; auto. destruct H1 as [_ B]; unfold v; rewrite B. + destruct b; apply Val.lessdef_normalize. + intros; Simplifs. +- econstructor; split. + eapply exec_straight_two. + reflexivity. reflexivity. reflexivity. reflexivity. + set (v1 := match eval_testcond (negate_testcond c1) rs with + | Some true => rs r2 + | Some false => rs rd + | None => Vundef + end). + rewrite eval_testcond_nextinstr, eval_testcond_set_ireg. + set (v2 := match eval_testcond (negate_testcond c2) rs with + | Some true => nextinstr rs # rd <- v1 r2 + | Some false => nextinstr rs # rd <- v1 rd + | None => Vundef + end). + split. rewrite nextinstr_inv, Pregmap.gss by eauto with asmgen. + destruct ob; simpl; auto. + destruct H1 as [_ B]. + destruct (eval_testcond (negate_testcond c1) rs) as [b1|]; try discriminate. + destruct (eval_testcond (negate_testcond c2) rs) as [b2|]; try discriminate. + inv B. apply negb_sym in H1. subst b. + replace v2 with (if b2 then rs#r2 else v1). + unfold v1. destruct b1, b2; apply Val.lessdef_normalize. + unfold v2. destruct b2; symmetry; Simplifs. + intros; Simplifs. +Qed. + +Lemma transl_sel_correct: + forall ty cond args rd r2 k c rs m, + transl_sel cond args rd r2 k = OK c -> + exists rs', + exec_straight ge fn c rs m k rs' m + /\ Val.lessdef (Val.select (eval_condition cond (map rs (map preg_of args)) m) rs#rd rs#r2 ty) rs'#rd + /\ forall r, data_preg r = true -> r <> rd -> rs'#r = rs r. +Proof. + unfold transl_sel; intros. destruct (ireg_eq rd r2); monadInv H. +- econstructor; split. + apply exec_straight_one; reflexivity. + split. rewrite nextinstr_inv, Pregmap.gss by auto with asmgen. + destruct eval_condition as [[]|]; simpl; auto using Val.lessdef_normalize. + intros; Simplifs. +- destruct (transl_cond_correct _ _ _ _ rs m EQ0) as (rs1 & A & B & C). + rewrite <- negate_testcond_for_condition in B. + destruct (mk_sel_correct _ ty _ _ _ _ _ rs1 m EQ n B) as (rs2 & D & E & F). + exists rs2; split. + eapply exec_straight_trans; eauto. + split. rewrite ! C in E by auto with asmgen. exact E. + intros. rewrite F; auto. +Qed. + (** Translation of arithmetic operations. *) Ltac ArgsInv := @@ -1142,7 +1259,9 @@ Ltac ArgsInv := | [ H: bind _ _ = OK _ |- _ ] => monadInv H; ArgsInv | [ H: match _ with left _ => _ | right _ => assertion_failed end = OK _ |- _ ] => monadInv H; ArgsInv | [ H: match _ with true => _ | false => assertion_failed end = OK _ |- _ ] => monadInv H; ArgsInv - | [ H: ireg_of _ = OK _ |- _ ] => simpl in *; rewrite (ireg_of_eq _ _ H) in *; clear H; ArgsInv + | [ H: ireg_of _ = OK _ |- _ ] => simpl in *; rewrite (ireg_of_eq _ _ H) in *; + let X := fresh "EQ" in generalize (ireg_of_eq _ _ H); intros X; + clear H; ArgsInv | [ H: freg_of _ = OK _ |- _ ] => simpl in *; rewrite (freg_of_eq _ _ H) in *; clear H; ArgsInv | _ => idtac end. @@ -1334,9 +1453,12 @@ Transparent destroyed_by_op. exists rs3. split. eapply exec_straight_trans. eexact P. eexact S. split. rewrite T. destruct (eval_condition cond rs ## (preg_of ## args) m). - rewrite Q. auto. + destruct Q as [Q _]. rewrite Q. auto. simpl; auto. intros. transitivity (rs2 r); auto. +(* selection *) + rewrite EQ1. exploit transl_sel_correct; eauto. intros (rs' & A & B & C). + exists rs'; split. eexact A. eauto. Qed. (** Translation of memory loads. *) diff --git a/x86/Builtins1.v b/x86/Builtins1.v new file mode 100644 index 00000000..f1d60961 --- /dev/null +++ b/x86/Builtins1.v @@ -0,0 +1,54 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, Collège de France and Inria Paris *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the GNU General Public License as published by *) +(* the Free Software Foundation, either version 2 of the License, or *) +(* (at your option) any later version. This file is also distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Platform-specific built-in functions *) + +Require Import String Coqlib. +Require Import AST Integers Floats Values. +Require Import Builtins0. + +Inductive platform_builtin : Type := + | BI_fmin + | BI_fmax. + +Local Open Scope string_scope. + +Definition platform_builtin_table : list (string * platform_builtin) := + ("__builtin_fmin", BI_fmin) + :: ("__builtin_fmax", BI_fmax) + :: nil. + +Definition platform_builtin_sig (b: platform_builtin) : signature := + match b with + | BI_fmin | BI_fmax => + mksignature (Tfloat :: Tfloat :: nil) Tfloat cc_default + end. + +Definition platform_builtin_sem (b: platform_builtin) : builtin_sem (sig_res (platform_builtin_sig b)) := + match b with + | BI_fmin => + mkbuiltin_n2t Tfloat Tfloat Tfloat + (fun f1 f2 => match Float.compare f1 f2 with + | Some Eq | Some Lt => f1 + | Some Gt | None => f2 + end) + | BI_fmax => + mkbuiltin_n2t Tfloat Tfloat Tfloat + (fun f1 f2 => match Float.compare f1 f2 with + | Some Eq | Some Gt => f1 + | Some Lt | None => f2 + end) + end. + diff --git a/x86/CBuiltins.ml b/x86/CBuiltins.ml index 69a2eb64..e7f714c7 100644 --- a/x86/CBuiltins.ml +++ b/x86/CBuiltins.ml @@ -26,13 +26,11 @@ let (va_list_type, va_list_scalar, size_va_list) = (TPtr(TVoid [], []), true, 4) let builtins = { - Builtins.typedefs = [ + builtin_typedefs = [ "__builtin_va_list", va_list_type; ]; - Builtins.functions = [ + builtin_functions = [ (* Integer arithmetic *) - "__builtin_bswap64", - (TInt(IULongLong, []), [TInt(IULongLong, [])], false); "__builtin_clz", (TInt(IInt, []), [TInt(IUInt, [])], false); "__builtin_clzl", @@ -75,9 +73,6 @@ let builtins = { (TVoid [], [TPtr(TInt(IUShort, []), []); TInt(IUShort, [])], false); "__builtin_write32_reversed", (TVoid [], [TPtr(TInt(IUInt, []), []); TInt(IUInt, [])], false); - (* no operation *) - "__builtin_nop", - (TVoid [], [], false); ] } diff --git a/x86/ConstpropOp.vp b/x86/ConstpropOp.vp index f59b9dba..ada8d54a 100644 --- a/x86/ConstpropOp.vp +++ b/x86/ConstpropOp.vp @@ -16,7 +16,7 @@ Require Import Coqlib Compopts. Require Import AST Integers Floats. Require Import Op Registers. -Require Import ValueDomain. +Require Import ValueDomain ValueAOp. (** * Converting known values to constants *) @@ -98,6 +98,15 @@ Nondetfunction make_cmp (c: condition) (args: list reg) (vl: list aval) := make_cmp_base c args vl end. +Definition make_select (c: condition) (ty: typ) + (r1 r2: reg) (args: list reg) (vl: list aval) := + match resolve_branch (eval_static_condition c vl) with + | Some b => (Omove, (if b then r1 else r2) :: nil) + | None => + let (c', args') := cond_strength_reduction c args vl in + (Osel c' ty, r1 :: r2 :: args') + end. + (** For addressing modes, we need to distinguish - reductions that produce pointers (i.e. that produce [Aglobal], [Ainstack], [Abased] and [Abasedscaled] addressing modes), which are valid only if the pointer size is right; - other reductions (producing [Aindexed] or [Aindexed2] modes), which are valid independently of the pointer size. @@ -416,6 +425,7 @@ Nondetfunction op_strength_reduction let (addr', args') := addr_strength_reduction_64 addr args vl in (Oleal addr', args') | Ocmp c, args, vl => make_cmp c args vl + | Osel c ty, r1 :: r2 :: args, v1 :: v2 :: vl => make_select c ty r1 r2 args vl | Omulf, r1 :: r2 :: nil, v1 :: F n2 :: nil => make_mulfimm n2 r1 r1 r2 | Omulf, r1 :: r2 :: nil, F n1 :: v2 :: nil => make_mulfimm n1 r2 r1 r2 | Omulfs, r1 :: r2 :: nil, v1 :: FS n2 :: nil => make_mulfsimm n2 r1 r1 r2 diff --git a/x86/ConstpropOpproof.v b/x86/ConstpropOpproof.v index 3bb0f3cd..6d2df9c1 100644 --- a/x86/ConstpropOpproof.v +++ b/x86/ConstpropOpproof.v @@ -14,7 +14,7 @@ Require Import Coqlib Compopts. Require Import Integers Floats Values Memory Globalenvs Events. -Require Import Op Registers RTL ValueDomain. +Require Import Op Registers RTL ValueDomain ValueAOp ValueAnalysis. Require Import ConstpropOp. Section STRENGTH_REDUCTION. @@ -371,6 +371,28 @@ Proof. - apply make_cmp_base_correct; auto. Qed. +Lemma make_select_correct: + forall c ty r1 r2 args vl, + vl = map (fun r => AE.get r ae) args -> + let (op', args') := make_select c ty r1 r2 args vl in + exists v, eval_operation ge (Vptr sp Ptrofs.zero) op' e##args' m = Some v + /\ Val.lessdef (Val.select (eval_condition c e##args m) e#r1 e#r2 ty) v. +Proof. + unfold make_select; intros. + destruct (resolve_branch (eval_static_condition c vl)) as [b|] eqn:RB. +- exists (if b then e#r1 else e#r2); split. ++ simpl. destruct b; auto. ++ destruct (eval_condition c e##args m) as [b'|] eqn:EC; simpl; auto. + assert (b = b'). + { eapply resolve_branch_sound; eauto. + rewrite <- EC. apply eval_static_condition_sound with bc. + subst vl. exact (aregs_sound _ _ _ args MATCH). } + subst b'. apply Val.lessdef_normalize. +- generalize (cond_strength_reduction_correct c args vl H). + destruct (cond_strength_reduction c args vl) as [cond' args']; intros EQ. + econstructor; split. simpl; eauto. rewrite EQ; auto. +Qed. + Lemma make_addimm_correct: forall n r, let (op, args) := make_addimm n r in @@ -905,6 +927,8 @@ Proof. auto. (* cond *) inv H0. apply make_cmp_correct; auto. +(* select *) + inv H0. apply make_select_correct; congruence. (* mulf *) InvApproxRegs; SimplVM; inv H0. rewrite <- H2. apply make_mulfimm_correct; auto. InvApproxRegs; SimplVM; inv H0. fold (Val.mulf (Vfloat n1) e#r2). diff --git a/x86/Conventions1.v b/x86/Conventions1.v index 646c4afb..fdd94239 100644 --- a/x86/Conventions1.v +++ b/x86/Conventions1.v @@ -99,22 +99,20 @@ Definition is_float_reg (r: mreg) := function with one integer result. *) Definition loc_result_32 (s: signature) : rpair mreg := - match s.(sig_res) with - | None => One AX - | Some (Tint | Tany32) => One AX - | Some (Tfloat | Tsingle) => One FP0 - | Some Tany64 => One X0 - | Some Tlong => Twolong DX AX + match proj_sig_res s with + | Tint | Tany32 => One AX + | Tfloat | Tsingle => One FP0 + | Tany64 => One X0 + | Tlong => Twolong DX AX end. (** In 64 bit mode, he result value of a function is passed back to the caller in registers [AX] or [X0]. *) Definition loc_result_64 (s: signature) : rpair mreg := - match s.(sig_res) with - | None => One AX - | Some (Tint | Tlong | Tany32 | Tany64) => One AX - | Some (Tfloat | Tsingle) => One X0 + match proj_sig_res s with + | Tint | Tlong | Tany32 | Tany64 => One AX + | Tfloat | Tsingle => One X0 end. Definition loc_result := @@ -126,8 +124,8 @@ Lemma loc_result_type: forall sig, subtype (proj_sig_res sig) (typ_rpair mreg_type (loc_result sig)) = true. Proof. - intros. unfold proj_sig_res, loc_result, loc_result_32, loc_result_64, mreg_type; - destruct Archi.ptr64; destruct (sig_res sig) as [[]|]; auto. + intros. unfold loc_result, loc_result_32, loc_result_64, mreg_type; + destruct Archi.ptr64; destruct (proj_sig_res sig); auto. Qed. (** The result locations are caller-save registers *) @@ -137,7 +135,7 @@ Lemma loc_result_caller_save: forall_rpair (fun r => is_callee_save r = false) (loc_result s). Proof. intros. unfold loc_result, loc_result_32, loc_result_64, is_callee_save; - destruct Archi.ptr64; destruct (sig_res s) as [[]|]; simpl; auto. + destruct Archi.ptr64; destruct (proj_sig_res s); simpl; auto. Qed. (** If the result is in a pair of registers, those registers are distinct and have type [Tint] at least. *) @@ -147,14 +145,14 @@ Lemma loc_result_pair: match loc_result sg with | One _ => True | Twolong r1 r2 => - r1 <> r2 /\ sg.(sig_res) = Some Tlong + r1 <> r2 /\ proj_sig_res sg = Tlong /\ subtype Tint (mreg_type r1) = true /\ subtype Tint (mreg_type r2) = true /\ Archi.ptr64 = false end. Proof. intros. unfold loc_result, loc_result_32, loc_result_64, mreg_type; - destruct Archi.ptr64; destruct (sig_res sg) as [[]|]; auto. + destruct Archi.ptr64; destruct (proj_sig_res sg); auto. split; auto. congruence. Qed. @@ -163,7 +161,7 @@ Qed. Lemma loc_result_exten: forall s1 s2, s1.(sig_res) = s2.(sig_res) -> loc_result s1 = loc_result s2. Proof. - intros. unfold loc_result, loc_result_32, loc_result_64. + intros. unfold loc_result, loc_result_32, loc_result_64, proj_sig_res. destruct Archi.ptr64; rewrite H; auto. Qed. @@ -222,36 +220,6 @@ Definition loc_arguments (s: signature) : list (rpair loc) := then loc_arguments_64 s.(sig_args) 0 0 0 else loc_arguments_32 s.(sig_args) 0. -(** [size_arguments s] returns the number of [Outgoing] slots used - to call a function with signature [s]. *) - -Fixpoint size_arguments_32 - (tyl: list typ) (ofs: Z) {struct tyl} : Z := - match tyl with - | nil => ofs - | ty :: tys => size_arguments_32 tys (ofs + typesize ty) - end. - -Fixpoint size_arguments_64 (tyl: list typ) (ir fr ofs: Z) {struct tyl} : Z := - match tyl with - | nil => ofs - | (Tint | Tlong | Tany32 | Tany64) :: tys => - match list_nth_z int_param_regs ir with - | None => size_arguments_64 tys ir fr (ofs + 2) - | Some ireg => size_arguments_64 tys (ir + 1) fr ofs - end - | (Tfloat | Tsingle) :: tys => - match list_nth_z float_param_regs fr with - | None => size_arguments_64 tys ir fr (ofs + 2) - | Some freg => size_arguments_64 tys ir (fr + 1) ofs - end - end. - -Definition size_arguments (s: signature) : Z := - if Archi.ptr64 - then size_arguments_64 s.(sig_args) 0 0 0 - else size_arguments_32 s.(sig_args) 0. - (** Argument locations are either caller-save registers or [Outgoing] stack slots at nonnegative offsets. *) @@ -353,123 +321,22 @@ Qed. Hint Resolve loc_arguments_acceptable: locs. -(** The offsets of [Outgoing] arguments are below [size_arguments s]. *) - -Remark size_arguments_32_above: - forall tyl ofs0, ofs0 <= size_arguments_32 tyl ofs0. +Lemma loc_arguments_main: + loc_arguments signature_main = nil. Proof. - induction tyl; simpl; intros. - omega. - apply Z.le_trans with (ofs0 + typesize a); auto. - generalize (typesize_pos a); omega. + unfold loc_arguments; destruct Archi.ptr64; reflexivity. Qed. -Remark size_arguments_64_above: - forall tyl ir fr ofs0, - ofs0 <= size_arguments_64 tyl ir fr ofs0. -Proof. - induction tyl; simpl; intros. - omega. - assert (A: ofs0 <= - match list_nth_z int_param_regs ir with - | Some _ => size_arguments_64 tyl (ir + 1) fr ofs0 - | None => size_arguments_64 tyl ir fr (ofs0 + 2) - end). - { destruct (list_nth_z int_param_regs ir); eauto. - apply Z.le_trans with (ofs0 + 2); auto. omega. } - assert (B: ofs0 <= - match list_nth_z float_param_regs fr with - | Some _ => size_arguments_64 tyl ir (fr + 1) ofs0 - | None => size_arguments_64 tyl ir fr (ofs0 + 2) - end). - { destruct (list_nth_z float_param_regs fr); eauto. - apply Z.le_trans with (ofs0 + 2); auto. omega. } - destruct a; auto. -Qed. +(** ** Normalization of function results *) -Lemma size_arguments_above: - forall s, size_arguments s >= 0. -Proof. - intros; unfold size_arguments. apply Z.le_ge. - destruct Archi.ptr64; [apply size_arguments_64_above|apply size_arguments_32_above]. -Qed. +(** In the x86 ABI, a return value of type "char" is returned in + register AL, leaving the top 24 bits of EAX unspecified. + Likewise, a return value of type "short" is returned in register + AH, leaving the top 16 bits of EAX unspecified. Hence, return + values of small integer types need re-normalization after calls. *) -Lemma loc_arguments_32_bounded: - forall ofs ty tyl ofs0, - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments_32 tyl ofs0)) -> - ofs + typesize ty <= size_arguments_32 tyl ofs0. -Proof. - induction tyl as [ | t l]; simpl; intros x IN. -- contradiction. -- rewrite in_app_iff in IN; destruct IN as [IN|IN]. -+ apply Z.le_trans with (x + typesize t); [|apply size_arguments_32_above]. - Ltac decomp := - match goal with - | [ H: _ \/ _ |- _ ] => destruct H; decomp - | [ H: S _ _ _ = S _ _ _ |- _ ] => inv H - | [ H: False |- _ ] => contradiction +Definition return_value_needs_normalization (t: rettype) : bool := + match t with + | Tint8signed | Tint8unsigned | Tint16signed | Tint16unsigned => true + | _ => false end. - destruct t; simpl in IN; decomp; simpl; omega. -+ apply IHl; auto. -Qed. - -Lemma loc_arguments_64_bounded: - forall ofs ty tyl ir fr ofs0, - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments_64 tyl ir fr ofs0)) -> - ofs + typesize ty <= size_arguments_64 tyl ir fr ofs0. -Proof. - induction tyl; simpl; intros. - contradiction. - assert (T: forall ty0, typesize ty0 <= 2). - { destruct ty0; simpl; omega. } - assert (A: forall ty0, - In (S Outgoing ofs ty) (regs_of_rpairs - match list_nth_z int_param_regs ir with - | Some ireg => - One (R ireg) :: loc_arguments_64 tyl (ir + 1) fr ofs0 - | None => One (S Outgoing ofs0 ty0) :: loc_arguments_64 tyl ir fr (ofs0 + 2) - end) -> - ofs + typesize ty <= - match list_nth_z int_param_regs ir with - | Some _ => size_arguments_64 tyl (ir + 1) fr ofs0 - | None => size_arguments_64 tyl ir fr (ofs0 + 2) - end). - { intros. destruct (list_nth_z int_param_regs ir); simpl in H0; destruct H0. - - discriminate. - - eapply IHtyl; eauto. - - inv H0. apply Z.le_trans with (ofs + 2). specialize (T ty). omega. apply size_arguments_64_above. - - eapply IHtyl; eauto. } - assert (B: forall ty0, - In (S Outgoing ofs ty) (regs_of_rpairs - match list_nth_z float_param_regs fr with - | Some ireg => - One (R ireg) :: loc_arguments_64 tyl ir (fr + 1) ofs0 - | None => One (S Outgoing ofs0 ty0) :: loc_arguments_64 tyl ir fr (ofs0 + 2) - end) -> - ofs + typesize ty <= - match list_nth_z float_param_regs fr with - | Some _ => size_arguments_64 tyl ir (fr + 1) ofs0 - | None => size_arguments_64 tyl ir fr (ofs0 + 2) - end). - { intros. destruct (list_nth_z float_param_regs fr); simpl in H0; destruct H0. - - discriminate. - - eapply IHtyl; eauto. - - inv H0. apply Z.le_trans with (ofs + 2). specialize (T ty). omega. apply size_arguments_64_above. - - eapply IHtyl; eauto. } - destruct a; eauto. -Qed. - -Lemma loc_arguments_bounded: - forall (s: signature) (ofs: Z) (ty: typ), - In (S Outgoing ofs ty) (regs_of_rpairs (loc_arguments s)) -> - ofs + typesize ty <= size_arguments s. -Proof. - unfold loc_arguments, size_arguments; intros. - destruct Archi.ptr64; eauto using loc_arguments_32_bounded, loc_arguments_64_bounded. -Qed. - -Lemma loc_arguments_main: - loc_arguments signature_main = nil. -Proof. - unfold loc_arguments; destruct Archi.ptr64; reflexivity. -Qed. diff --git a/x86/Machregs.v b/x86/Machregs.v index bdf492ed..6f3064b8 100644 --- a/x86/Machregs.v +++ b/x86/Machregs.v @@ -351,6 +351,7 @@ Definition two_address_op (op: operation) : bool := | Olongofsingle => false | Osingleoflong => false | Ocmp c => false + | Osel c op => true end. (* Constraints on constant propagation for builtins *) diff --git a/x86/NeedOp.v b/x86/NeedOp.v index 68ecc745..d9a58fbb 100644 --- a/x86/NeedOp.v +++ b/x86/NeedOp.v @@ -120,6 +120,7 @@ Definition needs_of_operation (op: operation) (nv: nval): list nval := | Ointoffloat | Ofloatofint | Ointofsingle | Osingleofint => op1 (default nv) | Olongoffloat | Ofloatoflong | Olongofsingle | Osingleoflong => op1 (default nv) | Ocmp c => needs_of_condition c + | Osel c ty => nv :: nv :: needs_of_condition c end. Definition operation_is_redundant (op: operation) (nv: nval): bool := @@ -231,6 +232,10 @@ Proof. erewrite needs_of_condition_sound by eauto. subst v; simpl. auto with na. subst v; auto with na. +- destruct (eval_condition c args m) as [b|] eqn:EC. + erewrite needs_of_condition_sound by eauto. + apply select_sound; auto. + simpl; auto with na. Qed. Lemma operation_is_redundant_sound: @@ -167,7 +167,9 @@ Inductive operation : Type := | Olongofsingle (**r [rd = signed_long_of_float32(r1)] *) | Osingleoflong (**r [rd = float32_of_signed_long(r1)] *) (*c Boolean tests: *) - | Ocmp (cond: condition). (**r [rd = 1] if condition holds, [rd = 0] otherwise. *) + | Ocmp (cond: condition) (**r [rd = 1] if condition holds, [rd = 0] otherwise. *) + | Osel: condition -> typ -> operation. + (**r [rd = rs1] if condition holds, [rd = rs2] otherwise. *) (** Comparison functions (used in modules [CSE] and [Allocation]). *) @@ -186,7 +188,7 @@ Defined. Definition beq_operation: forall (x y: operation), bool. Proof. - generalize Int.eq_dec Int64.eq_dec Float.eq_dec Float32.eq_dec ident_eq eq_addressing eq_condition; boolean_equality. + generalize Int.eq_dec Int64.eq_dec Float.eq_dec Float32.eq_dec ident_eq typ_eq eq_addressing eq_condition; boolean_equality. Defined. Definition eq_operation: forall (x y: operation), {x=y} + {x<>y}. @@ -407,6 +409,7 @@ Definition eval_operation | Olongofsingle, v1::nil => Val.longofsingle v1 | Osingleoflong, v1::nil => Val.singleoflong v1 | Ocmp c, _ => Some(Val.of_optbool (eval_condition c vl m)) + | Osel c ty, v1::v2::vl => Some(Val.select (eval_condition c vl m) v1 v2 ty) | _, _ => None end. @@ -578,6 +581,7 @@ Definition type_of_operation (op: operation) : list typ * typ := | Olongofsingle => (Tsingle :: nil, Tlong) | Osingleoflong => (Tlong :: nil, Tsingle) | Ocmp c => (type_of_condition c, Tint) + | Osel c ty => (ty :: ty :: type_of_condition c, ty) end. (** Weak type soundness results for [eval_operation]: @@ -735,6 +739,7 @@ Proof with (try exact I; try reflexivity). destruct v0; simpl in H0; inv H0. destruct (Float32.to_long f); inv H2... destruct v0; simpl in H0; inv H0... destruct (eval_condition cond vl m); simpl... destruct b... + unfold Val.select. destruct (eval_condition c vl m). apply Val.normalize_type. exact I. Qed. End SOUNDNESS. @@ -958,23 +963,42 @@ Definition is_trivial_op (op: operation) : bool := (** Operations that depend on the memory state. *) +Definition condition_depends_on_memory (c: condition) : bool := + match c with + | Ccompu _ => negb Archi.ptr64 + | Ccompuimm _ _ => negb Archi.ptr64 + | Ccomplu _ => Archi.ptr64 + | Ccompluimm _ _ => Archi.ptr64 + | _ => false + end. + Definition op_depends_on_memory (op: operation) : bool := match op with - | Ocmp (Ccompu _) => negb Archi.ptr64 - | Ocmp (Ccompuimm _ _) => negb Archi.ptr64 - | Ocmp (Ccomplu _) => Archi.ptr64 - | Ocmp (Ccompluimm _ _) => Archi.ptr64 + | Ocmp c => condition_depends_on_memory c + | Osel c ty => condition_depends_on_memory c | _ => false end. +Lemma condition_depends_on_memory_correct: + forall c args m1 m2, + condition_depends_on_memory c = false -> + eval_condition c args m1 = eval_condition c args m2. +Proof. + intros until m2. + destruct c; simpl; intros SF; auto; rewrite ? negb_false_iff in SF; + unfold Val.cmpu_bool, Val.cmplu_bool; rewrite SF; reflexivity. +Qed. + Lemma op_depends_on_memory_correct: forall (F V: Type) (ge: Genv.t F V) sp op args m1 m2, op_depends_on_memory op = false -> eval_operation ge sp op args m1 = eval_operation ge sp op args m2. Proof. - intros until m2. destruct op; simpl; try congruence. - destruct cond; simpl; intros SF; auto; rewrite ? negb_false_iff in SF; - unfold Val.cmpu_bool, Val.cmplu_bool; rewrite SF; reflexivity. + intros until m2. destruct op; simpl; try congruence; intros C. +- f_equal; f_equal; apply condition_depends_on_memory_correct; auto. +- destruct args; auto. destruct args; auto. + rewrite (condition_depends_on_memory_correct c args m1 m2 C). + auto. Qed. (** Global variables mentioned in an operation or addressing mode *) @@ -1290,6 +1314,9 @@ Proof. exploit eval_condition_inj; eauto. intros EQ; rewrite EQ. destruct b; simpl; constructor. simpl; constructor. + apply Val.select_inject; auto. + destruct (eval_condition c vl1 m1) eqn:?; auto. + right; symmetry; eapply eval_condition_inj; eauto. Qed. End EVAL_COMPAT. diff --git a/x86/PrintOp.ml b/x86/PrintOp.ml index faa5bb5f..6aa4d450 100644 --- a/x86/PrintOp.ml +++ b/x86/PrintOp.ml @@ -164,6 +164,10 @@ let print_operation reg pp = function | Olongofsingle, [r1] -> fprintf pp "longofsingle(%a)" reg r1 | Osingleoflong, [r1] -> fprintf pp "singleoflong(%a)" reg r1 | Ocmp c, args -> print_condition reg pp (c, args) + | Osel (c, ty), r1::r2::args -> + fprintf pp "%a ?%s %a : %a" + (print_condition reg) (c, args) + (PrintAST.name_of_type ty) reg r1 reg r2 | _ -> fprintf pp "<bad operator>" diff --git a/x86/SelectOp.vp b/x86/SelectOp.vp index a1583600..31be8c32 100644 --- a/x86/SelectOp.vp +++ b/x86/SelectOp.vp @@ -38,8 +38,9 @@ Require Import Coqlib. Require Import Compopts. -Require Import AST Integers Floats. +Require Import AST Integers Floats Builtins. Require Import Op CminorSel. +Require Archi. Local Open Scope cminorsel_scope. @@ -456,7 +457,35 @@ Nondetfunction cast16signed (e: expr) := Eop Ocast16signed (e ::: Enil) end. -(** Floating-point conversions *) +(** ** Selection *) + +Definition select_supported (ty: typ) : bool := + match ty with + | Tint => true + | Tlong => Archi.ptr64 + | _ => false + end. + +(** [Asmgen.mk_sel] cannot always handle the conditions that are + implemented as a "and" of two processor flags. However it can + handle the negation of those conditions, which are implemented + as an "or". So, for the risky conditions we just take their + negation and swap the two arguments of the [select]. *) + +Definition select_swap (cond: condition) := + match cond with + | Ccompf Cne | Ccompfs Cne | Cnotcompf Ceq | Cnotcompfs Ceq => true + | _ => false + end. + +Definition select (ty: typ) (cond: condition) (args: exprlist) (e1 e2: expr) := + if select_supported ty then + if select_swap cond + then Some (Eop (Osel (negate_condition cond) ty) (e2 ::: e1 ::: args)) + else Some (Eop (Osel cond ty) (e1 ::: e2 ::: args)) + else None. + +(** ** Floating-point conversions *) Definition singleoffloat (e: expr) := Eop Osingleoffloat (e ::: Enil). Definition floatofsingle (e: expr) := Eop Ofloatofsingle (e ::: Enil). @@ -470,21 +499,27 @@ Nondetfunction floatofint (e: expr) := end. Definition intuoffloat (e: expr) := - Elet e - (Elet (Eop (Ofloatconst (Float.of_intu Float.ox8000_0000)) Enil) - (Econdition (CEcond (Ccompf Clt) (Eletvar 1 ::: Eletvar 0 ::: Enil)) - (intoffloat (Eletvar 1)) - (addimm Float.ox8000_0000 (intoffloat (subf (Eletvar 1) (Eletvar 0))))))%nat. + if Archi.splitlong then + Elet e + (Elet (Eop (Ofloatconst (Float.of_intu Float.ox8000_0000)) Enil) + (Econdition (CEcond (Ccompf Clt) (Eletvar 1 ::: Eletvar 0 ::: Enil)) + (intoffloat (Eletvar 1)) + (addimm Float.ox8000_0000 (intoffloat (subf (Eletvar 1) (Eletvar 0))))))%nat + else + Eop Olowlong (Eop Olongoffloat (e ::: Enil) ::: Enil). Nondetfunction floatofintu (e: expr) := match e with | Eop (Ointconst n) Enil => Eop (Ofloatconst (Float.of_intu n)) Enil | _ => - let f := Eop (Ofloatconst (Float.of_intu Float.ox8000_0000)) Enil in - Elet e - (Econdition (CEcond (Ccompuimm Clt Float.ox8000_0000) (Eletvar O ::: Enil)) - (floatofint (Eletvar O)) - (addf (floatofint (addimm (Int.neg Float.ox8000_0000) (Eletvar O))) f)) + if Archi.splitlong then + let f := Eop (Ofloatconst (Float.of_intu Float.ox8000_0000)) Enil in + Elet e + (Econdition (CEcond (Ccompuimm Clt Float.ox8000_0000) (Eletvar O ::: Enil)) + (floatofint (Eletvar O)) + (addf (floatofint (addimm (Int.neg Float.ox8000_0000) (Eletvar O))) f)) + else + Eop Ofloatoflong (Eop Ocast32unsigned (e ::: Enil) ::: Enil) end. Definition intofsingle (e: expr) := Eop Ointofsingle (e ::: Enil). @@ -540,3 +575,8 @@ Nondetfunction builtin_arg (e: expr) := | Eload chunk (Ainstack ofs) Enil => BA_loadstack chunk ofs | _ => BA e end. + +(** Platform-specific known builtins *) + +Definition platform_builtin (b: platform_builtin) (args: exprlist) : option expr := + None. diff --git a/x86/SelectOpproof.v b/x86/SelectOpproof.v index fdbadaa8..961f602c 100644 --- a/x86/SelectOpproof.v +++ b/x86/SelectOpproof.v @@ -13,15 +13,9 @@ (** Correctness of instruction selection for operators *) Require Import Coqlib. -Require Import AST. -Require Import Integers. -Require Import Floats. -Require Import Values. -Require Import Memory. -Require Import Globalenvs. -Require Import Cminor. -Require Import Op. -Require Import CminorSel. +Require Import AST Integers Floats. +Require Import Values Memory Builtins Globalenvs. +Require Import Cminor Op CminorSel. Require Import SelectOp. Local Open Scope cminorsel_scope. @@ -387,9 +381,9 @@ Proof. - TrivialExists. simpl. rewrite Int.and_commut; auto. - TrivialExists. simpl. rewrite Val.and_assoc. rewrite Int.and_commut. auto. - rewrite Val.zero_ext_and. TrivialExists. rewrite Val.and_assoc. - rewrite Int.and_commut. auto. compute; auto. + rewrite Int.and_commut. auto. omega. - rewrite Val.zero_ext_and. TrivialExists. rewrite Val.and_assoc. - rewrite Int.and_commut. auto. compute; auto. + rewrite Int.and_commut. auto. omega. - TrivialExists. Qed. @@ -749,7 +743,7 @@ Proof. red; intros until x. unfold cast8unsigned. destruct (cast8unsigned_match a); intros; InvEval. TrivialExists. subst. rewrite Val.zero_ext_and. rewrite Val.and_assoc. - rewrite Int.and_commut. apply eval_andimm; auto. compute; auto. + rewrite Int.and_commut. apply eval_andimm; auto. omega. TrivialExists. Qed. @@ -765,10 +759,36 @@ Proof. red; intros until x. unfold cast16unsigned. destruct (cast16unsigned_match a); intros; InvEval. TrivialExists. subst. rewrite Val.zero_ext_and. rewrite Val.and_assoc. - rewrite Int.and_commut. apply eval_andimm; auto. compute; auto. + rewrite Int.and_commut. apply eval_andimm; auto. omega. TrivialExists. Qed. +Theorem eval_select: + forall le ty cond al vl a1 v1 a2 v2 a b, + select ty cond al a1 a2 = Some a -> + eval_exprlist ge sp e m le al vl -> + eval_expr ge sp e m le a1 v1 -> + eval_expr ge sp e m le a2 v2 -> + eval_condition cond vl m = Some b -> + exists v, + eval_expr ge sp e m le a v + /\ Val.lessdef (Val.select (Some b) v1 v2 ty) v. +Proof. + unfold select; intros. + destruct (select_supported ty); try discriminate. + destruct (select_swap cond); inv H. +- exists (Val.select (Some (negb b)) v2 v1 ty); split. + apply eval_Eop with (v2 :: v1 :: vl). + constructor; auto. constructor; auto. + simpl. rewrite eval_negate_condition, H3; auto. + destruct b; auto. +- exists (Val.select (Some b) v1 v2 ty); split. + apply eval_Eop with (v1 :: v2 :: vl). + constructor; auto. constructor; auto. + simpl. rewrite H3; auto. + auto. +Qed. + Theorem eval_singleoffloat: unary_constructor_sound singleoffloat Val.singleoffloat. Proof. red; intros. unfold singleoffloat. TrivialExists. @@ -808,7 +828,8 @@ Proof. intros. destruct x; simpl in H0; try discriminate. destruct (Float.to_intu f) as [n|] eqn:?; simpl in H0; inv H0. exists (Vint n); split; auto. unfold intuoffloat. - set (im := Int.repr Int.half_modulus). + destruct Archi.splitlong. +- set (im := Int.repr Int.half_modulus). set (fm := Float.of_intu im). assert (eval_expr ge sp e m (Vfloat fm :: Vfloat f :: le) (Eletvar (S O)) (Vfloat f)). constructor. auto. @@ -835,6 +856,11 @@ Proof. rewrite Int.add_neg_zero in A4. rewrite Int.add_zero in A4. auto. +- apply Float.to_intu_to_long in Heqo. repeat econstructor. eauto. + simpl. rewrite Heqo; reflexivity. + simpl. unfold Int64.loword. rewrite Int64.unsigned_repr, Int.repr_unsigned; auto. + assert (Int.modulus < Int64.max_unsigned) by reflexivity. + generalize (Int.unsigned_range n); omega. Qed. Theorem eval_floatofintu: @@ -844,10 +870,11 @@ Theorem eval_floatofintu: exists v, eval_expr ge sp e m le (floatofintu a) v /\ Val.lessdef y v. Proof. intros until y; unfold floatofintu. case (floatofintu_match a); intros. - InvEval. TrivialExists. - destruct x; simpl in H0; try discriminate. inv H0. +- InvEval. TrivialExists. +- destruct x; simpl in H0; try discriminate. inv H0. exists (Vfloat (Float.of_intu i)); split; auto. - econstructor. eauto. + destruct Archi.splitlong. ++ econstructor. eauto. set (fm := Float.of_intu Float.ox8000_0000). assert (eval_expr ge sp e m (Vint i :: le) (Eletvar O) (Vint i)). constructor. auto. @@ -863,6 +890,7 @@ Proof. constructor. EvalOp. simpl; eauto. constructor. simpl; eauto. fold fm. rewrite Float.of_intu_of_int_2; auto. rewrite Int.sub_add_opp. auto. ++ rewrite Float.of_intu_of_long. repeat econstructor. eauto. reflexivity. Qed. Theorem eval_intofsingle: @@ -984,4 +1012,16 @@ Proof. - constructor; auto. Qed. +(** Platform-specific known builtins *) + +Theorem eval_platform_builtin: + forall bf al a vl v le, + platform_builtin bf al = Some a -> + eval_exprlist ge sp e m le al vl -> + platform_builtin_sem bf vl = Some v -> + exists v', eval_expr ge sp e m le a v' /\ Val.lessdef v v'. +Proof. + intros. discriminate. +Qed. + End CMCONSTR. diff --git a/x86/Stacklayout.v b/x86/Stacklayout.v index d375febf..96b0c8ef 100644 --- a/x86/Stacklayout.v +++ b/x86/Stacklayout.v @@ -58,7 +58,7 @@ Lemma frame_env_separated: ** range sp (fe_ofs_callee_save fe) (size_callee_save_area b (fe_ofs_callee_save fe)) ** P. Proof. -Local Opaque Z.add Z.mul sepconj range. +Local Opaque Z.add Z.mul sepconj range'. intros; simpl. set (w := if Archi.ptr64 then 8 else 4). set (olink := align (4 * b.(bound_outgoing)) w). diff --git a/x86/TargetPrinter.ml b/x86/TargetPrinter.ml index 3ac2f36e..6159437e 100644 --- a/x86/TargetPrinter.ml +++ b/x86/TargetPrinter.ml @@ -62,8 +62,8 @@ let ireg64 oc r = output_string oc (int64_reg_name r) let ireg = if Archi.ptr64 then ireg64 else ireg32 let freg oc r = output_string oc (float_reg_name r) -let preg oc = function - | IR r -> ireg oc r +let preg_asm oc ty = function + | IR r -> if ty = Tlong then ireg64 oc r else ireg32 oc r | FR r -> freg oc r | _ -> assert false @@ -103,7 +103,7 @@ let rec log2 n = assert (n > 0); if n = 1 then 0 else 1 + log2 (n lsr 1) -(* System dependend printer functions *) +(* System dependent printer functions *) module type SYSTEM = sig val raw_symbol: out_channel -> string -> unit @@ -134,9 +134,9 @@ module ELF_System : SYSTEM = let name_of_section = function | Section_text -> ".text" | Section_data i | Section_small_data i -> - if i then ".data" else "COMM" + if i then ".data" else common_section () | Section_const i | Section_small_const i -> - if i then ".section .rodata" else "COMM" + if i || (not !Clflags.option_fcommon) then ".section .rodata" else "COMM" | Section_string -> ".section .rodata" | Section_literal -> ".section .rodata.cst8,\"aM\",@progbits,8" | Section_jumptable -> ".text" @@ -192,9 +192,9 @@ module MacOS_System : SYSTEM = let name_of_section = function | Section_text -> ".text" | Section_data i | Section_small_data i -> - if i then ".data" else "COMM" + if i || (not !Clflags.option_fcommon) then ".data" else "COMM" | Section_const i | Section_small_const i -> - if i then ".const" else "COMM" + if i || (not !Clflags.option_fcommon) then ".const" else "COMM" | Section_string -> ".const" | Section_literal -> ".literal8" | Section_jumptable -> ".text" (* needed in 64 bits, not a problem in 32 bits *) @@ -269,9 +269,9 @@ module Cygwin_System : SYSTEM = let name_of_section = function | Section_text -> ".text" | Section_data i | Section_small_data i -> - if i then ".data" else "COMM" + if i then ".data" else common_section () | Section_const i | Section_small_const i -> - if i then ".section .rdata,\"dr\"" else "COMM" + if i || (not !Clflags.option_fcommon) then ".section .rdata,\"dr\"" else "COMM" | Section_string -> ".section .rdata,\"dr\"" | Section_literal -> ".section .rdata,\"dr\"" | Section_jumptable -> ".text" @@ -399,7 +399,13 @@ module Target(System: SYSTEM):TARGET = (* Printing of instructions *) -(* Reminder on AT&T syntax: op source, dest *) +(* Reminder on X86 assembly syntaxes: + AT&T syntax Intel syntax + (used by GNU as) (used in reference manuals) + dst <- op(src) op src, dst op dst, src + dst <- op(dst, src2) op src2, dst op dst, src2 + dst <- op(dst, src2, src3) op src3, src2, dst op dst, src2, src3 +*) let print_instruction oc = function (* Moves *) @@ -752,29 +758,29 @@ module Target(System: SYSTEM):TARGET = | Pcfi_adjust sz -> cfi_adjust oc (camlint_of_coqint sz) | Pfmadd132 (res,a1,a2) -> - fprintf oc " vfmadd132sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfmadd132sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfmadd213 (res,a1,a2) -> - fprintf oc " vfmadd213sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfmadd213sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfmadd231 (res,a1,a2) -> - fprintf oc " vfmadd231sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfmadd231sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfmsub132 (res,a1,a2) -> - fprintf oc " vfmsub132sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfmsub132sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfmsub213 (res,a1,a2) -> - fprintf oc " vfmsub213sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfmsub213sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfmsub231 (res,a1,a2) -> - fprintf oc " vfmsub231sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfmsub231sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfnmadd132 (res,a1,a2) -> - fprintf oc " vfnmadd132sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfnmadd132sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfnmadd213 (res,a1,a2) -> - fprintf oc " vfnmadd213sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfnmadd213sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfnmadd231 (res,a1,a2) -> - fprintf oc " vfnmadd231sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfnmadd231sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfnmsub132 (res,a1,a2) -> - fprintf oc " vfnmsub132sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfnmsub132sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfnmsub213 (res,a1,a2) -> - fprintf oc " vfnmsub213sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfnmsub213sd %a, %a, %a\n" freg a2 freg a1 freg res | Pfnmsub231 (res,a1,a2) -> - fprintf oc " vfnmsub231sd %a, %a, %a\n" freg a1 freg a2 freg res + fprintf oc " vfnmsub231sd %a, %a, %a\n" freg a2 freg a1 freg res | Pmaxsd (res,a1) -> fprintf oc " maxsd %a, %a\n" freg a1 freg res | Pminsd (res,a1) -> @@ -826,7 +832,7 @@ module Target(System: SYSTEM):TARGET = (P.to_int kind) (extern_atom txt) args | EF_inline_asm(txt, sg, clob) -> fprintf oc "%s begin inline assembly\n\t" comment; - print_inline_asm preg oc (camlstring_of_coqstring txt) sg args res; + print_inline_asm preg_asm oc (camlstring_of_coqstring txt) sg args res; fprintf oc "%s end inline assembly\n" comment | _ -> assert false diff --git a/x86/ValueAOp.v b/x86/ValueAOp.v index 1021a9c8..d0b8427a 100644 --- a/x86/ValueAOp.v +++ b/x86/ValueAOp.v @@ -160,6 +160,7 @@ Definition eval_static_operation (op: operation) (vl: list aval): aval := | Olongofsingle, v1::nil => longofsingle v1 | Osingleoflong, v1::nil => singleoflong v1 | Ocmp c, _ => of_optbool (eval_static_condition c vl) + | Osel c ty, v1::v2::vl => select (eval_static_condition c vl) v1 v2 | _, _ => Vbot end. @@ -258,6 +259,7 @@ Proof. eapply eval_static_addressing_32_sound; eauto. eapply eval_static_addressing_64_sound; eauto. apply of_optbool_sound. eapply eval_static_condition_sound; eauto. + apply select_sound; auto. eapply eval_static_condition_sound; eauto. Qed. End SOUNDNESS. diff --git a/x86_32/Archi.v b/x86_32/Archi.v index f10570e2..e9d05c14 100644 --- a/x86_32/Archi.v +++ b/x86_32/Archi.v @@ -16,7 +16,7 @@ (** Architecture-dependent parameters for x86 in 32-bit mode *) -Require Import ZArith. +Require Import ZArith List. (*From Flocq*) Require Import Binary Bits. @@ -34,24 +34,33 @@ Proof. unfold splitlong. destruct ptr64; simpl; congruence. Qed. -Definition default_nan_64 : { x : binary64 | is_nan _ _ x = true } := - exist _ (B754_nan 53 1024 true (iter_nat 51 _ xO xH) (eq_refl true)) (eq_refl true). +Definition default_nan_64 := (true, iter_nat 51 _ xO xH). +Definition default_nan_32 := (true, iter_nat 22 _ xO xH). -Definition choose_binop_pl_64 (pl1 pl2 : positive) := - false. (**r always choose first NaN *) +(* Always choose the first NaN argument, if any *) -Definition default_nan_32 : { x : binary32 | is_nan _ _ x = true } := - exist _ (B754_nan 24 128 true (iter_nat 22 _ xO xH) (eq_refl true)) (eq_refl true). +Definition choose_nan_64 (l: list (bool * positive)) : bool * positive := + match l with nil => default_nan_64 | n :: _ => n end. -Definition choose_binop_pl_32 (pl1 pl2 : positive) := - false. (**r always choose first NaN *) +Definition choose_nan_32 (l: list (bool * positive)) : bool * positive := + match l with nil => default_nan_32 | n :: _ => n end. -Definition fpu_returns_default_qNaN := false. +Lemma choose_nan_64_idem: forall n, + choose_nan_64 (n :: n :: nil) = choose_nan_64 (n :: nil). +Proof. auto. Qed. + +Lemma choose_nan_32_idem: forall n, + choose_nan_32 (n :: n :: nil) = choose_nan_32 (n :: nil). +Proof. auto. Qed. + +Definition fma_order {A: Type} (x y z: A) := (x, y, z). + +Definition fma_invalid_mul_is_nan := false. Definition float_of_single_preserves_sNaN := false. Global Opaque ptr64 big_endian splitlong - default_nan_64 choose_binop_pl_64 - default_nan_32 choose_binop_pl_32 - fpu_returns_default_qNaN + default_nan_64 choose_nan_64 + default_nan_32 choose_nan_32 + fma_order fma_invalid_mul_is_nan float_of_single_preserves_sNaN. diff --git a/x86_64/Archi.v b/x86_64/Archi.v index 01eb36dd..959d8dc1 100644 --- a/x86_64/Archi.v +++ b/x86_64/Archi.v @@ -16,7 +16,7 @@ (** Architecture-dependent parameters for x86 in 64-bit mode *) -Require Import ZArith. +Require Import ZArith List. (*From Flocq*) Require Import Binary Bits. @@ -34,24 +34,33 @@ Proof. unfold splitlong. destruct ptr64; simpl; congruence. Qed. -Definition default_nan_64 : { x : binary64 | is_nan _ _ x = true } := - exist _ (B754_nan 53 1024 true (iter_nat 51 _ xO xH) (eq_refl true)) (eq_refl true). +Definition default_nan_64 := (true, iter_nat 51 _ xO xH). +Definition default_nan_32 := (true, iter_nat 22 _ xO xH). -Definition choose_binop_pl_64 (pl1 pl2 : positive) := - false. (**r always choose first NaN *) +(* Always choose the first NaN argument, if any *) -Definition default_nan_32 : { x : binary32 | is_nan _ _ x = true } := - exist _ (B754_nan 24 128 true (iter_nat 22 _ xO xH) (eq_refl true)) (eq_refl true). +Definition choose_nan_64 (l: list (bool * positive)) : bool * positive := + match l with nil => default_nan_64 | n :: _ => n end. -Definition choose_binop_pl_32 (pl1 pl2 : positive) := - false. (**r always choose first NaN *) +Definition choose_nan_32 (l: list (bool * positive)) : bool * positive := + match l with nil => default_nan_32 | n :: _ => n end. -Definition fpu_returns_default_qNaN := false. +Lemma choose_nan_64_idem: forall n, + choose_nan_64 (n :: n :: nil) = choose_nan_64 (n :: nil). +Proof. auto. Qed. + +Lemma choose_nan_32_idem: forall n, + choose_nan_32 (n :: n :: nil) = choose_nan_32 (n :: nil). +Proof. auto. Qed. + +Definition fma_order {A: Type} (x y z: A) := (x, y, z). + +Definition fma_invalid_mul_is_nan := false. Definition float_of_single_preserves_sNaN := false. Global Opaque ptr64 big_endian splitlong - default_nan_64 choose_binop_pl_64 - default_nan_32 choose_binop_pl_32 - fpu_returns_default_qNaN + default_nan_64 choose_nan_64 + default_nan_32 choose_nan_32 + fma_order fma_invalid_mul_is_nan float_of_single_preserves_sNaN. |