diff --git a/Changelog b/Changelog
index 6f9defaad92614bdc2408d1181e72354b045301d..e0472fb787ea02c7da8ce95cd6f2d5469bce6d4c 100644
--- a/Changelog
+++ b/Changelog
@@ -18,6 +18,9 @@
 Open Source Release <next-release>
 ##################################
 
+o   Kernel    [2022-05-03] Prototype of AST comparison between two versions
+              of the same program, via the new option -ast-diff.
+-   Eva       [2022-05-02] Fixes stack overflow errors on very large C functions.
 -   Eva       [2022-04-25] Improve the multidim abstract domain: it is now
               more precise and more robust, it is able to infer simple array
               invariants on some loops without unrolling, and has a new option
@@ -34,6 +37,10 @@ o!  Kernel    [2022-02-23] Remove State_selection.Static (deprecated since
               integer literal constants
 *   Kernel    [2022-02-17] Reject programs using unsupported
               vector_size attribute (fixes ##2573)
+o   Eva       [2022-02-15] New API to run the analysis and access its results,
+              intended to replace the old API in Db.Value. It is more precise
+              as it uses all available domains to evaluate values and locations.
+              See file Eva.mli for more details.
 *   Kernel    [2022-02-08] Reject array whose size is too big with a proper
               error message instead of a crash (fixes ##2590)
 o!  Kernel    [2022-01-19] Removed obsolete AST nodes IndexPI and Info
diff --git a/Makefile b/Makefile
index af9f1b7bef88ce2ef2c4eff555bbd034677060af..6b917cf38ef061f06e297d00a414b01557f93c12 100644
--- a/Makefile
+++ b/Makefile
@@ -86,7 +86,6 @@ PLUGIN_BIN_DOC_LIST:=
 PLUGIN_DIST_EXTERNAL_LIST:=
 PLUGIN_DIST_TESTS_LIST:=
 PLUGIN_DISTRIBUTED_NAME_LIST:=
-MERLIN_PACKAGES:=
 
 PLUGIN_HEADER_SPEC_LIST :=
 PLUGIN_HEADER_DIRS_LIST :=
@@ -563,6 +562,7 @@ KERNEL_CMO=\
 	src/kernel_services/ast_data/annotations.cmo                    \
 	src/kernel_services/ast_printing/printer.cmo                    \
 	src/kernel_internals/typing/logic_builtin.cmo                 \
+	src/kernel_services/ast_queries/ast_diff.cmo                  \
 	src/kernel_services/ast_printing/cabs_debug.cmo                 \
 	src/kernel_internals/parsing/lexerhack.cmo                     \
 	src/kernel_internals/parsing/clexer.cmo                        \
diff --git a/Makefile.generating b/Makefile.generating
index 9787f4afc4e4e93a07912a8381cd4c6d09b835c7..993a0603c970f690112f6384ec975db08bdec200 100644
--- a/Makefile.generating
+++ b/Makefile.generating
@@ -161,12 +161,22 @@ endif
 # Merlin #
 
 .PHONY:merlin
-.merlin merlin: share/Makefile.config Makefile.generating
+
+# hack to account for the fact that merlin and ocamlfind treat
+# differently dependencies between ppx rewriting.
+PPX_DEPENDENCIES=ppx_deriving.eq ppx_import
+
 #create Merlin file
+.merlin merlin: share/Makefile.config Makefile.generating
 	$(PRINT_MAKING) $@
 	echo "FLG -c $(FLAGS) $(FRAMAC_USER_MERLIN_FLAGS)" > .merlin
-	for PKG in $(LIBRARY_NAMES); do echo PKG $$PKG >> .merlin; done
-	for PKG in $(LIBRARY_NAMES_GUI); do echo PKG $$PKG >> .merlin; done
+	for PKG in $(filter-out $(PPX_DEPENDENCIES),$(LIBRARY_NAMES)); \
+          do echo PKG $$PKG >> .merlin; done
+	for PKG in $(filter-out $(PPX_DEPENDENCIES),$(LIBRARY_NAMES_GUI)); \
+	 do echo PKG $$PKG >> .merlin; done
+	for PKG in \
+         $(filter $(LIBRARY_NAMES) $(LIBRARY_NAMES_GUI),$(PPX_DEPENDENCIES)); \
+         do echo PKG $$PKG >> .merlin; done
 	for PKG in $(MERLIN_PACKAGES); do echo PKG $$PKG >> .merlin; done
 	echo "B lib/plugins" >> .merlin
 	echo "B lib/plugins/gui" >> .merlin
diff --git a/bin/check-reference-configuration.sh b/bin/check-reference-configuration.sh
index 6557c2eca7defd285aebdb24afa512ccad39385e..e91365642881b711352f863c119deecc00de901e 100755
--- a/bin/check-reference-configuration.sh
+++ b/bin/check-reference-configuration.sh
@@ -7,6 +7,17 @@ if ! type "opam" > /dev/null; then
     opam="NOT"
 else
     opam="$(opam --version)"
+    if [[ $opam =~ ^([0-9+]).([0-9+]) ]]; then
+        major="${BASH_REMATCH[1]}"
+        minor="${BASH_REMATCH[2]}"
+        if [[ "$major" -eq 2 && "$minor" -eq 0 ]]; then
+            echo "warning: opam > 2.1 expected, got $major.$minor."
+            echo "         Suggested commands may not work properly."
+        fi
+    else
+        echo "warning: unknown opam version."
+        echo "         Suggested commands may not work properly."
+    fi
 fi
 
 if ! type "ocaml" > /dev/null; then
@@ -63,7 +74,6 @@ echo "All packages checked."
 if [ $has_any_diffs -ne 0 ]; then
     echo "Useful commands:"
     echo "    opam switch create ${working_ocaml}"
-    echo "    opam install depext"
-    echo "    opam depext --install$all_packages"
+    echo "    opam install$all_packages"
     echo "    rm -f ~/.why3.conf && why3 config detect"
 fi
diff --git a/configure.in b/configure.in
index 14992096643bf50419d820e9eb81198625d24ca3..e98ef587598339a0405baf03cc58c93a361943e5 100644
--- a/configure.in
+++ b/configure.in
@@ -336,6 +336,17 @@ else
   AC_MSG_RESULT(found $YOJSON)
 fi
 
+# ppx_import
+############
+
+AC_MSG_CHECKING(for ppx_import)
+PPX_IMPORT=$($OCAMLFIND query ppx_import -format %v)
+if test -z "$PPX_IMPORT"; then
+   AC_MSG_ERROR(Cannot find ppx_import via ocamlfind)
+else
+   AC_MSG_RESULT(found $PPX_IMPORT)
+fi
+
 #################################################
 # Check for other (optional) tools/libraries    #
 #################################################
diff --git a/devel_tools/docker/Makefile b/devel_tools/docker/Makefile
index 64be01e60a9cfade46cece24845addf9449f848b..04d1c97261bf1c50ddebd680e3f9f7458ddffaca 100644
--- a/devel_tools/docker/Makefile
+++ b/devel_tools/docker/Makefile
@@ -96,10 +96,11 @@ conf-graphviz.0.1 \\\
 mlgmpidl.1.2.12 \\\
 ocamlfind.1.8.1 \\\
 ocamlgraph.1.8.8 \\\
-ppx_deriving_yojson.3.5.2 \\\
-why3.1.4.0 \\\
+ppx_deriving_yojson.3.6.1 \\\
+ppx_import.1.9.0 \\\
+why3.1.5.0 \\\
 yojson.1.7.0 \\\
-zarith.1.9.1 \\\
+zarith.1.10 \\\
 zmq.5.1.3 \\\
 conf-python-3.1.0.0 \\\
 conf-time.1|' | \
diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 85a742841df0fe295e65d22132756dd7788da302..e34347acdd061e025cfff74a0507b0f5dfac4d46 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -594,6 +594,8 @@ src/kernel_services/ast_printing/printer_tag.mli: CEA_LGPL
 src/kernel_services/ast_queries/README.md: .ignore
 src/kernel_services/ast_queries/acsl_extension.ml: CEA_LGPL
 src/kernel_services/ast_queries/acsl_extension.mli: CEA_LGPL
+src/kernel_services/ast_queries/ast_diff.ml: CEA_LGPL
+src/kernel_services/ast_queries/ast_diff.mli: CEA_LGPL
 src/kernel_services/ast_queries/ast_info.ml: CEA_LGPL
 src/kernel_services/ast_queries/ast_info.mli: CEA_LGPL
 src/kernel_services/ast_queries/cil.ml: CIL
diff --git a/ivette/INSTALL.md b/ivette/INSTALL.md
index 243b80ae3468ca91adbad0195c8e95b4ff03ac69..db3201e0c7c338f327ed8b9001bb608663a7a2ca 100644
--- a/ivette/INSTALL.md
+++ b/ivette/INSTALL.md
@@ -3,42 +3,93 @@
 Required package to be installed:
 - `yarn` for node pakage management;
 - `pandoc` for generating the documentation;
-- `node` version 16.x (codename: gallium)
+- `node` version 16.x;
+
+We recommand to use node v16 as a workaroung against incompatible versions of
+SSL and Webpack packages. However, under Arch Linux, the standard node v17 is
+also known to work.
+
+## Linux
+
+```sh
+$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
+$ nvm install 16
+$ nvm use 16
+$ npm install yarn
+```
+
+Under Arch you can simply rely on the `yarn` package and its `node` standard
+dependency:
+
+```sh
+$ pacman -S yarn
+```
+
+## macOS
+
+```sh
+$ brew install yarn
+$ brew install nvm # follow instructions
+$ nvm install 16
+$ nvm use 16
+```
 
 # Installation
 
+You shall have configured `frama-c` before installing Ivette. Notice that, by
+default, the installed `ivette` command will look for an installed `frama-c`
+command to run the server.
+
 From the `Frama-C` main directory, simply type:
 
 ```
+$ autoconf -f
+$ ./configure
 $ make -C ivette dist
+$ [sudo] make -C ivette install
 ```
 
 If this is the first time you compile `ivette`, this might take some time to download
 all the necessary packages and Electron binaries from the web.
 
-Once finished, the Ivette application is available in `ivette/dist/<platform>` directory.
+The first `make` command builds a binary distribution of Ivette for your
+architecture in `ivette/dist/<arch>` ; the second `make` command installs it on
+your system accordingly.
+
+The installed command is `<prefix>/bin/ivette` which is actually just a wrapper
+to launch the Ivette application. The Ivette application itself is installed:
+
+- **Linux:** in `<prefix>/lib/ivette/*`
+- **macOS:** in `/Applications/Ivette.app`
 
-# Developer Install
+# Developer Setup
 
 Ivette can be compiled and used with different modes:
+
 - `make dev` builds and start the development version with live-code-editing enabled. It uses
   local binaries of Electron framework. This is _not_ a full packaged
   application.
+
 - `make app` pre-builds the production application. It is not _yet_ packaged and still
   uses the local Electron binaries.
+
 - `make dist` packages the pre-built application into a new application for the host
-  operating system.
+  operating system, see Installation section above.
+
+The development and production applications can be launched from the command
+line with `Frama-C/ivette/bin/ivette` wrapper. The generated `ivette` script
+will use the local `Frama-C/bin/frama-c` binary by default, although you can
+change this behaviour by using `ivette` command-line options.
 
-The development and production applications can be launched from the command line
-with `Frama-C/ivette/bin/frama-c-gui` binary.
-The generated `frama-c-gui` script will use the local `Frama-C/bin/frama-c` binary by default,
-although you can change the default settings by using the following options:
+The `ivette` application and its installed or local wrappers accept the
+following command line options:
 
 ```
-frama-c-gui [ivette options] [frama-c command line]
-  --cwd <dir> change the working directory used by ivette & frama-c
-  --command <bin> set the frama-c server to be launched
-  --socket <socket> set the IPC socket name to be used for the frama-c server
+ivette [ivette options] [frama-c command line]
+  -R|--reload re-run the last command from history (other options are discarded)
+  -C|--working <dir> change the working directory used by ivette & frama-c
+  -B|--command <bin> set the frama-c server to be launched
+  --socket <socket> set the Linux socket name to be used for the frama-c server
 ```
 
 See also the [CONTRIBUTING] guide for editor configuration if you want to hack in Ivette
diff --git a/ivette/Makefile b/ivette/Makefile
index f6a0cf4f1f4d5e4dcfe9ea8b8e2687a5aca5909a..2fdd3467f40a35bb3f38e68c34ab9e0390436648 100644
--- a/ivette/Makefile
+++ b/ivette/Makefile
@@ -25,7 +25,7 @@ APP=Ivette
 DOME=./src/dome
 DOME_ARGS=--command $$(dirname $$0)/../../bin/frama-c
 DOME_DEV=-server-debug 1
-DOME_CLI=./bin/frama-c-gui
+DOME_CLI=./bin/ivette
 DOME_API=./src/frama-c
 DOME_CUSTOM_ENTRIES= yes
 COPYRIGHT=CEA LIST / LSL
@@ -37,7 +37,7 @@ all: pkg lint app
 
 app: dome-app
 dev: dome-dev
-dist: dome-dist
+dist: dist-dir dome-dist
 
 lint: dome-pkg dome-templ checkcase
 	@echo "[Ivette] running typechecker & linter"
@@ -148,3 +148,65 @@ $(NODEBIN)/%:
 # --------------------------------------------------------------------------
 include $(DOME)/template/makefile
 # --------------------------------------------------------------------------
+
+# --------------------------------------------------------------------------
+# --- Ivette Installation
+# --------------------------------------------------------------------------
+
+sinclude ../share/Makefile.config
+
+dist-dir:
+	@echo "Cleaning dist"
+	@rm -fr dist
+
+IVETTE_DIST=$(wildcard dist/linux-unpacked dist/mac dist/mac-arm64)
+
+ifdef LIBDIR
+ifdef BINDIR
+ifeq ($(IVETTE_DIST),dist/linux-unpacked)
+
+install:
+	@echo "Installing Ivette (Linux)"
+	@rm -fr $(LIBDIR)/ivette
+	@rm -f $(BINDIR)/ivette
+	@mv $(IVETTE_DIST) $(LIBDIR)/ivette
+	@ln -s $(LIBDIR)/ivette/ivette $(BINDIR)/ivette
+
+else ifeq ($(IVETTE_DIST:-arm64=),dist/mac)
+
+install:
+	@echo "Installing Ivette (macOS)"
+	@rm -fr /Applications/Ivette.app
+	@mv $(IVETTE_DIST)/Ivette.app /Applications/
+	@rm -fr $(IVETTE_DIST)
+	@install ivette-macos.sh $(BINDIR)/ivette
+
+else
+
+install:
+	@echo "No distribution to install"
+	@echo "use 'make dist' and retry"
+	@exit 1
+
+endif
+endif
+endif
+
+# --------------------------------------------------------------------------
+# --- Ivette Un-Installation
+# --------------------------------------------------------------------------
+
+ifdef LIBDIR
+ifdef BINDIR
+
+uninstall:
+	@echo "Uninstall Ivette"
+	@rm -fr dist
+	@rm -fr /Applications/Ivette.app
+	@rm -fr $(LIBDIR)/ivette
+	@rm -f $(BINDIR)/ivette
+
+endif
+endif
+
+# --------------------------------------------------------------------------
diff --git a/ivette/electron-builder.json b/ivette/electron-builder.json
new file mode 100644
index 0000000000000000000000000000000000000000..8a9e8291fb56caa9a9b4b279dea6261fcf265645
--- /dev/null
+++ b/ivette/electron-builder.json
@@ -0,0 +1,4 @@
+{
+  productName: "Ivette",
+  mac: { icon: "ivette.icns" }
+}
diff --git a/ivette/ivette-macos.sh b/ivette/ivette-macos.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8e11dedfccca2c9f3e64980f0ceb19444f8d3ffd
--- /dev/null
+++ b/ivette/ivette-macos.sh
@@ -0,0 +1,3 @@
+#!/bin/zsh
+
+exec open -na Ivette.app --args --cwd $PWD $*
diff --git a/ivette/ivette.icns b/ivette/ivette.icns
new file mode 100644
index 0000000000000000000000000000000000000000..6e8ec524ac725709613bb0ea80900ea759b21fc5
Binary files /dev/null and b/ivette/ivette.icns differ
diff --git a/ivette/src/dome/main/dome.ts b/ivette/src/dome/main/dome.ts
index e735470e986db9d7ffbadfe6580aeeba71978384..9ab3ca18f76f7f9c695166ab8ea33a3fe241f931 100644
--- a/ivette/src/dome/main/dome.ts
+++ b/ivette/src/dome/main/dome.ts
@@ -167,6 +167,7 @@ function obtainGlobalSettings() {
 type Store = { [key: string]: unknown };
 
 interface Handle {
+  primary: boolean; // Primary window
   window: BrowserWindow; // Also prevents Gc
   frame: Electron.Rectangle; // Window frame
   devtools: boolean; // Developper tools visible
@@ -348,6 +349,7 @@ function lookupConfig(pwd = '.') {
 // --------------------------------------------------------------------------
 
 function createBrowserWindow(
+  primary: boolean,
   config: BrowserWindowConstructorOptions,
   argv?: string[],
   wdir?: string,
@@ -387,6 +389,7 @@ function createBrowserWindow(
   const wid = webContents.id;
 
   const handle: Handle = {
+    primary,
     window: theWindow,
     config: configFile,
     reloaded: false,
@@ -487,23 +490,31 @@ function createPrimaryWindow() {
   applyThemeSettings(globals);
 
   // Create Window
-  createBrowserWindow({ title: appName }, argv, wdir);
+  createBrowserWindow(true, { title: appName }, argv, wdir);
 }
 
 let appCount = 1;
 
 function createSecondaryWindow(
   _event: Electron.Event,
-  electronArgv: string[],
+  chromiumArgv: string[],
   wdir: string,
 ) {
-  const argv = stripElectronArgv(electronArgv);
-  createBrowserWindow({ title: `${appName} #${++appCount}` }, argv, wdir);
+  const argStart = "--second-instance=";
+  let argString = chromiumArgv.find(a => a.startsWith(argStart));
+  if (argString) {
+    argString = argString.substring(argStart.length);
+    const electronArgv = JSON.parse(argString);
+    const argv = stripElectronArgv(electronArgv);
+    const title = `${appName} #${++appCount}`;
+    createBrowserWindow(false, { title }, argv, wdir);
+  }
 }
 
 function createDesktopWindow() {
   const wdir = app.getPath('home');
-  createBrowserWindow({ title: `${appName} #${++appCount}` }, [], wdir);
+  const title = `${appName} #${++appCount}`;
+  createBrowserWindow(false, { title }, [], wdir);
 }
 
 // --------------------------------------------------------------------------
@@ -535,7 +546,8 @@ let PreferenceWindow: BrowserWindow | undefined;
 
 function showSettingsWindow() {
   if (!PreferenceWindow)
-    PreferenceWindow = createBrowserWindow({
+    PreferenceWindow = createBrowserWindow(
+      false, {
       title: `${appName} Settings`,
       width: 256,
       height: 248,
@@ -582,6 +594,11 @@ ipcMain.on('dome.app.paths', (event) => {
 /** Starts the main process. */
 export function start() {
 
+  // Workaround to recover the original commandline of a second instance
+  // after chromium messes with the argument order.
+  // See https://github.com/electron/electron/issues/20322 for more details.
+  app.commandLine.appendSwitch("second-instance", JSON.stringify(process.argv));
+
   // Ensures second instance triggers the main one
   if (!app.requestSingleInstanceLock()) app.quit();
 
@@ -614,7 +631,7 @@ export function start() {
 // --------------------------------------------------------------------------
 
 /**
-    Define a custom main window menu.
+   Define a custom main window menu.
 */
 export function addMenu(label: string) {
   Menubar.addMenu(label);
@@ -634,9 +651,21 @@ export function setMenuItem(spec: Menubar.CustomMenuItem) {
   Menubar.setMenuItem(spec);
 }
 
-ipcMain.on('dome.ipc.menu.addmenu', (_evt, label) => addMenu(label));
-ipcMain.on('dome.ipc.menu.addmenuitem', (_evt, spec) => addMenuItem(spec));
-ipcMain.on('dome.ipc.menu.setmenuitem', (_evt, spec) => setMenuItem(spec));
+function isPrimary(evt: IpcMainEvent): boolean {
+  const h = WindowHandles.get(evt.sender.id);
+  return h ? h.primary : false;
+}
+
+ipcMain.on('dome.ipc.menu.addmenu', (evt, label) =>
+  isPrimary(evt) && Menubar.addMenu(label)
+);
+ipcMain.on('dome.ipc.menu.addmenuitem', (evt, spec) =>
+  isPrimary(evt) && Menubar.addMenuItem(spec)
+);
+ipcMain.on('dome.ipc.menu.setmenuitem', (_evt, spec) =>
+  // Always update menu items
+  Menubar.setMenuItem(spec)
+);
 
 // --------------------------------------------------------------------------
 // --- Dialogs Management
diff --git a/ivette/src/dome/main/menubar.ts b/ivette/src/dome/main/menubar.ts
index 52f4212c96f0e09a6e8a85866acf4ac1b1e5b16e..417631484b83afa11b2f56767782084e7ddf15a6 100644
--- a/ivette/src/dome/main/menubar.ts
+++ b/ivette/src/dome/main/menubar.ts
@@ -273,12 +273,15 @@ const windowMenuItemsMacos: MenuSpec = windowMenuItemsLinux.concat([
 // --- Help Menu Items
 // --------------------------------------------------------------------------
 
+let learnMoreLink = '';
+ipcMain.handle('dome.ipc.updateLearnMore', (_, link) => {
+  if (typeof link === 'string') learnMoreLink = link;
+});
+
 const helpMenuItems: MenuSpec = [
   {
     label: 'Learn More',
-    click() {
-      shell.openExternal('http://electron.atom.io');
-    },
+    click() { shell.openExternal(learnMoreLink); },
   },
 ];
 
diff --git a/ivette/src/dome/misc/system.ts b/ivette/src/dome/misc/system.ts
index 7c726722647b4feefedce7e891b83eeb2d54b824..58c8badba810a5a07a0cabb44b89caa0b66600a8 100644
--- a/ivette/src/dome/misc/system.ts
+++ b/ivette/src/dome/misc/system.ts
@@ -537,21 +537,23 @@ export function spawn(
   return new Promise((result, reject) => {
 
     const cwd = options ? options.cwd : undefined;
-    const env = options && options.env ? ({ ...process.env, ...options.env }) : undefined;
+    const opt = options ? options.env : undefined;
+    const env = // Forces 'PWD' env. variable for executing a non-shell process
+      (cwd || opt) ? { ...process.env, ...opt, 'PWD': cwd } : undefined;
     const stdin = stdSpec(options && options.stdin, false);
     const stdout = stdSpec(options && options.stdout, true);
     const stderr = stdSpec(options && options.stderr, true);
     const stdio = [stdin.io, stdout.io, stderr.io];
-    const opt: Exec.ForkOptions = { cwd, env, stdio };
-    const fork = options && options.fork;
+    const fopt: Exec.ForkOptions = { cwd, env, stdio };
+    const forked = options && options.fork;
     const cargs = args ? args.slice() : [];
     let child: Exec.ChildProcess | undefined;
 
-    if (fork) {
+    if (forked) {
       stdio.push('ipc');
-      child = Exec.fork(command, cargs, opt);
+      child = Exec.fork(command, cargs, fopt);
     } else {
-      child = Exec.spawn(command, cargs, opt);
+      child = Exec.spawn(command, cargs, fopt);
     }
 
     if (!child) reject(new Error(
diff --git a/ivette/src/dome/renderer/dome.tsx b/ivette/src/dome/renderer/dome.tsx
index 51eb64b19f4f5f77a58e6cb45c3ecacd0fcfc471..7fb76ae8e9dd3ad5d000e8eb6012a90cf78c1737 100644
--- a/ivette/src/dome/renderer/dome.tsx
+++ b/ivette/src/dome/renderer/dome.tsx
@@ -364,13 +364,15 @@ const customItemCallbacks = new Map<string, callback>();
 /**
    Create a new custom menu in the menu bar.
 
-   This function can be triggered at any time, and will eventually trigger
-   an update of the whole application menubar.
+
+   This function shall be called statically, although calls from _secondary_
+   windows would be ignored. It is also possible to call this function from the
+   main process.
 
    It is also possible to call this function from the main process.
 
    @param label - the menu title (shall be unique)
-*/
+ */
 export function addMenu(label: string) {
   ipcRenderer.send('dome.ipc.menu.addmenu', label);
 }
@@ -399,8 +401,7 @@ export interface MenuItemProps {
   onClick?: () => void;
 }
 
-/**
-   Inserts a new custom item in a menu.
+/** Inserts a new custom item in a menu.
 
    The menu can be modified later with [[setMenuItem]].
 
@@ -408,15 +409,15 @@ export interface MenuItemProps {
    event on all application windows.  The item callback, if any, is invoked only
    in the process that specify it.
 
-   Key short cuts shall be specified with the following codes:
-   - `"Cmd+<Key>"` for command (MacOS) or control (Linux) key
-   - `"Alt+<Key>"` for command+option (MacOS) or alt (Linux) key
-   - `"Meta+<Key>"` for command+shift (MacOS) or control+alt (Linux) key
+   Key short cuts shall be specified with the following codes: - `"Cmd+<Key>"`
+   for command (MacOS) or control (Linux) key - `"Alt+<Key>"` for command+option
+   (MacOS) or alt (Linux) key - `"Meta+<Key>"` for command+shift (MacOS) or
+   control+alt (Linux) key
 
-   This function can be triggered at any time, and will eventually trigger
-   an update of the complete application menubar.
-   It is also possible to call this function from the main process.
-*/
+   This function shall be called statically, although calls from _secondary_
+   windows would be ignored. It is also possible to call this function from the
+   main process.
+ */
 export function addMenuItem(props: MenuItemProps) {
   if (!props.id && props.type !== 'separator') {
     // eslint-disable-next-line no-console
@@ -444,10 +445,6 @@ export interface MenuItemOptions {
    You shall specify `null` to remove the previously registered callback
    (`undefined` callback is ignored).
 
-   This function can be triggered at any time, and will possibly trigger
-   an update of the application menubar if the properties
-   can not be changed dynamically in Electron.
-
    It is also possible to call this function from the main process.
  */
 export function setMenuItem(options: MenuItemOptions) {
@@ -601,7 +598,7 @@ export function useCache<K, V>(r: (k: K) => V, s?: Serialize<K>): (k: K) => V {
     const v = r(k);
     cache.set(id, v);
     return v;
-  }, [ cache, r,  serialize ]);
+  }, [cache, r, serialize]);
   return get;
 }
 
diff --git a/ivette/src/frama-c/index.tsx b/ivette/src/frama-c/index.tsx
index 171fbe23f912103aca11d18cb28e0add4fb7d2e0..af10318d6712b108301dfc243982bd083a431163 100644
--- a/ivette/src/frama-c/index.tsx
+++ b/ivette/src/frama-c/index.tsx
@@ -116,6 +116,7 @@ Ivette.registerView({
   id: 'source',
   rank: 1,
   label: 'Source Code',
+  defaultView: true,
   layout: [
     ['frama-c.astview', 'frama-c.sourcecode'],
     'frama-c.astinfo',
@@ -123,8 +124,18 @@ Ivette.registerView({
 });
 
 Ivette.registerView({
-  id: 'pivot-table',
+  id: 'properties',
   rank: 2,
+  label: 'Properties',
+  layout: [
+    ['frama-c.astview', 'frama-c.sourcecode'],
+    'frama-c.properties',
+  ],
+});
+
+Ivette.registerView({
+  id: 'pivot-table',
+  rank: 6,
   label: 'Pivot Table',
   layout: [
     ['frama-c.pivottable'],
diff --git a/ivette/src/frama-c/menu.ts b/ivette/src/frama-c/menu.ts
index f20ed94c60fce333d5aa9c23d85c8f80d90cd604..1b1af614c3b4b3149ccc614d5534696201008436 100644
--- a/ivette/src/frama-c/menu.ts
+++ b/ivette/src/frama-c/menu.ts
@@ -31,6 +31,7 @@ import * as Services from 'frama-c/kernel/api/services';
 import * as Ast from 'frama-c/kernel/api/ast';
 import * as Status from 'frama-c/kernel/Status';
 import * as States from 'frama-c/states';
+import { ipcRenderer } from 'electron';
 
 const cFilter = {
   name: 'C source files',
@@ -134,6 +135,7 @@ async function saveSession(): Promise<void> {
 }
 
 export function init(): void {
+  ipcRenderer.invoke('dome.ipc.updateLearnMore', 'https://frama-c.com/');
   Dome.addMenuItem({
     menu: 'File',
     label: 'Set source files…',
diff --git a/ivette/src/frama-c/plugins/dive/index.tsx b/ivette/src/frama-c/plugins/dive/index.tsx
index ddc964318ce75e33a70bbb39883adf23e2fa9805..0f89148e3e6c418c7cbf5a00f5f61735b873525c 100644
--- a/ivette/src/frama-c/plugins/dive/index.tsx
+++ b/ivette/src/frama-c/plugins/dive/index.tsx
@@ -656,10 +656,10 @@ Ivette.registerComponent({
 Ivette.registerView({
   id: 'dive',
   label: 'Dive Dataflow',
-  rank: 2,
+  rank: 5,
   layout: [
-    ['frama-c.astview', 'frama-c.plugins.dive', 'frama-c.locations'],
-    ['frama-c.properties', 'frama-c.console'],
+    ['frama-c.astview', 'frama-c.plugins.dive'],
+    ['frama-c.properties', 'frama-c.locations'],
   ],
 });
 
diff --git a/ivette/src/frama-c/plugins/eva/index.tsx b/ivette/src/frama-c/plugins/eva/index.tsx
index 2df245b2abe7bf7506b891b4be2c375a894f0e8a..8aeb22bb817b5b6b873944d8b6f18ab43d2c4dff 100644
--- a/ivette/src/frama-c/plugins/eva/index.tsx
+++ b/ivette/src/frama-c/plugins/eva/index.tsx
@@ -36,7 +36,7 @@ import './style.css';
 
 Ivette.registerView({
   id: 'summary',
-  rank: 1,
+  rank: 3,
   label: 'Eva Summary',
   layout: [
     ['frama-c.plugins.eva_summary', 'frama-c.plugins.eva_coverage'],
@@ -46,11 +46,11 @@ Ivette.registerView({
 
 Ivette.registerView({
   id: 'values',
-  rank: 1,
+  rank: 4,
   label: 'Eva Values',
   layout: [
-    ['frama-c.astview', 'frama-c.plugins.values'],
-    'frama-c.properties',
+    ['frama-c.astview', 'frama-c.astinfo'],
+    'frama-c.plugins.values',
   ],
 });
 
diff --git a/ivette/src/frama-c/plugins/eva/valuetable.tsx b/ivette/src/frama-c/plugins/eva/valuetable.tsx
index 00c3b7738bac9049344c90b3addc42ce9b28d34f..5f0aef4d0a9f6fd0e24d1e82978ccbda8e8b9ef6 100644
--- a/ivette/src/frama-c/plugins/eva/valuetable.tsx
+++ b/ivette/src/frama-c/plugins/eva/valuetable.tsx
@@ -914,6 +914,15 @@ interface EvaluationModeProps {
   setLocPin: (loc: Location, pin: boolean) => void;
 }
 
+Dome.addMenuItem({
+  menu: 'Edit',
+  id: 'EvaluateMenu',
+  type: 'normal',
+  label: 'Evaluate',
+  key: 'Cmd+E',
+  onClick: () => evaluateEvent.emit(),
+});
+
 function useEvaluationMode(props: EvaluationModeProps): void {
   const { computationState, selection, setLocPin } = props;
   const handleError = (): void => { return; };
@@ -941,14 +950,6 @@ function useEvaluationMode(props: EvaluationModeProps): void {
     Toolbars.RegisterMode.emit(evalMode);
     return () => Toolbars.UnregisterMode.emit(evalMode);
   });
-  Dome.addMenuItem({
-    menu: 'Edit',
-    id: 'EvaluateMenu',
-    type: 'normal',
-    label: 'Evaluate',
-    key: 'Cmd+E',
-    onClick: () => evaluateEvent.emit(),
-  });
   React.useEffect(() => {
     Dome.setMenuItem({ id: 'EvaluateMenu', enabled: true });
     return () => Dome.setMenuItem({ id: 'EvaluateMenu', enabled: false });
diff --git a/ivette/src/frama-c/server.ts b/ivette/src/frama-c/server.ts
index a621798462c09a889bdb455af074f4205445c2bb..e4d80bb036136405cb392510e5ec90167c29b4b3 100644
--- a/ivette/src/frama-c/server.ts
+++ b/ivette/src/frama-c/server.ts
@@ -379,7 +379,7 @@ export interface Configuration {
   /** Process environment variables (default: `undefined`). */
   env?: { [VAR: string]: string };
   /** Working directory (default: current). */
-  cwd?: string;
+  working?: string;
   /** Server command (default: `frama-c`). */
   command?: string;
   /** Additional server arguments (default: empty). */
@@ -423,7 +423,7 @@ export function getConfig(): Configuration {
 async function _launch(): Promise<void> {
   let {
     env,
-    cwd,
+    working,
     command = 'frama-c',
     params,
     sockaddr,
@@ -453,12 +453,12 @@ async function _launch(): Promise<void> {
     const pid = Dome.getPID();
     sockaddr = System.join(tmp, `ivette.frama-c.${pid}.io`);
   }
-  if (!cwd) cwd = System.getWorkingDir();
-  logout = logout && System.join(cwd, logout);
-  logerr = logerr && System.join(cwd, logerr);
+  if (!working) working = System.getWorkingDir();
+  logout = logout && System.join(working, logout);
+  logerr = logerr && System.join(working, logerr);
   params = client.commandLine(sockaddr, params);
   const options = {
-    cwd,
+    cwd: working,
     stdout: { path: logout, pipe: true },
     stderr: { path: logerr, pipe: true },
     env,
diff --git a/ivette/src/renderer/Controller.tsx b/ivette/src/renderer/Controller.tsx
index 996c3c0878b8d1b8ae46831698fb6e446c77c0ce..50ab35b2bf0da992b523cf338d656d6530f62b20 100644
--- a/ivette/src/renderer/Controller.tsx
+++ b/ivette/src/renderer/Controller.tsx
@@ -20,8 +20,6 @@
 /*                                                                          */
 /* ************************************************************************ */
 
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
-
 // --------------------------------------------------------------------------
 // --- Server Controller
 // --------------------------------------------------------------------------
@@ -31,13 +29,14 @@ import * as Dome from 'dome';
 import * as Json from 'dome/data/json';
 import * as Settings from 'dome/data/settings';
 import * as Preferences from 'ivette/prefs';
-
 import * as Toolbars from 'dome/frame/toolbars';
 import { IconButton } from 'dome/controls/buttons';
 import { LED, LEDstatus } from 'dome/controls/displays';
 import { Label, Code } from 'dome/controls/labels';
 import { RichTextBuffer } from 'dome/text/buffers';
 import { Text } from 'dome/text/editors';
+import { resolve } from 'dome/system';
+
 import * as Ivette from 'ivette';
 import * as Server from 'frama-c/server';
 
@@ -55,8 +54,8 @@ const unquote = (s: string): string =>
 
 function dumpServerConfig(sc: Server.Configuration): string {
   let buffer = '';
-  const { cwd, command, sockaddr, params } = sc;
-  if (cwd) buffer += `--cwd ${quote(cwd)}\n`;
+  const { working, command, sockaddr, params } = sc;
+  if (working) buffer += `--working ${quote(working)}\n`;
   if (command) buffer += `--command ${quote(command)}\n`;
   if (sockaddr) buffer += `--socket ${sockaddr}\n`;
   if (params) {
@@ -74,22 +73,26 @@ function dumpServerConfig(sc: Server.Configuration): string {
   return buffer;
 }
 
-function buildServerConfig(argv: string[], cwd?: string) {
+function buildServerConfig(argv: string[], cwd?: string): Server.Configuration {
   const params = [];
   let command;
   let sockaddr;
-  let cwdir = cwd ? unquote(cwd) : undefined;
+  let working = cwd ? unquote(cwd) : undefined;
   for (let k = 0; k < (argv ? argv.length : 0); k++) {
     const v = argv[k];
     switch (v) {
-      case '--cwd':
+      case '-C':
+      case '--working':
+      case '--cwd': // Deprecated
         k += 1;
-        cwdir = unquote(argv[k]);
+        working = resolve(unquote(argv[k]));
         break;
+      case '-B':
       case '--command':
         k += 1;
-        command = unquote(argv[k]);
+        command = resolve(unquote(argv[k]));
         break;
+      case '-U':
       case '--socket':
         k += 1;
         sockaddr = argv[k];
@@ -99,18 +102,37 @@ function buildServerConfig(argv: string[], cwd?: string) {
     }
   }
   return {
-    cwd: cwdir,
+    working,
     command,
     sockaddr,
     params,
   };
 }
 
-function buildServerCommand(cmd: string) {
+function buildServerCommand(cmd: string): Server.Configuration {
   return buildServerConfig(cmd.trim().split(/[ \t\n]+/));
 }
 
-function insertConfig(hs: string[], cfg: Server.Configuration) {
+/* -------------------------------------------------------------------------- */
+/* --- History Management                                                 --- */
+/* -------------------------------------------------------------------------- */
+
+const historySetting = 'Controller.history';
+const historyDecoder = Json.jList(Json.jString);
+
+function getHistory(): string[] {
+  return Settings.getLocalStorage(historySetting, historyDecoder, []);
+}
+
+function setHistory(hs: string[]): void {
+  Settings.setLocalStorage(historySetting, hs);
+}
+
+function useHistory(): [string[], ((hs: string[]) => void)] {
+  return Settings.useLocalStorage(historySetting, historyDecoder, []);
+}
+
+function insertConfig(hs: string[], cfg: Server.Configuration): string[] {
   const cmd = dumpServerConfig(cfg).trim();
   const newhs =
     hs.map((h) => h.trim())
@@ -127,9 +149,7 @@ function insertConfig(hs: string[], cfg: Server.Configuration) {
 let reloadCommand: string | undefined;
 
 Dome.reload.on(() => {
-  const [lastCmd] = Settings.getLocalStorage(
-    'Controller.history', Json.jList(Json.jString), [],
-  );
+  const [lastCmd] = getHistory();
   reloadCommand = lastCmd;
 });
 
@@ -138,7 +158,13 @@ Dome.onCommand((argv: string[], cwd: string) => {
   if (reloadCommand) {
     cfg = buildServerCommand(reloadCommand);
   } else {
-    cfg = buildServerConfig(argv, cwd);
+    const hs = getHistory();
+    if (argv.find((v) => v === '--reload' || v === '-R')) {
+      cfg = buildServerCommand(hs[0]);
+    } else {
+      cfg = buildServerConfig(argv, cwd);
+      setHistory(insertConfig(hs, cfg));
+    }
   }
   Server.setConfig(cfg);
   Server.start();
@@ -148,7 +174,7 @@ Dome.onCommand((argv: string[], cwd: string) => {
 // --- Server Control
 // --------------------------------------------------------------------------
 
-export const Control = () => {
+export const Control = (): JSX.Element => {
   const status = Server.useStatus();
 
   let play = { enabled: false, onClick: () => { /* do nothing */ } };
@@ -199,17 +225,15 @@ export const Control = () => {
 
 const editor = new RichTextBuffer();
 
-const RenderConsole = () => {
+const RenderConsole = (): JSX.Element => {
   const scratch = React.useRef([] as string[]);
   const [cursor, setCursor] = React.useState(-1);
   const [isEmpty, setEmpty] = React.useState(true);
   const [noTrash, setNoTrash] = React.useState(true);
-  const [history, setHistory] = Settings.useLocalStorage(
-    'Controller.history', Json.jList(Json.jString), [],
-  );
+  const [history, setHistory] = useHistory();
 
   React.useEffect(() => {
-    const callback = () => {
+    const callback = (): void => {
       const cmd = editor.getValue().trim();
       setEmpty(cmd === '');
       setNoTrash(noTrash && cmd === history[0]);
@@ -223,7 +247,7 @@ const RenderConsole = () => {
     Server.buffer.setMaxlines(maxLines);
   });
 
-  const doReload = () => {
+  const doReload = (): void => {
     const cfg = Server.getConfig();
     const hst = insertConfig(history, cfg);
     const cmd = hst[0];
@@ -234,7 +258,7 @@ const RenderConsole = () => {
     setCursor(0);
   };
 
-  const doSwitch = () => {
+  const doSwitch = (): void => {
     if (cursor < 0) doReload();
     else {
       editor.clear();
@@ -243,7 +267,7 @@ const RenderConsole = () => {
     }
   };
 
-  const doExec = () => {
+  const doExec = (): void => {
     const cfg = buildServerCommand(editor.getValue());
     const hst = insertConfig(history, cfg);
     setHistory(hst);
@@ -253,9 +277,9 @@ const RenderConsole = () => {
     Server.restart();
   };
 
-  const doMove = (target: number) => {
+  const doMove = (target: number): (undefined | (() => void)) => {
     if (0 <= target && target < history.length && target !== cursor)
-      return () => {
+      return (): void => {
         const cmd = editor.getValue();
         const pad = scratch.current;
         pad[cursor] = cmd;
@@ -267,7 +291,7 @@ const RenderConsole = () => {
     return undefined;
   };
 
-  const doRemove = () => {
+  const doRemove = (): void => {
     const n = history.length;
     if (n <= 1) doReload();
     else {
@@ -361,7 +385,6 @@ Ivette.registerView({
   id: 'console',
   rank: -1,
   label: 'Console',
-  defaultView: true,
   layout: 'frama-c.console',
 });
 
@@ -369,7 +392,7 @@ Ivette.registerView({
 // --- Status
 // --------------------------------------------------------------------------
 
-export const Status = () => {
+export const Status = (): JSX.Element => {
   const status = Server.useStatus();
   const pending = Server.getPending();
   let led: LEDstatus = 'inactive';
@@ -434,7 +457,7 @@ export const Status = () => {
 // --- Server Stats
 // --------------------------------------------------------------------------
 
-export const Stats = () => {
+export const Stats = (): (null | JSX.Element) => {
   Server.useStatus();
   const pending = Server.getPending();
   return pending > 0 ? <Code>{pending} rq.</Code> : null;
diff --git a/nix/default.nix b/nix/default.nix
index 5bd4d181773a37483950d211d9759ab67b7cff4b..d9f8349f5d7db3034a05fff70433d66e6ed7a9e5 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -9,9 +9,9 @@ let mydir = builtins.getEnv("PWD");
     };
      opamPackages =
       [ "ocamlfind" "zarith" "ocamlgraph" "yojson" "zmq"
-        "ppx_deriving" "ppx_deriving_yojson"
+        "ppx_import" "ppx_deriving" "ppx_deriving_yojson"
         "coq=8.13.0" "alt-ergo=2.2.0"
-        "why3=1.4.0" "why3-coq=1.4.0"
+        "why3=1.5.0" "why3-coq=1.5.0"
         "menhir=20211012"
         "easy-format=1.3.2"
       ];
diff --git a/opam/opam b/opam/opam
index b7c3f147d041645e31f22f9a08626b5a440b6ff6..4c6a1fa77fe2003a2cc26bbf6cd9510e17119e2f 100644
--- a/opam/opam
+++ b/opam/opam
@@ -120,7 +120,7 @@ depends: [
   "ocaml" { >= "4.08.1" }
   "ocamlfind" # needed beyond build stage, used by -load-module
   "ocamlgraph" { >= "1.8.8" }
-  "why3" { >= "1.4.0" & < "1.5~" }
+  "why3" { >= "1.5.0" }
   "yojson" {>= "1.6.0"}
   "zarith" {>= "1.5"}
 ]
diff --git a/reference-configuration.md b/reference-configuration.md
index f7e6d6ae2f052e2597e66c66e354803f956b4a7b..df8c90259b25650348bcf916304d871158e1ef1b 100644
--- a/reference-configuration.md
+++ b/reference-configuration.md
@@ -7,17 +7,17 @@ support libraries (notably gtksourceview). lablgtk3 should be preferred.
 
 - OCaml 4.08.1
 - alt-ergo.2.2.0 (for wp, optional)
-- apron.v0.9.12 (for eva, optional)
+- apron.v0.9.13 (for eva, optional)
 - coq.8.13.0 (for wp, optional)
 - lablgtk3.3.1.1 + lablgtk3-sourceview3.3.1.1 | lablgtk.2.18.11
-- mlgmpidl.1.2.12 (for eva, optional)
+- mlgmpidl.1.2.14 (for eva, optional)
 - ocamlfind.1.8.1
 - ocamlgraph.1.8.8
-- ppx_deriving_yojson.3.5.2 (for mdr, optional)
-- why3.1.4.0
+- ppx_deriving_yojson.3.6.1 (for mdr, optional)
+- ppx_import.1.9.1
+- why3.1.5.0
 - yojson.1.7.0
-- zarith.1.9.1
-- zmq.5.1.3 (for server, optional)
+- zarith.1.12
 
 [^gcc-10]: As mentioned in this [OCaml PR](https://github.com/ocaml/ocaml/issues/9144)
 gcc 10 changed its default linking conventions to make them more stringent,
diff --git a/share/Makefile.config.in b/share/Makefile.config.in
index 505153e2f1f6a9204002f5b82a59d777febce816..c6d808f04e906cb195778fae19e2a20e7a9070a1 100644
--- a/share/Makefile.config.in
+++ b/share/Makefile.config.in
@@ -199,7 +199,8 @@ ENABLE_EVA                      ?=@ENABLE_EVA@
 
 #bytes is part of the stdlib, but is used as a transitional package.
 LIBRARY_NAMES := \
-  findlib ocamlgraph unix str dynlink bytes zarith yojson bigarray
+  findlib ocamlgraph unix str dynlink bytes zarith yojson bigarray \
+  ppx_import ppx_deriving.eq
 
 ifeq ($(HAS_LANDMARKS),yes)
 LIBRARY_NAMES += landmarks landmarks-ppx
diff --git a/src/kernel_services/ast_queries/ast_diff.ml b/src/kernel_services/ast_queries/ast_diff.ml
new file mode 100644
index 0000000000000000000000000000000000000000..c7551b0733be41b396abf71790e09045a5c16ef7
--- /dev/null
+++ b/src/kernel_services/ast_queries/ast_diff.ml
@@ -0,0 +1,1622 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+
+module Orig_project =
+  State_builder.Option_ref(Project.Datatype)(
+  struct
+    let name = "Ast_diff.OrigProject"
+    let dependencies = []
+  end)
+
+type 'a correspondance =
+  [ `Same of 'a (** symbol with identical definition has been found. *)
+  | `Not_present (** no correspondance *)
+  ]
+
+module Correspondance_input =
+struct
+  type 'a t = 'a correspondance
+  let name a = Type.name a ^ " correspondance"
+  let module_name = "Correspondance"
+  let structural_descr _ = Structural_descr.t_abstract
+  let reprs x = [ `Not_present; `Same x]
+  let mk_equal eq x y =
+    match x,y with
+    | `Same x, `Same y -> eq x y
+    | `Not_present, `Not_present -> true
+    | `Same _, `Not_present
+    | `Not_present, `Same _ -> false
+  let mk_compare cmp x y =
+    match x,y with
+    | `Not_present, `Not_present -> 0
+    | `Not_present, `Same _ -> -1
+    | `Same x, `Same y -> cmp x y
+    | `Same _, `Not_present -> 1
+  let mk_hash h = function
+    | `Same x -> 117 * h x
+    | `Not_present -> 43
+  let map f = function
+    | `Same x -> `Same (f x)
+    | `Not_present -> `Not_present
+  let mk_internal_pretty_code pp prec fmt = function
+    | `Not_present -> Format.pp_print_string fmt "`Not_present"
+    | `Same x ->
+      let pp fmt = Format.fprintf fmt "`Same %a" (pp Type.Call) x in
+      Type.par prec Call fmt pp
+  let mk_pretty pp fmt = function
+    | `Not_present -> Format.pp_print_string fmt "N/A"
+    | `Same x -> Format.fprintf fmt " => %a" pp x
+  let mk_varname v = function
+    | `Not_present -> "x"
+    | `Same x -> v x ^ "_c"
+  let mk_mem_project mem query = function
+    | `Not_present -> false
+    | `Same x -> mem query x
+end
+
+module Correspondance = Datatype.Polymorphic(Correspondance_input)
+
+(* for kernel function, we are a bit more precise than a yes/no answer.
+   More precisely, we check whether a function has the same spec,
+   the same body, and whether its callees have changed (provided
+   the body itself is identical, otherwise, there's no point in checking
+   the callees.
+*)
+type partial_correspondance =
+  [ `Spec_changed (* body and callees haven't changed *)
+  | `Body_changed (* spec hasn't changed *)
+  | `Callees_changed (* spec and body haven't changed *)
+  | `Callees_spec_changed (* body hasn't changed *)
+  ]
+
+type body_correspondance =
+  [ `Body_changed
+  | `Callees_changed
+  | `Same_body
+  ]
+
+let (<=>) res (cmp,x,y) = if res <> 0 then res else cmp x y
+
+let compare_pc pc1 pc2 =
+  match pc1, pc2 with
+  | `Spec_changed, `Spec_changed -> 0
+  | `Spec_changed, _ -> -1
+  | _, `Spec_changed -> 1
+  | `Body_changed, `Body_changed -> 0
+  | `Body_changed, _ -> -1
+  | _, `Body_changed -> 1
+  | `Callees_changed, `Callees_changed -> 0
+  | `Callees_changed, _ -> -1
+  | _, `Callees_changed -> 1
+  | `Callees_spec_changed, `Callees_spec_changed -> 0
+
+let string_of_pc = function
+  | `Spec_changed -> "Spec_changed"
+  | `Body_changed -> "Body_changed"
+  | `Callees_changed -> "Callees_changed"
+  | `Callees_spec_changed -> "Callees_spec_changed"
+
+let pretty_pc fmt =
+  let open Format in
+  function
+  | `Spec_changed -> pp_print_string fmt "(spec changed)"
+  | `Body_changed -> pp_print_string fmt "(body changed)"
+  | `Callees_changed -> pp_print_string fmt "(callees changed)"
+  | `Callees_spec_changed -> pp_print_string fmt "(callees and spec changed)"
+
+type 'a code_correspondance =
+  [ 'a correspondance
+  | `Partial of 'a * partial_correspondance
+  ]
+
+module Code_correspondance_input =
+struct
+  type 'a t = 'a code_correspondance
+  let name a = Type.name a ^ " code_correspondance"
+  let module_name = "Code_correspondance"
+  let structural_descr _ = Structural_descr.t_abstract
+  let reprs = Correspondance_input.reprs
+  let mk_equal eq x y =
+    match x,y with
+    | `Partial(x,pc), `Partial(x',pc') -> eq x x' && (compare_pc pc pc' = 0)
+    | `Partial _, _ | _, `Partial _ -> false
+    | (#correspondance as c), (#correspondance as c') ->
+      Correspondance_input.mk_equal eq c c'
+
+  let mk_compare cmp x y =
+    match x,y with
+    | `Partial(x,pc), `Partial(x',pc') ->
+      cmp x x' <=> (compare_pc,pc,pc')
+    | `Partial _, `Same _ -> -1
+    | `Same _, `Partial _ -> 1
+    | `Partial _, `Not_present -> 1
+    | `Not_present, `Partial _ -> -1
+    | (#correspondance as c), (#correspondance as c') ->
+      Correspondance_input.mk_compare cmp c c'
+
+  let mk_hash hash = function
+    | `Partial (x,_) -> 57 * hash x
+    | #correspondance as x -> Correspondance_input.mk_hash hash x
+
+  let map f = function
+    | `Partial(x,pc) -> `Partial(f x,pc)
+    | (#correspondance as x) -> Correspondance_input.map f x
+
+  let mk_internal_pretty_code pp prec fmt = function
+    | `Partial (x,flags) ->
+      let pp fmt =
+        Format.fprintf fmt "`Partial (%a,%s)"
+          (pp Type.Call) x (string_of_pc flags)
+      in
+      Type.par prec Call fmt pp
+    | #correspondance as x ->
+      Correspondance_input.mk_internal_pretty_code pp prec fmt x
+
+  let mk_pretty pp fmt = function
+    | `Partial(x,flags) ->
+      Format.fprintf fmt "-> %a %a" pp x pretty_pc flags
+    | #correspondance as x -> Correspondance_input.mk_pretty pp fmt x
+
+  let mk_varname f = function
+    | `Partial (x,_) -> f x ^ "_pc"
+    | #correspondance as x -> Correspondance_input.mk_varname f x
+
+  let mk_mem_project f p = function
+    | `Partial (x,_) -> f p x
+    | #correspondance as x -> Correspondance_input.mk_mem_project f p x
+end
+
+module Code_correspondance = Datatype.Polymorphic(Code_correspondance_input)
+
+module Info(I: sig val name: string end) =
+  (struct
+    let name = "Ast_diff." ^ I.name
+    let dependencies = [ Ast.self; Orig_project.self ]
+    let size = 43
+  end)
+
+(* Map of symbols under analysis, in case of recursion.
+   Note that this can only happen with aggregate types, logic
+   types, and function (both C and ACSL). Other symbols must be
+   correctly ordered in a well-formed AST
+*)
+type is_same_env =
+  {
+    compinfo: compinfo Cil_datatype.Compinfo.Map.t;
+    kernel_function: kernel_function Kernel_function.Map.t;
+    local_vars: varinfo Cil_datatype.Varinfo.Map.t;
+    logic_info: logic_info Cil_datatype.Logic_info.Map.t;
+    logic_type_info: logic_type_info Cil_datatype.Logic_type_info.Map.t;
+    logic_local_vars: logic_var Cil_datatype.Logic_var.Map.t;
+    logic_type_vars: string Datatype.String.Map.t;
+    (* goto targets pairs are checked afterwards, so that forward gotos
+       do not interrupt the linear visit.
+       We thus collect them in the environment.
+    *)
+    goto_targets: (stmt * stmt) list;
+  }
+
+module type Correspondance_table = sig
+  include State_builder.Hashtbl
+  val pretty_data: Format.formatter -> data -> unit
+end
+
+module Build(H:Datatype.S_with_collections)(D:Datatype.S):
+  Correspondance_table with type key = H.t and type data = D.t =
+struct
+  include
+    State_builder.Hashtbl(H.Hashtbl)(D)
+      (Info(struct let name = "Ast_diff." ^ D.name end))
+  let pretty_data = D.pretty
+end
+
+module Build_correspondance(H:Datatype.S_with_collections) =
+  Build(H)(Correspondance.Make(H))
+
+module Build_code_correspondance(H:Datatype.S_with_collections) =
+  Build(H)(Code_correspondance.Make(H))
+
+module Varinfo = Build_correspondance(Cil_datatype.Varinfo)
+
+module Compinfo = Build_correspondance(Cil_datatype.Compinfo)
+
+module Enuminfo = Build_correspondance(Cil_datatype.Enuminfo)
+
+module Enumitem = Build_correspondance(Cil_datatype.Enumitem)
+
+module Typeinfo = Build_correspondance(Cil_datatype.Typeinfo)
+
+module Stmt = Build_code_correspondance(Cil_datatype.Stmt)
+
+module Logic_info = Build_correspondance(Cil_datatype.Logic_info)
+
+module Logic_type_info = Build_correspondance(Cil_datatype.Logic_type_info)
+
+module Logic_ctor_info = Build_correspondance(Cil_datatype.Logic_ctor_info)
+
+module Fieldinfo = Build_correspondance(Cil_datatype.Fieldinfo)
+
+module Model_info = Build_correspondance(Cil_datatype.Model_info)
+
+module Logic_var = Build_correspondance(Cil_datatype.Logic_var)
+
+module Kf = Kernel_function
+
+module Kernel_function = Build_code_correspondance(Kernel_function)
+
+module Fundec = Build_correspondance(Cil_datatype.Fundec)
+
+let make_correspondance candidate has_same_spec code_corres =
+  match has_same_spec, code_corres with
+  | false, `Body_changed -> `Not_present
+  | false, `Callees_changed ->
+    `Partial(candidate,`Callees_spec_changed)
+  | false, `Same_body ->
+    `Partial(candidate, `Spec_changed)
+  | true, `Same_body ->
+    `Same candidate
+  | true, ((`Body_changed|`Callees_changed) as c) ->
+    `Partial(candidate, c)
+
+let (&&>) (res,env) f =
+  match res with
+  | `Body_changed -> `Body_changed, env
+  | `Same_body -> f env
+  | `Callees_changed ->
+    let res', env = f env in
+    match res' with
+    | `Body_changed -> `Body_changed, env
+    | `Same_body | `Callees_changed -> `Callees_changed, env
+
+let (&&&) (res, env) f = if res then f env else false, env
+
+let empty_env =
+  { compinfo = Cil_datatype.Compinfo.Map.empty;
+    kernel_function = Kf.Map.empty;
+    local_vars = Cil_datatype.Varinfo.Map.empty;
+    logic_info = Cil_datatype.Logic_info.Map.empty;
+    logic_type_info = Cil_datatype.Logic_type_info.Map.empty;
+    logic_local_vars = Cil_datatype.Logic_var.Map.empty;
+    logic_type_vars = Datatype.String.Map.empty;
+    goto_targets = []
+  }
+
+let add_locals f f' env =
+  let add_one env v v' =
+    { env with local_vars = Cil_datatype.Varinfo.Map.add v v' env.local_vars }
+  in
+  List.fold_left2 add_one env f f'
+
+(* local static variables are in fact global. As soon as we have determined
+   that they have a correspondance, we add them to the global bindings *)
+let add_statics l l' =
+  let add_one v v' = Varinfo.add v (`Same v') in
+  List.iter2 add_one l l'
+
+let add_logic_vars p p' env =
+  let add_one env lv lv' =
+    { env with
+      logic_local_vars =
+        Cil_datatype.Logic_var.Map.add lv lv' env.logic_local_vars }
+  in
+  List.fold_left2 add_one env p p'
+
+let add_logic_info v v' env =
+  { env with logic_info = Cil_datatype.Logic_info.Map.add v v' env.logic_info }
+
+let logic_type_vars_env l l' env =
+  if List.length l = List.length l' then begin
+    let logic_type_vars =
+      List.fold_left2 (fun env s s' -> Datatype.String.Map.add s s' env)
+        env.logic_type_vars l l'
+    in
+    true, { env with logic_type_vars }
+  end else false, env
+
+let formals_correspondance f f' =
+  let add_one v v' = Varinfo.add v (`Same v') in
+  List.iter2 add_one f f'
+
+let logic_prms_correspondance p p' =
+  let add_one lv lv' =
+    Logic_var.add lv (`Same lv') in
+  List.iter2 add_one p p'
+
+(** TODO: use location info to detect potential renaming.
+    Requires some information about syntactic diff. *)
+let find_candidate_type ?loc:_loc ti =
+  if Globals.Types.mem_type Logic_typing.Typedef ti.tname then begin
+    match Globals.Types.global Logic_typing.Typedef ti.tname with
+    | GType(ti,_) -> Some ti
+    | g ->
+      Kernel.fatal
+        "Expected typeinfo instead of %a" Cil_datatype.Global.pretty g
+  end else None
+
+let find_candidate_compinfo ?loc:_loc ci =
+  let su = if ci.cstruct then Logic_typing.Struct else Logic_typing.Union in
+  if Globals.Types.mem_type su ci.cname then begin
+    match Globals.Types.find_type su ci.cname with
+    | TComp(ci', _) -> Some ci'
+    | t ->
+      Kernel.fatal
+        "Expected compinfo instead of %a"
+        Printer.pp_typ t
+  end else None
+
+let find_candidate_enuminfo ?loc:_loc ei =
+  if Globals.Types.mem_type Logic_typing.Enum ei.ename then begin
+    match Globals.Types.find_type Logic_typing.Enum ei.ename with
+    | TEnum(ei,_) -> Some ei
+    | t ->
+      Kernel.fatal
+        "Expected enuminfo instead of %a"
+        Printer.pp_typ t
+  end else None
+
+let find_candidate_varinfo ?loc:_loc vi where =
+  try
+    Some (Globals.Vars.find_from_astinfo vi.vname where)
+  with Not_found -> None
+
+let find_candidate_func ?loc:_loc kf =
+  try
+    Some (Globals.Functions.find_by_name (Kf.get_name kf))
+  with Not_found -> None
+
+let find_candidate_logic_type ?loc:_loc ti =
+  try
+    Some (Logic_env.find_logic_type ti.lt_name)
+  with Not_found -> None
+
+let is_same_opt f o o' env =
+  match o, o' with
+  | None, None -> true
+  | Some v, Some v' -> f v v' env
+  | _ -> false
+
+let is_same_opt_env f o o' env =
+  match o, o' with
+  | None, None -> true, env
+  | Some v, Some v' -> f v v' env
+  | _ -> false, env
+
+let is_same_pair f1 f2 (x1,x2) (y1,y2) env = f1 x1 y1 env && f2 x2 y2 env
+
+let rec is_same_list f l l' env =
+  match l, l' with
+  | [], [] -> true
+  | h::t, h'::t' -> f h h' env && is_same_list f t t' env
+  | _ -> false
+
+let rec is_same_list_env f l l' env =
+  match l, l' with
+  | [], [] -> true, env
+  | h::t, h'::t' -> f h h' env &&& is_same_list_env f t t'
+  | _ -> false, env
+
+let get_original_kf vi =
+  let selection = State_selection.of_list
+      [Kernel_function.self; Annotations.funspec_state; Globals.Functions.self]
+  in
+  Project.on ~selection (Orig_project.get()) Globals.Functions.get vi
+
+let check_goto_targets env =
+  let check_one (s,s') =
+    match Stmt.find s with
+    | `Not_present -> false
+    | `Same s'' | `Partial (s'',_) ->
+      (* From the goto point of view, what matters is that the targets
+         themselves have a correspondance. If they're e.g. calls to a
+         function that has itself changed, or blocks whose content has
+         changed, it has already been detected when comparing the targets,
+         and will be dealt with accordingly as the fundec level. *)
+      Cil_datatype.Stmt.equal s' s''
+    | exception Not_found -> false
+  in
+  if List.for_all check_one env.goto_targets then `Same_body, env
+  else `Body_changed, env
+
+let is_matching_fieldinfo fi fi' =
+  match Fieldinfo.find fi with
+  | `Not_present -> false
+  | `Same fi'' -> Cil_datatype.Fieldinfo.equal fi' fi''
+  | exception Not_found ->
+    Kernel.fatal "Unbound field %a in AST diff"
+      Cil_datatype.Fieldinfo.pretty fi
+
+let is_matching_model_info mf mf' =
+  match Model_info.find mf with
+  | `Not_present -> false
+  | `Same mf'' -> Cil_datatype.Model_info.equal mf' mf''
+  | exception Not_found ->
+    Kernel.fatal "Unbound model field %a in AST diff"
+      Cil_datatype.Model_info.pretty  mf
+
+let is_matching_logic_type_var a a' env =
+  match Datatype.String.Map.find_opt a env.logic_type_vars with
+  | None -> false
+  | Some a'' -> Datatype.String.equal a' a''
+
+module Unop = struct
+  type t = [%import: Cil_types.unop] [@@deriving eq]
+end
+
+module Binop = struct
+  type t = [%import: Cil_types.binop] [@@deriving eq]
+end
+
+module Ikind = struct
+  type t = [%import: Cil_types.ikind] [@@deriving eq]
+end
+
+module Fkind = struct
+  type t = [%import: Cil_types.fkind] [@@deriving eq]
+end
+
+module Predicate_kind = struct
+  type t = [%import: Cil_types.predicate_kind] [@@deriving eq]
+end
+
+module Logic_builtin_label = struct
+  type t = [%import: Cil_types.logic_builtin_label] [@@deriving eq]
+end
+
+module Relation = struct
+  type t = [%import: Cil_types.relation] [@@deriving eq]
+end
+
+module Termination_kind = struct
+  type t = [%import: Cil_types.termination_kind] [@@deriving eq]
+end
+
+let is_same_behavior_set l l' =
+  Datatype.String.Set.(equal (of_list l) (of_list l'))
+
+let are_same_cd_clauses l l' =
+  let module StringSetSet = Set.Make(Datatype.String.Set) in
+  let of_list l =
+    StringSetSet.of_list (List.map Datatype.String.Set.of_list l)
+  in
+  StringSetSet.equal (of_list l) (of_list l')
+
+let is_same_logic_label l l' _env =
+  match l, l' with
+  | StmtLabel s, StmtLabel s' ->
+    (* Contrarily to labels in goto statement, C labels in the logic can only
+       refer to previous statements thanks to ACSL typing rules. Hence, we can
+       directly check the Stmt table without resorting to deferred check by
+       updating env.goto_targets
+    *)
+    (match Stmt.find !s with
+     | `Not_present -> false
+     | `Same s'' | `Partial(s'',_) ->
+       Cil_datatype.Stmt.equal !s' s''
+     | exception Not_found -> false)
+  | FormalLabel s, FormalLabel s' -> Datatype.String.equal s s'
+  | BuiltinLabel l, BuiltinLabel l' -> Logic_builtin_label.equal l l'
+  | (StmtLabel _ | FormalLabel _ | BuiltinLabel _), _ -> false
+
+let rec is_same_predicate p p' env =
+  (* names are semantically irrelevant. *)
+  is_same_predicate_node p.pred_content p'.pred_content env
+
+and is_same_predicate_node p p' env =
+  match p, p' with
+  | Pfalse, Pfalse -> true
+  | Ptrue, Ptrue -> true
+  | Papp(p,labs,args), Papp(p',labs',args') ->
+    is_matching_logic_info p p' env &&
+    is_same_list is_same_logic_label labs labs' env &&
+    is_same_list is_same_term args args' env
+  | Pseparated t, Pseparated t' -> is_same_list is_same_term t t' env
+  | Prel (r,t1,t2), Prel(r',t1',t2') ->
+    Relation.equal r r' && is_same_term t1 t1' env && is_same_term t2 t2' env
+  | Pand(p1,p2), Pand(p1',p2')
+  | Por(p1,p2), Por(p1',p2')
+  | Pxor(p1,p2), Pxor(p1',p2')
+  | Pimplies(p1,p2), Pimplies(p1',p2')
+  | Piff(p1,p2), Piff(p1',p2') ->
+    is_same_predicate p1 p1' env && is_same_predicate p2 p2' env
+  | Pnot p, Pnot p' -> is_same_predicate p p' env
+  | Pif(t,p1,p2), Pif(t',p1',p2') ->
+    is_same_term t t' env &&
+    is_same_predicate p1 p1' env &&
+    is_same_predicate p2 p2' env
+  | Plet(v,p), Plet(v',p') ->
+    if is_same_logic_info v v' env then begin
+      let env = add_logic_info v v' env in
+      let env = add_logic_vars [v.l_var_info] [v'.l_var_info] env in
+      is_same_predicate p p' env
+    end else false
+  | Pforall(q,p), Pforall(q',p')
+  | Pexists(q,p), Pexists(q',p') ->
+    if is_same_list is_same_logic_var q q' env then begin
+      let env = add_logic_vars q q' env in
+      is_same_predicate p p' env
+    end else false
+  | Pat(p,l), Pat(p',l') ->
+    is_same_predicate p p' env && is_same_logic_label l l' env
+  | Pobject_pointer(l,t), Pobject_pointer(l',t')
+  | Pvalid_read(l,t), Pvalid_read(l',t')
+  | Pvalid(l,t), Pvalid(l',t')
+  | Pinitialized(l,t), Pinitialized(l',t')
+  | Pdangling(l,t), Pdangling(l',t')
+  | Pallocable(l,t), Pallocable(l',t')
+  | Pfreeable(l,t), Pfreeable(l',t') ->
+    is_same_logic_label l l' env && is_same_term t t' env
+  | Pfresh(l1,l2,p,s), Pfresh(l1',l2',p',s') ->
+    is_same_logic_label l1 l1' env &&
+    is_same_logic_label l2 l2' env &&
+    is_same_term p p' env &&
+    is_same_term s s' env
+  | Pvalid_function(t), Pvalid_function(t') -> is_same_term t t' env
+  | (Pfalse | Ptrue | Papp _ | Pseparated _ | Prel _ | Pand _ | Por _ | Pxor _
+    | Pimplies _ | Piff _ | Pnot _ | Pif _ | Plet _ | Pforall _ | Pexists _
+    | Pat _ | Pobject_pointer _ | Pvalid_read _ | Pvalid _ | Pinitialized _
+    | Pdangling _ | Pallocable _ | Pfreeable _ | Pfresh _
+    | Pvalid_function _), _ -> false
+
+and is_same_logic_constant c c' env =
+  match c,c' with
+  | LEnum ei, LEnum ei' ->
+    (match enumitem_correspondance ei env with
+     | `Same ei'' -> Cil_datatype.Enumitem.equal ei' ei''
+     | `Not_present -> false)
+  | LEnum _, _ | _, LEnum _ -> false
+  | (Integer _ | LStr _ | LWStr _ | LChr _ | LReal _), _ ->
+    Cil_datatype.Logic_constant.equal c c'
+
+and is_same_term t t' env =
+  is_same_term_node t.term_node t'.term_node env
+
+and is_same_term_node t t' env =
+  match t,t' with
+  | TConst c, TConst c' -> is_same_logic_constant c c' env
+  | TLval lv, TLval lv' -> is_same_term_lval lv lv' env
+  | TSizeOf t, TSizeOf t'
+  | TAlignOf t, TAlignOf t' -> is_same_type t t' env
+  | TSizeOfE t, TSizeOfE t'
+  | TAlignOfE t, TAlignOfE t' -> is_same_term t t' env
+  | TSizeOfStr s, TSizeOfStr s' -> String.length s = String.length s'
+  | TUnOp(op,t), TUnOp(op',t') -> Unop.equal op op' && is_same_term t t' env
+  | TBinOp(op,t1,t2), TBinOp(op',t1',t2') ->
+    Binop.equal op op' && is_same_term t1 t1' env && is_same_term t2 t2' env
+  | TCastE(typ,term), TCastE(typ',term') ->
+    is_same_type typ typ' env && is_same_term term term' env
+  | TAddrOf lv, TAddrOf lv'
+  | TStartOf lv, TStartOf lv' -> is_same_term_lval lv lv' env
+  | Tapp(f,labs,args), Tapp(f',labs',args') ->
+    is_matching_logic_info f f' env &&
+    is_same_list is_same_logic_label labs labs' env &&
+    is_same_list is_same_term args args' env
+  | Tlambda(q,t), Tlambda(q',t') ->
+    if is_same_list is_same_logic_var q q' env then begin
+      let env = add_logic_vars q q' env in
+      is_same_term t t' env
+    end else false
+  | TDataCons(c,args), TDataCons(c',args') ->
+    is_matching_logic_ctor c c' env &&
+    is_same_list is_same_term args args' env
+  | Tif(c,t1,t2), Tif(c',t1',t2') ->
+    is_same_term c c' env &&
+    is_same_term t1 t1' env &&
+    is_same_term t2 t2' env
+  | Tat(t,l), Tat(t',l') ->
+    is_same_term t t' env && is_same_logic_label l l' env
+  | Tbase_addr(l,t), Tbase_addr(l',t')
+  | Toffset(l,t), Toffset(l',t')
+  | Tblock_length(l,t), Tblock_length(l',t') ->
+    is_same_logic_label l l' env && is_same_term t t' env
+  | Tnull, Tnull -> true
+  | TLogic_coerce(typ,t), TLogic_coerce(typ',t') ->
+    is_same_logic_type typ typ' env && is_same_term t t' env
+  | TUpdate(a,o,v), TUpdate(a',o',v') ->
+    is_same_term a a' env &&
+    is_same_term_offset o o' env &&
+    is_same_term v v' env
+  | Ttypeof t, Ttypeof t' -> is_same_term t t' env
+  | Ttype t, Ttype t' -> is_same_type t t' env
+  | Tempty_set, Tempty_set -> true
+  | Tunion l, Tunion l'
+  | Tinter l, Tinter l' -> is_same_list is_same_term l l' env
+  | Tcomprehension(t,q,p), Tcomprehension(t',q',p') ->
+    if is_same_list is_same_logic_var q q' env then begin
+      let env = add_logic_vars q q' env in
+      is_same_term t t' env && is_same_opt is_same_predicate p p' env
+    end else false
+  | Trange(l,u), Trange(l',u') ->
+    is_same_opt is_same_term l l' env && is_same_opt is_same_term u u' env
+  | Tlet(v,t), Tlet(v',t') ->
+    if is_same_logic_info v v' env then begin
+      let env = add_logic_info v v' env in
+      let env = add_logic_vars [v.l_var_info] [v'.l_var_info] env in
+      is_same_term t t' env
+    end else false
+  | (TConst _ | TLval _ | TSizeOf _ | TSizeOfE _ | TSizeOfStr _ | TAlignOf _
+    | TAlignOfE _ |  TUnOp _ | TBinOp _ | TCastE _ | TAddrOf _ | TStartOf _
+    | Tapp _ | Tlambda _ | TDataCons _ | Tif _ | Tat _ | Tbase_addr _
+    | Toffset _ | Tblock_length _ | Tnull | TLogic_coerce _ | TUpdate _
+    | Ttypeof _ | Ttype _ | Tempty_set | Tunion _ | Tinter _ | Tcomprehension _
+    | Tlet _ | Trange _), _ -> false
+
+and is_same_term_lval (lh,lo) (lh',lo') env =
+  is_same_term_lhost lh lh' env && is_same_term_offset lo lo' env
+
+and is_same_term_lhost lh lh' env =
+  match lh, lh' with
+  | TVar lv, TVar lv' -> is_matching_logic_var lv lv' env
+  | TResult _, TResult _ -> true
+  | TMem p, TMem p' -> is_same_term p p' env
+  | (TVar _ | TResult _ | TMem _), _ -> false
+
+and is_matching_logic_var lv lv' env =
+  match lv.lv_origin, lv'.lv_origin with
+  | Some vi, Some vi' -> is_matching_varinfo vi vi' env
+  | None, None ->
+    (match Cil_datatype.Logic_var.Map.find_opt lv env.logic_local_vars with
+     | Some lv'' -> Cil_datatype.Logic_var.equal lv' lv''
+     | None ->
+       (match Logic_var.find lv with
+        | `Not_present -> false
+        | `Same lv'' -> Cil_datatype.Logic_var.equal lv' lv''
+        | exception Not_found ->
+          if lv.lv_name = "\\exit_status" && lv'.lv_name = "\\exit_status"
+          then begin Logic_var.add lv (`Same lv'); true end
+          else begin
+            match logic_var_correspondance lv env with
+            | None -> false
+            | Some lv'' -> Cil_datatype.Logic_var.equal lv' lv''
+          end))
+  | _ -> false
+
+and logic_var_correspondance lv env =
+  match find_candidate_logic_var lv env with
+  | None -> None
+  | Some lv' -> Logic_var.add lv (`Same lv'); Some lv'
+
+and is_same_term_offset lo lo' env =
+  match lo, lo' with
+  | TNoOffset, TNoOffset -> true
+  | TField(f,o), TField(f',o') ->
+    is_matching_fieldinfo f f' && is_same_term_offset o o' env
+  | TModel(f,o), TModel(f',o') ->
+    is_matching_model_info f f' && is_same_term_offset o o' env
+  | TIndex(i,o), TIndex(i',o') ->
+    is_same_term i i' env && is_same_term_offset o o' env
+  | (TNoOffset | TField _ | TModel _ | TIndex _), _ -> false
+
+and is_same_toplevel_predicate p p' env =
+  Predicate_kind.equal p.tp_kind p'.tp_kind &&
+  is_same_predicate p.tp_statement p'.tp_statement env
+
+and is_same_identified_predicate p p' env =
+  is_same_toplevel_predicate p.ip_content p'.ip_content env
+
+and is_same_identified_term t t' env =
+  is_same_term t.it_content t'.it_content env
+
+and is_same_post_cond (k,p) (k',p') env =
+  Termination_kind.equal k k' && is_same_identified_predicate p p' env
+
+and is_same_deps d d' env =
+  match d,d' with
+  | FromAny, FromAny -> true
+  | From l, From l' -> is_same_list is_same_identified_term l l' env
+  | (FromAny | From _), _ -> false
+
+and is_same_from (t,f) (t',f') env =
+  is_same_identified_term t t' env && is_same_deps f f' env
+
+and is_same_assigns a a' env =
+  match a,a' with
+  | WritesAny, WritesAny -> true
+  | Writes l, Writes l' -> is_same_list is_same_from l l' env
+  | (WritesAny | Writes _), _ -> false
+
+and is_same_allocation a a' env =
+  match a,a' with
+  | FreeAllocAny, FreeAllocAny -> true
+  | FreeAlloc(f,a), FreeAlloc(f',a') ->
+    is_same_list is_same_identified_term f f' env &&
+    is_same_list is_same_identified_term a a' env
+  | (FreeAllocAny | FreeAlloc _),_ -> false
+
+and is_same_behavior b b' env =
+  is_same_list is_same_identified_predicate b.b_requires b'.b_requires env &&
+  is_same_list is_same_identified_predicate b.b_assumes b'.b_assumes env &&
+  is_same_list is_same_post_cond b.b_post_cond b'.b_post_cond env &&
+  is_same_assigns b.b_assigns b'.b_assigns env &&
+  is_same_allocation b.b_allocation b'.b_allocation env
+(* TODO: also consider ACSL extensions, with the help of the plugins
+   that handle them. *)
+
+and is_same_variant (v,m) (v',m') env =
+  is_same_term v v' env && is_same_opt is_matching_logic_info m m' env
+
+and is_same_loop_pragma p p' env =
+  match p, p' with
+  | Unroll_specs l, Unroll_specs l'
+  | Widen_hints l, Widen_hints l'
+  | Widen_variables l, Widen_variables l' ->
+    is_same_list is_same_term l l' env
+  | (Unroll_specs _ | Widen_hints _ | Widen_variables _), _ -> false
+
+and is_same_slice_pragma p p' env =
+  match p, p' with
+  | SPexpr t, SPexpr t' -> is_same_term t t' env
+  | SPctrl, SPctrl -> true
+  | SPstmt, SPstmt -> true
+  | (SPexpr _ | SPctrl | SPstmt), _ -> false
+
+and is_same_impact_pragma p p' env =
+  match p, p' with
+  | IPexpr t, IPexpr t' -> is_same_term t t' env
+  | IPstmt, IPstmt -> true
+  | (IPexpr _ | IPstmt), _ -> false
+
+and is_same_pragma p p' env =
+  match p,p' with
+  | Loop_pragma p, Loop_pragma p' -> is_same_loop_pragma p p' env
+  | Slice_pragma p, Slice_pragma p' -> is_same_slice_pragma p p' env
+  | Impact_pragma p, Impact_pragma p' -> is_same_impact_pragma p p' env
+  | (Loop_pragma _ | Slice_pragma _ | Impact_pragma _), _ -> false
+
+and are_same_behaviors bhvs bhvs' env =
+  let treat_one_behavior acc b =
+    match List.partition (fun b' -> b.b_name = b'.b_name) acc with
+    | [], _ -> raise Exit
+    | [b'], acc ->
+      if is_same_behavior b b' env then acc else raise Exit
+    | _ ->
+      Kernel.fatal "found several behaviors with the same name %s" b.b_name
+  in
+  try
+    match List.fold_left treat_one_behavior bhvs' bhvs with
+    | [] -> true
+    | _ -> (* new behaviors appeared: spec has changed. *) false
+  with Exit -> false
+
+and is_same_funspec s s' env =
+  are_same_behaviors s.spec_behavior s'.spec_behavior env &&
+  is_same_opt is_same_variant s.spec_variant s'.spec_variant env &&
+  is_same_opt is_same_identified_predicate
+    s.spec_terminates s'.spec_terminates env &&
+  are_same_cd_clauses s.spec_complete_behaviors s'.spec_complete_behaviors &&
+  are_same_cd_clauses s.spec_disjoint_behaviors s'.spec_disjoint_behaviors
+
+and is_same_code_annotation a a' env =
+  match a.annot_content, a'.annot_content with
+  | AAssert (bhvs, p), AAssert(bhvs',p') ->
+    is_same_behavior_set bhvs bhvs' && is_same_toplevel_predicate p p' env
+  | AStmtSpec (bhvs, s), AStmtSpec(bhvs', s') ->
+    is_same_behavior_set bhvs bhvs' && is_same_funspec s s' env
+  | AInvariant (bhvs, is_loop, p), AInvariant(bhvs', is_loop', p') ->
+    is_same_behavior_set bhvs bhvs' && is_loop = is_loop' &&
+    is_same_toplevel_predicate p p' env
+  | AVariant v, AVariant v' -> is_same_variant v v' env
+  | AAssigns(bhvs, a), AAssigns(bhvs', a') ->
+    is_same_behavior_set bhvs bhvs' && is_same_assigns a a' env
+  | AAllocation(bhvs, a), AAllocation(bhvs',a') ->
+    is_same_behavior_set bhvs bhvs' && is_same_allocation a a' env
+  | APragma p, APragma p' -> is_same_pragma p p' env
+  | AExtended _, AExtended _ -> true (*TODO: checks also for extended clauses*)
+  | (AAssert _ | AStmtSpec _ | AInvariant _ | AVariant _ | AAssigns _
+    | AAllocation _ | APragma _ | AExtended _), _ -> false
+
+and is_same_logic_type t t' env =
+  match t,t' with
+  | Ctype t, Ctype t' -> is_same_type t t' env
+  | Ltype (t,prms), Ltype (t',prms') ->
+    is_matching_logic_type_info t t' env &&
+    is_same_list is_same_logic_type prms prms' env
+  | Lvar s, Lvar s' -> is_matching_logic_type_var s s' env
+  | Linteger, Linteger -> true
+  | Lreal, Lreal -> true
+  | Larrow(args,rt), Larrow(args', rt')  ->
+    is_same_list is_same_logic_type args args' env &&
+    is_same_logic_type rt rt' env
+  | (Ctype _ | Ltype _ | Lvar _ | Linteger | Lreal | Larrow _),_ -> false
+
+and is_same_inductive_case (_,labs,tprms,p) (_,labs',tprms',p') env =
+  let res, env =
+    (is_same_list is_same_logic_label labs labs' env, env) &&&
+    logic_type_vars_env tprms tprms'
+  in
+  res && is_same_predicate p p' env
+
+and is_same_logic_body b b' env =
+  match b,b' with
+  | LBnone, LBnone -> true
+  | LBreads l, LBreads l' ->
+    is_same_list is_same_identified_term l l' env
+  | LBterm t, LBterm t' -> is_same_term t t' env
+  | LBpred p, LBpred p' -> is_same_predicate p p' env
+  | LBinductive l, LBinductive l' ->
+    is_same_list is_same_inductive_case l l' env
+  | (LBnone | LBreads _ | LBterm _ | LBpred _ | LBinductive _), _ -> false
+
+and is_same_logic_ctor_info c c' env =
+  (* we rely on order in the type declaration to match constructors,
+     not on names. *)
+  is_same_list is_same_logic_type c.ctor_params c'.ctor_params env
+
+and is_same_logic_type_def d d' env =
+  match d,d' with
+  | LTsum l, LTsum l' ->
+    if is_same_list is_same_logic_ctor_info l l' env then begin
+      List.iter2 (fun c c' -> Logic_ctor_info.add c (`Same c')) l l';
+      true
+    end else begin
+      List.iter (fun c -> Logic_ctor_info.add c `Not_present) l;
+      false
+    end
+  | LTsyn t, LTsyn t' -> is_same_logic_type t t' env
+  | (LTsum _ | LTsyn _), _ -> false
+
+and is_same_logic_info li li' env =
+  let res,env =
+    (is_same_list is_same_logic_label li.l_labels li'.l_labels env, env) &&&
+    logic_type_vars_env li.l_tparams li'.l_tparams &&&
+    logic_vars_env li.l_profile li'.l_profile
+  in
+  res && is_same_opt is_same_logic_type li.l_type li'.l_type env &&
+  is_same_logic_body li.l_body li'.l_body env
+
+and is_same_logic_type_info ti ti' env =
+  let res,env =
+    (Cil_datatype.Attributes.equal ti.lt_attr ti'.lt_attr, env) &&&
+    logic_type_vars_env ti.lt_params ti'.lt_params
+  in
+  res && is_same_opt is_same_logic_type_def ti.lt_def ti'.lt_def env
+
+and is_same_model_info mi mi' env =
+  is_same_type mi.mi_base_type mi'.mi_base_type env &&
+  is_same_logic_type mi.mi_field_type mi'.mi_field_type env &&
+  Cil_datatype.Attributes.equal mi.mi_attr mi'.mi_attr
+
+and is_same_type t t' env =
+  match t, t' with
+  | TVoid a, TVoid a' -> Cil_datatype.Attributes.equal a a'
+  | TInt (ik,a), TInt(ik',a') ->
+    Ikind.equal ik ik' && Cil_datatype.Attributes.equal a a'
+  | TFloat (fk,a), TFloat(fk', a') ->
+    Fkind.equal fk fk' && Cil_datatype.Attributes.equal a a'
+  | TBuiltin_va_list a, TBuiltin_va_list a' ->
+    Cil_datatype.Attributes.equal a a'
+  | TPtr(t,a), TPtr(t',a') ->
+    is_same_type t t' env && Cil_datatype.Attributes.equal a a'
+  | TArray(t,s,a), TArray(t',s',a') ->
+    is_same_type t t' env &&
+    is_same_opt is_same_exp s s' env &&
+    Cil_datatype.Attributes.equal a a'
+  | TFun(rt,l,var,a), TFun(rt', l', var', a') ->
+    is_same_type rt rt' env &&
+    is_same_opt (is_same_list is_same_formal) l l' env &&
+    (var = var') &&
+    Cil_datatype.Attributes.equal a a'
+  | TNamed(t,a), TNamed(t',a') ->
+    let correspondance = typeinfo_correspondance t env in
+    (match correspondance with
+     | `Not_present -> false
+     | `Same t'' -> Cil_datatype.Typeinfo.equal t' t'') &&
+    Cil_datatype.Attributes.equal a a'
+  | TComp(c,a), TComp(c', a') ->
+    let correspondance = compinfo_correspondance c env in
+    (match correspondance with
+     | `Not_present -> false
+     | `Same c'' -> Cil_datatype.Compinfo.equal c' c'') &&
+    Cil_datatype.Attributes.equal a a'
+  | TEnum(e,a), TEnum(e',a') ->
+    let correspondance = enuminfo_correspondance e env in
+    (match correspondance with
+     | `Not_present -> false
+     | `Same e'' -> Cil_datatype.Enuminfo.equal e' e'') &&
+    Cil_datatype.Attributes.equal a a'
+  | (TVoid _ | TInt _ | TFloat _ | TBuiltin_va_list _ | TPtr _ | TArray _
+    | TFun _ | TNamed _ | TComp _ | TEnum _), _ -> false
+
+and is_same_compinfo ci ci' env =
+  ci.cstruct = ci'.cstruct &&
+  Cil_datatype.Attributes.equal ci.cattr ci'.cattr &&
+  is_same_opt (is_same_list is_same_fieldinfo) ci.cfields ci'.cfields env
+
+and is_same_enuminfo ei ei' env =
+  Cil_datatype.Attributes.equal ei.eattr ei'.eattr &&
+  Ikind.equal ei.ekind ei'.ekind &&
+  is_same_list is_same_enumitem ei.eitems ei'.eitems env
+
+and is_same_fieldinfo fi fi' env =
+  (* we don't compare names: it's the order in which they appear in the
+     definition of the aggregate that counts. *)
+  fi.forder = fi'.forder &&
+  is_same_type fi.ftype fi'.ftype env &&
+  is_same_opt (fun x y _ -> x = y) fi.fbitfield fi'.fbitfield env &&
+  Cil_datatype.Attributes.equal fi.fattr fi'.fattr
+
+and is_same_enumitem ei ei' env = is_same_exp ei.eival ei'.eival env
+
+and is_same_formal (_,t,a) (_,t',a') env =
+  is_same_type t t' env && Cil_datatype.Attributes.equal a a'
+
+and is_same_compound_init (o,i) (o',i') env =
+  is_same_offset o o' env && is_same_init i i' env
+
+and is_same_init i i' env =
+  match i, i' with
+  | SingleInit e, SingleInit e' -> is_same_exp e e' env
+  | CompoundInit (t,l), CompoundInit (t', l') ->
+    is_same_type t t' env &&
+    (is_same_list is_same_compound_init) l l' env
+  | (SingleInit _ | CompoundInit _), _ -> false
+
+and is_same_initinfo i i' env = is_same_opt is_same_init i.init i'.init env
+
+and is_same_local_init i i' env =
+  match i, i' with
+  | AssignInit i, AssignInit i' ->
+    if is_same_init i i' env then `Same_body
+    else `Body_changed
+  | (ConsInit(c,args,Plain_func), ConsInit(c',args',Plain_func))
+  | (ConsInit(c,args,Constructor),ConsInit(c',args',Constructor))  ->
+    if is_same_varinfo c c' env &&
+       is_same_list is_same_exp args args' env
+    then begin
+      match gfun_correspondance c env with
+      | `Partial _ | `Not_present -> `Callees_changed
+      | `Same _ -> `Same_body
+    end else `Body_changed
+  | (AssignInit _| ConsInit _), _ -> `Body_changed
+
+and is_same_constant c c' env =
+  match c,c' with
+  | CEnum ei, CEnum ei' ->
+    (match enumitem_correspondance ei env with
+     | `Same ei'' -> Cil_datatype.Enumitem.equal ei' ei''
+     | `Not_present -> false)
+  | CEnum _, _ | _, CEnum _ -> false
+  | (CInt64 _ | CStr _ | CWStr _ | CChr _ | CReal _), _ ->
+    Cil_datatype.Constant.equal c c'
+
+and is_same_exp e e' env =
+  match e.enode, e'.enode with
+  | Const c, Const c' -> is_same_constant c c' env
+  | Lval lv, Lval lv' -> is_same_lval lv lv' env
+  | SizeOf t, SizeOf t' -> is_same_type t t' env
+  | SizeOfE e, SizeOfE e' -> is_same_exp e e' env
+  | SizeOfStr s, SizeOfStr s' -> String.length s = String.length s'
+  | AlignOf t, AlignOf t' -> is_same_type t t' env
+  | AlignOfE e, AlignOfE e' -> is_same_exp e e' env
+  | UnOp(op,e,t), UnOp(op',e',t') ->
+    Unop.equal op op' && is_same_exp e e' env && is_same_type t t' env
+  | BinOp(op,e1,e2,t), BinOp(op',e1',e2',t') ->
+    Binop.equal op op' && is_same_exp e1 e1' env && is_same_exp e2 e2' env
+    && is_same_type t t' env
+  | CastE(t,e), CastE(t',e') -> is_same_type t t' env && is_same_exp e e' env
+  | AddrOf lv, AddrOf lv' -> is_same_lval lv lv' env
+  | StartOf lv, StartOf lv' -> is_same_lval lv lv' env
+  | (Const _ | Lval _ | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _
+    | AlignOfE _ | UnOp _ | BinOp _  | CastE _ | AddrOf _ | StartOf _),_-> false
+
+and is_same_lval lv lv' env =
+  is_same_pair is_same_lhost is_same_offset lv lv' env
+
+and is_same_lhost h h' env =
+  match h, h' with
+  | Var vi, Var vi' -> is_matching_varinfo vi vi' env
+  | Mem p, Mem p' -> is_same_exp p p' env
+  | (Var _ | Mem _), _ -> false
+
+and is_matching_varinfo vi vi' env =
+  if vi.vglob then begin
+    match gvar_correspondance vi env with
+    | `Not_present -> false
+    | `Same vi'' -> Cil_datatype.Varinfo.equal vi' vi''
+  end else begin
+    try
+      let vi'' = Cil_datatype.Varinfo.Map.find vi env.local_vars in
+      Cil_datatype.Varinfo.equal vi' vi''
+    with Not_found ->
+      Kernel.fatal "Unbound variable %a in AST diff"
+        Cil_datatype.Varinfo.pretty vi
+  end
+
+and is_same_offset o o' env =
+  match o, o' with
+  | NoOffset, NoOffset -> true
+  | Field (i,o), Field(i',o') ->
+    is_matching_fieldinfo i i' && is_same_offset o o' env
+  | Index(i,o), Index(i',o') ->
+    is_same_exp i i' env && is_same_offset o o' env
+  | (NoOffset | Field _ | Index _), _ -> false
+
+and is_same_extended_asm a a' env =
+  let is_same_out (_,c,l) (_,c',l') env =
+    Datatype.String.equal c c' && is_same_lval l l' env
+  and is_same_in (_,c,e) (_,c',e') env =
+    Datatype.String.equal c c' && is_same_exp e e' env
+  in
+  let res =
+    is_same_list is_same_out a.asm_outputs a'.asm_outputs env &&
+    is_same_list is_same_in a.asm_inputs a'.asm_inputs env &&
+    is_same_list
+      (fun s1 s2 _ -> Datatype.String.equal s1 s2)
+      a.asm_clobbers a'.asm_clobbers env
+    &&
+    List.length a.asm_gotos = List.length a'.asm_gotos
+  in
+  let bind s s' env =
+    (true, { env with goto_targets = (!s,!s') :: env.goto_targets})
+  in
+  (res, env) &&& is_same_list_env bind a.asm_gotos a'.asm_gotos
+
+and is_same_instr i i' env: body_correspondance*is_same_env =
+  match i, i' with
+  | Set(lv,e,_), Set(lv',e',_) ->
+    if is_same_lval lv lv' env && is_same_exp e e' env then
+      `Same_body, env
+    else
+      `Body_changed, env
+  | Call(lv,f,args,_), Call(lv',f',args',_) ->
+    if is_same_opt is_same_lval lv lv' env &&
+       is_same_list is_same_exp args args' env
+    then begin
+      match f.enode, f'.enode with
+      | Lval(Var f,NoOffset), Lval(Var f', NoOffset) ->
+        (match gfun_correspondance f env with
+         | `Partial _ | `Not_present -> `Callees_changed, env
+         | `Same f'' ->
+           if Cil_datatype.Varinfo.equal f' (Kf.get_vi f'') then
+             `Same_body, env
+           else
+             `Callees_changed, env)
+      | _ -> `Callees_changed, env
+      (* by default, we consider that indirect call might have changed *)
+    end else `Body_changed, env
+  | Local_init(v,i,_), Local_init(v',i',_) ->
+    if is_same_varinfo v v' env then begin
+      let env = add_locals [v] [v'] env in
+      let res = is_same_local_init i i' env in
+      res, env
+    end else `Body_changed, env
+  | Asm(a,c,e,_), Asm(a',c',e',_) ->
+    let res =
+      Cil_datatype.Attributes.equal a a' &&
+      is_same_list (fun s1 s2 _ -> Datatype.String.equal s1 s2) c c' env
+    in
+    let (res,env) = (res,env) &&& is_same_opt_env is_same_extended_asm e e' in
+    if res then `Same_body, env else `Body_changed, env
+  | Skip _, Skip _ -> `Same_body, env
+  | Code_annot _, Code_annot _ ->
+    (* should not be present in normalized AST *)
+    Kernel.fatal "Unexpected Code_annot instruction in normalized AST"
+  | _ -> `Body_changed, env
+
+and is_same_instr_list l l' env =
+  match l, l' with
+  | [], [] -> `Same_body, env
+  | [], _ | _, [] -> `Body_changed, env
+  | i::tl, i'::tl' ->
+    is_same_instr i i' env &&> is_same_instr_list tl tl'
+
+and same_switch_labels l l' env =
+  let l = List.filter Cil.is_case_label l in
+  let l' = List.filter Cil.is_case_label l' in
+  let is_same_label lab lab' =
+    match lab, lab' with
+    | (Label _ as l, _) |  (_, (Label _ as l)) ->
+      Kernel.fatal "Label %a should have been filtered beforehand"
+        Printer.pp_label l
+    | Case(e,_), Case(e', _) -> is_same_exp e e' env
+    | Default _, Default _ -> true
+    | (Case _ | Default _), _ -> false
+  in
+  let exist_same_label l lab = List.exists (is_same_label lab) l in
+  (* it is not sufficient to ensure that any case label present in the original
+     also exists in the new AST: we also must check that no new label was
+     introduced that would change the cfg for this case.
+  *)
+  List.for_all (exist_same_label l') l &&
+  List.for_all (exist_same_label l) l'
+
+and is_same_stmt s s' env =
+  let selection =
+    State_selection.with_codependencies Annotations.code_annot_state
+  in
+  let annots =
+    Project.on ~selection (Orig_project.get()) Annotations.code_annot s
+  in
+  let annots' = Annotations.code_annot s' in
+  let annot_res = is_same_list is_same_code_annotation annots annots' env in
+  let code_res, env =
+    if s.ghost = s'.ghost && Cil_datatype.Attributes.equal s.sattr s'.sattr &&
+       same_switch_labels s.labels s'.labels env
+    then
+      begin
+        match s.skind,s'.skind with
+        | Instr i, Instr i' -> is_same_instr i i' env
+        | Return (r,_), Return(r', _) ->
+          if is_same_opt is_same_exp r r' env then `Same_body, env
+          else `Body_changed, env
+        | Goto (s,_), Goto(s',_) ->
+          `Same_body, { env with goto_targets = (!s,!s') :: env.goto_targets }
+        | Break _, Break _ -> `Same_body, env
+        | Continue _, Continue _ -> `Same_body, env
+        | If(e,b1,b2,_), If(e',b1',b2',_) ->
+          if is_same_exp e e' env then begin
+            is_same_block b1 b1' env &&>
+            is_same_block b2 b2'
+          end else `Body_changed, env
+        | Switch(e,b,_,_), Switch(e',b',_,_) ->
+          if is_same_exp e e' env then begin
+            is_same_block b b' env
+          end else `Body_changed, env
+        | Loop(_,b,_,_,_), Loop(_,b',_,_,_) ->
+          is_same_block b b' env
+        | Block b, Block b' ->
+          is_same_block b b' env
+        | UnspecifiedSequence l, UnspecifiedSequence l' ->
+          let b = Cil.block_from_unspecified_sequence l in
+          let b' = Cil.block_from_unspecified_sequence l' in
+          is_same_block b b' env
+        | Throw (e,_), Throw (e',_) ->
+          if is_same_opt (is_same_pair is_same_exp is_same_type) e e' env then
+            `Same_body, env
+          else `Body_changed, env
+        | TryCatch (b,c,_), TryCatch(b',c',_) ->
+          let rec is_same_catch_list l l' env =
+            match l, l' with
+            | [], [] -> `Same_body, env
+            | [],_ | _, [] -> `Body_changed, env
+            | (bind, b) :: tl, (bind', b') :: tl' ->
+              is_same_binder bind bind' env &&>
+              is_same_block b b' &&>
+              is_same_catch_list tl tl'
+          in
+          is_same_block b b' env &&> is_same_catch_list c c'
+        | TryFinally(b1,b2,_), TryFinally(b1',b2',_) ->
+          is_same_block b1 b1' env &&>
+          (is_same_block b2 b2')
+        | TryExcept(b1,(h,e),b2,_), TryExcept(b1',(h',e'),b2',_) ->
+          if is_same_exp e e' env then begin
+            is_same_block b1 b1' env &&>
+            is_same_instr_list h h' &&>
+            is_same_block b2 b2'
+          end else `Body_changed, env
+        | _ -> `Body_changed, env
+      end else `Body_changed, env
+  in
+  let res = make_correspondance s' annot_res code_res in
+  Stmt.add s res; code_res, env
+
+(* is_same_block will return its modified environment in order
+   to update correspondance table with respect to locals, in case
+   the body of the enclosing function is unchanged. *)
+and is_same_block b b' env =
+  let local_decls = List.filter (fun x -> not x.vdefined) b.blocals in
+  let local_decls' = List.filter (fun x -> not x.vdefined) b'.blocals in
+  if is_same_list is_same_varinfo b.bstatics b'.bstatics env &&
+     Cil_datatype.Attributes.equal b.battrs b'.battrs
+  then begin
+    let res, env = is_same_list_env varinfo_env local_decls local_decls' env in
+    if res then begin
+      add_statics b.bstatics b'.bstatics;
+      let rec is_same_stmts l l' env =
+        match l, l' with
+        | [], [] -> `Same_body,env
+        | [], _ | _, [] -> `Body_changed, env
+        | s :: tl, s' :: tl' ->
+          is_same_stmt s s' env &&> (is_same_stmts tl tl')
+      in
+      is_same_stmts b.bstmts b'.bstmts env
+    end else `Body_changed, env
+  end else `Body_changed, env
+
+and is_same_binder b b' env =
+  match b, b' with
+  | Catch_exn(v,conv), Catch_exn(v', conv') ->
+    if is_same_varinfo v v' env then begin
+      let env = add_locals [v] [v'] env in
+      let rec is_same_conv l l' env =
+        match l, l' with
+        | [], [] -> `Same_body, env
+        | [], _ | _, [] -> `Body_changed, env
+        | (v,b)::tl, (v',b')::tl' ->
+          if is_same_varinfo v v' env then begin
+            let env = add_locals [v] [v'] env in
+            is_same_block b b' env &&>
+            (is_same_conv tl tl')
+          end else `Body_changed, env
+      in
+      is_same_conv conv conv' env
+    end else `Body_changed, env
+  | Catch_all, Catch_all -> `Same_body, env
+  | (Catch_exn _ | Catch_all), _ -> `Body_changed, env
+
+(* correspondance of formals is supposed to have already been checked,
+   and formals mapping to have been put in the local env
+*)
+and is_same_fundec f f' env: body_correspondance =
+  (* The goto_targets field of [env] accumulates visited goto targets to be
+     verified after the function body. If the given environment is not empty,
+     resets this field for this function. *)
+  let env = { env with goto_targets = [] } in
+  let res, env =
+    is_same_block f.sbody f'.sbody env &&>
+    check_goto_targets
+  in
+  (* Since we add the locals only if the body is the same,
+     we have explored all nodes, and added all locals bindings.
+     Otherwise, is_same_block would have returned `Body_changed.
+     Hence [Not_found] cannot be raised. *)
+  let add_local v =
+    let v' = Cil_datatype.Varinfo.Map.find v env.local_vars in
+    Varinfo.add v (`Same v')
+  in
+  (match res with
+   | `Same_body | `Callees_changed ->
+     List.iter add_local f.slocals
+   | `Body_changed -> ());
+  res
+
+and is_same_varinfo vi vi' env =
+  is_same_type vi.vtype vi'.vtype env &&
+  Cil_datatype.Attributes.equal vi.vattr vi'.vattr
+
+and varinfo_env vi vi' env =
+  if is_same_varinfo vi vi' env then true, add_locals [vi] [vi'] env
+  else false, env
+
+and is_same_logic_var lv lv' env =
+  is_same_logic_type lv.lv_type lv'.lv_type env &&
+  Cil_datatype.Attributes.equal lv.lv_attr lv'.lv_attr
+
+and logic_vars_env l l' env =
+  if is_same_list is_same_logic_var l l' env then
+    true, add_logic_vars l l' env
+  else
+    false, env
+
+and find_candidate_logic_var ?loc:_loc lv env =
+  let candidates = Logic_env.find_all_logic_functions lv.lv_name in
+  match List.find_opt (fun li -> li.l_profile = []) candidates with
+  | None -> None
+  | Some li ->
+    if is_same_logic_var lv li.l_var_info env then
+      Some li.l_var_info
+    else None
+
+(* because of overloading, we have to check for a corresponding profile,
+   leading to potentially recursive calls to is_same_* functions. *)
+and find_candidate_logic_info ?loc:_loc li env =
+  let candidates = Logic_env.find_all_logic_functions li.l_var_info.lv_name in
+  let find_one li' =
+    let res, env = logic_type_vars_env li.l_tparams li'.l_tparams env in
+    res && is_same_list is_same_logic_var li.l_profile li'.l_profile env
+    && is_same_opt is_same_logic_type li.l_type li'.l_type env
+  in
+  List.find_opt find_one candidates
+
+and find_candidate_model_info ?loc:_loc mi env =
+  let candidates = Logic_env.find_all_model_fields mi.mi_name in
+  let find_one mi' =
+    is_same_type mi.mi_base_type mi'.mi_base_type env
+  in
+  List.find_opt find_one candidates
+
+and typeinfo_correspondance ?loc ti env =
+  let add ti =
+    match find_candidate_type ?loc ti with
+    | None -> `Not_present
+    | Some ti' ->
+      let res = is_same_type ti.ttype ti'.ttype env in
+      if res then `Same ti' else `Not_present
+  in
+  Typeinfo.memo add ti
+
+and compinfo_correspondance ?loc ci env =
+  let add ci =
+    match find_candidate_compinfo ?loc ci with
+    | None -> `Not_present
+    | Some ci' ->
+      let env' =
+        {env with compinfo = Cil_datatype.Compinfo.Map.add ci ci' env.compinfo}
+      in
+      let res = is_same_compinfo ci ci' env' in
+      if res then begin
+        (match ci.cfields, ci'.cfields with
+         | Some fl, Some fl' ->
+           (* by definition, if is_same_compinfo returns true,
+              we have the same number of fields in ci and ci'. *)
+           List.iter2 (fun fi fi' -> Fieldinfo.add fi (`Same fi')) fl fl'
+         | _ -> ());
+        `Same ci'
+      end else begin
+        (* fields are considered different, even if it might be possible
+           to consider that the beginning of the struct hasn't changed. *)
+        (match ci.cfields with
+         | Some fl -> List.iter (fun fi -> Fieldinfo.add fi `Not_present) fl
+         | None -> ());
+        `Not_present
+      end
+  in
+  match Cil_datatype.Compinfo.Map.find_opt ci env.compinfo with
+  | Some ci' -> `Same ci'
+  | None -> Compinfo.memo add ci
+
+and enuminfo_correspondance ?loc ei env =
+  let add ei =
+    match find_candidate_enuminfo ?loc ei with
+    | None -> `Not_present
+    | Some ei' ->
+      if is_same_enuminfo ei ei' env then begin
+        (* add items correspondance. By definition, we have
+           the same number of items here. *)
+        List.iter2 (fun ei ei' -> Enumitem.add ei (`Same ei'))
+          ei.eitems ei'.eitems;
+        `Same ei'
+      end else begin
+        (* consider that all items are different.
+           Might be refined at some point. *)
+        List.iter (fun ei -> Enumitem.add ei `Not_present) ei.eitems;
+        `Not_present
+      end
+  in
+  Enuminfo.memo add ei
+
+and enumitem_correspondance ?loc:_loc ei _env = Enumitem.find ei
+
+and gvar_correspondance ?loc vi env =
+  let add vi =
+    match find_candidate_varinfo ?loc vi Cil_types.VGlobal with
+    | None when Cil.isFunctionType vi.vtype ->
+      begin
+        match gfun_correspondance ?loc vi env with
+        | `Same kf' -> `Same (Kf.get_vi kf')
+        | `Partial(kf',_) ->
+          (* a partial match at kf level is still the
+             identity for the underlying varinfo *)
+          `Same (Kf.get_vi kf')
+        | `Not_present -> `Not_present
+      end
+    | None -> `Not_present
+    | Some vi' ->
+      if is_same_varinfo vi vi' env
+      then
+        let selection = State_selection.singleton Globals.Vars.self in
+        let init =
+          Project.on ~selection (Orig_project.get()) Globals.Vars.find vi
+        in
+        let init' = Globals.Vars.find vi' in
+        let res = is_same_initinfo init init' env in
+        if res then `Same vi' else `Not_present
+      else `Not_present
+  in
+  Varinfo.memo add vi
+
+and gfun_correspondance ?loc vi env =
+  (* NB: we also take care of the correspondance between the underlying varinfo,
+     in case we have to refer to it directly, e.g. as an AddrOf argument.
+  *)
+  let kf = get_original_kf vi in
+  let add kf =
+    match find_candidate_func ?loc kf with
+    | None -> Varinfo.add vi `Not_present; `Not_present
+    | Some kf' ->
+      let formals = Kf.get_formals kf in
+      let formals' = Kf.get_formals kf' in
+      let res, env =
+        (is_same_type (Kf.get_return_type kf) (Kf.get_return_type kf') env, env)
+        &&& is_same_list_env varinfo_env formals formals'
+      in
+      if res then begin
+        (* from a variable point of view, e.g. if we take its address,
+           they are similar *)
+        Varinfo.add vi (`Same (Kf.get_vi kf'));
+        (* we only add formals to global correspondance tables if some
+           part of the kf is unchanged (otherwise, we can't reuse information
+           about the formals anyways). Hence, we only add them into the local
+           env for now. *)
+        let env =
+          { env with kernel_function = Kf.Map.add kf kf' env.kernel_function }
+        in
+        let same_spec = is_same_funspec kf.spec kf'.spec env in
+        let same_body =
+          match (Kf.has_definition kf, Kf.has_definition kf') with
+          | false, false -> `Same_body
+          | false, true | true, false -> `Body_changed
+          | true, true ->
+            is_same_fundec (Kf.get_definition kf) (Kf.get_definition kf') env
+        in
+        let res = make_correspondance kf' same_spec same_body in
+        (match res with
+         | `Not_present -> ()
+         | `Same _ | `Partial _ -> formals_correspondance formals formals');
+        res
+      end else begin
+        (* signatures do not match, we consider that pointers
+           are not equivalent. *)
+        Varinfo.add vi `Not_present;
+        `Not_present
+      end
+  in
+  match Kf.Map.find_opt kf env.kernel_function with
+  | Some kf' -> `Same kf'
+  | None -> Kernel_function.memo add kf
+
+and is_matching_logic_info li li' env =
+  match Cil_datatype.Logic_info.Map.find_opt li env.logic_info with
+  | None ->
+    (match Logic_info.find li with
+     | `Not_present -> false
+     | `Same li'' -> Cil_datatype.Logic_info.equal li' li''
+     | exception Not_found ->
+       begin
+         let res = logic_info_correspondance li env in
+         Logic_info.add li res;
+         match res with
+         | `Not_present -> false
+         | `Same li'' -> Cil_datatype.Logic_info.equal li' li''
+       end)
+  | Some li'' -> Cil_datatype.Logic_info.equal li' li''
+
+and logic_info_correspondance ?loc li env =
+  let add li =
+    match find_candidate_logic_info ?loc li env with
+    | None -> `Not_present
+    | Some li' ->
+      let env =
+        { env with
+          logic_info=Cil_datatype.Logic_info.Map.add li li' env.logic_info }
+      in
+      let res = is_same_logic_info li li' env in
+      if res then begin
+        logic_prms_correspondance li.l_profile li'.l_profile;
+        `Same li'
+      end else `Not_present
+  in
+  match Cil_datatype.Logic_info.Map.find_opt li env.logic_info with
+  | Some li' -> `Same li'
+  | None -> Logic_info.memo add li
+
+and is_matching_logic_ctor c c' env =
+  match Logic_ctor_info.find c with
+  | `Not_present -> false
+  | `Same c'' -> Cil_datatype.Logic_ctor_info.equal c' c''
+  | exception Not_found ->
+    let ty = c.ctor_type in
+    let res = logic_type_correspondance ty env in
+    Logic_type_info.add ty res;
+    if not (Logic_ctor_info.mem c) then
+      Kernel.fatal "Unbound logic type constructor %a in AST diff"
+        Cil_datatype.Logic_ctor_info.pretty c;
+    is_matching_logic_ctor c c' env
+
+and is_matching_logic_type_info t t' env =
+  match Logic_type_info.find t with
+  | `Not_present -> false
+  | `Same t'' -> Cil_datatype.Logic_type_info.equal t' t''
+  | exception Not_found ->
+    (match Cil_datatype.Logic_type_info.Map.find_opt t env.logic_type_info with
+     | Some t'' -> Cil_datatype.Logic_type_info.equal t' t''
+     | None ->
+       let res = logic_type_correspondance t env in
+       Logic_type_info.add t res;
+       (match res with
+        | `Same t'' -> Cil_datatype.Logic_type_info.equal t' t''
+        | `Not_present -> false))
+
+and logic_type_correspondance ?loc ti env =
+  let add ti =
+    match find_candidate_logic_type ?loc ti with
+    | None -> `Not_present
+    | Some ti' ->
+      let env =
+        { env with
+          logic_type_info =
+            Cil_datatype.Logic_type_info.Map.add ti ti' env.logic_type_info }
+      in
+      let res = is_same_logic_type_info ti ti' env in
+      (* In case of a sum type, the constructors table
+         is updated by is_same_logic_type_info. *)
+      if res then `Same ti' else `Not_present
+  in
+  match Cil_datatype.Logic_type_info.Map.find_opt ti env.logic_type_info with
+  | Some ti' -> `Same ti'
+  | None -> Logic_type_info.memo add ti
+
+let model_info_correspondance ?loc mi =
+  let add mi =
+    match find_candidate_model_info ?loc mi empty_env with
+    | None -> `Not_present
+    | Some mi' ->
+      let res = is_same_model_info mi mi' empty_env in
+      if res then `Same mi' else `Not_present
+  in
+  Model_info.memo add mi
+
+let rec gannot_correspondance =
+  function
+  | Dfun_or_pred (li,loc) ->
+    ignore (logic_info_correspondance ~loc li empty_env)
+
+  | Dvolatile _ -> ()
+  (* reading and writing function themselves will be checked elsewhere. *)
+
+  | Daxiomatic(_,l,_,_) ->
+    List.iter gannot_correspondance l
+  | Dtype (ti,loc) -> ignore (logic_type_correspondance ~loc ti empty_env)
+  | Dlemma _ -> ()
+  (* TODO: we currently do not have any appropriate structure for
+     storing information about lemmas. *)
+  | Dinvariant(li, loc) ->
+    ignore (logic_info_correspondance ~loc li empty_env)
+  | Dtype_annot(li,loc) ->
+    ignore (logic_info_correspondance ~loc li empty_env)
+  | Dmodel_annot (mi,loc) ->
+    ignore (model_info_correspondance ~loc mi)
+  | Dextended _ -> ()
+(* TODO: provide mechanism for extension themselves
+   to give relevant information. *)
+
+let global_correspondance g =
+  match g with
+  | GType (ti,loc) -> ignore (typeinfo_correspondance ~loc ti empty_env)
+  | GCompTag(ci,loc) | GCompTagDecl(ci,loc) ->
+    ignore (compinfo_correspondance ~loc ci empty_env)
+  | GEnumTag(ei,loc) | GEnumTagDecl(ei,loc) ->
+    ignore (enuminfo_correspondance ~loc ei empty_env)
+  | GVar(vi,_,loc) | GVarDecl(vi,loc) ->
+    ignore (gvar_correspondance ~loc vi empty_env)
+  | GFunDecl(_,vi,loc) -> ignore (gfun_correspondance ~loc vi empty_env)
+  | GFun(f,loc) -> ignore (gfun_correspondance ~loc f.svar empty_env)
+  | GAnnot (g,_) -> gannot_correspondance g
+  | GAsm _ | GPragma _ | GText _ -> ()
+
+
+let compare_ast () =
+  let prj = Orig_project.get () in
+  let ast = Project.on prj Ast.get () in
+  Cil.iterGlobals ast global_correspondance
+
+let compare_from_prj prj =
+  Orig_project.set prj;
+  compare_ast ()
+
+let prepare_project () =
+  if Kernel.AstDiff.get () then begin
+    let orig = Project.create_by_copy ~last:false
+        ("orig_" ^ Project.get_name (Project.current()))
+    in
+    Orig_project.set orig
+  end
+
+let () = Cmdline.run_after_configuring_stage prepare_project
+
+let compute_diff _ =
+  if Kernel.AstDiff.get () then begin
+    Ast.compute (); compare_ast ()
+  end
+
+let () = Cmdline.run_after_setting_files compute_diff
diff --git a/src/kernel_services/ast_queries/ast_diff.mli b/src/kernel_services/ast_queries/ast_diff.mli
new file mode 100644
index 0000000000000000000000000000000000000000..f40887842305a56d7080ae01b67af66146018c0d
--- /dev/null
+++ b/src/kernel_services/ast_queries/ast_diff.mli
@@ -0,0 +1,127 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Compute diff information from an existing project.
+
+    @since Frama-C+dev
+*)
+
+open Cil_types
+
+(** the original project from which a diff is computed. *)
+module Orig_project: State_builder.Option_ref with type data = Project.t
+
+(** possible correspondances between new items and original ones. *)
+type 'a correspondance =
+  [ `Same of 'a (** symbol with identical definition has been found. *)
+  | `Not_present (** no correspondance *)
+  ]
+
+(** for kernel function, we are a bit more precise than a yes/no answer.
+    More precisely, we check whether a function has the same spec,
+    the same body, and whether its callees have changed (provided
+    the body itself is identical, otherwise, there's no point in checking
+    the callees.
+*)
+type partial_correspondance =
+  [ `Spec_changed (** body and callees haven't changed *)
+  | `Body_changed (** spec hasn't changed *)
+  | `Callees_changed (** spec and body haven't changed *)
+  | `Callees_spec_changed (** body hasn't changed *)
+  ]
+
+type 'a code_correspondance =
+  [ 'a correspondance
+  | `Partial of 'a * partial_correspondance
+  ]
+
+module type Correspondance_table = sig
+  include State_builder.Hashtbl
+  val pretty_data: Format.formatter -> data -> unit
+end
+
+(** varinfos correspondances *)
+module Varinfo:
+  Correspondance_table
+  with type key = varinfo and type data = varinfo correspondance
+
+module Compinfo:
+  Correspondance_table
+  with type key = compinfo and type data = compinfo correspondance
+
+module Enuminfo:
+  Correspondance_table
+  with type key = enuminfo and type data = enuminfo correspondance
+
+module Enumitem:
+  Correspondance_table
+  with type key = enumitem and type data = enumitem correspondance
+
+module Typeinfo:
+  Correspondance_table
+  with type key = typeinfo and type data = typeinfo correspondance
+
+module Stmt:
+  Correspondance_table
+  with type key = stmt and type data = stmt code_correspondance
+
+module Logic_info:
+  Correspondance_table
+  with type key = logic_info and type data = logic_info correspondance
+
+module Logic_type_info:
+  Correspondance_table
+  with type key = logic_type_info and type data = logic_type_info correspondance
+
+module Logic_ctor_info:
+  Correspondance_table
+  with type key = logic_ctor_info and type data = logic_ctor_info correspondance
+
+module Fieldinfo:
+  Correspondance_table
+  with type key = fieldinfo and type data = fieldinfo correspondance
+
+module Model_info:
+  Correspondance_table
+  with type key = model_info and type data = model_info correspondance
+
+module Logic_var:
+  Correspondance_table
+  with type key = logic_var and type data = logic_var correspondance
+
+module Kernel_function:
+  Correspondance_table
+  with type key = kernel_function
+   and type data = kernel_function code_correspondance
+
+module Fundec:
+  Correspondance_table
+  with type key = fundec and type data = fundec correspondance
+
+(** performs a comparison of AST between the current and the original
+    project, which must have been set beforehand.
+*)
+val compare_ast: unit-> unit
+
+(** [compare_from_prj prj] sets [prj] as the original project
+    and fill the tables. *)
+val compare_from_prj: Project.t -> unit
diff --git a/src/kernel_services/ast_queries/cil_datatype.ml b/src/kernel_services/ast_queries/cil_datatype.ml
index bbee2f5bcc4a4bd554a6836df84f1fbdcca20f1e..31b139bdd6bd451ebf2f4fedcb10d11c8cb89481 100644
--- a/src/kernel_services/ast_queries/cil_datatype.ml
+++ b/src/kernel_services/ast_queries/cil_datatype.ml
@@ -737,7 +737,7 @@ module Label = struct
         let reprs =
           [ Label("", Location.unknown, false); Default Location.unknown ]
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt l = !pretty_ref fmt l
         let varname = Datatype.undefined
         let hash = function
           | Default _ -> 7
@@ -1334,7 +1334,7 @@ module Builtin_logic_info = struct
         let equal i1 i2 = i1.bl_name = i2.bl_name
         let copy = Datatype.identity (* works only if an AST is never modified *)
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt li = !pretty_ref fmt li
         let varname = Datatype.undefined
       end)
 end
@@ -1352,7 +1352,7 @@ module Logic_type_info = struct
         let hash t = Hashtbl.hash t.lt_name
         let copy = Datatype.identity (* works only if an AST is never modified *)
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt lt = !pretty_ref fmt lt
         let varname = Datatype.undefined
       end)
 end
@@ -1372,7 +1372,7 @@ module Logic_ctor_info = struct
         let hash t = Hashtbl.hash t.ctor_name
         let copy = Datatype.identity (* works only if an AST is never modified *)
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt c = !pretty_ref fmt c
         let varname = Datatype.undefined
       end)
 end
@@ -1387,7 +1387,7 @@ module Initinfo = struct
           { init = None } ::
           List.map (fun t -> { init = Some (CompoundInit(t, [])) }) Typ.reprs
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt i = !pretty_ref fmt i
         let varname = Datatype.undefined
       end)
 end
@@ -1413,7 +1413,7 @@ module Logic_info = struct
         let hash i = Logic_var.hash i.l_var_info
         let copy = Datatype.undefined
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt li = !pretty_ref fmt li
         let varname _ = "logic_varinfo"
       end)
 end
@@ -1498,7 +1498,7 @@ module Logic_info_structural = struct
         let hash i = Logic_var.hash i.l_var_info
         let copy = Datatype.undefined
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt li = !Logic_info.pretty_ref fmt li
         let varname _ = "logic_varinfo"
       end)
 end
@@ -2085,7 +2085,7 @@ module Logic_constant = struct
         let hash = hash_logic_constant
         let copy = Datatype.undefined
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt lc = !pretty_ref fmt lc
         let varname _ = "lconst"
       end)
 end
@@ -2129,7 +2129,7 @@ module Identified_term = struct
           { it_id = x.it_id; it_content = Term.copy x.it_content }
         let hash x = x.it_id
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt t = !pretty_ref fmt t
         let varname _ = "id_term"
       end)
 end
@@ -2154,7 +2154,7 @@ module Term_lhost = struct
         let hash = hash_fct hash_tlhost
         let copy = Datatype.undefined
         let internal_pretty_code = Datatype.undefined
-        let pretty = !pretty_ref
+        let pretty fmt h = !pretty_ref fmt h
         let varname = Datatype.undefined
       end)
 end
@@ -2658,7 +2658,7 @@ module Fundec = struct
         let equal v1 v2 = v1.svar.vid = v2.svar.vid
         let rehash = Datatype.identity
         let copy = Datatype.undefined
-        let pretty = Datatype.undefined
+        let pretty fmt f = !pretty_ref fmt f
         let internal_pretty_code = Datatype.undefined
         let mem_project = Datatype.never_any_project
       end)
diff --git a/src/kernel_services/plugin_entry_points/kernel.ml b/src/kernel_services/plugin_entry_points/kernel.ml
index 79d3b8c13ee0971dcb5998151e3518a3ed9e62bf..057ba6786496350a2e133b771f3ee7e68feccc7f 100644
--- a/src/kernel_services/plugin_entry_points/kernel.ml
+++ b/src/kernel_services/plugin_entry_points/kernel.ml
@@ -821,6 +821,17 @@ module EagerLoadSources =
                 in memory"
   end)
 
+let () = Parameter_customize.set_group inout_source
+let () = Parameter_customize.do_not_projectify ()
+module AstDiff =
+  False
+    (struct
+      let option_name = "-ast-diff"
+      let module_name = "AstDiff"
+      let help = "creates a new project and computes a diff of the AST \
+                  from the current one"
+    end)
+
 (* ************************************************************************* *)
 (** {2 Save/Load} *)
 (* ************************************************************************* *)
diff --git a/src/kernel_services/plugin_entry_points/kernel.mli b/src/kernel_services/plugin_entry_points/kernel.mli
index c0c5520fecd00d44fd7af902841648e080d9164e..28f166f0a9e829e848fabb05321a332484a2a476 100644
--- a/src/kernel_services/plugin_entry_points/kernel.mli
+++ b/src/kernel_services/plugin_entry_points/kernel.mli
@@ -334,6 +334,9 @@ module CodeOutput : sig
   val output: (Format.formatter -> unit) -> unit
 end
 
+(** Behavior of option "-ast-diff" *)
+module AstDiff: Parameter_sig.Bool
+
 (** Behavior of option "-add-symbolic-path"
     @since Neon-20140301
     @modify 23.0-Vanadium inversed argument order (now uses path:name) *)
diff --git a/src/libraries/utils/filepath.ml b/src/libraries/utils/filepath.ml
index c0070248e9f622d3f4a594834412f326ffa02341..35da51d54ec8b3402b08cfd97bf2c2e572d07d2c 100644
--- a/src/libraries/utils/filepath.ml
+++ b/src/libraries/utils/filepath.ml
@@ -132,7 +132,14 @@ let insert base path_name =
    Instead of forcing the user to always provide resolved paths, we
    currently choose to never resolve them.
    Note that, in rare situations (e.g. some Docker images), PWD does not
-   exist in the environment, so in that case, we fallback to Sys.getcwd. *)
+   exist in the environment, so in that case, we fallback to Sys.getcwd.
+
+   REMARK[LC]: when the Frama-C binary is directly invoked by Node without
+   going through a shell script wrapper like ./bin/frama-c, the environment
+   variable "PWD" is _no more_ synchronized with Sys.getcwd.
+   This problem has been solved in `Dome.spawn()` by forcing the `PWD`
+   environement variable accordingly.
+*)
 let cwd = insert dummy (try Sys.getenv "PWD" with Not_found -> Sys.getcwd ())
 
 type existence =
diff --git a/src/libraries/utils/wto.ml b/src/libraries/utils/wto.ml
index 07056a2b7f00edc1f7a00a5b171f084d91a4b140..db672f0228d6a349ca46f52ba1e7e3809087b2b7 100644
--- a/src/libraries/utils/wto.ml
+++ b/src/libraries/utils/wto.ml
@@ -153,8 +153,12 @@ module Make(N:sig
         this loop with the lowest level. This vertex is also the deepest in the
         stack and the neareast vertex from the root that is below [vertex] in
         the spanning tree built by the DFS);
-      - [partition] is returned completed. *)
-  let rec visit ~pref state vertex partition =
+      - [partition] is returned completed.
+
+      A tail-recursive implementation of this function is now used to avoid
+      stack overflow errors on very large functions. This function is kept
+      for documentation purposes, as it is simpler and more readable. *)
+  let rec _visit ~pref state vertex partition =
     match DFN.find state.dfn vertex with
     (* The vertex is already in the partition *)
     | Invisible -> NoLoop, partition (* skip it *)
@@ -171,7 +175,7 @@ module Make(N:sig
       (* Visit all its successors *)
       let succs = state.succs vertex in
       let (loop,partition) = List.fold_left (fun (loop,partition) succ ->
-          let (loop',partition) = visit ~pref state succ partition in
+          let (loop',partition) = _visit ~pref state succ partition in
           let loop = min_loop loop loop' in
           (loop,partition)
         ) (NoLoop,partition) succs
@@ -211,7 +215,7 @@ module Make(N:sig
         (* Restart the component analysis *)
         let component = List.fold_left
             (fun component succ ->
-               let (loop,component) = visit ~pref state succ component in
+               let (loop,component) = _visit ~pref state succ component in
                (* Since we reset the component we should have no loop *)
                assert (loop = NoLoop);
                component
@@ -224,12 +228,123 @@ module Make(N:sig
            be added during the second visit of the SCC. *)
         (loop,partition)
 
+
+  (* Types for the tail-recursive function of [visit]. *)
+
+  (* Whether to apply [min_loop] at the end of the visit of a vertex. *)
+  type kind = Default | MinLoop
+
+  (* [visit] contains two recursive calls; its tail-recursive version is split
+     into several steps accordingly. For the visit of a vertex, this type
+     indicates the step that must be done. *)
+  type step =
+    | Start of kind
+    (* Starts the visit of a vertex. If [kind] = [MinLoop], then apply
+       [min_loop] between the previous and the resulting loop. *)
+    | Continue
+    (* Resumes the visit of a vertex after the first recursive call. *)
+    | End of N.t partition
+    (* Ends the visit of a vertex after the second recursive call. *)
+    | Min_loop of loop
+    (* Apply [Min_loop] between [loop] (from the previous result) and the
+       current loop. *)
+
+  (* The list of steps that must be done, with their related vertex. It starts
+     with [init, Start Default] and grows for each original recursive call. *)
+  type continuation = (N.t * step) list
+
+  (** Tail-recursive version of the [visit] function above. *)
+  let tail_recursive_visit ~pref state vertex partition =
+    (* Visit according to the next step to be done. *)
+    let rec visit (cont: continuation) (loop, partition) =
+      match cont with
+      | [] -> loop, partition
+      | (vertex, step) :: cont ->
+        match step with
+        | Start kind ->
+          let cont =
+            if kind = MinLoop then (vertex, Min_loop loop) :: cont else cont
+          in
+          visit_first cont vertex partition
+        | Continue ->
+          visit_second cont vertex loop partition
+        | End partition' ->
+          assert (loop = NoLoop);
+          let r = NoLoop, Component (vertex, partition) :: partition' in
+          visit cont r
+        | Min_loop loop' ->
+          let r = min_loop loop' loop, partition in
+          visit cont r
+
+    (* Visits all successors of [vertex], then continue with [next] and the
+       [continuation]. The status of [vertex] is first set to [status]. *)
+    and visit_succs continuation next (vertex, status) kind acc =
+      let succs = state.succs vertex in
+      DFN.replace state.dfn vertex status;
+      let list = List.map (fun v -> v, Start kind) succs in
+      visit (list @ (vertex, next) :: continuation) (NoLoop, acc)
+
+    and visit_first continuation vertex partition =
+      match DFN.find state.dfn vertex with
+      (* The vertex is already in the partition *)
+      | Invisible ->
+        visit continuation (NoLoop, partition) (* skip it *)
+      (* The vertex have been visited but is not yet in the partition *)
+      | Parent i ->
+        visit continuation (Loop (vertex,i), partition) (* we are in a loop *)
+      (* The vertex have not been visited yet *)
+      | exception Not_found ->
+        (* Put the current vertex into the stack *)
+        Stack.push vertex state.stack;
+        (* Number it and mark it as visited *)
+        let n = state.num + 1 in
+        state.num <- n;
+        (* Visit all its successors. *)
+        visit_succs continuation Continue (vertex, Parent n) MinLoop partition
+
+    and visit_second continuation vertex loop partition =
+      match loop with
+      (* We are not in a loop. Add the vertex to the partition *)
+      | NoLoop ->
+        let _ = Stack.pop state.stack in
+        DFN.replace state.dfn vertex Invisible;
+        visit continuation (NoLoop,Node(vertex)::partition)
+      (* We are in a loop and the current vertex is the head of this loop *)
+      | Loop(head,_) when N.equal head vertex ->
+        (* Unmark all vertices in the loop, and, if pref is given, try to
+           return a better head *)
+        let rec reset_SCC best_head =
+          (* pop until vertex *)
+          let element = Stack.pop state.stack in
+          DFN.remove state.dfn element;
+          if not (N.equal element vertex) then begin
+            (* the strict is important because we are conservative *)
+            let best_head =
+              if pref best_head element < 0 then element else best_head
+            in
+            reset_SCC best_head
+          end
+          else
+            best_head
+        in
+        let best_head = reset_SCC vertex in
+        let vertex = if N.equal best_head vertex then vertex else best_head in
+        (* Makes [vertex] invisible and visits all its successors. *)
+        visit_succs continuation (End partition) (vertex, Invisible) Default []
+      | _ ->
+        (* [vertex] is part of a strongly connected component but is not the
+           head. Do not update partition; the vertex will
+           be added during the second visit of the SCC. *)
+        visit continuation (loop, partition)
+    in
+    visit [vertex, Start Default] (NoLoop, partition)
+
   type pref = N.t -> N.t -> int
 
   let partition ~pref ~init ~succs =
     let state = {dfn = DFN.create 17; num = 0; succs;
                  stack = Stack.create () } in
-    let loop,component = visit ~pref state init [] in
+    let loop,component = tail_recursive_visit ~pref state init [] in
     assert (loop = NoLoop);
     component
 
diff --git a/src/plugins/value/domains/multidim/abstract_structure.ml b/src/plugins/value/domains/multidim/abstract_structure.ml
index 39540bf0c84060a113139d23bf694cb07e45a46c..2dec7b6004d652c25f0822f0447bbbb0198bc9a7 100644
--- a/src/plugins/value/domains/multidim/abstract_structure.ml
+++ b/src/plugins/value/domains/multidim/abstract_structure.ml
@@ -256,7 +256,7 @@ struct
 
   let pick (m : t) =
     match Map.choose_opt m with
-    | None -> Kernel.fatal "The disjunction should never be empty"
+    | None -> assert false (* the disjunction should never be empty *)
     | Some (k,s) -> k,s,Map.remove k m
 
   let pretty fmt (m : t) =
diff --git a/src/plugins/value/domains/multidim/segmentation.ml b/src/plugins/value/domains/multidim/segmentation.ml
index f8781b9f2f554bbb65b7b31096616fa5da4e4603..1b0c1ec71614c26f8c5008ae75f2c6f3f01dcfbf 100644
--- a/src/plugins/value/domains/multidim/segmentation.ml
+++ b/src/plugins/value/domains/multidim/segmentation.ml
@@ -292,7 +292,7 @@ struct
     match Int_val.min_int (to_int_val ~oracle b) with
     | Some l -> `Value l
     | None ->
-      Kernel.warning ~current:true "cannot retrieve a lower bound for %a"
+      Self.warning ~current:true "cannot retrieve a lower bound for %a"
         pretty b;
       `Top
 
@@ -300,7 +300,7 @@ struct
     match Int_val.max_int (to_int_val ~oracle b) with
     | Some u -> `Value u
     | None ->
-      Kernel.warning ~current:true "cannot retrieve an upper bound for %a"
+      Self.warning ~current:true "cannot retrieve an upper bound for %a"
         pretty b;
       `Top
 
@@ -870,7 +870,7 @@ struct
       | `Value m -> m
       | `Bottom -> assert false
       | `Top ->
-        Kernel.warning ~current:true
+        Self.warning ~current:true
           "failed to introduce %a inside the array segmentation"
           Bound.pretty b;
         m
diff --git a/src/plugins/wp/Changelog b/src/plugins/wp/Changelog
index 395f3faead128c1f7ea7edff039dbd19ef888a2c..0355202d2b37b706e0f4203780ac6ecb77ba80d9 100644
--- a/src/plugins/wp/Changelog
+++ b/src/plugins/wp/Changelog
@@ -24,6 +24,7 @@
 Plugin WP <next-release>
 ########################
 
+- WP          [2022-05-06] Upgrade to Why3 1.5
 - WP          [2022-02-28] New option -wp-fct-timeout, used to customize the
                            provers timeout per function
 - WP          [2022-02-02] New option -wp-smoke-dead-local-init, smoke tests on
diff --git a/src/plugins/wp/Cint.ml b/src/plugins/wp/Cint.ml
index 6053d614cd0a5021adc3d0ed24c637bc4c8d78d8..f375c1e28c1f4fe5e61b43b944fdc31bf8b3fc28 100644
--- a/src/plugins/wp/Cint.ml
+++ b/src/plugins/wp/Cint.ml
@@ -629,15 +629,16 @@ let smp_leq_improved f a b =
   else raise Not_found
 
 let smp_leq_with_land a b =
-  try
-    let es = match_fun f_land a in
-    let a1,_ = match_list_head match_positive_or_null_integer es in
-    if F.decide (F.e_leq (e_zint a1) b)
-    then e_true
-    else raise Not_found
-  with Not_found ->
+  let es = match_fun f_land a in
+  let a1,_ = match_list_head match_positive_or_null_integer es in
+  if F.decide (F.e_leq (e_zint a1) b)
+  then e_true
+  else raise Not_found
+(* Disabled rule :
+   with Not_found ->
     (* a <= 0 && 0 <= (x&y) ==> a <= (x & y) *)
     smp_leq_improved f_land a b
+*)
 
 let smp_eq_with_land a b =
   let es = match_fun f_land a in
diff --git a/src/plugins/wp/ProverWhy3.ml b/src/plugins/wp/ProverWhy3.ml
index 44ab3d0f7f41c3a5b67289e40192f687657f0ffe..314769066fb4a6cd8edb0a31f0dd036445ba7c5f 100644
--- a/src/plugins/wp/ProverWhy3.ml
+++ b/src/plugins/wp/ProverWhy3.ml
@@ -39,13 +39,19 @@ let option_qual =
 let why3_failure msg =
   Pretty_utils.ksfprintf failwith msg
 
-module Env = WpContext.Index(struct
+type why3_conf = {
+  env : Why3.Env.env ;
+  libdir : string ;
+  datadir : string ;
+}
+
+module Conf = WpContext.Index(struct
     include Datatype.Unit
     type key = unit
-    type data = Why3.Env.env
+    type data = why3_conf
   end)
 
-let get_why3_env = Env.memoize
+let get_why3_conf = Conf.memoize
     begin fun () ->
       let config = Why3Provers.config () in
       let main = Why3.Whyconf.get_main config in
@@ -53,12 +59,14 @@ let get_why3_env = Env.memoize
         (WpContext.directory () :> string)::
         ((Wp_parameters.Share.get_dir ~mode:`Must_exist "why3") :> string)::
         (Why3.Whyconf.loadpath main) in
-      Why3.Env.create_env ld
+      let libdir = Why3.Whyconf.libdir main in
+      let datadir = Why3.Whyconf.datadir main in
+      { env = Why3.Env.create_env ld ; libdir ; datadir }
     end
 
 type context = {
   mutable th : Why3.Theory.theory_uc;
-  env: Why3.Env.env;
+  conf: why3_conf;
 }
 
 type convert = {
@@ -120,14 +128,14 @@ let fold_map map fold = function
 
 let empty_context name : context = {
   th = Why3.Theory.create_theory (Why3.Ident.id_fresh name);
-  env = get_why3_env ();
+  conf = get_why3_conf ();
 }
 
 let empty_cnv ?(polarity=`NoPolarity) (ctx:context) : convert = {
   th = ctx.th;
   subst = Lang.F.Tmap.empty;
   pool = Lang.F.pool ();
-  env = ctx.env;
+  env = ctx.conf.env;
   polarity;
   incomplete_symbols = Hashtbl.create 3;
   incomplete_types = Hashtbl.create 3;
@@ -779,7 +787,7 @@ class visitor (ctx:context) c =
         (if import then "import" else "")
         Why3.Pp.(print_list (Why3.Pp.constant_string ".") string) file
         thy name ;
-      let thy = Why3.Env.read_theory ctx.env file thy in
+      let thy = Why3.Env.read_theory ctx.conf.env file thy in
       let th = ctx.th in
       let th = Why3.Theory.open_scope th name in
       let th = Why3.Theory.use_export th thy in
@@ -1153,7 +1161,7 @@ type prover_call = {
   mutable killed : bool ;
 }
 
-let ping_prover_call p =
+let ping_prover_call ~libdir p =
   match Why3.Call_provers.query_call p.call with
   | NoUpdates
   | ProverStarted ->
@@ -1170,7 +1178,7 @@ let ping_prover_call p =
               Wp_parameters.debug ~dkey
                 "Hard Kill (late why3server timeout)" ;
               p.interrupted <- true ;
-              Why3.Call_provers.interrupt_call p.call ;
+              Why3.Call_provers.interrupt_call ~libdir p.call ;
             end
     in Task.Wait 100
   | InternalFailure exn ->
@@ -1205,7 +1213,8 @@ let ping_prover_call p =
       VCS.pp_result r;
     Task.Return (Task.Result r)
 
-let call_prover_task ~timeout ~steps prover call =
+let call_prover_task ~timeout ~steps ~conf prover call =
+  let libdir = conf.libdir in
   Wp_parameters.debug ~dkey "Why3 run prover %a with timeout %d, steps %d@."
     Why3.Whyconf.print_prover prover
     (Why3.Opt.get_def (-1) timeout)
@@ -1220,9 +1229,9 @@ let call_prover_task ~timeout ~steps prover call =
   let ping = function
     | Task.Kill ->
       pcall.killed <- true ;
-      Why3.Call_provers.interrupt_call call ;
+      Why3.Call_provers.interrupt_call ~libdir call ;
       Task.Yield
-    | Task.Coin -> ping_prover_call pcall
+    | Task.Coin -> ping_prover_call ~libdir pcall
   in
   Task.async ping
 
@@ -1243,7 +1252,7 @@ let digest wpo drv prover task =
       end
   in Digest.file file |> Digest.to_hex
 
-let batch pconf driver ?script ~timeout ~steplimit prover task =
+let batch pconf driver ~conf ?script ~timeout ~steplimit prover task =
   let steps = match steplimit with Some 0 -> None | _ -> steplimit in
   let limit =
     let memlimit = Why3.Whyconf.memlimit (Why3.Whyconf.get_main (Why3Provers.config ())) in
@@ -1261,8 +1270,8 @@ let batch pconf driver ?script ~timeout ~steplimit prover task =
   let command = Why3.Whyconf.get_complete_command pconf ~with_steps in
   let inplace = if script <> None then Some true else None in
   let call = Why3.Driver.prove_task_prepared ?old:script ?inplace
-      ~command ~limit driver task in
-  call_prover_task ~timeout ~steps prover call
+      ~command ~limit ~libdir:conf.libdir ~datadir:conf.datadir driver task in
+  call_prover_task ~conf ~timeout ~steps prover call
 
 (* -------------------------------------------------------------------------- *)
 (* --- Interactive Prover (Coq)                                           --- *)
@@ -1295,20 +1304,23 @@ let updatescript ~script driver task =
   let d_new = Digest.file script in
   if String.equal d_new d_old then Extlib.safe_remove backup
 
-let editor ~script ~merge wpo pconf driver prover task =
+let editor ~script ~merge ~conf wpo pconf driver prover task =
   Task.sync editor_mutex
     begin fun () ->
       Wp_parameters.feedback ~ontty:`Transient "Editing %S..." script ;
       Cache.clear_result ~digest:(digest wpo driver) prover task ;
       if merge then updatescript ~script driver task ;
       let command = editor_command pconf in
-      let call = Why3.Call_provers.call_editor ~command script in
-      call_prover_task ~timeout:None ~steps:None pconf.prover call
+      let call =
+        Why3.Call_provers.call_editor
+          ~command ~datadir:conf.datadir ~libdir:conf.libdir script
+      in
+      call_prover_task ~conf ~timeout:None ~steps:None pconf.prover call
     end
 
-let compile ~script ~timeout wpo pconf driver prover task =
+let compile ~script ~timeout ~conf wpo pconf driver prover task =
   let digest = digest wpo driver in
-  let runner = batch pconf driver ~script in
+  let runner = batch ~conf pconf driver ~script in
   Cache.get_result ~digest ~runner ~timeout ~steplimit:None prover task
 
 let prepare ~mode wpo driver task =
@@ -1329,33 +1341,34 @@ let prepare ~mode wpo driver task =
 let interactive ~mode wpo pconf driver prover task =
   let time = Wp_parameters.InteractiveTimeout.get () in
   let timeout = if time <= 0 then None else Some time in
+  let conf = get_why3_conf () in
   match prepare ~mode wpo driver task with
   | None -> Task.return VCS.unknown
   | Some (script, merge) ->
     match mode with
     | VCS.Batch ->
-      compile ~script ~timeout wpo pconf driver prover task
+      compile ~script ~timeout ~conf wpo pconf driver prover task
     | VCS.Update ->
       if merge then updatescript ~script driver task ;
-      compile ~script ~timeout wpo pconf driver prover task
+      compile ~script ~timeout ~conf wpo pconf driver prover task
     | VCS.Edit ->
       let open Task in
-      editor ~script ~merge wpo pconf driver prover task >>= fun _ ->
-      compile ~script ~timeout wpo pconf driver prover task
+      editor ~script ~merge ~conf wpo pconf driver prover task >>= fun _ ->
+      compile ~script ~timeout ~conf wpo pconf driver prover task
     | VCS.Fix ->
       let open Task in
-      compile ~script ~timeout wpo pconf driver prover task >>= fun r ->
+      compile ~script ~timeout ~conf wpo pconf driver prover task >>= fun r ->
       if VCS.is_valid r then return r else
-        editor ~script ~merge wpo pconf driver prover task >>= fun _ ->
-        compile ~script ~timeout wpo pconf driver prover task
+        editor ~script ~merge ~conf wpo pconf driver prover task >>= fun _ ->
+        compile ~script ~timeout ~conf wpo pconf driver prover task
     | VCS.FixUpdate ->
       let open Task in
       if merge then updatescript ~script driver task ;
-      compile ~script ~timeout wpo pconf driver prover task >>= fun r ->
+      compile ~script ~timeout ~conf wpo pconf driver prover task >>= fun r ->
       if VCS.is_valid r then return r else
         let merge = false in
-        editor ~script ~merge wpo pconf driver prover task >>= fun _ ->
-        compile ~script ~timeout wpo pconf driver prover task
+        editor ~script ~merge ~conf wpo pconf driver prover task >>= fun _ ->
+        compile ~script ~timeout ~conf wpo pconf driver prover task
 
 (* -------------------------------------------------------------------------- *)
 (* --- Prove WPO                                                          --- *)
@@ -1373,8 +1386,8 @@ let build_proof_task ?(mode=VCS.Batch) ?timeout ?steplimit ~prover wpo () =
     if Wp_parameters.Generate.get ()
     then Task.return VCS.no_result (* Only generate *)
     else
-      let env = WpContext.on_context context get_why3_env () in
-      let drv , pconf , task = prover_task env prover task in
+      let conf = WpContext.on_context context get_why3_conf () in
+      let drv , pconf , task = prover_task conf.env prover task in
       if is_trivial task then
         Task.return VCS.valid
       else
@@ -1383,7 +1396,7 @@ let build_proof_task ?(mode=VCS.Batch) ?timeout ?steplimit ~prover wpo () =
       else
         Cache.get_result
           ~digest:(digest wpo drv)
-          ~runner:(batch pconf drv ?script:None)
+          ~runner:(batch ~conf pconf drv ?script:None)
           ~timeout ~steplimit prover task
   with exn ->
     if Wp_parameters.has_dkey dkey_api then
diff --git a/src/plugins/wp/TacClear.ml b/src/plugins/wp/TacClear.ml
index 3808c2909095b05c7af9415ce7f47e2085947f0c..bf1f7aeaeee5ee9305a2d56a58abcb875acb0ebf 100644
--- a/src/plugins/wp/TacClear.ml
+++ b/src/plugins/wp/TacClear.ml
@@ -37,12 +37,33 @@ let tactical_step step =
     (* Note: the provided name is used in the GUi for the subgoal title *)
     ("Removed Step", Conditions.Have Lang.F.p_true)
 
-module TermLset = Qed.Listset.Make(Lang.F.QED.Term)
+module Filtered =
+struct
+  type t =
+    | Fun of Lang.Fun.t
+    | Other of Lang.F.term
+
+  let of_term t =
+    match Lang.F.repr t with
+    | Qed.Logic.Fun(f, _) -> Fun f
+    | _ -> Other t
+
+  let pretty fmt = function
+    | Fun f -> Format.fprintf fmt "%a(...)" Lang.Fun.pretty f
+    | Other t -> Lang.F.pp_term fmt t
+
+  let compare a b =
+    match a, b with
+    | Fun a, Fun b -> Lang.Fun.compare a b
+    | Other a, Other b -> Lang.F.compare a b
+    | Fun _, _ -> 1
+    | _, Fun _ -> -1
+
+  let equal a b = 0 = compare a b
+end
 
-let pp_filtered fmt t =
-  match Lang.F.repr t with
-  | Qed.Logic.Fun(f,_) -> Format.fprintf fmt "%a(...)" Lang.Fun.pretty f
-  | _ -> Lang.F.pp_term fmt t
+module TermLset = Qed.Listset.Make(Lang.F.QED.Term)
+module Filteredset = Qed.Listset.Make(Filtered)
 
 
 let tactical_inside step remove =
@@ -56,12 +77,17 @@ let tactical_inside step remove =
     | Type p | Have p | When p | Core p | Init p ->
       let ps = Lang.F.e_props @@ collect p in
       let kept = TermLset.diff ps remove in
+      let removed =
+        let add s e = Filteredset.add (Filtered.of_term e) s in
+        List.fold_left add Filteredset.empty remove
+      in
       let feedback =
+        let pp fmt f = Format.fprintf fmt "'%a'" Filtered.pretty f in
         Format.asprintf "Filtered: %a"
-          (Pretty_utils.pp_list ~sep:", " pp_filtered) remove
+          (Pretty_utils.pp_list ~sep:", " pp) removed
       in
       let cond = condition step @@ Lang.F.p_bool @@ Lang.F.e_and kept in
-      Tactical.replace_single ~at:step.id (feedback, cond)
+      removed, Tactical.replace_single ~at:step.id (feedback, cond)
 
     | _ -> raise Not_found
   end
@@ -83,8 +109,18 @@ let collect_remove m = function
 
 let fold_selection s seq =
   let m = List.fold_left collect_remove Smap.empty s in
-  "Filtered mutiple selection",
-  Smap.fold (fun s l seq -> snd @@ tactical_inside s l seq) m seq
+  let tactical s l (acc_rm, seq) =
+    let rm, op = tactical_inside s l in
+    Filteredset.union rm acc_rm, snd @@ op seq
+  in
+  let removed, seq = Smap.fold tactical m ([], seq) in
+  let feedback =
+    let pp fmt f = Format.fprintf fmt "'%a'" Filtered.pretty f in
+    Format.asprintf "Filtered: %a"
+      (Pretty_utils.pp_list ~sep:", " pp) removed
+  in
+  feedback, seq
+
 
 let process (f: sequent -> string * sequent) s = [ f s ]
 
@@ -101,7 +137,7 @@ class clear =
         Applicable(process @@ tactical_step step)
       | Inside(Step step, remove) ->
         begin
-          try Applicable(process @@ tactical_inside step [remove])
+          try Applicable(process @@ snd @@ tactical_inside step [remove])
           with Not_found -> Not_applicable
         end
       | Multi es ->
diff --git a/src/plugins/wp/TacUnfold.ml b/src/plugins/wp/TacUnfold.ml
index f72efde27b4351668c9ad6f5d474747613b8ed08..a30d3798e06589c1e4ed673a63cda1e608de72bf 100644
--- a/src/plugins/wp/TacUnfold.ml
+++ b/src/plugins/wp/TacUnfold.ml
@@ -113,27 +113,29 @@ let unfolds_from_list phis es =
 let unfolds_from_smap phis m =
   Smap.map (fun _s es -> unfolds_from_list phis es) m
 
+module Unfoldedset = Qed.Listset.Make(Lang.Fun)
+
 let tactical_inside step unfolds sequent =
   if Lang.F.Tmap.is_empty unfolds
   then raise Not_found
   else match step.condition with
     | Type p | Have p | When p | Core p | Init p ->
-      let unfolded = ref [] in
+      let unfolded = ref Unfoldedset.empty in
       let subst t =
         let result = Lang.F.Tmap.find t unfolds in
         begin match F.repr t with
-          | Qed.Logic.Fun(f,_) -> unfolded := f :: !unfolded
+          | Qed.Logic.Fun(f,_) -> unfolded := Unfoldedset.add f !unfolded
           | _ -> ()
         end ;
         result
       in
       let p = condition step @@ Lang.p_subst subst p in
-      let unfolded = List.sort_uniq Lang.Fun.compare !unfolded in
       let feedback =
-        Format.asprintf "Unfolded: %a"
-          (Pretty_utils.pp_list ~sep:", " Lang.Fun.pretty) unfolded
+        let pp fmt f = Format.fprintf fmt "'%a'" Lang.Fun.pretty f in
+        Format.asprintf "Unfold %a"
+          (Pretty_utils.pp_list ~sep:", " pp) !unfolded
       in
-      snd @@ Tactical.replace_single ~at:step.id (feedback, p) sequent
+      !unfolded, snd @@ Tactical.replace_single ~at:step.id (feedback, p) sequent
     | _ -> raise Not_found
 
 let tactical_goal unfolds (seq, g) =
@@ -144,12 +146,21 @@ let tactical_goal unfolds (seq, g) =
     seq, Lang.p_subst subst g
 
 let fold_selection goal_unfolds step_unfolds sequent =
-  "Unfolded multiple selection",
+  let tactical s l (acc_unfolded, seq) =
+    let unfolded, seq = tactical_inside s l seq in
+    Unfoldedset.union unfolded acc_unfolded, seq
+  in
+  let unfolded, seq = Smap.fold tactical step_unfolds ([], sequent) in
+  let feedback =
+    let pp fmt f = Format.fprintf fmt "'%a'" Lang.Fun.pretty f in
+    Format.asprintf "Unfold %a"
+      (Pretty_utils.pp_list ~sep:", " pp) unfolded
+  in
   let add_goal = match goal_unfolds with
     | None -> Extlib.id
     | Some goal_unfolds -> tactical_goal goal_unfolds
   in
-  add_goal @@ Smap.fold tactical_inside step_unfolds sequent
+  feedback, add_goal seq
 
 let process (f: sequent -> string * sequent) s = [ f s ]
 
diff --git a/src/plugins/wp/cfgAnnot.ml b/src/plugins/wp/cfgAnnot.ml
index 425f3df70fe94de264fd1e941535ffcd4c14e45e..9e529fc7c44f60f8bfd71fb04f74271d5573bcc8 100644
--- a/src/plugins/wp/cfgAnnot.ml
+++ b/src/plugins/wp/cfgAnnot.ml
@@ -91,6 +91,12 @@ let normalize_post ~goal kf bhv tk ?assumes (itk,ip) =
     Some (id , implies ?assumes p)
   else None
 
+let normalize_decreases (d, li) =
+  let module L = NormAtLabels in
+  let at_pre e = Logic_const.tat (e, BuiltinLabel Pre) in
+  let labels = L.labels_fct_pre in
+  (at_pre @@ L.preproc_term labels d, li)
+
 let normalize_froms tk froms =
   let module L = NormAtLabels in
   let labels = L.labels_fct_assigns ~exit:(tk=Exits) in
@@ -246,12 +252,13 @@ let get_terminates_hyp kf =
 
 let check_variant_relation = function
   | (_, None) -> ()
-  | (_, Some rel) ->
-    Wp_parameters.hypothesis ~once:true
+  | ({ term_loc }, Some rel) ->
+    Wp_parameters.hypothesis
+      ~source:(fst term_loc) ~once:true
       "'%a' relation must be well-founded" Cil_printer.pp_logic_info rel
 
 let get_decreases_goal kf =
-  let defined t = WpPropId.mk_decrease_id kf Kglobal t, t in
+  let defined t = WpPropId.mk_decrease_id kf Kglobal t, normalize_decreases t in
   match Annotations.decreases ~populate:false kf with
   | None -> None
   | Some v -> check_variant_relation v ; Some (defined v)
@@ -348,7 +355,7 @@ module CallContract = WpContext.StaticGenerator(Kernel_function)
           | Writes froms -> Writes (normalize_froms Normal froms)
         in
         let terminates = get_terminates_hyp kf in
-        let decreases = get_decreases_hyp kf in
+        let decreases = Option.map normalize_decreases @@ get_decreases_hyp kf in
         {
           contract_cond = List.rev !wcond ;
           contract_hpre = List.rev !whpre ;
diff --git a/src/plugins/wp/cfgWP.ml b/src/plugins/wp/cfgWP.ml
index ee6d749805a8fb90468fd8eb263e8b22a4cc6ca5..110ef26efaed4f63b55589df6495d57071948a0d 100644
--- a/src/plugins/wp/cfgWP.ml
+++ b/src/plugins/wp/cfgWP.ml
@@ -1162,7 +1162,7 @@ struct
              | (caller_d, rel), Some (callee_d,_ ) ->
                let rel caller callee = match rel with
                  | None ->
-                   p_and (p_leq e_zero callee) (p_lt callee caller)
+                   p_and (p_leq e_zero caller) (p_lt callee caller)
                  | Some rel ->
                    (L.in_frame call_f (L.call_pred call_e))
                      rel [] [caller ; callee]
diff --git a/src/plugins/wp/configure.ac b/src/plugins/wp/configure.ac
index 8df1b3908a149065d28db136450806405afc82dc..010a5beef14a3a671c0bdc352dbf5ae391bf7659 100644
--- a/src/plugins/wp/configure.ac
+++ b/src/plugins/wp/configure.ac
@@ -56,15 +56,15 @@ case $WHY3VERSION in
         AC_MSG_RESULT([not found!])
         plugin_disable(wp,[why3 not found])
         ;;
-      0.* | 1.[[0123]].*)
-        AC_MSG_RESULT([found $WHY3VERSION: requires 1.4.0+])
+      0.* | 1.[[01234]].*)
+        AC_MSG_RESULT([found $WHY3VERSION: requires 1.5.0+])
         plugin_disable(wp,[non-supported why3 $WHY3VERSION])
         ;;
-      1.4.*)
+      1.5.*)
         AC_MSG_RESULT([found $WHY3VERSION: ok])
         ;;
       *)
-        AC_MSG_RESULT([found $WHY3VERSION: might work (should be 1.4.0+)])
+        AC_MSG_RESULT([found $WHY3VERSION: might work (should be 1.5.0+)])
         ;;
 esac
 fi
diff --git a/src/plugins/wp/normAtLabels.ml b/src/plugins/wp/normAtLabels.ml
index 0033fe47bf1468749bc80c1fa958a8ebb601297a..4ae48410ed5e5b8e6a7bb8e302698328ab627e62 100644
--- a/src/plugins/wp/normAtLabels.ml
+++ b/src/plugins/wp/normAtLabels.ml
@@ -214,6 +214,10 @@ let preproc_annot labels p =
   let visitor = new norm_at labels in
   Visitor.visitFramacPredicate visitor p
 
+let preproc_term labels t =
+  let visitor = new norm_at labels in
+  Visitor.visitFramacTerm visitor t
+
 (** @raise LabelError if there is a label in [p] that is incompatible
  * with the [labels] translation *)
 let preproc_assigns labels asgns =
diff --git a/src/plugins/wp/normAtLabels.mli b/src/plugins/wp/normAtLabels.mli
index dfa2f0962e42c72ebf47b3d4e9453987416a19c4..73e5c5ca0be2074e063955cf51a19497f891895f 100644
--- a/src/plugins/wp/normAtLabels.mli
+++ b/src/plugins/wp/normAtLabels.mli
@@ -42,6 +42,7 @@ val labels_stmt_assigns_l : kf:kernel_function -> stmt -> c_label option -> labe
 val labels_predicate : (logic_label * logic_label) list -> label_mapping
 val labels_axiom : label_mapping
 
+val preproc_term : label_mapping -> term -> term
 val preproc_annot : label_mapping -> predicate -> predicate
 val preproc_assigns : label_mapping -> from list -> from list
 val has_postassigns : assigns -> bool
diff --git a/src/plugins/wp/tests/why3/oracle_qualif/spec_memory.res.oracle b/src/plugins/wp/tests/why3/oracle_qualif/spec_memory.res.oracle
index e2ecb2158690c4c94a95668c87a9268fd4ee92e2..ad74ce62fd60dd44ff2ec66b030b29c1b9d017f1 100644
--- a/src/plugins/wp/tests/why3/oracle_qualif/spec_memory.res.oracle
+++ b/src/plugins/wp/tests/why3/oracle_qualif/spec_memory.res.oracle
@@ -1,60 +1,60 @@
 File "spec_memory.why", line 24, characters 4-56:
-Verification condition check_valid_rd.
+Goal check_valid_rd.
 Prover result is: Valid.
 
 File "spec_memory.why", line 27, characters 4-56:
-Verification condition check_valid.
+Goal check_valid.
 Prover result is: Valid.
 
 File "spec_memory.why", line 30, characters 4-62:
-Verification condition invalid_spec.
+Goal invalid_spec.
 Prover result is: Valid.
 
 File "spec_memory.why", line 34, characters 4-76:
-Verification condition invalid_null_spec.
+Goal invalid_null_spec.
 Prover result is: Valid.
 
 File "spec_memory.why", line 38, characters 4-51:
-Verification condition invalid_null.
+Goal invalid_null.
 Prover result is: Valid.
 
 File "spec_memory.why", line 42, characters 4-79:
-Verification condition invalid_empty.
+Goal invalid_empty.
 Prover result is: Valid.
 
 File "spec_memory.why", line 46, characters 4-59:
-Verification condition valid_rd_null.
+Goal valid_rd_null.
 Prover result is: Valid.
 
 File "spec_memory.why", line 49, characters 4-59:
-Verification condition valid_rw_null.
+Goal valid_rw_null.
 Prover result is: Valid.
 
 File "spec_memory.why", line 52, characters 4-34:
-Verification condition valid_obj_null.
+Goal valid_obj_null.
 Prover result is: Valid.
 
 File "spec_memory.why", line 56, characters 4-77:
-Verification condition INC_P.
+Goal INC_P.
 Prover result is: Valid.
 
 File "spec_memory.why", line 60, characters 4-169:
-Verification condition INC_A.
+Goal INC_A.
 Prover result is: Valid.
 
 File "spec_memory.why", line 67, characters 4-63:
-Verification condition INC_1.
+Goal INC_1.
 Prover result is: Valid.
 
 File "spec_memory.why", line 71, characters 4-64:
-Verification condition INC_2.
+Goal INC_2.
 Prover result is: Valid.
 
 File "spec_memory.why", line 75, characters 4-161:
-Verification condition INC_3.
+Goal INC_3.
 Prover result is: Valid.
 
 File "spec_memory.why", line 82, characters 4-178:
-Verification condition INC_4.
+Goal INC_4.
 Prover result is: Valid.
 
diff --git a/src/plugins/wp/tests/wp/oracle/sharing.res.oracle b/src/plugins/wp/tests/wp/oracle/sharing.res.oracle
index b01d3e9c63ef85cdfcb16c2ae11da55dc0141dc7..efd2e9f9df793726354d9a57d5856a32742bedf2 100644
--- a/src/plugins/wp/tests/wp/oracle/sharing.res.oracle
+++ b/src/plugins/wp/tests/wp/oracle/sharing.res.oracle
@@ -57,7 +57,7 @@ end
        let m3 = set m2 (shift_sint32 a 3) (get m2 a2) in
        0 <= i1 ->
        0 <= i ->
-       region (base a) <= 0 ->
+       region (a.base) <= 0 ->
        i1 <= 9 ->
        i <= 9 ->
        linked t ->
diff --git a/src/plugins/wp/tests/wp_acsl/decreases.i b/src/plugins/wp/tests/wp_acsl/decreases.i
index b4d482fe2e6de2726ac360465942eae7a6a6f128..c264819eb94cff0ae29c3bde85d226946495dea0 100644
--- a/src/plugins/wp/tests/wp_acsl/decreases.i
+++ b/src/plugins/wp/tests/wp_acsl/decreases.i
@@ -111,3 +111,15 @@ void mw2(unsigned x){
 
   (void)fact(x); // no verification of decreases here
 }
+
+// Recursion with side-effect
+
+int x ;
+
+//@ decreases x ;
+void se(void){
+  if (x > 0){
+    x -- ;
+    se();
+  }
+}
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/chunk_typing_usable.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/chunk_typing_usable.res.oracle
index 58865eff22dc2d0733b3c82c8270a883ca4ae487..2067e2fba853bee466f0b264d6e023006ef6e01b 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/chunk_typing_usable.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/chunk_typing_usable.res.oracle
@@ -75,13 +75,13 @@ theory A_Occ
   
   (* use Chunk *)
   
-  Q_empty :
+  axiom Q_empty :
     forall mint:addr -> int, v:int, p:addr, f:int, t:int.
      t <= f -> is_sint32_chunk mint -> is_sint32 v -> L_occ mint v p f t = 0
   
   (* use Compound *)
   
-  Q_is :
+  axiom Q_is :
     forall mint:addr -> int, v:int, p:addr, f:int, t:int.
      let x = (- 1) + t in
      let x1 = get mint (shift_sint32 p x) in
@@ -91,7 +91,7 @@ theory A_Occ
      is_sint32 v ->
      is_sint32 x1 -> (1 + L_occ mint v p f x) = L_occ mint v p f t
   
-  Q_isnt :
+  axiom Q_isnt :
     forall mint:addr -> int, v:int, p:addr, f:int, t:int.
      let x = (- 1) + t in
      let x1 = get mint (shift_sint32 p x) in
@@ -154,7 +154,7 @@ end
       forall t:addr -> int, a:addr, i:int, i1:int, i2:int, i3:int.
        i3 <= i1 ->
        i < i3 ->
-       region (base a) <= 0 ->
+       region (a.base) <= 0 ->
        is_sint32_chunk t ->
        is_uint32 i3 ->
        is_uint32 i1 ->
@@ -233,14 +233,14 @@ theory A_Occ1
   
   (* use Chunk1 *)
   
-  Q_empty1 :
+  axiom Q_empty1 :
     forall mint:addr1 -> int, v:int, p:addr1, f:int, t:int.
      t <=' f ->
      is_sint32_chunk1 mint -> is_sint321 v -> L_occ1 mint v p f t = 0
   
   (* use Compound1 *)
   
-  Q_is1 :
+  axiom Q_is1 :
     forall mint:addr1 -> int, v:int, p:addr1, f:int, t:int.
      let x = (- 1) +' t in
      let x1 = get1 mint (shift_sint321 p x) in
@@ -250,7 +250,7 @@ theory A_Occ1
      is_sint321 v ->
      is_sint321 x1 -> (1 +' L_occ1 mint v p f x) = L_occ1 mint v p f t
   
-  Q_isnt1 :
+  axiom Q_isnt1 :
     forall mint:addr1 -> int, v:int, p:addr1, f:int, t:int.
      let x = (- 1) +' t in
      let x1 = get1 mint (shift_sint321 p x) in
@@ -315,7 +315,7 @@ end
        let x = (- 1) +' i1 in
        not get1 t (shift_sint321 a x) = i2 ->
        i <' i1 ->
-       region1 (base1 a) <=' 0 ->
+       region1 (a.base1) <=' 0 ->
        i1 <=' 1000 ->
        is_sint32_chunk1 t ->
        is_uint321 i1 ->
@@ -392,14 +392,14 @@ theory A_Occ2
   
   (* use Chunk2 *)
   
-  Q_empty2 :
+  axiom Q_empty2 :
     forall mint:addr2 -> int, v:int, p:addr2, f:int, t:int.
      t <='' f ->
      is_sint32_chunk2 mint -> is_sint322 v -> L_occ2 mint v p f t = 0
   
   (* use Compound2 *)
   
-  Q_is2 :
+  axiom Q_is2 :
     forall mint:addr2 -> int, v:int, p:addr2, f:int, t:int.
      let x = (- 1) +'' t in
      let x1 = get2 mint (shift_sint322 p x) in
@@ -409,7 +409,7 @@ theory A_Occ2
      is_sint322 v ->
      is_sint322 x1 -> (1 +'' L_occ2 mint v p f x) = L_occ2 mint v p f t
   
-  Q_isnt2 :
+  axiom Q_isnt2 :
     forall mint:addr2 -> int, v:int, p:addr2, f:int, t:int.
      let x = (- 1) +'' t in
      let x1 = get2 mint (shift_sint322 p x) in
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/clusters.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/clusters.res.oracle
index 61a3435b5bbc033d1871b398ddf626f180645976..65805aa212de29ff538de8e68b6cd7ea4bc82640 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/clusters.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/clusters.res.oracle
@@ -122,7 +122,7 @@ Prove: true.
 Goal Recursion variant:
 Call Effect at line 12
 Assume { Type: is_uint32(n). (* Then *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/decreases.0.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/decreases.0.res.oracle
index 1c0241e1df554d73e7532ba44337a17271391de2..84ff1f41b857153c800622b41c6622c7af1a46e0 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/decreases.0.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/decreases.0.res.oracle
@@ -2,11 +2,14 @@
 [kernel] Parsing decreases.i (no preprocessing)
 [wp] Running WP plugin...
 [wp] Warning: Missing RTE guards
-[wp:hypothesis] decreases.i:108: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:44: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:25: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:30: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:71: Warning: No decreases clause for missing_2
-[wp:hypothesis] decreases.i:108: Warning: 'Wrong' relation must be well-founded
+[wp:hypothesis] decreases.i:107: Warning: 'Wrong' relation must be well-founded
 [wp] decreases.i:109: Warning: 
   On call to mw1, relation (Wrong) does not match caller (Rel)
+[wp:hypothesis] decreases.i:103: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:105: Warning: 
   On call to mw2, relation (Rel) does not match caller (Wrong)
 ------------------------------------------------------------
@@ -21,7 +24,7 @@ Prove: true.
 Goal Recursion variant:
 Call Effect at line 17
 Assume { Type: is_uint32(n). (* Else *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -49,7 +52,7 @@ Prove: 0 < n.
 Goal Recursion variant:
 Call Effect at line 40
 Assume { Type: is_sint32(n). (* Else *) Have: n != 0. }
-Prove: 0 < n.
+Prove: 0 <= n.
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -117,7 +120,7 @@ Prove: P_Rel(n, n).
 Goal Recursion variant:
 Call Effect at line 55
 Assume { Type: is_uint32(n). (* Then *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -127,14 +130,14 @@ Prove: to_uint32(n - 1) < n.
 Goal Recursion variant (1/2):
 Call Effect at line 59
 Assume { Type: is_uint32(x). (* Then *) Have: x != 0. }
-Prove: to_uint32(x - 1) < x.
+Prove: (0 <= x) /\ (to_uint32(x - 1) < x).
 
 ------------------------------------------------------------
 
 Goal Recursion variant (2/2):
 Call Effect at line 60
 Assume { Type: is_uint32(x). (* Then *) Have: 11 <= x. }
-Prove: to_uint32(x - 1) < x.
+Prove: (0 <= x) /\ (to_uint32(x - 1) < x).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -154,7 +157,7 @@ Prove: false.
 Goal Recursion variant (2/2):
 Call Effect at line 72
 Assume { Type: is_uint32(n). (* Then *) Have: 31 <= n. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -176,7 +179,7 @@ Prove: to_uint32(n - 1) <= 10.
 Goal Recursion variant:
 Call Effect at line 86
 Assume { Type: is_uint32(n). (* Then *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -197,14 +200,14 @@ Prove: true.
 Goal Recursion variant (1/2):
 Call Effect at line 91
 Assume { Type: is_uint32(x). (* Then *) Have: x != 0. }
-Prove: to_uint32(x - 1) < x.
+Prove: (0 <= x) /\ (to_uint32(x - 1) < x).
 
 ------------------------------------------------------------
 
 Goal Recursion variant (2/2):
 Call Effect at line 92
 Assume { Type: is_uint32(x). (* Then *) Have: 11 <= x. }
-Prove: to_uint32(x - 1) < x.
+Prove: (0 <= x) /\ (to_uint32(x - 1) < x).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -240,3 +243,12 @@ Assume { Type: is_uint32(x). (* Then *) Have: 11 <= x. }
 Prove: P_Wrong(x, to_uint32(x - 1)).
 
 ------------------------------------------------------------
+------------------------------------------------------------
+  Function se
+------------------------------------------------------------
+
+Goal Recursion variant:
+Call Effect at line 123
+Prove: true.
+
+------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/decreases.1.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/decreases.1.res.oracle
index 503c417688228f6147615930e14def6d47a18997..f45e5a772fee6d25d380583944f4b4417991a2f2 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/decreases.1.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/decreases.1.res.oracle
@@ -2,11 +2,14 @@
 [kernel] Parsing decreases.i (no preprocessing)
 [wp] Running WP plugin...
 [wp] Warning: Missing RTE guards
-[wp:hypothesis] decreases.i:108: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:44: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:25: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:30: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:71: Warning: No decreases clause for missing_2
-[wp:hypothesis] decreases.i:108: Warning: 'Wrong' relation must be well-founded
+[wp:hypothesis] decreases.i:107: Warning: 'Wrong' relation must be well-founded
 [wp] decreases.i:109: Warning: 
   On call to mw1, relation (Wrong) does not match caller (Rel)
+[wp:hypothesis] decreases.i:103: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:105: Warning: 
   On call to mw2, relation (Rel) does not match caller (Wrong)
 ------------------------------------------------------------
@@ -21,7 +24,7 @@ Prove: true.
 Goal Recursion variant:
 Call Effect at line 17
 Assume { Type: is_uint32(n). (* Else *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -48,14 +51,7 @@ Prove: 0 < n.
 
 Goal Recursion variant:
 Call Effect at line 40
-Assume {
-  Type: is_sint32(n).
-  (* Goal *)
-  When: 0 <= n.
-  (* Else *)
-  Have: n != 0.
-}
-Prove: 0 < n.
+Prove: true.
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -129,7 +125,7 @@ Prove: P_Rel(n, n).
 Goal Recursion variant:
 Call Effect at line 55
 Assume { Type: is_uint32(n). (* Then *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -139,14 +135,14 @@ Prove: to_uint32(n - 1) < n.
 Goal Recursion variant (1/2):
 Call Effect at line 59
 Assume { Type: is_uint32(x). (* Then *) Have: x != 0. }
-Prove: to_uint32(x - 1) < x.
+Prove: (0 <= x) /\ (to_uint32(x - 1) < x).
 
 ------------------------------------------------------------
 
 Goal Recursion variant (2/2):
 Call Effect at line 60
 Assume { Type: is_uint32(x). (* Then *) Have: 11 <= x. }
-Prove: to_uint32(x - 1) < x.
+Prove: (0 <= x) /\ (to_uint32(x - 1) < x).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -166,7 +162,7 @@ Prove: false.
 Goal Recursion variant (2/2):
 Call Effect at line 72
 Assume { Type: is_uint32(n). (* Then *) Have: 31 <= n. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -188,7 +184,7 @@ Prove: to_uint32(n - 1) <= 10.
 Goal Recursion variant:
 Call Effect at line 86
 Assume { Type: is_uint32(n). (* Then *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
@@ -215,7 +211,7 @@ Assume {
   (* Then *)
   Have: x != 0.
 }
-Prove: to_uint32(x - 1) < x.
+Prove: (0 <= x) /\ (to_uint32(x - 1) < x).
 
 ------------------------------------------------------------
 
@@ -257,3 +253,12 @@ Assume { Type: is_uint32(x). (* Then *) Have: 11 <= x. }
 Prove: P_Wrong(x, to_uint32(x - 1)).
 
 ------------------------------------------------------------
+------------------------------------------------------------
+  Function se
+------------------------------------------------------------
+
+Goal Recursion variant:
+Call Effect at line 123
+Prove: true.
+
+------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/inductive.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/inductive.res.oracle
index 982b66a1342f47f627b60d988b9cc4890a35b48a..4469b4881435e5e65c5c1203e12cd5c46261760d 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/inductive.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/inductive.res.oracle
@@ -138,42 +138,42 @@ end
       forall a:int, b:int, d:int. P_is_gcd2 a b d -> not P_is_gcd2 b d a
     
     predicate P_same_array (mint:addr -> int) (mint1:addr -> int) (a:addr) (b:
-      addr) (begin:int) (end1:int) =
+      addr) (begin1:int) (end1:int) =
       forall i:int.
-       begin <= i ->
+       begin1 <= i ->
        i < end1 -> get mint1 (shift_sint32 a i) = get mint (shift_sint32 b i)
     
     predicate P_swap (mint:addr -> int) (mint1:addr -> int) (a:addr) (b:addr)
-      (begin:int) (i:int) (j:int) (end1:int) =
+      (begin1:int) (i:int) (j:int) (end1:int) =
       ((((get mint1 (shift_sint32 a i) = get mint (shift_sint32 b j) /\
           get mint1 (shift_sint32 a j) = get mint (shift_sint32 b i)) /\
-         begin <= i) /\
+         begin1 <= i) /\
         i < j) /\
        j < end1) /\
       (forall i1:int.
         not i1 = i ->
         not j = i1 ->
-        begin <= i1 ->
+        begin1 <= i1 ->
         i1 < end1 ->
         get mint1 (shift_sint32 a i1) = get mint (shift_sint32 b i1))
     
     inductive P_same_elements (addr -> int) (addr -> int) addr addr int int =
       | Q_refl :
-          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin:
+          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin1:
            int, end1:int.
-           P_same_array mint mint1 a b begin end1 ->
-           P_same_elements mint mint1 a b begin end1
+           P_same_array mint mint1 a b begin1 end1 ->
+           P_same_elements mint mint1 a b begin1 end1
       | Q_swap :
-          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin:
+          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin1:
            int, i:int, j:int, end1:int.
-           P_swap mint mint1 a b begin i j end1 ->
-           P_same_elements mint mint1 a b begin end1
+           P_swap mint mint1 a b begin1 i j end1 ->
+           P_same_elements mint mint1 a b begin1 end1
       | Q_trans :
           forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, a:
-           addr, b:addr, c:addr, begin:int, end1:int.
-           P_same_elements mint mint1 b c begin end1 ->
-           P_same_elements mint1 mint2 a b begin end1 ->
-           P_same_elements mint mint2 a c begin end1
+           addr, b:addr, c:addr, begin1:int, end1:int.
+           P_same_elements mint mint1 b c begin1 end1 ->
+           P_same_elements mint1 mint2 a b begin1 end1 ->
+           P_same_elements mint mint2 a c begin1 end1
     
     goal wp_goal :
       forall t:addr -> int, t1:addr -> int, t2:addr -> int, a:addr, a1:addr, i:
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/predicates_functions.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/predicates_functions.res.oracle
index 67a25621bb869debb0266cef37f13f481f8aa753..fa26e5047b49c275c7402b281b9632446b4fd3d9 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/predicates_functions.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/predicates_functions.res.oracle
@@ -22,13 +22,14 @@
     
     predicate P_RP int
     
-    P_RP_def : forall i:int. i <= 0 \/ P_P i /\ P_RP ((- 1) + i) <-> P_RP i
+    axiom P_RP_def :
+      forall i:int. i <= 0 \/ P_P i /\ P_RP ((- 1) + i) <-> P_RP i
     
     function L_F (i:int) : int = 2 * i
     
     function L_RF int : int
     
-    L_RF_def :
+    axiom L_RF_def :
       forall i:int. L_RF i = (if i <= 0 then 0 else L_F i + L_RF ((- 1) + i))
     
     goal wp_goal : forall i:int. 0 < i -> P_RP (L_RF i)
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle
index eb82e6b610d5fb5de77eb82ff7b6f44706de6d8d..35ee39d24887322fed5fb20fc1901f30dae31203 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle
@@ -120,13 +120,13 @@ theory Compound
     Init_S1_X1 (get init (shiftfield_F1_X_a p))
     (get init (shiftfield_F1_X_b p)) (get init (shiftfield_F1_X_c p))
   
-  Q_Load_S1_X_update_Mchar0 :
+  axiom Q_Load_S1_X_update_Mchar0 :
     forall mchar:addr -> int, mint:addr -> int, mint1:addr -> int, p:addr, q:
      addr, v:int [Load_S1_X p (set mchar q v) mint mint1].
      separated p 3 q 1 ->
      Load_S1_X p (set mchar q v) mint mint1 = Load_S1_X p mchar mint mint1
   
-  Q_Load_S1_X_eqmem_Mchar0 :
+  axiom Q_Load_S1_X_eqmem_Mchar0 :
     forall mchar:addr -> int, mchar1:addr -> int, mint:addr -> int, mint1:
      addr -> int, n:int, p:addr, q:addr [Load_S1_X p mchar mint mint1,
      eqmem mchar mchar1 q n| Load_S1_X p mchar1 mint mint1,
@@ -135,21 +135,21 @@ theory Compound
      eqmem mchar mchar1 q n ->
      Load_S1_X p mchar1 mint mint1 = Load_S1_X p mchar mint mint1
   
-  Q_Load_S1_X_havoc_Mchar0 :
+  axiom Q_Load_S1_X_havoc_Mchar0 :
     forall mchar:addr -> int, mchar1:addr -> int, mint:addr -> int, mint1:
      addr -> int, n:int, p:addr, q:addr
      [Load_S1_X p (havoc mchar1 mchar q n) mint mint1].
      separated p 3 q n ->
-     Load_S1_X p (havoc mchar1 mchar q n) mint mint1
-     = Load_S1_X p mchar mint mint1
+     Load_S1_X p (havoc mchar1 mchar q n) mint mint1 =
+     Load_S1_X p mchar mint mint1
   
-  Q_Load_S1_X_update_Mint1 :
+  axiom Q_Load_S1_X_update_Mint1 :
     forall mchar:addr -> int, mint:addr -> int, mint1:addr -> int, p:addr, q:
      addr, v:int [Load_S1_X p mchar (set mint1 q v) mint].
      separated p 3 q 1 ->
      Load_S1_X p mchar (set mint1 q v) mint = Load_S1_X p mchar mint1 mint
   
-  Q_Load_S1_X_eqmem_Mint1 :
+  axiom Q_Load_S1_X_eqmem_Mint1 :
     forall mchar:addr -> int, mint:addr -> int, mint1:addr -> int, mint2:addr
      -> int, n:int, p:addr, q:addr [Load_S1_X p mchar mint1 mint,
      eqmem mint1 mint2 q n| Load_S1_X p mchar mint2 mint,
@@ -158,21 +158,21 @@ theory Compound
      eqmem mint1 mint2 q n ->
      Load_S1_X p mchar mint2 mint = Load_S1_X p mchar mint1 mint
   
-  Q_Load_S1_X_havoc_Mint1 :
+  axiom Q_Load_S1_X_havoc_Mint1 :
     forall mchar:addr -> int, mint:addr -> int, mint1:addr -> int, mint2:addr
      -> int, n:int, p:addr, q:addr
      [Load_S1_X p mchar (havoc mint2 mint1 q n) mint].
      separated p 3 q n ->
-     Load_S1_X p mchar (havoc mint2 mint1 q n) mint
-     = Load_S1_X p mchar mint1 mint
+     Load_S1_X p mchar (havoc mint2 mint1 q n) mint =
+     Load_S1_X p mchar mint1 mint
   
-  Q_Load_S1_X_update_Mint2 :
+  axiom Q_Load_S1_X_update_Mint2 :
     forall mchar:addr -> int, mint:addr -> int, mint1:addr -> int, p:addr, q:
      addr, v:int [Load_S1_X p mchar mint (set mint1 q v)].
      separated p 3 q 1 ->
      Load_S1_X p mchar mint (set mint1 q v) = Load_S1_X p mchar mint mint1
   
-  Q_Load_S1_X_eqmem_Mint2 :
+  axiom Q_Load_S1_X_eqmem_Mint2 :
     forall mchar:addr -> int, mint:addr -> int, mint1:addr -> int, mint2:addr
      -> int, n:int, p:addr, q:addr [Load_S1_X p mchar mint mint1,
      eqmem mint1 mint2 q n| Load_S1_X p mchar mint mint2,
@@ -181,28 +181,28 @@ theory Compound
      eqmem mint1 mint2 q n ->
      Load_S1_X p mchar mint mint2 = Load_S1_X p mchar mint mint1
   
-  Q_Load_S1_X_havoc_Mint2 :
+  axiom Q_Load_S1_X_havoc_Mint2 :
     forall mchar:addr -> int, mint:addr -> int, mint1:addr -> int, mint2:addr
      -> int, n:int, p:addr, q:addr
      [Load_S1_X p mchar mint (havoc mint2 mint1 q n)].
      separated p 3 q n ->
-     Load_S1_X p mchar mint (havoc mint2 mint1 q n)
-     = Load_S1_X p mchar mint mint1
+     Load_S1_X p mchar mint (havoc mint2 mint1 q n) =
+     Load_S1_X p mchar mint mint1
   
-  Q_Load_Init_S1_X_update_Init0 :
+  axiom Q_Load_Init_S1_X_update_Init0 :
     forall init:addr -> bool, p:addr, q:addr, v:bool
      [Load_Init_S1_X p (set init q v)].
      separated p 3 q 1 ->
      Load_Init_S1_X p (set init q v) = Load_Init_S1_X p init
   
-  Q_Load_Init_S1_X_eqmem_Init0 :
+  axiom Q_Load_Init_S1_X_eqmem_Init0 :
     forall init:addr -> bool, init1:addr -> bool, n:int, p:addr, q:addr
      [Load_Init_S1_X p init, eqmem init init1 q n| Load_Init_S1_X p init1,
      eqmem init init1 q n].
      included p 3 q n ->
      eqmem init init1 q n -> Load_Init_S1_X p init1 = Load_Init_S1_X p init
   
-  Q_Load_Init_S1_X_havoc_Init0 :
+  axiom Q_Load_Init_S1_X_havoc_Init0 :
     forall init:addr -> bool, init1:addr -> bool, n:int, p:addr, q:addr
      [Load_Init_S1_X p (havoc init1 init q n)].
      separated p 3 q n ->
@@ -271,7 +271,7 @@ end
     goal wp_goal :
       forall t:addr -> bool, t1:int -> int, t2:addr -> int, t3:addr -> int, t4:
        addr -> int, a:addr, x:S1_X.
-       region (base a) <= 0 ->
+       region (a.base) <= 0 ->
        IsS1_X x ->
        is_sint16_chunk t3 ->
        is_sint32_chunk t4 ->
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/terminates_formulae.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/terminates_formulae.res.oracle
index d9bd70618f767ce264637cc140e5ee6199eadd5d..484dd6e31ec2b96eeb5bc83bfc02f60bb486c818 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/terminates_formulae.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/terminates_formulae.res.oracle
@@ -2,7 +2,9 @@
 [kernel] Parsing terminates_formulae.i (no preprocessing)
 [wp] Running WP plugin...
 [wp] Warning: Missing RTE guards
-[wp:hypothesis] terminates_formulae.i:90: Warning: 
+[wp:hypothesis] terminates_formulae.i:84: Warning: 
+  'Rel' relation must be well-founded
+[wp:hypothesis] terminates_formulae.i:67: Warning: 
   'Rel' relation must be well-founded
 [wp] [CFG] Goal general_variant_terminates : Valid (Trivial)
 [wp] [CFG] Goal variant_terminates : Valid (Trivial)
@@ -75,7 +77,7 @@ Prove: true.
 Goal Recursion variant:
 Call Effect at line 80
 Assume { Type: is_uint32(n). (* Goal *) When: P_Q. (* Then *) Have: n != 0. }
-Prove: to_uint32(n - 1) < n.
+Prove: (0 <= n) /\ (to_uint32(n - 1) < n).
 
 ------------------------------------------------------------
 ------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.0.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.0.res.oracle
index 62e79688690128fd49abbc91e54a2e4c560df53d..a87bf3324219a0f849a14aee28f61793d56182f3 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.0.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.0.res.oracle
@@ -2,14 +2,17 @@
 [kernel] Parsing decreases.i (no preprocessing)
 [wp] Running WP plugin...
 [wp] Warning: Missing RTE guards
-[wp:hypothesis] decreases.i:108: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:44: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:25: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:30: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:71: Warning: No decreases clause for missing_2
-[wp:hypothesis] decreases.i:108: Warning: 'Wrong' relation must be well-founded
+[wp:hypothesis] decreases.i:107: Warning: 'Wrong' relation must be well-founded
 [wp] decreases.i:109: Warning: 
   On call to mw1, relation (Wrong) does not match caller (Rel)
+[wp:hypothesis] decreases.i:103: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:105: Warning: 
   On call to mw2, relation (Rel) does not match caller (Wrong)
-[wp] 26 goals scheduled
+[wp] 27 goals scheduled
 [wp] [Qed] Goal typed_fact_terminates : Valid
 [wp] [Alt-Ergo] Goal typed_fact_variant : Valid
 [wp] [Alt-Ergo] Goal typed_fails_fact_variant : Unsuccess
@@ -36,8 +39,9 @@
 [wp] [Alt-Ergo] Goal typed_mw2_variant_part1 : Unsuccess (Degenerated)
 [wp] [Alt-Ergo] Goal typed_mw2_variant_part2 : Valid
 [wp] [Alt-Ergo] Goal typed_mw1_variant : Unsuccess (Degenerated)
-[wp] Proved goals:   17 / 26
-  Qed:             6 
+[wp] [Qed] Goal typed_se_variant : Valid
+[wp] Proved goals:   18 / 27
+  Qed:             7 
   Alt-Ergo:       11  (unsuccess: 9)
 ------------------------------------------------------------
  Functions                 WP     Alt-Ergo  Total   Success
@@ -54,4 +58,5 @@
   mt1                       1        1        3      66.7%
   mw2                       -        1        2      50.0%
   mw1                       -        -        1       0.0%
+  se                        1        -        1       100%
 ------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.1.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.1.res.oracle
index 63e3fba1f7426ce524df7c0e5c5f319d6f557091..f18fe8e1bdfe5c40aead8f912bb2e0cfdcc05743 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.1.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle_qualif/decreases.1.res.oracle
@@ -2,14 +2,17 @@
 [kernel] Parsing decreases.i (no preprocessing)
 [wp] Running WP plugin...
 [wp] Warning: Missing RTE guards
-[wp:hypothesis] decreases.i:108: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:44: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:25: Warning: 'Rel' relation must be well-founded
+[wp:hypothesis] decreases.i:30: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:71: Warning: No decreases clause for missing_2
-[wp:hypothesis] decreases.i:108: Warning: 'Wrong' relation must be well-founded
+[wp:hypothesis] decreases.i:107: Warning: 'Wrong' relation must be well-founded
 [wp] decreases.i:109: Warning: 
   On call to mw1, relation (Wrong) does not match caller (Rel)
+[wp:hypothesis] decreases.i:103: Warning: 'Rel' relation must be well-founded
 [wp] decreases.i:105: Warning: 
   On call to mw2, relation (Rel) does not match caller (Wrong)
-[wp] 26 goals scheduled
+[wp] 27 goals scheduled
 [wp] [Qed] Goal typed_fact_terminates : Valid
 [wp] [Alt-Ergo] Goal typed_fact_variant : Valid
 [wp] [Alt-Ergo] Goal typed_fails_fact_variant : Unsuccess
@@ -17,7 +20,7 @@
 [wp] [Alt-Ergo] Goal typed_fails_facto_gen_variant : Unsuccess
 [wp] [Qed] Goal typed_fact_i_terminates_part1 : Valid
 [wp] [Alt-Ergo] Goal typed_fact_i_terminates_part2 : Valid
-[wp] [Alt-Ergo] Goal typed_fact_i_variant : Valid
+[wp] [Qed] Goal typed_fact_i_variant : Valid
 [wp] [Qed] Goal typed_fails_fact_i_terminates_part1 : Valid
 [wp] [Alt-Ergo] Goal typed_fails_fact_i_terminates_part2 : Unsuccess
 [wp] [Alt-Ergo] Goal typed_fails_fact_i_variant : Unsuccess
@@ -36,16 +39,17 @@
 [wp] [Alt-Ergo] Goal typed_mw2_variant_part1 : Unsuccess (Degenerated)
 [wp] [Alt-Ergo] Goal typed_mw2_variant_part2 : Valid
 [wp] [Alt-Ergo] Goal typed_mw1_variant : Unsuccess (Degenerated)
-[wp] Proved goals:   18 / 26
-  Qed:             7 
-  Alt-Ergo:       11  (unsuccess: 8)
+[wp] [Qed] Goal typed_se_variant : Valid
+[wp] Proved goals:   19 / 27
+  Qed:             9 
+  Alt-Ergo:       10  (unsuccess: 8)
 ------------------------------------------------------------
  Functions                 WP     Alt-Ergo  Total   Success
   fact                      1        1        2       100%
   fails_fact                -        -        1       0.0%
   facto_gen                 -        1        1       100%
   fails_facto_gen           -        -        1       0.0%
-  fact_i                    1        2        3       100%
+  fact_i                    2        1        3       100%
   fails_fact_i              1        -        3      33.3%
   m2                        -        2        2       100%
   m1                        -        1        1       100%
@@ -54,4 +58,5 @@
   mt1                       1        1        3      66.7%
   mw2                       -        1        2      50.0%
   mw1                       -        -        1       0.0%
+  se                        1        -        1       100%
 ------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_acsl/oracle_qualif/terminates_formulae.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle_qualif/terminates_formulae.res.oracle
index cf90d39ba3fc26032ba408a35e80cd1c1badb88a..cb8634fd614c650bd783d59986c144dc533969fa 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle_qualif/terminates_formulae.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle_qualif/terminates_formulae.res.oracle
@@ -2,7 +2,9 @@
 [kernel] Parsing terminates_formulae.i (no preprocessing)
 [wp] Running WP plugin...
 [wp] Warning: Missing RTE guards
-[wp:hypothesis] terminates_formulae.i:90: Warning: 
+[wp:hypothesis] terminates_formulae.i:84: Warning: 
+  'Rel' relation must be well-founded
+[wp:hypothesis] terminates_formulae.i:67: Warning: 
   'Rel' relation must be well-founded
 [wp] [CFG] Goal general_variant_terminates : Valid (Trivial)
 [wp] [CFG] Goal variant_terminates : Valid (Trivial)
diff --git a/src/plugins/wp/tests/wp_bts/oracle/bts_2110.res.oracle b/src/plugins/wp/tests/wp_bts/oracle/bts_2110.res.oracle
index fe74a260b12b87a301e7cc4bbddfbdc4cc00bd2c..c8d3f169d3faade2e0c503923e59a7f7ccb141d4 100644
--- a/src/plugins/wp/tests/wp_bts/oracle/bts_2110.res.oracle
+++ b/src/plugins/wp/tests/wp_bts/oracle/bts_2110.res.oracle
@@ -59,19 +59,19 @@ theory Compound
   
   function shiftfield_F1_FD_pos (p:addr) : addr = shift p 0
   
-  Q_Load_S2_A_update_Mint0 :
+  axiom Q_Load_S2_A_update_Mint0 :
     forall mint:addr -> int, p:addr, q:addr, v:int
      [Load_S2_A p (set mint q v)].
      not q = p -> Load_S2_A p (set mint q v) = Load_S2_A p mint
   
-  Q_Load_S2_A_eqmem_Mint0 :
+  axiom Q_Load_S2_A_eqmem_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, n:int, p:addr, q:addr
      [Load_S2_A p mint, eqmem mint mint1 q n| Load_S2_A p mint1,
      eqmem mint mint1 q n].
      included p 1 q n ->
      eqmem mint mint1 q n -> Load_S2_A p mint1 = Load_S2_A p mint
   
-  Q_Load_S2_A_havoc_Mint0 :
+  axiom Q_Load_S2_A_havoc_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, n:int, p:addr, q:addr
      [Load_S2_A p (havoc mint1 mint q n)].
      separated p 1 q n ->
@@ -104,8 +104,8 @@ end
        let a3 = Load_S2_A a t in
        let a4 = Load_S2_A a (set (havoc t1 t a 1) a2 i) in
        not x = i ->
-       region (base a1) <= 0 ->
-       region (base a) <= 0 ->
+       region (a1.base) <= 0 ->
+       region (a.base) <= 0 ->
        is_sint32 i -> IsS2_A a3 -> is_sint32 x -> IsS2_A a4 -> EqS2_A a4 a3
   end
 [wp] 1 goal generated
diff --git a/src/plugins/wp/tests/wp_bts/oracle/issue_751.res.oracle b/src/plugins/wp/tests/wp_bts/oracle/issue_751.res.oracle
index 2e738afb004c169c4707afb644b505700e014741..8c36850543d3af2d512bfb7f864d20b326ef757c 100644
--- a/src/plugins/wp/tests/wp_bts/oracle/issue_751.res.oracle
+++ b/src/plugins/wp/tests/wp_bts/oracle/issue_751.res.oracle
@@ -24,17 +24,18 @@ Prove: true.
 Goal Loop assigns (file issue_751.i, line 8) (2/2):
 Effect at line 11
 Let x = land(3840, R).
-Let x_1 = j - 1.
+Let x_1 = lsr(x, 8).
+Let x_2 = j - 1.
 Assume {
-  Type: is_sint32(R) /\ is_sint32(j) /\ is_sint32(x_1).
+  Type: is_sint32(R) /\ is_sint32(j) /\ is_sint32(x_2) /\ is_sint32(x_1).
   (* Heap *)
   Type: (region(Data_0.base) <= 0) /\ linked(Malloc_0).
   (* Goal *)
-  When: !invalid(Malloc_0, shift_sint32(Data_0, x_1), 1).
+  When: !invalid(Malloc_0, shift_sint32(Data_0, x_2), 1).
   (* Pre-condition *)
   Have: (0 < x) /\ (x <= 2303).
   (* Invariant 'RANGE' *)
-  Have: (0 < j) /\ (j <= (1 + lsr(x, 8))).
+  Have: (0 < j) /\ (j <= (1 + x_1)).
   (* Then *)
   Have: j <= (x / 256).
 }
diff --git a/src/plugins/wp/tests/wp_plugin/oracle/inductive.res.oracle b/src/plugins/wp/tests/wp_plugin/oracle/inductive.res.oracle
index 973a39e45fa5af1ba55ed02d29a1bfe1666b0a7a..25cdbf437dc4817a633ae894932889549b4a583c 100644
--- a/src/plugins/wp/tests/wp_plugin/oracle/inductive.res.oracle
+++ b/src/plugins/wp/tests/wp_plugin/oracle/inductive.res.oracle
@@ -101,42 +101,42 @@ end
        P_reachable1 malloc mptr (get mptr (shiftfield_F1__list_next root)) node
     
     predicate P_same_array (mint:addr -> int) (mint1:addr -> int) (a:addr) (b:
-      addr) (begin:int) (end1:int) =
+      addr) (begin1:int) (end1:int) =
       forall i:int.
-       begin <= i ->
+       begin1 <= i ->
        i < end1 -> get mint1 (shift_sint32 a i) = get mint (shift_sint32 b i)
     
     predicate P_swap (mint:addr -> int) (mint1:addr -> int) (a:addr) (b:addr)
-      (begin:int) (i:int) (j:int) (end1:int) =
+      (begin1:int) (i:int) (j:int) (end1:int) =
       ((((get mint1 (shift_sint32 a i) = get mint (shift_sint32 b j) /\
           get mint1 (shift_sint32 a j) = get mint (shift_sint32 b i)) /\
-         begin <= i) /\
+         begin1 <= i) /\
         i < j) /\
        j < end1) /\
       (forall i1:int.
         not i1 = i ->
         not j = i1 ->
-        begin <= i1 ->
+        begin1 <= i1 ->
         i1 < end1 ->
         get mint1 (shift_sint32 a i1) = get mint (shift_sint32 b i1))
     
     inductive P_same_elements (addr -> int) (addr -> int) addr addr int int =
       | Q_refl :
-          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin:
+          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin1:
            int, end1:int.
-           P_same_array mint mint1 a b begin end1 ->
-           P_same_elements mint mint1 a b begin end1
+           P_same_array mint mint1 a b begin1 end1 ->
+           P_same_elements mint mint1 a b begin1 end1
       | Q_swap :
-          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin:
+          forall mint:addr -> int, mint1:addr -> int, a:addr, b:addr, begin1:
            int, i:int, j:int, end1:int.
-           P_swap mint mint1 a b begin i j end1 ->
-           P_same_elements mint mint1 a b begin end1
+           P_swap mint mint1 a b begin1 i j end1 ->
+           P_same_elements mint mint1 a b begin1 end1
       | Q_trans :
           forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, a:
-           addr, b:addr, c:addr, begin:int, end1:int.
-           P_same_elements mint mint1 b c begin end1 ->
-           P_same_elements mint1 mint2 a b begin end1 ->
-           P_same_elements mint mint2 a c begin end1
+           addr, b:addr, c:addr, begin1:int, end1:int.
+           P_same_elements mint mint1 b c begin1 end1 ->
+           P_same_elements mint1 mint2 a b begin1 end1 ->
+           P_same_elements mint mint2 a c begin1 end1
     
     goal wp_goal :
       forall t:addr -> int, t1:addr -> int, a:addr, a1:addr, i:int, i1:int, i2:
diff --git a/src/plugins/wp/tests/wp_plugin/oracle/model.res.oracle b/src/plugins/wp/tests/wp_plugin/oracle/model.res.oracle
index 70fd22d935e3729b93f16fb6b817a9b65b97d11a..29f604c34eaf0c3f72c10cc6cee353d6b77e173e 100644
--- a/src/plugins/wp/tests/wp_plugin/oracle/model.res.oracle
+++ b/src/plugins/wp/tests/wp_plugin/oracle/model.res.oracle
@@ -74,7 +74,7 @@ end
     goal wp_goal :
       forall t:addr -> int, i:int, a:addr.
        let x = get t (shift_sint32 a i) in
-       region (base a) <= 0 -> is_sint32 i -> is_sint32 x -> P_P x
+       region (a.base) <= 0 -> is_sint32 i -> is_sint32 x -> P_P x
   end
 [wp] 1 goal generated
 ------------------------------------------------------------
@@ -162,7 +162,7 @@ end
     goal wp_goal :
       forall t:addr1 -> int, i:int, a:addr1.
        let x = get1 t (shift_sint321 a i) in
-       region1 (base1 a) <=' 0 -> is_sint321 i -> is_sint321 x -> P_P1 x
+       region1 (a.base1) <=' 0 -> is_sint321 i -> is_sint321 x -> P_P1 x
   end
 [wp] 1 goal generated
 ------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_plugin/oracle_qualif/config.res.oracle b/src/plugins/wp/tests/wp_plugin/oracle_qualif/config.res.oracle
index fc2bd44fdd5bf6ac2dc4cfe36213ff7ff1950c6a..bdae904b176d5799a42a2c3f84f8b6190c311e27 100644
--- a/src/plugins/wp/tests/wp_plugin/oracle_qualif/config.res.oracle
+++ b/src/plugins/wp/tests/wp_plugin/oracle_qualif/config.res.oracle
@@ -2,5 +2,5 @@
 WP Requirements for Qualif Tests (3)
 ----------------------------------------------------------
 1. The Alt-Ergo theorem prover, version 2.2.0
-2. The Why3 platform, version 1.4.0
+2. The Why3 platform, version 1.5.0
 ----------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_plugin/oracle_qualif/model.res.oracle b/src/plugins/wp/tests/wp_plugin/oracle_qualif/model.res.oracle
index ff97bc71c534b5b33638bfe120f1c0de928f8e66..16921421e1bdbcb0a10f13b41d0179b4f67afae7 100644
--- a/src/plugins/wp/tests/wp_plugin/oracle_qualif/model.res.oracle
+++ b/src/plugins/wp/tests/wp_plugin/oracle_qualif/model.res.oracle
@@ -74,7 +74,7 @@ end
     goal wp_goal :
       forall t:addr -> int, i:int, a:addr.
        let x = get t (shift_sint32 a i) in
-       region (base a) <= 0 -> is_sint32 i -> is_sint32 x -> P_P x
+       region (a.base) <= 0 -> is_sint32 i -> is_sint32 x -> P_P x
   end
 [wp] [Alt-Ergo] Goal typed_f_ensures : Unsuccess
 [wp] Proved goals:    0 / 1
@@ -154,7 +154,7 @@ end
     goal wp_goal :
       forall t:addr1 -> int, i:int, a:addr1.
        let x = get1 t (shift_sint321 a i) in
-       region1 (base1 a) <=' 0 -> is_sint321 i -> is_sint321 x -> P_P1 x
+       region1 (a.base1) <=' 0 -> is_sint321 i -> is_sint321 x -> P_P1 x
   end
 [wp] [Alt-Ergo] Goal typed_ref_f_ensures : Unsuccess
 [wp] Proved goals:    0 / 1
diff --git a/src/plugins/wp/tests/wp_tip/oracle/clear.res.oracle b/src/plugins/wp/tests/wp_tip/oracle/clear.res.oracle
index 7af1209d3c472e1e717ba1dcdf4090b5f4b641ba..9c2a626fc58df6e504db7003ad19ac459f55babc 100644
--- a/src/plugins/wp/tests/wp_tip/oracle/clear.res.oracle
+++ b/src/plugins/wp/tests/wp_tip/oracle/clear.res.oracle
@@ -51,7 +51,7 @@
   typed_clear_in_step_check subgoal:
   
   Goal Wp.Tactical.typed_clear_in_step_check-0 (generated):
-  Assume { (* Filtered: P_P(...) *) Have: P_Q /\ P_R. }
+  Assume { (* Filtered: 'P_P(...)' *) Have: P_Q /\ P_R. }
   Prove: P_S(42).
   
   ------------------------------------------------------------
@@ -77,7 +77,7 @@
   typed_clear_in_step_check subgoal:
   
   Goal Wp.Tactical.typed_clear_in_step_check-1 (generated):
-  Assume { (* Filtered: P_Q(...) *) Have: P_R. }
+  Assume { (* Filtered: 'P_Q(...)' *) Have: P_R. }
   Prove: P_S(42).
   
   ------------------------------------------------------------
diff --git a/src/plugins/wp/tests/wp_typed/oracle/multi_matrix_types.res.oracle b/src/plugins/wp/tests/wp_typed/oracle/multi_matrix_types.res.oracle
index 324a789c9b982c3478ddd70178df49f3c1c4adc5..268995c9ffb2bf6b68baaf179a0d0a43b0f371ee 100644
--- a/src/plugins/wp/tests/wp_typed/oracle/multi_matrix_types.res.oracle
+++ b/src/plugins/wp/tests/wp_typed/oracle/multi_matrix_types.res.oracle
@@ -103,61 +103,61 @@ theory Compound
     (Array_uint32 (shiftfield_F1_S_a p) 5 mint)
     (Array_sint64 (shiftfield_F1_S_b p) 5 mint2)
   
-  Q_Array_uint32_access :
+  axiom Q_Array_uint32_access :
     forall mint:addr -> int, i:int, n:int, p:addr
      [get (Array_uint32 p n mint) i].
      0 <= i ->
      i < n -> get (Array_uint32 p n mint) i = get mint (shift_uint32 p i)
   
-  Q_Array_uint32_update_Mint0 :
+  axiom Q_Array_uint32_update_Mint0 :
     forall mint:addr -> int, n:int, p:addr, q:addr, v:int
      [Array_uint32 p n (set mint q v)].
      not q = p -> Array_uint32 p n (set mint q v) = Array_uint32 p n mint
   
-  Q_Array_uint32_eqmem_Mint0 :
+  axiom Q_Array_uint32_eqmem_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, n:int, n1:int, p:addr, q:addr
      [Array_uint32 p n mint, eqmem mint mint1 q n1| Array_uint32 p n mint1,
      eqmem mint mint1 q n1].
      included p 1 q n1 ->
      eqmem mint mint1 q n1 -> Array_uint32 p n mint1 = Array_uint32 p n mint
   
-  Q_Array_uint32_havoc_Mint0 :
+  axiom Q_Array_uint32_havoc_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, n:int, n1:int, p:addr, q:addr
      [Array_uint32 p n (havoc mint1 mint q n1)].
      separated p 1 q n1 ->
      Array_uint32 p n (havoc mint1 mint q n1) = Array_uint32 p n mint
   
-  Q_Array_sint64_access :
+  axiom Q_Array_sint64_access :
     forall mint:addr -> int, i:int, n:int, p:addr
      [get (Array_sint64 p n mint) i].
      0 <= i ->
      i < n -> get (Array_sint64 p n mint) i = get mint (shift_sint64 p i)
   
-  Q_Array_sint64_update_Mint0 :
+  axiom Q_Array_sint64_update_Mint0 :
     forall mint:addr -> int, n:int, p:addr, q:addr, v:int
      [Array_sint64 p n (set mint q v)].
      not q = p -> Array_sint64 p n (set mint q v) = Array_sint64 p n mint
   
-  Q_Array_sint64_eqmem_Mint0 :
+  axiom Q_Array_sint64_eqmem_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, n:int, n1:int, p:addr, q:addr
      [Array_sint64 p n mint, eqmem mint mint1 q n1| Array_sint64 p n mint1,
      eqmem mint mint1 q n1].
      included p 1 q n1 ->
      eqmem mint mint1 q n1 -> Array_sint64 p n mint1 = Array_sint64 p n mint
   
-  Q_Array_sint64_havoc_Mint0 :
+  axiom Q_Array_sint64_havoc_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, n:int, n1:int, p:addr, q:addr
      [Array_sint64 p n (havoc mint1 mint q n1)].
      separated p 1 q n1 ->
      Array_sint64 p n (havoc mint1 mint q n1) = Array_sint64 p n mint
   
-  Q_Load_S1_S_update_Mint0 :
+  axiom Q_Load_S1_S_update_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, p:addr, q:
      addr, v:int [Load_S1_S p (set mint q v) mint1 mint2].
      separated p 11 q 1 ->
      Load_S1_S p (set mint q v) mint1 mint2 = Load_S1_S p mint mint1 mint2
   
-  Q_Load_S1_S_eqmem_Mint0 :
+  axiom Q_Load_S1_S_eqmem_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, mint3:addr
      -> int, n:int, p:addr, q:addr [Load_S1_S p mint mint2 mint3,
      eqmem mint mint1 q n| Load_S1_S p mint1 mint2 mint3,
@@ -166,21 +166,21 @@ theory Compound
      eqmem mint mint1 q n ->
      Load_S1_S p mint1 mint2 mint3 = Load_S1_S p mint mint2 mint3
   
-  Q_Load_S1_S_havoc_Mint0 :
+  axiom Q_Load_S1_S_havoc_Mint0 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, mint3:addr
      -> int, n:int, p:addr, q:addr
      [Load_S1_S p (havoc mint1 mint q n) mint2 mint3].
      separated p 11 q n ->
-     Load_S1_S p (havoc mint1 mint q n) mint2 mint3
-     = Load_S1_S p mint mint2 mint3
+     Load_S1_S p (havoc mint1 mint q n) mint2 mint3 =
+     Load_S1_S p mint mint2 mint3
   
-  Q_Load_S1_S_update_Mint1 :
+  axiom Q_Load_S1_S_update_Mint1 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, p:addr, q:
      addr, v:int [Load_S1_S p mint2 (set mint1 q v) mint].
      separated p 11 q 1 ->
      Load_S1_S p mint2 (set mint1 q v) mint = Load_S1_S p mint2 mint1 mint
   
-  Q_Load_S1_S_eqmem_Mint1 :
+  axiom Q_Load_S1_S_eqmem_Mint1 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, mint3:addr
      -> int, n:int, p:addr, q:addr [Load_S1_S p mint3 mint1 mint,
      eqmem mint1 mint2 q n| Load_S1_S p mint3 mint2 mint,
@@ -189,21 +189,21 @@ theory Compound
      eqmem mint1 mint2 q n ->
      Load_S1_S p mint3 mint2 mint = Load_S1_S p mint3 mint1 mint
   
-  Q_Load_S1_S_havoc_Mint1 :
+  axiom Q_Load_S1_S_havoc_Mint1 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, mint3:addr
      -> int, n:int, p:addr, q:addr
      [Load_S1_S p mint3 (havoc mint2 mint1 q n) mint].
      separated p 11 q n ->
-     Load_S1_S p mint3 (havoc mint2 mint1 q n) mint
-     = Load_S1_S p mint3 mint1 mint
+     Load_S1_S p mint3 (havoc mint2 mint1 q n) mint =
+     Load_S1_S p mint3 mint1 mint
   
-  Q_Load_S1_S_update_Mint2 :
+  axiom Q_Load_S1_S_update_Mint2 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, p:addr, q:
      addr, v:int [Load_S1_S p mint1 mint (set mint2 q v)].
      separated p 11 q 1 ->
      Load_S1_S p mint1 mint (set mint2 q v) = Load_S1_S p mint1 mint mint2
   
-  Q_Load_S1_S_eqmem_Mint2 :
+  axiom Q_Load_S1_S_eqmem_Mint2 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, mint3:addr
      -> int, n:int, p:addr, q:addr [Load_S1_S p mint1 mint mint2,
      eqmem mint2 mint3 q n| Load_S1_S p mint1 mint mint3,
@@ -212,13 +212,13 @@ theory Compound
      eqmem mint2 mint3 q n ->
      Load_S1_S p mint1 mint mint3 = Load_S1_S p mint1 mint mint2
   
-  Q_Load_S1_S_havoc_Mint2 :
+  axiom Q_Load_S1_S_havoc_Mint2 :
     forall mint:addr -> int, mint1:addr -> int, mint2:addr -> int, mint3:addr
      -> int, n:int, p:addr, q:addr
      [Load_S1_S p mint1 mint (havoc mint3 mint2 q n)].
      separated p 11 q n ->
-     Load_S1_S p mint1 mint (havoc mint3 mint2 q n)
-     = Load_S1_S p mint1 mint mint2
+     Load_S1_S p mint1 mint (havoc mint3 mint2 q n) =
+     Load_S1_S p mint1 mint mint2
 end
 [wp:print-generated] 
   theory WP
@@ -246,7 +246,7 @@ end
       forall t:addr -> int, t1:addr -> int, t2:addr -> int, a:addr, i:int.
        let a1 = Load_S1_S a t t2 t1 in
        let a2 = Load_S1_S a t (set t2 (shiftfield_F1_S_f a) i) t1 in
-       region (base a) <= 0 -> IsS1_S a1 -> IsS1_S a2 -> EqS1_S a2 a1
+       region (a.base) <= 0 -> IsS1_S a1 -> IsS1_S a2 -> EqS1_S a2 a1
   end
 [wp] 1 goal generated
 ------------------------------------------------------------
diff --git a/tests/syntax/ast_diff_1.i b/tests/syntax/ast_diff_1.i
new file mode 100644
index 0000000000000000000000000000000000000000..4f5db3d6867a1bd94ba7e9073d5e5b384cdd4d78
--- /dev/null
+++ b/tests/syntax/ast_diff_1.i
@@ -0,0 +1,96 @@
+/* run.config
+   MODULE: @PTEST_NAME@
+     OPT: -then -ast-diff ast_diff_2.i
+*/
+int X;
+int Y=3;
+
+int f(int x) {
+  if (x <= 0)
+    X = 0;
+  else
+    X = x;
+  return X;
+}
+
+/*@ requires Y > 0;
+    ensures X > 0;
+    assigns X;
+*/
+int g() {
+  X = Y;
+  return X;
+}
+
+/*@ requires X > 0;
+    ensures X > 0;
+    assigns X;
+*/
+int h() {
+  if (Y > 0)
+    X = Y;
+  return Y;
+}
+
+/*@ requires \is_finite(x);
+    requires \is_finite(y);
+    assigns \nothing;
+    ensures \true != \false;
+*/
+int use_logic_builtin(double x, float y);
+
+int has_static_local(void) {
+  static int x = 0;
+  x++;
+  return x;
+}
+
+/*@ exits \exit_status == 1; */
+int decl(void);
+
+int used_in_decl;
+
+int decl() {
+  used_in_decl++;
+  return used_in_decl;
+}
+
+void (*ptr_func)(void);
+
+extern void i(void);
+
+void (*ptr_func)(void) = &i;
+
+/*@ type nat = Zero | Succ(nat); */
+
+/*@ logic nat succ(nat n) = Succ(n); */
+
+/*@ ensures succ(Zero) == Succ(Zero); */
+extern void i(void);
+
+void local_var_use(int v, int a[][v]) {
+  int x;
+  int y [sizeof(x)];
+}
+
+struct s;
+
+extern struct s s;
+
+enum e;
+
+struct s* use_s() { enum e* x; return &s; }
+
+void with_goto_changed(int c) {
+  if (c) goto L1;
+  X++;
+  L1: X++;
+  L2: X++;
+}
+
+void with_goto_unchanged(int c) {
+  if (c) goto L1;
+  X++;
+  L1: X++;
+  L2: X++;
+}
diff --git a/tests/syntax/ast_diff_1.ml b/tests/syntax/ast_diff_1.ml
new file mode 100644
index 0000000000000000000000000000000000000000..fd43df597f6eb2b8a04e96c5db8600afcd28a6f1
--- /dev/null
+++ b/tests/syntax/ast_diff_1.ml
@@ -0,0 +1,27 @@
+include Plugin.Register(
+    struct
+      let name = "AST diff test"
+      let shortname = "AST diff test"
+      let help = "Show results of AST diff computation"
+    end)
+
+let show_var vi c =
+  result "Variable %a: %a"
+    Cil_datatype.Varinfo.pretty vi
+    Ast_diff.Varinfo.pretty_data c
+
+let show_fun kf c =
+  result "Function %a: %a"
+    Kernel_function.pretty kf
+    Ast_diff.Kernel_function.pretty_data c
+
+let show_correspondances () =
+  if Kernel.AstDiff.get () then begin
+    result "Showing correspondances between %s and %s"
+      (Project.get_name (Ast_diff.Orig_project.get()))
+      (Project.get_name (Project.current()));
+    Ast_diff.Varinfo.iter show_var;
+    Ast_diff.Kernel_function.iter show_fun;
+  end
+
+let () = Db.Main.extend show_correspondances
diff --git a/tests/syntax/ast_diff_2.i b/tests/syntax/ast_diff_2.i
new file mode 100644
index 0000000000000000000000000000000000000000..9f6b60d8dce7a6a02858b0796b3ca74dd91cc289
--- /dev/null
+++ b/tests/syntax/ast_diff_2.i
@@ -0,0 +1,89 @@
+/* run.config
+   DONTRUN: main test is in ast_diff_1.i
+*/
+int X;
+int Y=4;
+
+int f(int x) {
+  if (x <= 0)
+    X = 0;
+  else
+    X = x;
+  return X;
+}
+
+/*@ requires Y > 0;
+    ensures X > 0;
+    assigns X;
+*/
+int g() {
+  X = Y;
+  return X;
+}
+
+/*@ requires X > 0;
+    ensures X > 0;
+    assigns X;
+*/
+int h() {
+  if (Y > 0)
+    X = Y;
+  return Y;
+}
+
+/*@ requires \is_finite(x);
+    requires \is_finite(y);
+    assigns \nothing;
+    ensures \true != \false;
+*/
+int use_logic_builtin(double x, float y);
+
+int has_static_local(void) {
+  static int y = 0;
+  y++;
+  return y;
+}
+
+int used_in_decl;
+
+/*@ exits \exit_status == 1; */
+int decl() {
+  used_in_decl++;
+  return used_in_decl;
+}
+
+extern void i(void);
+
+void (*ptr_func)(void) = &i;
+
+/*@ type nat = Zero | Succ(nat); */
+
+/*@ logic nat succ(nat n) = Succ(n); */
+
+/*@ ensures succ(Zero) == Succ(Zero); */
+extern void i(void);
+
+void local_var_use(int w, int q[][w]) {
+  int z;
+  int t [sizeof(z)];
+}
+
+extern struct s s;
+
+enum e;
+
+struct s* use_s() { enum e* x; return &s; }
+
+void with_goto_changed(int c) {
+  if (c) goto L2;
+  X++;
+  L1: X++;
+  L2: X++;
+}
+
+void with_goto_unchanged(int c) {
+  if (c) goto L1;
+  X++;
+  L1: X++;
+  L2: X++;
+}
diff --git a/tests/syntax/oracle/ast_diff_1.res.oracle b/tests/syntax/oracle/ast_diff_1.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..7bf1eac2f1efc9d09c809869b7562cc0b0fa57f1
--- /dev/null
+++ b/tests/syntax/oracle/ast_diff_1.res.oracle
@@ -0,0 +1,42 @@
+[kernel] Parsing ast_diff_1.i (no preprocessing)
+[kernel] Parsing ast_diff_2.i (no preprocessing)
+[AST diff test] Showing correspondances between orig_default and default
+[AST diff test] Variable i:  => i
+[AST diff test] Variable local_var_use:  => local_var_use
+[AST diff test] Variable v:  => w
+[AST diff test] Variable a:  => q
+[AST diff test] Variable x:  => z
+[AST diff test] Variable y:  => t
+[AST diff test] Variable s:  => s
+[AST diff test] Variable use_s:  => use_s
+[AST diff test] Variable x:  => x
+[AST diff test] Variable with_goto_changed:  => with_goto_changed
+[AST diff test] Variable X:  => X
+[AST diff test] Variable Y: N/A
+[AST diff test] Variable c:  => c
+[AST diff test] Variable f:  => f
+[AST diff test] Variable with_goto_unchanged:  => with_goto_unchanged
+[AST diff test] Variable x:  => x
+[AST diff test] Variable c:  => c
+[AST diff test] Variable g:  => g
+[AST diff test] Variable has_static_local_x:  => has_static_local_y
+[AST diff test] Variable __retres:  => __retres
+[AST diff test] Variable h:  => h
+[AST diff test] Variable use_logic_builtin:  => use_logic_builtin
+[AST diff test] Variable x:  => x
+[AST diff test] Variable y:  => y
+[AST diff test] Variable has_static_local:  => has_static_local
+[AST diff test] Variable decl:  => decl
+[AST diff test] Variable used_in_decl:  => used_in_decl
+[AST diff test] Variable ptr_func:  => ptr_func
+[AST diff test] Function i:  => i
+[AST diff test] Function local_var_use:  => local_var_use
+[AST diff test] Function use_s:  => use_s
+[AST diff test] Function with_goto_changed: -> with_goto_changed (body changed)
+[AST diff test] Function f:  => f
+[AST diff test] Function with_goto_unchanged:  => with_goto_unchanged
+[AST diff test] Function g: N/A
+[AST diff test] Function h: -> h (body changed)
+[AST diff test] Function use_logic_builtin:  => use_logic_builtin
+[AST diff test] Function has_static_local:  => has_static_local
+[AST diff test] Function decl:  => decl